Compare commits

...

10 commits

Author SHA1 Message Date
Daniel Garcia
f9d6024d6a Add value parameter to AutoComplete component 2021-09-20 16:41:09 +02:00
Daniel Garcia
712f92d5f5 Change gitignore 2021-09-09 16:10:17 +02:00
Daniel Garcia
f7c162a866 Refactor the library 2021-09-09 15:27:26 +02:00
Daniel Garcia
0386054842 WIP - 21.08.31 2021-08-31 22:31:08 +02:00
Daniel Garcia
d4e422354d Fix minor error on README.md 2021-06-21 16:44:07 +02:00
Daniel Garcia
dadd5358cc Add README.md 2021-06-21 16:06:39 +02:00
Daniel Garcia
d5100b3fb9 Update library version 2021-06-11 10:09:10 +02:00
Daniel Garcia
ac5ca8cc9a Upgrade uploadFile component 2021-06-11 08:27:16 +02:00
Daniel Garcia
07c005a291 Add folders to .gitignore 2021-03-17 17:41:14 +01:00
dgarcia
80c50d08d1 Export remove function to unmount react components 2020-03-18 11:04:58 +01:00
25 changed files with 8770 additions and 5514 deletions

View file

@ -1,9 +1,3 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": [
[
"@babel/plugin-proposal-class-properties",
{"loose": true}
]
]
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

55
.eslintrc.js Normal file
View file

@ -0,0 +1,55 @@
module.exports = {
// 'plugins': ['jest'],
'env': {
'commonjs': true,
'es6': true,
'node': true,
// 'jest/globals': true,
},
'extends': [
'eslint:recommended',
'plugin:react/recommended',
],
'globals': {
'Atomics': 'readonly',
'SharedArrayBuffer': 'readonly',
},
'parserOptions': {
'ecmaVersion': 2018,
'sourceType': 'module',
},
'rules': {
'indent': [
'error',
2,
],
'linebreak-style': [
'error',
'unix',
],
'quotes': [
'error',
'single',
{ 'avoidEscape': true },
],
'semi': [
'error',
'never',
],
'comma-dangle': [
'error',
'always-multiline',
],
'arrow-parens': [
'error',
'as-needed',
],
'no-var': [
'error',
],
'prefer-const': ['error', {
'destructuring': 'all',
'ignoreReadBeforeAssign': true,
}],
},
}

3
.gitignore vendored
View file

@ -1 +1,2 @@
node_modules/
/node_modules/
/.idea/

View file

@ -1,10 +1,21 @@
# MCF-Components-Lib
# MCF-Components
This library exports an unique method to render components using *React* and *Material-UI* through a javascript object where is placed all
the information needed.
Se trata de una biblioteca de componentes de basada en `react` y `material-ui`. Con ella se pueden usar estas bibliotecas definiendo los componentes mediante objetos javascript planos.
The method is **render**
## Entorno de desarrollo
## Installation
La biblioteca está escrita usando `react` y `material-ui`.
To install the library you can get the repository or install it as a dependency.
Se transpila usando `babel` y se compila con `webpack`.
## Arquitectura
El código de la aplicación se encuentra condensado en el archivo `index.js`, el cual exporta los métodos `render` y `remove`.
El método `render` genera toda la estructura de componentes react a partir de la definición que se le pasa como primer argumento.
Hace falta un segundo argumento: el ID del nodo HTML dentro del cual se quiere generar los componentes.
La definición es pasada a una factoría de componentes que es quien se encarga de formar de manera recursiva todos los elementos.
El método `remove` permite eliminar la estructura de componentes creada en un nodo HTML.

File diff suppressed because one or more lines are too long

74
dist/mcf-components.js.LICENSE.txt vendored Normal file
View file

@ -0,0 +1,74 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/**
* A better abstraction over CSS.
*
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
* @website https://github.com/cssinjs/jss
* @license MIT
*/
/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.12.0
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**!
* @fileOverview Kickass library to create and place poppers near their reference elements.
* @version 1.16.1-lts
* @license
* Copyright (c) 2016 Federico Zivolo and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

File diff suppressed because one or more lines are too long

12423
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,34 +1,34 @@
{
"name": "mcf-components",
"version": "0.1.1",
"description": "Library to render component templates pre-created using React and Material-UI",
"version": "0.2.0",
"description": "Library to render component templates created using React and Material-UI",
"main": "dist/mcf-components.js",
"scripts": {
"start": "webpack-dev-server --open --config webpack.dev.js",
"dist": "webpack --config webpack.prod.js",
"test": "jest",
"test:watch": "jest --watch"
},
"devDependencies": {
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/preset-env": "^7.7.6",
"@babel/preset-react": "^7.7.4",
"babel-loader": "^8.0.6",
"css-loader": "^3.4.0",
"jest": "^24.9.0",
"mini-css-extract-plugin": "^0.8.1",
"style-loader": "^1.0.2",
"webpack": "^4.41.3",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0",
"webpack-merge": "^4.2.2"
"@babel/core": "^7.15.0",
"@babel/plugin-proposal-class-properties": "^7.14.5",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/runtime": "^7.14.8",
"babel-loader": "^8.2.2",
"eslint": "^7.32.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"jest": "^27.0.6",
"webpack": "^5.49.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2",
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@material-ui/core": "4.3.3",
"prop-types": "15.7.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-select": "^3.0.8"
"@material-ui/core": "^4.12.3",
"@material-ui/lab": "^4.0.0-alpha.60",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-select": "^4.3.1"
}
}

49
src/ComponentFactory.js Normal file
View file

@ -0,0 +1,49 @@
import React from 'react'
import MCF_Autocomplete from './components/MCF_AutoComplete'
import MCF_Button from './components/MCF_Button'
import MCF_Card from './components/MCF_Card'
import MCF_Checkbox from './components/MCF_Checkbox'
import MCF_CheckboxesGroup from './components/MCF_CheckboxesGroup'
import MCF_Dialog from './components/MCF_Dialog'
import MCF_Divider from './components/MCF_Divider'
import MCF_FileUpload from './components/MCF_FileUpload'
import MCF_MediaCard from './components/MCF_MediaCard'
import MCF_Select from './components/MCF_Select'
import MCF_Text from './components/MCF_Text'
export default class ComponentFactory {
static createComponent(componentConfig) {
const params = componentConfig.params
params.key = params.parentKey !== undefined ? params.parentKey + params.id : params.id
switch (componentConfig.type) {
case 'autocomplete':
return <MCF_Autocomplete key={params.key} params={params}/>
case 'button':
return <MCF_Button key={params.key} params={params}/>
case 'card':
return <MCF_Card key={params.key} params={params}/>
case 'checkbox':
return <MCF_Checkbox key={params.key} params={params}/>
case 'checkboxesGroup':
return <MCF_CheckboxesGroup key={params.key} params={params}/>
case 'dialog':
return <MCF_Dialog key={params.key} params={params}/>
case 'divider':
return <MCF_Divider key={params.key} params={params}/>
case 'fileUpload':
return <MCF_FileUpload key={params.key} params={params}/>
case 'mediaCard':
return <MCF_MediaCard key={params.key} params={params}/>
case 'select':
return <MCF_Select key={params.key} params={params}/>
case 'text':
return <MCF_Text key={params.key} params={params}/>
default:
throw new Error(`MCF-Components ==> The component type ${componentConfig.type} does not exist. Check the component config with id ${componentConfig.params.id}`)
}
}
}

View file

@ -0,0 +1,38 @@
/* eslint-disable react/prop-types */
import React, { useState } from 'react'
import Autocomplete from '@material-ui/lab/Autocomplete'
import TextField from '@material-ui/core/TextField'
export default function MCF_Autocomplete (props) {
const [params, setParams] = useState(props.params)
const handleChange = (event, newValue) => {
setParams({...params, value: newValue})
if (params.onChangeListener) {
params.onChangeListener(newValue)
}
}
return (
<Autocomplete
id={params.id} // string
value={params.value} // an option from params.options
style={{width: params.width}} // number
options={params.options} // options to select
defaultValue={params.defaultValue} // an option from params.options
getOptionLabel={option => option.key}
onChange={handleChange}
renderInput={parameters => <TextField
{...parameters}
label={params.label} // string
variant={params.variant} // 'filled' | 'outlined' | 'standard'
margin={params.margin} // 'dense' | 'none' | 'normal'
/>}
/>
)
}

View file

@ -0,0 +1,27 @@
/* eslint-disable react/prop-types */
import React, {useState} from 'react'
import Button from '@material-ui/core/Button'
import { useStyles } from '../index'
export default function MCF_Button (props) {
const [params, setParams] = useState(props.params)
const classes = useStyles()
return (
<Button
className={classes.button}
color={params.color} // 'default' | 'inherit' | 'primary' | 'secondary'
size={params.size} // 'large' | 'medium' | 'small'
variant={params.variant} // 'contained' | 'outlined' | 'text'
disabled={params.disabled} // boolean
onClick={params.onClickListener}
>
{params.text/* string */}
</Button>
)
}

View file

@ -0,0 +1,36 @@
/* eslint-disable react/prop-types */
import React, {useState} from 'react'
import Card from '@material-ui/core/Card'
import CardContent from '@material-ui/core/CardContent'
import CardHeader from '@material-ui/core/CardHeader'
import ComponentFactory from '../ComponentFactory'
import {useStyles} from '../index'
export default function MCF_Card (props) {
const [params, setParams] = useState(props.params)
const classes = useStyles()
const renderContents = () => {
return params.contents.map(contentConfig => {
contentConfig.params.parentKey = params.key
return ComponentFactory.createComponent(contentConfig)
})
}
return (
<Card className={classes.card}>
<CardHeader
title={params.mainText} // string
subheader={params.secondaryText} // string
/>
<CardContent>
{renderContents()}
</CardContent>
</Card>
)
}

View file

@ -0,0 +1,32 @@
/* eslint-disable react/prop-types */
import React, {useState} from 'react'
import Checkbox from '@material-ui/core/Checkbox'
import FormControlLabel from '@material-ui/core/FormControlLabel'
export default function MCF_Checkbox (props) {
const [params, setParams] = useState(props.params)
const handleChange = event => {
params.onChangeListener(params.id, event.target.checked)
setParams({...params, checked: event.target.checked})
}
return (
<FormControlLabel
id={params.id} // string
key={params.id}
control={
<Checkbox
checked={params.checked} // boolean
onChange={handleChange}
name={params.id}
/>
}
label={params.label} // string
/>
)
}

View file

@ -0,0 +1,59 @@
/* eslint-disable react/prop-types */
import React, {useState} from 'react'
import FormControl from '@material-ui/core/FormControl'
import FormGroup from '@material-ui/core/FormGroup'
import FormHelperText from '@material-ui/core/FormHelperText'
import FormLabel from '@material-ui/core/FormLabel'
import ComponentFactory from '../ComponentFactory'
import {useStyles} from '../index'
export default function MCF_CheckboxesGroup (props) {
const [params, setParams] = useState(props.params)
const classes = useStyles()
params.checkboxesState = {}
params.checkboxes.map(checkbox => {
params.checkboxesState[checkbox.id] = checkbox.isChecked
})
const handleChange = (checkboxID, checkboxValue) => {
params.onChangeListener(checkboxID, checkboxValue)
params.checkboxesState[checkboxID] = checkboxValue
setParams(params)
}
const createCheckboxes = () => {
return (
params.checkboxes.map(checkbox => {
const checkboxConfig = {
type: 'checkbox',
params: {
id: checkbox.id, // string
checked: checkbox.checked, // boolean
label: checkbox.label, // string
onChangeListener: handleChange,
},
}
return ComponentFactory.createComponent(checkboxConfig)
})
)
}
return (
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">{params.label}</FormLabel>
<FormHelperText>{params.helperText/* string */}</FormHelperText>
<FormGroup
id={params.id} // string
row={params.isRow} // boolean
>
{createCheckboxes()}
</FormGroup>
</FormControl>
)
}

View file

@ -0,0 +1,88 @@
/* eslint-disable react/prop-types */
import React, {useState} from 'react'
import Button from '@material-ui/core/Button'
import Dialog from '@material-ui/core/Dialog'
import DialogActions from '@material-ui/core/DialogActions'
import DialogContent from '@material-ui/core/DialogContent'
import DialogContentText from '@material-ui/core/DialogContentText'
import DialogTitle from '@material-ui/core/DialogTitle'
import ComponentFactory from '../ComponentFactory'
export default function MCF_Dialog (props) {
const [params, setParams] = useState(props.params)
const handleOpen = () => {
setParams({...params, open: true})
if (params.openListener) {
params.openListener()
}
}
const handleClose = () => {
setParams({...params, open: false})
if (params.closeListener) {
params.closeListener()
}
}
const renderContents = () => {
return params.contents.map(contentParams => {
contentParams.params.parentParams = params
contentParams.params.parentParams.handleClose = handleClose
contentParams.params.parentParams.handleClickOpen = handleOpen
contentParams.params.parentKey = params.key
contentParams.setParentParams = setParams
return ComponentFactory.createComponent(contentParams)
})
}
const renderActionContents = () => {
return params.actionContents.map(contentParams => {
contentParams.params.parentParams = params
contentParams.params.parentParams.handleClose = handleClose
contentParams.params.parentParams.handleClickOpen = handleOpen
contentParams.params.parentKey = params.key
contentParams.setParentParams = setParams
return ComponentFactory.createComponent(contentParams)
})
}
return (
<div>
<Button
variant={params.openButtonVariant} // 'contained' | 'outlined' | 'text'
color={params.openButtonColor} // 'default' | 'inherit' | 'primary' | 'secondary'
onClick={handleOpen}>
{params.openButtonText/* string */}
</Button>
<Dialog
open={params.open} // boolean
onClose={handleClose}
aria-labelledby={params.id}
>
<DialogTitle id={params.id/* string */}>
{params.titleText/* string */}
</DialogTitle>
<DialogContent>
<DialogContentText>
{params.contentText/* string */}
</DialogContentText>
{renderContents()}
</DialogContent>
<DialogActions>
{renderActionContents()}
<Button
variant={params.closeButtonVariant} // 'contained' | 'outlined' | 'text'
color={params.closeButtonColor} // 'default' | 'inherit' | 'primary' | 'secondary'
onClick={handleClose}>
{params.closeButtonText/* string */}
</Button>
</DialogActions>
</Dialog>
</div>
)
}

View file

@ -0,0 +1,13 @@
import React from 'react'
import {useStyles} from '../index'
export default function MCF_Divider () {
const classes = useStyles()
return (
<div className={classes.divider}/>
)
}

View file

@ -0,0 +1,77 @@
/* eslint-disable react/prop-types */
import React, {useState, useEffect} from 'react'
import Button from '@material-ui/core/Button'
import {useStyles} from '../index'
export default function MCF_FileUpload (props) {
const [params, setParams] = useState(props.params)
const classes = useStyles()
if (params.acceptedFormats === undefined) params.acceptedFormats = '*!/!*'
useEffect(() => {
const input = document.getElementById(params.id)
input.addEventListener('change', event => {
event.preventDefault()
const files = document.querySelector('input[type=file]').files
if (files.length > 0) {
const formData = new FormData()
formData.append('tkn', params.token)
if (params.storagePath) formData.append('path', params.storagePath)
for (let i = 0; i < files.length; i++) {
formData.append('files[]', files[i])
}
if (params.beforeFetchListener) params.beforeFetchListener(files)
fetch(params.url, {
method: 'POST',
body: formData,
}).then(response => {
if (response) params.files = files
handleUploadSuccess(response)
})
.catch(error => handleUploadError(error))
}
})
}, [])
const handleUploadSuccess = response => {
if (params.onUpload) params.onUpload(response, params.files)
setParams(params)
}
const handleUploadError = error => {
if (params.onError) params.onError(error)
params.files = null
}
return (
<div>
<input
id={params.id} // string
className={classes.input}
accept={params.acceptedFormats} // strings array
multiple
type="file"
/>
<label htmlFor={params.id}>
<Button
className={classes.button}
color={params.color} // 'default' | 'inherit' | 'primary' | 'secondary'
size={params.size} // 'large' | 'medium' | 'small'
variant={params.variant} // 'contained' | 'outlined' | 'text'
component="span">
{params.text}
</Button>
</label>
</div>
)
}

View file

@ -0,0 +1,43 @@
/* eslint-disable react/prop-types */
import React, {useState} from 'react'
import Card from '@material-ui/core/Card'
import CardContent from '@material-ui/core/CardContent'
import CardHeader from '@material-ui/core/CardHeader'
import CardMedia from '@material-ui/core/CardMedia'
import ComponentFactory from '../ComponentFactory'
import {useStyles} from '../index'
export default function MCF_MediaCard (props) {
const [params, setParams] = useState(props.params)
const classes = useStyles()
const renderContents = () => {
return params.contents.map(contentConfig => {
contentConfig.params.parentKey = params.key
return ComponentFactory.createComponent(contentConfig)
})
}
return (
<Card className={classes.card}>
<CardHeader
title={params.mainText} // string
subheader={params.secondaryText} // string
/>
<CardMedia
className={classes.media}
image={params.imageURL} // string: URL
title={params.imageTitle} // string
alt={params.imageAlt} // string
/>
<CardContent>
{renderContents()}
</CardContent>
</Card>
)
}

View file

@ -0,0 +1,72 @@
/* eslint-disable react/prop-types */
import React, {useState} from 'react'
import FormControl from '@material-ui/core/FormControl'
import FormHelperText from '@material-ui/core/FormHelperText'
import Input from '@material-ui/core/Input'
import InputLabel from '@material-ui/core/InputLabel'
import MenuItem from '@material-ui/core/MenuItem'
import Select from '@material-ui/core/Select'
import {useStyles} from '../index'
export default function MCF_Select (props) {
const [params, setParams] = useState(props.params)
const classes = useStyles()
const handleInteraction = event => {
const option = params.options.find(option => option.value === event.target.value)
params.value = option.value
if (params.onSelectListener) params.onSelectListener(option.id, option.value)
setParams(params)
}
const createList = () => {
return (
params.options.map(option => {
return (
<MenuItem
key={option.id} // string
value={option.value}
>
{option.label/*string*/}
</MenuItem>
)
})
)
}
return (
<FormControl
className={classes.formControl}
disabled={params.disabled} // boolean
error={params.error} // boolean
required={params.required} // boolean
>
<InputLabel
shrink={params.shrink} // boolean
htmlFor={params.id}
>
{params.label}
</InputLabel>
<Select
value={params.value}
displayEmpty={params.displayEmpty} // boolean
autoWidth={params.autoWidth} // boolean
onChange={handleInteraction}
input={<Input
name={params.id}
id={params.id} // string
readOnly={params.readOnly} // boolean
/>}
>
{createList()}
</Select>
<FormHelperText>
{params.helperText/* string */}
</FormHelperText>
</FormControl>
)
}

View file

@ -0,0 +1,51 @@
/* eslint-disable react/prop-types */
import React, {useState} from 'react'
import TextField from '@material-ui/core/TextField'
import {useStyles} from '../index'
export default function MCF_Text (props) {
const [params, setParams] = useState(props.params)
const classes = useStyles()
const handleChange = event => {
const setError = isError => {
setParams({...params, value: event.target.value, error: isError})
}
setParams({...params, value: event.target.value})
if (params.onChangeListener){
params.onChangeListener(event.target.value, setError)
}
}
return (
<form className={classes.container} noValidate autoComplete="off">
<TextField
id={params.id} // string
label={params.label} // string
className={classes.textField}
value={params.value}
onChange={handleChange}
margin={params.margin} // 'dense' | 'none' | 'normal'
required={params.required} // boolean
error={params.error} // boolean
disabled={params.disabled} // boolean
placeholder={params.placeholder} // string
helperText={params.helperText} // string
fullWidth={params.fullWidth} // boolean
variant={params.variant} // 'filled' | 'outlined' | 'standard'
type={params.type} // HTML input type
InputLabelProps={{
shrink: params.shrink, // boolean
}}
/>
</form>
)
}

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
const path = require('path');
const path = require('path')
module.exports = {
entry: './src/index.js',
@ -6,7 +6,7 @@ module.exports = {
path: path.resolve(__dirname, 'dist'),
filename: 'mcf-components.js',
library: 'mcfComponents',
libraryTarget: 'umd'
libraryTarget: 'umd',
},
module: {
rules: [
@ -19,4 +19,4 @@ module.exports = {
},
],
},
};
}

View file

@ -1,5 +1,5 @@
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const {merge} = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'development',
@ -7,4 +7,4 @@ module.exports = merge(common, {
devServer: {
contentBase: './dist',
},
});
})

View file

@ -1,7 +1,7 @@
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const {merge} = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
});
})