Skip to content

Commit

Permalink
fix(material): Improve usability of date renderers
Browse files Browse the repository at this point in the history
Previously, data wasn't cleared when the input field was emptied.
This commit will set the value to 'undefined' when the input is empty or
invalid upon blurring. During editing, the data is only updated when the
current input is valid.

Closes #2183
  • Loading branch information
LukasBoll committed Dec 4, 2023
1 parent da3321b commit 53b5a73
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 19 deletions.
29 changes: 26 additions & 3 deletions packages/material-renderers/src/controls/MaterialDateControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
THE SOFTWARE.
*/
import merge from 'lodash/merge';
import React, { useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import {
ControlProps,
isDateControl,
Expand All @@ -35,7 +35,12 @@ import { withJsonFormsControlProps } from '@jsonforms/react';
import { FormHelperText, Hidden } from '@mui/material';
import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { createOnChangeHandler, getData, useFocus } from '../util';
import {
createOnBlurHandler,
createOnChangeHandler,
getData,
useFocus,
} from '../util';

export const MaterialDateControl = (props: ControlProps) => {
const [focused, onFocus, onBlur] = useFocus();
Expand All @@ -62,6 +67,8 @@ export const MaterialDateControl = (props: ControlProps) => {
appliedUiSchemaOptions.showUnfocusedDescription
);

const [key, setKey] = useState<number>(0);

const format = appliedUiSchemaOptions.dateFormat ?? 'YYYY-MM-DD';
const saveFormat = appliedUiSchemaOptions.dateSaveFormat ?? 'YYYY-MM-DD';

Expand All @@ -73,17 +80,33 @@ export const MaterialDateControl = (props: ControlProps) => {
? errors
: null;
const secondFormHelperText = showDescription && !isValid ? errors : null;

const updateChild = useCallback(() => setKey((key) => key + 1), []);

const onChange = useMemo(
() => createOnChangeHandler(path, handleChange, saveFormat),
[path, handleChange, saveFormat]
);

const onBlurHandler = useMemo(
() =>
createOnBlurHandler(
path,
handleChange,
format,
saveFormat,
updateChild,
onBlur
),
[path, handleChange, format, saveFormat, updateChild]
);
const value = getData(data, saveFormat);

return (
<Hidden xsUp={!visible}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
key={key}
label={label}
value={value}
onChange={onChange}
Expand All @@ -109,7 +132,7 @@ export const MaterialDateControl = (props: ControlProps) => {
},
InputLabelProps: data ? { shrink: true } : undefined,
onFocus: onFocus,
onBlur: onBlur,
onBlur: onBlurHandler,
},
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
import React, { useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import merge from 'lodash/merge';
import {
ControlProps,
Expand All @@ -35,7 +35,12 @@ import { withJsonFormsControlProps } from '@jsonforms/react';
import { FormHelperText, Hidden } from '@mui/material';
import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { createOnChangeHandler, getData, useFocus } from '../util';
import {
createOnBlurHandler,
createOnChangeHandler,
getData,
useFocus,
} from '../util';

export const MaterialDateTimeControl = (props: ControlProps) => {
const [focused, onFocus, onBlur] = useFocus();
Expand Down Expand Up @@ -66,6 +71,8 @@ export const MaterialDateTimeControl = (props: ControlProps) => {
const format = appliedUiSchemaOptions.dateTimeFormat ?? 'YYYY-MM-DD HH:mm';
const saveFormat = appliedUiSchemaOptions.dateTimeSaveFormat ?? undefined;

const [key, setKey] = useState<number>(0);

const views = appliedUiSchemaOptions.views ?? [
'year',
'day',
Expand All @@ -80,17 +87,32 @@ export const MaterialDateTimeControl = (props: ControlProps) => {
: null;
const secondFormHelperText = showDescription && !isValid ? errors : null;

const updateChild = useCallback(() => setKey((key) => key + 1), []);

const onChange = useMemo(
() => createOnChangeHandler(path, handleChange, saveFormat),
[path, handleChange, saveFormat]
);

const onBlurHandler = useMemo(
() =>
createOnBlurHandler(
path,
handleChange,
format,
saveFormat,
updateChild,
onBlur
),
[path, handleChange, format, saveFormat, updateChild]
);
const value = getData(data, saveFormat);

return (
<Hidden xsUp={!visible}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DateTimePicker
key={key}
label={label}
value={value}
onChange={onChange}
Expand All @@ -117,7 +139,7 @@ export const MaterialDateTimeControl = (props: ControlProps) => {
},
InputLabelProps: data ? { shrink: true } : undefined,
onFocus: onFocus,
onBlur: onBlur,
onBlur: onBlurHandler,
},
}}
/>
Expand Down
29 changes: 26 additions & 3 deletions packages/material-renderers/src/controls/MaterialTimeControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
import React, { useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import merge from 'lodash/merge';
import {
ControlProps,
Expand All @@ -35,7 +35,12 @@ import { withJsonFormsControlProps } from '@jsonforms/react';
import { FormHelperText, Hidden } from '@mui/material';
import { TimePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { createOnChangeHandler, getData, useFocus } from '../util';
import {
createOnBlurHandler,
createOnChangeHandler,
getData,
useFocus,
} from '../util';

export const MaterialTimeControl = (props: ControlProps) => {
const [focused, onFocus, onBlur] = useFocus();
Expand All @@ -56,6 +61,8 @@ export const MaterialTimeControl = (props: ControlProps) => {
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const isValid = errors.length === 0;

const [key, setKey] = useState<number>(0);

const showDescription = !isDescriptionHidden(
visible,
description,
Expand All @@ -75,16 +82,32 @@ export const MaterialTimeControl = (props: ControlProps) => {
: null;
const secondFormHelperText = showDescription && !isValid ? errors : null;

const updateChild = useCallback(() => setKey((key) => key + 1), []);

const onChange = useMemo(
() => createOnChangeHandler(path, handleChange, saveFormat),
[path, handleChange, saveFormat]
);

const onBlurHandler = useMemo(
() =>
createOnBlurHandler(
path,
handleChange,
format,
saveFormat,
updateChild,
onBlur
),
[path, handleChange, format, saveFormat, updateChild]
);
const value = getData(data, saveFormat);

return (
<Hidden xsUp={!visible}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<TimePicker
key={key}
label={label}
value={value}
onChange={onChange}
Expand All @@ -111,7 +134,7 @@ export const MaterialTimeControl = (props: ControlProps) => {
},
InputLabelProps: data ? { shrink: true } : undefined,
onFocus: onFocus,
onBlur: onBlur,
onBlur: onBlurHandler,
},
}}
/>
Expand Down
27 changes: 24 additions & 3 deletions packages/material-renderers/src/util/datejs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,31 @@ export const createOnChangeHandler =
(time: dayjs.Dayjs) => {
if (!time) {
handleChange(path, undefined);
return;
} else if (time.toString() !== 'Invalid Date') {
const result = dayjs(time).format(saveFormat);
handleChange(path, result);
}
const result = dayjs(time).format(saveFormat);
handleChange(path, result);
};

export const createOnBlurHandler =
(
path: string,
handleChange: (path: string, value: any) => void,
format: string | undefined,
saveFormat: string | undefined,
rerenderChild: () => void,
onBlur: () => void
) =>
(e: React.FocusEvent<HTMLTextAreaElement | HTMLInputElement, Element>) => {
const date = e.target.value;
const formatedValue = dayjs(date, format);
if (formatedValue.toString() === 'Invalid Date') {
handleChange(path, undefined);
rerenderChild();
} else {
handleChange(path, dayjs(formatedValue).format(saveFormat));
}
onBlur();
};

export const getData = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ describe('Material date control', () => {
);
const input = wrapper.find('input').first();
(input.getDOMNode() as HTMLInputElement).value = '1961-04-12';
input.simulate('change', input);
input.simulate('blur', input);
expect(onChangeData.data.foo).toBe('1961-04-12');
});

Expand Down Expand Up @@ -421,7 +421,7 @@ describe('Material date control', () => {
expect(input.props().value).toBe('1980/06');

(input.getDOMNode() as HTMLInputElement).value = '1961/04';
input.simulate('change', input);
input.simulate('blur', input);
expect(onChangeData.data.foo).toBe('04---1961');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ describe('Material date time control', () => {
);
const input = wrapper.find('input').first();
(input.getDOMNode() as HTMLInputElement).value = '1961-12-12 20:15';
input.simulate('change', input);
input.simulate('blur', input);
expect(onChangeData.data.foo).toBe(dayjs('1961-12-12 20:15').format());
});

Expand Down Expand Up @@ -427,7 +427,7 @@ describe('Material date time control', () => {
expect(input.props().value).toBe('23-04-80 01:37:pm');

(input.getDOMNode() as HTMLInputElement).value = '10-12-05 11:22:am';
input.simulate('change', input);
input.simulate('blur', input);
expect(onChangeData.data.foo).toBe('2005/12/10 11:22 am');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ describe('Material time control', () => {
);
const input = wrapper.find('input').first();
(input.getDOMNode() as HTMLInputElement).value = '08:40';
input.simulate('change', input);
expect(onChangeData.data.foo).toBe('08:40:05');
input.simulate('blur', input);
expect(onChangeData.data.foo).toBe('08:40:00');
});

it('should update via action', () => {
Expand Down Expand Up @@ -421,7 +421,7 @@ describe('Material time control', () => {
expect(input.props().value).toBe('02-13');

(input.getDOMNode() as HTMLInputElement).value = '12-01';
input.simulate('change', input);
input.simulate('blur', input);
expect(onChangeData.data.foo).toBe('1//12 am');
});
});

0 comments on commit 53b5a73

Please sign in to comment.