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 #2183
  • Loading branch information
LukasBoll committed Oct 27, 2023
1 parent bf12c01 commit 85d995e
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 26 deletions.
2 changes: 1 addition & 1 deletion packages/examples/src/examples/dates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export const uischema = {

export const data = {
schemaBased: {
date: '999/12/11',
date: new Date().toISOString().substr(0, 10),
time: '13:37',
datetime: new Date().toISOString(),
},
Expand Down
29 changes: 16 additions & 13 deletions packages/material-renderers/src/controls/MaterialDateControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
import dayjs from 'dayjs';

export const MaterialDateControl = (props: ControlProps) => {
Expand Down Expand Up @@ -78,12 +83,19 @@ export const MaterialDateControl = (props: ControlProps) => {
? errors
: null;
const secondFormHelperText = showDescription && !isValid ? errors : null;

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

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

useEffect(() => {
const dayjsData = dayjs(data, saveFormat);
const dayjsData = dayjs(data, format);
if (dayjsData.toString() !== 'Invalid Date') {
setDisplayedData(dayjsData);
}
Expand All @@ -95,7 +107,7 @@ export const MaterialDateControl = (props: ControlProps) => {
<DatePicker
label={label}
value={displayedData}
onChange={onChange}
onAccept={onChange}
format={format}
views={views}
disabled={!enabled}
Expand All @@ -118,16 +130,7 @@ export const MaterialDateControl = (props: ControlProps) => {
},
InputLabelProps: data ? { shrink: true } : undefined,
onFocus: onFocus,
onBlur: (e) => {
console.log('unblure');
console.log(e.target.value);
if (e.target.value == format) {
handleChange(path, undefined);
} else {
console.log(dayjs(e.target.value, format));
onChange(dayjs(e.target.value, format));
}
},
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, format)
);

const views = appliedUiSchemaOptions.views ?? [
'year',
'day',
Expand All @@ -85,15 +95,25 @@ 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(() => {
const dayjsData = dayjs(data, format);
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
33 changes: 27 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,29 @@ export const MaterialTimeControl = (props: ControlProps) => {
[path, handleChange, saveFormat]
);

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

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

useEffect(() => {
const dayjsData = dayjs(data, format);
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
21 changes: 21 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,27 @@ 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;
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(dayjs(date, format));
}
}
};

export const getData = (
data: any,
saveFormat: string | undefined
Expand Down

0 comments on commit 85d995e

Please sign in to comment.