Skip to content

Commit

Permalink
fix(material): Improve usability of date renderers
Browse files Browse the repository at this point in the history
Set value to 'undefined' if the text input is empty. If the input is
invalid, set the actual value instead of "invalid data" and display the
last input instead of an empty field.

Closes eclipsesource#2183 (+1 squashed commits)

Squashed commits:

[bf12c01] fix(material): Improve usability of date renderers

Set value to 'undefined' if the text input is empty. If the input is
invalid, set the actual value instead of "invalid data" and display the
last input instead of an empty field.

Closes eclipsesource#2183
  • Loading branch information
LukasBoll committed Oct 31, 2023
1 parent f905c82 commit 3b41788
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 26 deletions.
39 changes: 32 additions & 7 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, { useEffect, useMemo, useState } from 'react';
import {
ControlProps,
isDateControl,
Expand All @@ -35,10 +35,16 @@ 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';
import dayjs from 'dayjs';

export const MaterialDateControl = (props: ControlProps) => {
const [focused, onFocus, onBlur] = useFocus();
const [focused, onFocus] = useFocus();
const {
description,
id,
Expand All @@ -65,6 +71,10 @@ export const MaterialDateControl = (props: ControlProps) => {
const format = appliedUiSchemaOptions.dateFormat ?? 'YYYY-MM-DD';
const saveFormat = appliedUiSchemaOptions.dateSaveFormat ?? 'YYYY-MM-DD';

const [displayedData, setDisplayedData] = useState<any>(
getData(data, saveFormat)
);

const views = appliedUiSchemaOptions.views ?? ['year', 'day'];

const firstFormHelperText = showDescription
Expand All @@ -73,20 +83,35 @@ export const MaterialDateControl = (props: ControlProps) => {
? errors
: null;
const secondFormHelperText = showDescription && !isValid ? errors : null;

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

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

useEffect(() => {
if (data === null || data === undefined) {
setDisplayedData(null);
} else {
const dayjsData = dayjs(data, saveFormat);
if (dayjsData.toString() !== 'Invalid Date') {
setDisplayedData(dayjsData);
}
}
}, [data]);

return (
<Hidden xsUp={!visible}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
label={label}
value={value}
onChange={onChange}
value={displayedData}
onAccept={onChange}
format={format}
views={views}
disabled={!enabled}
Expand All @@ -109,7 +134,7 @@ export const MaterialDateControl = (props: ControlProps) => {
},
InputLabelProps: data ? { shrink: true } : undefined,
onFocus: onFocus,
onBlur: onBlur,
onBlur,
},
}}
/>
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, { useEffect, useMemo, useState } from 'react';
import merge from 'lodash/merge';
import {
ControlProps,
Expand All @@ -35,10 +35,16 @@ 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';
import dayjs from 'dayjs';

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

const [displayedData, setDisplayedData] = useState<any>(
getData(data, saveFormat)
);

const views = appliedUiSchemaOptions.views ?? [
'year',
'day',
Expand All @@ -85,15 +95,29 @@ export const MaterialDateTimeControl = (props: ControlProps) => {
[path, handleChange, saveFormat]
);

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

useEffect(() => {
if (data === null || data === undefined) {
setDisplayedData(null);
} else {
const dayjsData = dayjs(data, saveFormat);
if (dayjsData.toString() !== 'Invalid Date') {
setDisplayedData(dayjsData);
}
}
}, [data]);

return (
<Hidden xsUp={!visible}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DateTimePicker
label={label}
value={value}
onChange={onChange}
value={displayedData}
onAccept={onChange}
format={format}
ampm={!!appliedUiSchemaOptions.ampm}
views={views}
Expand Down
37 changes: 31 additions & 6 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, { useEffect, useMemo, useState } from 'react';
import merge from 'lodash/merge';
import {
ControlProps,
Expand All @@ -35,10 +35,16 @@ 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';
import dayjs from 'dayjs';

export const MaterialTimeControl = (props: ControlProps) => {
const [focused, onFocus, onBlur] = useFocus();
const [focused, onFocus] = useFocus();
const {
id,
description,
Expand Down Expand Up @@ -80,14 +86,33 @@ export const MaterialTimeControl = (props: ControlProps) => {
[path, handleChange, saveFormat]
);

const value = getData(data, saveFormat);
const [displayedData, setDisplayedData] = useState<any>(
getData(data, saveFormat)
);

const onBlur = useMemo(
() => createOnBlurHandler(path, handleChange, format, onChange),
[path, handleChange, saveFormat, onChange]
);

useEffect(() => {
if (data === null || data === undefined) {
setDisplayedData(null);
} else {
const dayjsData = dayjs(data, saveFormat);
if (dayjsData.toString() !== 'Invalid Date') {
setDisplayedData(dayjsData);
}
}
}, [data]);

return (
<Hidden xsUp={!visible}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<TimePicker
label={label}
value={value}
onChange={onChange}
value={displayedData}
onAccept={onChange}
format={format}
ampm={!!appliedUiSchemaOptions.ampm}
views={views}
Expand Down
22 changes: 22 additions & 0 deletions packages/material-renderers/src/util/datejs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ export const createOnChangeHandler =
handleChange(path, result);
};

export const createOnBlurHandler =
(
path: string,
handleChange: (path: string, value: any) => void,
format: string | undefined,
onChange: (time: dayjs.Dayjs) => void
) =>
(e: React.FocusEvent<HTMLTextAreaElement | HTMLInputElement, Element>) => {
const date = e.target.value;
console.log('problem TODO');
if (!format.localeCompare(date, undefined, { sensitivity: 'base' })) {
handleChange(path, undefined);
} else {
const formatedValue = dayjs(date, format);
if (formatedValue.toString() === 'Invalid Date') {
handleChange(path, date);
} else {
onChange(formatedValue);
}
}
};

export const getData = (
data: any,
saveFormat: string | undefined
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 3b41788

Please sign in to comment.