From 29e42b721408b56f37b00f728e96c0ba35dc7f58 Mon Sep 17 00:00:00 2001 From: LukasBoll Date: Thu, 21 Sep 2023 14:23:21 +0200 Subject: [PATCH] react-material: Reset data in anyOf renderer for incompatible data types (#2175) Reset data in anyOfRenderer if data types dont match Co-authored-by: Lucas Koehler --- .../src/complex/MaterialAnyOfRenderer.tsx | 55 ++++++++++++++-- .../src/complex/MaterialOneOfRenderer.tsx | 66 ++++++------------- .../src/complex/TabSwitchConfirmDialog.tsx | 56 ++++++++++++++++ 3 files changed, 126 insertions(+), 51 deletions(-) create mode 100644 packages/material-renderers/src/complex/TabSwitchConfirmDialog.tsx diff --git a/packages/material-renderers/src/complex/MaterialAnyOfRenderer.tsx b/packages/material-renderers/src/complex/MaterialAnyOfRenderer.tsx index 178cff836..6b65a7b13 100644 --- a/packages/material-renderers/src/complex/MaterialAnyOfRenderer.tsx +++ b/packages/material-renderers/src/complex/MaterialAnyOfRenderer.tsx @@ -25,18 +25,22 @@ import React, { useCallback, useState } from 'react'; import { + CombinatorRendererProps, createCombinatorRenderInfos, + createDefaultValue, isAnyOfControl, JsonSchema, RankedTester, rankWith, - StatePropsOfCombinator, } from '@jsonforms/core'; import { JsonFormsDispatch, withJsonFormsAnyOfProps } from '@jsonforms/react'; import { Hidden, Tab, Tabs } from '@mui/material'; import CombinatorProperties from './CombinatorProperties'; +import isEmpty from 'lodash/isEmpty'; +import { TabSwitchConfirmDialog } from './TabSwitchConfirmDialog'; export const MaterialAnyOfRenderer = ({ + handleChange, schema, rootSchema, indexOfFittingSchema, @@ -46,12 +50,44 @@ export const MaterialAnyOfRenderer = ({ cells, uischema, uischemas, -}: StatePropsOfCombinator) => { + id, + data, +}: CombinatorRendererProps) => { const [selectedAnyOf, setSelectedAnyOf] = useState(indexOfFittingSchema || 0); - const handleChange = useCallback( - (_ev: any, value: number) => setSelectedAnyOf(value), - [setSelectedAnyOf] + const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); + const [newSelectedIndex, setNewSelectedIndex] = useState(0); + + const handleClose = useCallback( + () => setConfirmDialogOpen(false), + [setConfirmDialogOpen] + ); + + const handleTabChange = useCallback( + (_event: any, newIndex: number) => { + if ( + isEmpty(data) || + typeof data === + typeof createDefaultValue(anyOfRenderInfos[newIndex].schema) + ) { + setSelectedAnyOf(newIndex); + } else { + setNewSelectedIndex(newIndex); + setConfirmDialogOpen(true); + } + }, + [setConfirmDialogOpen, setSelectedAnyOf, data] ); + + const openNewTab = (newIndex: number) => { + handleChange(path, createDefaultValue(anyOfRenderInfos[newIndex].schema)); + setSelectedAnyOf(newIndex); + }; + + const confirm = useCallback(() => { + openNewTab(newSelectedIndex); + setConfirmDialogOpen(false); + }, [handleChange, createDefaultValue, newSelectedIndex]); + const anyOf = 'anyOf'; const anyOfRenderInfos = createCombinatorRenderInfos( (schema as JsonSchema).anyOf, @@ -69,7 +105,7 @@ export const MaterialAnyOfRenderer = ({ combinatorKeyword={anyOf} path={path} /> - + {anyOfRenderInfos.map((anyOfRenderInfo) => ( ))} @@ -87,6 +123,13 @@ export const MaterialAnyOfRenderer = ({ /> ) )} + ); }; diff --git a/packages/material-renderers/src/complex/MaterialOneOfRenderer.tsx b/packages/material-renderers/src/complex/MaterialOneOfRenderer.tsx index 6ea9a6fbf..d6cc99f24 100644 --- a/packages/material-renderers/src/complex/MaterialOneOfRenderer.tsx +++ b/packages/material-renderers/src/complex/MaterialOneOfRenderer.tsx @@ -25,6 +25,8 @@ import React, { useCallback, useState } from 'react'; import isEmpty from 'lodash/isEmpty'; +import { TabSwitchConfirmDialog } from './TabSwitchConfirmDialog'; + import { CombinatorRendererProps, createCombinatorRenderInfos, @@ -35,17 +37,7 @@ import { RankedTester, rankWith, } from '@jsonforms/core'; -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, - Hidden, - Tab, - Tabs, -} from '@mui/material'; +import { Hidden, Tab, Tabs } from '@mui/material'; import { JsonFormsDispatch, withJsonFormsOneOfProps } from '@jsonforms/react'; import CombinatorProperties from './CombinatorProperties'; @@ -67,13 +59,16 @@ export const MaterialOneOfRenderer = ({ uischemas, data, }: CombinatorRendererProps) => { - const [open, setOpen] = useState(false); + const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); const [selectedIndex, setSelectedIndex] = useState(indexOfFittingSchema || 0); const [newSelectedIndex, setNewSelectedIndex] = useState(0); - const handleClose = useCallback(() => setOpen(false), [setOpen]); + const handleClose = useCallback( + () => setConfirmDialogOpen(false), + [setConfirmDialogOpen] + ); const cancel = useCallback(() => { - setOpen(false); - }, [setOpen]); + setConfirmDialogOpen(false); + }, [setConfirmDialogOpen]); const oneOfRenderInfos = createCombinatorRenderInfos( (schema as JsonSchema).oneOf, rootSchema, @@ -90,18 +85,19 @@ export const MaterialOneOfRenderer = ({ const confirm = useCallback(() => { openNewTab(newSelectedIndex); - setOpen(false); + setConfirmDialogOpen(false); }, [handleChange, createDefaultValue, newSelectedIndex]); + const handleTabChange = useCallback( (_event: any, newOneOfIndex: number) => { setNewSelectedIndex(newOneOfIndex); if (isEmpty(data)) { openNewTab(newOneOfIndex); } else { - setOpen(true); + setConfirmDialogOpen(true); } }, - [setOpen, setSelectedIndex, data] + [setConfirmDialogOpen, setSelectedIndex, data] ); return ( @@ -129,33 +125,13 @@ export const MaterialOneOfRenderer = ({ /> ) )} - - {'Clear form?'} - - - Your data will be cleared if you navigate away from this tab. Do you - want to proceed? - - - - - - - + ); }; diff --git a/packages/material-renderers/src/complex/TabSwitchConfirmDialog.tsx b/packages/material-renderers/src/complex/TabSwitchConfirmDialog.tsx new file mode 100644 index 000000000..eb8e69937 --- /dev/null +++ b/packages/material-renderers/src/complex/TabSwitchConfirmDialog.tsx @@ -0,0 +1,56 @@ +import React from 'react'; + +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from '@mui/material'; + +export interface TabSwitchConfirmDialogProps { + open: boolean; + handleClose: () => void; + confirm: () => void; + cancel: () => void; + id: string; +} + +export const TabSwitchConfirmDialog = ({ + open, + handleClose, + confirm, + cancel, + id, +}: TabSwitchConfirmDialogProps) => { + return ( + + {'Clear form?'} + + + Your data will be cleared if you navigate away from this tab. Do you + want to proceed? + + + + + + + + ); +};