Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/widget validation #86

Merged
merged 12 commits into from
Nov 13, 2019
27 changes: 3 additions & 24 deletions cogboard-webapp/src/components/AddBoard.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import styled from '@emotion/styled/macro';

import { useToggle } from '../hooks';
import { addNewBoard } from '../actions/thunks';
import { getIsAuthenticated } from '../selectors';

import { Button, IconButton } from '@material-ui/core';
import { IconButton } from '@material-ui/core';
import { Add } from '@material-ui/icons';
import AppDialog from './AppDialog';
import CancelButton from './CancelButton';
import BoardForm from './BoardForm';

const StyledCancelButton = styled(CancelButton)`
margin-left: 20px;
`;

const AddBoard = () => {
const [dialogOpened, openDialog, handleDialogClose] = useToggle();
const dispatch = useDispatch();
Expand Down Expand Up @@ -51,23 +45,8 @@ const AddBoard = () => {
title="Add new board"
>
<BoardForm
onSubmit={handleAddActionClick}
renderActions={() => (
<>
<Button
color="primary"
variant="contained"
type="submit"
data-cy="board-form-submit-button"
>
Add
</Button>
<StyledCancelButton
handleCancelClick={handleDialogClose}
data-cy="board-form-cancel-button"
/>
</>
)}
handleSubmit={handleAddActionClick}
handleCancel={handleDialogClose}
/>
</AppDialog>
</>
Expand Down
31 changes: 5 additions & 26 deletions cogboard-webapp/src/components/AddWidget.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,30 @@
import React from 'react';
import { func } from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import styled from '@emotion/styled/macro';

import { addNewWidget } from '../actions/thunks';

import Button from '@material-ui/core/Button';
import CancelButton from './CancelButton';
import WidgetForm from './WidgetForm';

const StyledCancelButton = styled(CancelButton)`
margin-left: 20px;
`;

const AddWidget = ({ closeDialog }) => {
const currentBoardId = useSelector(({ ui }) => ui.currentBoard);
const dispatch = useDispatch();

const handleAddClick = values => () => {
const handleAddWidget = ( values ) => {
dispatch(addNewWidget({ currentBoardId, values }));
closeDialog();
};

return (
<WidgetForm
renderActions={values => (
<>
<Button
onClick={handleAddClick(values)}
color="primary"
variant="contained"
data-cy="widget-form-submit-button"
>
Add
</Button>
<StyledCancelButton
handleCancelClick={closeDialog}
data-cy="widget-form-cancel-button"
/>
</>
)}
handleSubmit={handleAddWidget}
handleCancel={closeDialog}
/>
);
};

AddWidget.propTypes = {
closeDialog: func.isRequired
closeDialog: func.isRequired,
};

export default AddWidget;
export default AddWidget;
151 changes: 44 additions & 107 deletions cogboard-webapp/src/components/BoardForm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,121 +2,58 @@ import React from 'react';
import { useSelector } from 'react-redux';
import { string, number, bool } from 'prop-types';

import {
FormControl,
FormControlLabel,
Switch,
TextField
} from '@material-ui/core';
import { StyledFieldset, StyledValidationMessages } from './styled';
import { useFormData } from '../../hooks';
import { getBoards } from '../../selectors';
import { createValidationSchema } from './validators';
import { trimLeadingZeros } from '../../helpers';
import { createValidationSchema } from '../validation';

import NumberInput from '../widgets/dialogFields/NumberInput';
import { Button } from '@material-ui/core';
import DynamicForm from '../DynamicForm';
import { StyledCancelButton } from './styled';

const BoardForm = ({
onSubmit,
renderActions,
boardId,
...initialFormValues
}) => {
const boards = useSelector(getBoards);
const validationSchema = createValidationSchema(boardId, boards);
const { values, handleChange, handleSubmit, errors } = useFormData(
initialFormValues,
validationSchema,
true
);

const handleNumberInput = event => {
const {
target: { value }
} = event;
import { BOARD_TITLE_LENGTH_LIMIT, BOARD_COLUMNS_MIN, BOARD_COLUMNS_MAX, SWITCH_INTERVAL_MIN } from '../../constants';

event.target.value = trimLeadingZeros(value);
const BoardForm = ({ handleSubmit, handleCancel, boardId, ...initialFormValues }) => {
const boards = useSelector(getBoards);
const formFields = ['UniqueTitleField', 'ColumnField', 'AutoSwitchField', 'SwitchInterval'];
const constraints = {
'UniqueTitleField' : {
max: BOARD_TITLE_LENGTH_LIMIT,
boardId: boardId,
boards: boards,
},
'ColumnField': {
min: BOARD_COLUMNS_MIN,
max: BOARD_COLUMNS_MAX,
},
'SwitchInterval': {
min: SWITCH_INTERVAL_MIN,
}
};

const validationSchema = createValidationSchema(formFields, constraints);
const {values, handleChange, withValidation, errors} = useFormData(initialFormValues, {initialSchema: validationSchema, onChange: true});

return (
<form onSubmit={handleSubmit(onSubmit)} noValidate="novalidate">
<StyledFieldset component="fieldset">
<TextField
onChange={handleChange('title')}
id="title"
InputLabelProps={{
shrink: true
}}
label="Title"
margin="normal"
value={values.title}
error={errors.title !== undefined}
helperText={
<StyledValidationMessages
messages={errors.title}
data-cy={'board-form-title-error'}
/>
}
inputProps={{ 'data-cy': 'board-form-title-input' }}
/>
<NumberInput
onChange={handleChange('columns')}
onInput={handleNumberInput}
id="columns"
InputLabelProps={{
shrink: true
}}
inputProps={{ 'data-cy': 'board-form-columns-input' }}
label="Columns"
margin="normal"
value={values.columns}
error={errors.columns !== undefined}
FormHelperTextProps={{ component: 'div' }}
helperText={
<StyledValidationMessages
messages={errors.columns}
data-cy="board-form-columns-error"
/>
}
/>
<FormControl margin="normal">
<FormControlLabel
control={
<Switch
onChange={handleChange('autoSwitch')}
checked={values.autoSwitch}
color="primary"
value="autoSwitch"
inputProps={{ 'data-cy': 'board-form-auto-switch-checkbox' }}
/>
}
label="Auto switch"
/>
</FormControl>
{values.autoSwitch && (
<NumberInput
onChange={handleChange('switchInterval')}
onInput={handleNumberInput}
id="switchInterval"
InputLabelProps={{
shrink: true
}}
label="Switch interval [s]"
margin="normal"
value={values.switchInterval}
error={errors.switchInterval !== undefined}
FormHelperTextProps={{ component: 'div' }}
helperText={
<StyledValidationMessages
messages={errors.switchInterval}
data-cy="board-form-switch-interval-error"
/>
}
inputProps={{ 'data-cy': 'board-form-switch-interval-input' }}
/>
)}
</StyledFieldset>
{renderActions()}
<form onSubmit={withValidation(handleSubmit)} noValidate="novalidate">
<DynamicForm
fields={formFields}
values={values}
handleChange={handleChange}
errors={errors}
rootName='board-form'
/>
<Button
color="primary"
variant="contained"
type="submit"
data-cy="board-form-submit-button"
>
Save
</Button>
<StyledCancelButton
handleCancelClick={handleCancel}
data-cy="board-form-cancel-button"
/>
</form>
);
};
Expand Down
15 changes: 3 additions & 12 deletions cogboard-webapp/src/components/BoardForm/styled.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
import styled from '@emotion/styled/macro';

import { FormControl } from '@material-ui/core';
import ValidationMessages from '../ValidationMessages';
import CancelButton from '../CancelButton';

export const StyledFieldset = styled(FormControl)`
display: flex;
margin-bottom: 32px;
min-width: 300px;
`;

export const StyledValidationMessages = styled(ValidationMessages)`
list-style-type: none;
margin: 0;
padding: 0;
export const StyledCancelButton = styled(CancelButton)`
margin-left: 20px;
`;
48 changes: 0 additions & 48 deletions cogboard-webapp/src/components/BoardForm/validators.js

This file was deleted.

2 changes: 2 additions & 0 deletions cogboard-webapp/src/components/DropdownField.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const DropdownField = props => {
children,
dropdownItems,
itemsUrl,
dataCy,
...other
} = props;
const initialLoaded = !itemsUrl;
Expand Down Expand Up @@ -53,6 +54,7 @@ const DropdownField = props => {
input={<Input name={name} id={id} />}
name={name}
SelectDisplayProps={other}
data-cy={dataCy}
>
{loaded && children(options)}
</Select>
Expand Down
25 changes: 25 additions & 0 deletions cogboard-webapp/src/components/DynamicForm/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { splitPropsGroupName } from '../../helpers';

export const camelToKebab = ( string ) => string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();

export const createValueRef = (values, initialValue, name) => {
const [groupName, propName] = splitPropsGroupName(name);

if (groupName) {
if (!values[groupName]) {
values[groupName] = {};
}

if(values[groupName][propName] === undefined) {
values[groupName][propName] = initialValue;
}

return values[groupName][propName];
}

if (values[propName] === undefined) {
values[propName] = initialValue;
}

return values[propName];
};
Loading