From 5b2b2e7c279505e64e12dd02a77368b679a45c8e Mon Sep 17 00:00:00 2001 From: Lucas Koehler Date: Tue, 23 Sep 2025 17:23:00 +0200 Subject: [PATCH] react-material: Update MUI X date pickers from v7 to v8 - Update dev and peer dependency of `@mui/x-date-pickers` from v7 to v8. - Adapt input variant handling for picker text fields - Adapt picker variant handling - Adapt textfield input property handling - Fix pickers to still autoclose on selecting the smallest unit. - Make autoclose configurable via uischema option `closeOnSelect` NOTE: The pickers needed to be updated to handle onChange in addition to onAccept. Otherwise, the value in the open picker is not updated. This results in data updates after each date/time component selection (e.g. hours). --- packages/material-renderers/package.json | 4 +- .../src/controls/MaterialDateControl.tsx | 17 ++-- .../src/controls/MaterialDateTimeControl.tsx | 16 ++-- .../src/controls/MaterialTimeControl.tsx | 16 ++-- pnpm-lock.yaml | 84 ++++++++++++++++--- 5 files changed, 106 insertions(+), 31 deletions(-) diff --git a/packages/material-renderers/package.json b/packages/material-renderers/package.json index 939a93f434..6872331640 100644 --- a/packages/material-renderers/package.json +++ b/packages/material-renderers/package.json @@ -90,7 +90,7 @@ "@jsonforms/react": "3.7.0-alpha.1", "@mui/icons-material": "^7.0.0", "@mui/material": "^7.0.0", - "@mui/x-date-pickers": "^7.28.0", + "@mui/x-date-pickers": "^8.0.0", "react": "^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "devDependencies": { @@ -100,7 +100,7 @@ "@jsonforms/react": "workspace:*", "@mui/icons-material": "^7.3.0", "@mui/material": "^7.3.0", - "@mui/x-date-pickers": "^7.29.4", + "@mui/x-date-pickers": "^8.11.3", "@rollup/plugin-commonjs": "^23.0.3", "@rollup/plugin-json": "^5.0.2", "@rollup/plugin-node-resolve": "^15.0.1", diff --git a/packages/material-renderers/src/controls/MaterialDateControl.tsx b/packages/material-renderers/src/controls/MaterialDateControl.tsx index 488f97aa53..c8eb82dab2 100644 --- a/packages/material-renderers/src/controls/MaterialDateControl.tsx +++ b/packages/material-renderers/src/controls/MaterialDateControl.tsx @@ -41,10 +41,12 @@ import { createOnChangeHandler, getData, useFocus, + useInputVariant, } from '../util'; export const MaterialDateControl = (props: ControlProps) => { const [focused, onFocus, onBlur] = useFocus(); + const inputVariant = useInputVariant(); const { description, id, @@ -75,6 +77,7 @@ export const MaterialDateControl = (props: ControlProps) => { const saveFormat = appliedUiSchemaOptions.dateSaveFormat ?? defaultDateFormat; const views = appliedUiSchemaOptions.views ?? ['year', 'day']; + const closeOnSelect = appliedUiSchemaOptions.closeOnSelect ?? true; const firstFormHelperText = showDescription ? description @@ -100,7 +103,7 @@ export const MaterialDateControl = (props: ControlProps) => { updateChild, onBlur ), - [path, handleChange, format, saveFormat, updateChild] + [path, handleChange, format, saveFormat, updateChild, onBlur] ); const value = getData(data, saveFormat); @@ -121,23 +124,25 @@ export const MaterialDateControl = (props: ControlProps) => { format={format} views={views} disabled={!enabled} + closeOnSelect={closeOnSelect} slotProps={{ - actionBar: ({ wrapperVariant }) => ({ + actionBar: ({ pickerVariant }) => ({ actions: - wrapperVariant === 'desktop' ? [] : ['clear', 'cancel', 'accept'], + pickerVariant === 'desktop' ? [] : ['clear', 'cancel', 'accept'], }), textField: { id: id + '-input', required: required && !appliedUiSchemaOptions.hideRequiredAsterisk, - autoFocus: appliedUiSchemaOptions.focus, error: !isValid, fullWidth: !appliedUiSchemaOptions.trim, + variant: inputVariant, inputProps: { + autoFocus: appliedUiSchemaOptions.focus, type: 'text', + onFocus: onFocus, + onBlur: onBlurHandler, }, InputLabelProps: data ? { shrink: true } : undefined, - onFocus: onFocus, - onBlur: onBlurHandler, }, }} /> diff --git a/packages/material-renderers/src/controls/MaterialDateTimeControl.tsx b/packages/material-renderers/src/controls/MaterialDateTimeControl.tsx index 9860d1c368..4c512201c1 100644 --- a/packages/material-renderers/src/controls/MaterialDateTimeControl.tsx +++ b/packages/material-renderers/src/controls/MaterialDateTimeControl.tsx @@ -41,10 +41,12 @@ import { createOnChangeHandler, getData, useFocus, + useInputVariant, } from '../util'; export const MaterialDateTimeControl = (props: ControlProps) => { const [focused, onFocus, onBlur] = useFocus(); + const inputVariant = useInputVariant(); const { id, description, @@ -82,6 +84,7 @@ export const MaterialDateTimeControl = (props: ControlProps) => { 'hours', 'minutes', ]; + const closeOnSelect = appliedUiSchemaOptions.closeOnSelect ?? true; const firstFormHelperText = showDescription ? description @@ -124,27 +127,30 @@ export const MaterialDateTimeControl = (props: ControlProps) => { label={label} value={value} onAccept={onChange} + onChange={onChange} format={format} ampm={!!appliedUiSchemaOptions.ampm} views={views} + closeOnSelect={closeOnSelect} disabled={!enabled} slotProps={{ - actionBar: ({ wrapperVariant }) => ({ + actionBar: ({ pickerVariant }) => ({ actions: - wrapperVariant === 'desktop' ? [] : ['clear', 'cancel', 'accept'], + pickerVariant === 'desktop' ? [] : ['clear', 'cancel', 'accept'], }), textField: { id: id + '-input', required: required && !appliedUiSchemaOptions.hideRequiredAsterisk, - autoFocus: appliedUiSchemaOptions.focus, error: !isValid, fullWidth: !appliedUiSchemaOptions.trim, + variant: inputVariant, inputProps: { + autoFocus: appliedUiSchemaOptions.focus, type: 'text', + onFocus: onFocus, + onBlur: onBlurHandler, }, InputLabelProps: data ? { shrink: true } : undefined, - onFocus: onFocus, - onBlur: onBlurHandler, }, }} /> diff --git a/packages/material-renderers/src/controls/MaterialTimeControl.tsx b/packages/material-renderers/src/controls/MaterialTimeControl.tsx index 9ef8af3d66..e22638b1aa 100644 --- a/packages/material-renderers/src/controls/MaterialTimeControl.tsx +++ b/packages/material-renderers/src/controls/MaterialTimeControl.tsx @@ -41,10 +41,12 @@ import { createOnChangeHandler, getData, useFocus, + useInputVariant, } from '../util'; export const MaterialTimeControl = (props: ControlProps) => { const [focused, onFocus, onBlur] = useFocus(); + const inputVariant = useInputVariant(); const { id, description, @@ -76,6 +78,7 @@ export const MaterialTimeControl = (props: ControlProps) => { const saveFormat = appliedUiSchemaOptions.timeSaveFormat ?? defaultTimeFormat; const views = appliedUiSchemaOptions.views ?? ['hours', 'minutes']; + const closeOnSelect = appliedUiSchemaOptions.closeOnSelect ?? true; const firstFormHelperText = showDescription ? description @@ -118,27 +121,30 @@ export const MaterialTimeControl = (props: ControlProps) => { label={label} value={value} onAccept={onChange} + onChange={onChange} format={format} ampm={!!appliedUiSchemaOptions.ampm} views={views} + closeOnSelect={closeOnSelect} disabled={!enabled} slotProps={{ - actionBar: ({ wrapperVariant }) => ({ + actionBar: ({ pickerVariant }) => ({ actions: - wrapperVariant === 'desktop' ? [] : ['clear', 'cancel', 'accept'], + pickerVariant === 'desktop' ? [] : ['clear', 'cancel', 'accept'], }), textField: { id: id + '-input', required: required && !appliedUiSchemaOptions.hideRequiredAsterisk, - autoFocus: appliedUiSchemaOptions.focus, error: !isValid, fullWidth: !appliedUiSchemaOptions.trim, + variant: inputVariant, inputProps: { + autoFocus: appliedUiSchemaOptions.focus, type: 'text', + onBlur: onBlurHandler, + onFocus: onFocus, }, InputLabelProps: data ? { shrink: true } : undefined, - onFocus: onFocus, - onBlur: onBlurHandler, }, }} /> diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3df99feb11..0026141505 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -639,8 +639,8 @@ importers: specifier: ^7.3.0 version: 7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@mui/x-date-pickers': - specifier: ^7.29.4 - version: 7.29.4(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@mui/material@7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(@mui/system@7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(dayjs@1.10.7)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + specifier: ^8.11.3 + version: 8.11.3(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@mui/material@7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(@mui/system@7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(dayjs@1.10.7)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@rollup/plugin-commonjs': specifier: ^23.0.3 version: 23.0.7(rollup@2.79.1) @@ -2760,6 +2760,10 @@ packages: resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + '@babel/template@7.24.0': resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} engines: {node: '>=6.9.0'} @@ -3739,6 +3743,14 @@ packages: '@types/react': optional: true + '@mui/types@7.4.6': + resolution: {integrity: sha512-NVBbIw+4CDMMppNamVxyTccNv0WxtDb7motWDlMeSC8Oy95saj1TIZMGynPpFLePt3yOD8TskzumeqORCgRGWw==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/utils@7.3.0': resolution: {integrity: sha512-YdL6ebwFV7PIOidIsees3HxkZ8hZjj+/atKLuI1ENwvJJ1puiEoLEmuDU72qSbKu911/GeFa7pc7Cn/ZmAj6yQ==} engines: {node: '>=14.0.0'} @@ -3749,8 +3761,18 @@ packages: '@types/react': optional: true - '@mui/x-date-pickers@7.29.4': - resolution: {integrity: sha512-wJ3tsqk/y6dp+mXGtT9czciAMEO5Zr3IIAHg9x6IL0Eqanqy0N3chbmQQZv3iq0m2qUpQDLvZ4utZBUTJdjNzw==} + '@mui/utils@7.3.2': + resolution: {integrity: sha512-4DMWQGenOdLnM3y/SdFQFwKsCLM+mqxzvoWp9+x2XdEzXapkznauHLiXtSohHs/mc0+5/9UACt1GdugCX2te5g==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/x-date-pickers@8.11.3': + resolution: {integrity: sha512-RCvaACeTvlT930jn4s4weD/3Ow30sy7A4dRK8vmA3Pc3RIBy/b2CC4nYdkMiOwI56cjOVF4WPLFp5hBcEeGSvQ==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.9.0 @@ -3786,8 +3808,8 @@ packages: moment-jalaali: optional: true - '@mui/x-internals@7.29.0': - resolution: {integrity: sha512-+Gk6VTZIFD70XreWvdXBwKd8GZ2FlSCuecQFzm6znwqXg1ZsndavrhG9tkxpxo2fM1Zf7Tk8+HcOO0hCbhTQFA==} + '@mui/x-internals@8.11.3': + resolution: {integrity: sha512-Fmp4Op+nNSqsWn2Jwv9yA8WXi3Wem9jmgdUplvMK6JZAt7iA0ZdzGltCcHrdxOcK1Nu/2F7H8KOZuBzpy1lspw==} engines: {node: '>=14.0.0'} peerDependencies: react: ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -11501,6 +11523,9 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} @@ -12889,6 +12914,11 @@ packages: resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} engines: {node: '>= 0.4'} + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -15607,6 +15637,8 @@ snapshots: '@babel/runtime@7.28.2': {} + '@babel/runtime@7.28.4': {} + '@babel/template@7.24.0': dependencies: '@babel/code-frame': 7.24.2 @@ -16644,6 +16676,12 @@ snapshots: optionalDependencies: '@types/react': 17.0.80 + '@mui/types@7.4.6(@types/react@17.0.80)': + dependencies: + '@babel/runtime': 7.28.4 + optionalDependencies: + '@types/react': 17.0.80 + '@mui/utils@7.3.0(@types/react@17.0.80)(react@17.0.2)': dependencies: '@babel/runtime': 7.28.2 @@ -16656,13 +16694,25 @@ snapshots: optionalDependencies: '@types/react': 17.0.80 - '@mui/x-date-pickers@7.29.4(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@mui/material@7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(@mui/system@7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(dayjs@1.10.7)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@mui/utils@7.3.2(@types/react@17.0.80)(react@17.0.2)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/types': 7.4.6(@types/react@17.0.80) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 17.0.2 + react-is: 19.1.1 + optionalDependencies: + '@types/react': 17.0.80 + + '@mui/x-date-pickers@8.11.3(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@mui/material@7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(@mui/system@7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(dayjs@1.10.7)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@babel/runtime': 7.28.2 '@mui/material': 7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@mui/system': 7.3.0(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2))(@types/react@17.0.80)(react@17.0.2) - '@mui/utils': 7.3.0(@types/react@17.0.80)(react@17.0.2) - '@mui/x-internals': 7.29.0(@types/react@17.0.80)(react@17.0.2) + '@mui/utils': 7.3.2(@types/react@17.0.80)(react@17.0.2) + '@mui/x-internals': 8.11.3(@types/react@17.0.80)(react@17.0.2) '@types/react-transition-group': 4.4.12(@types/react@17.0.80) clsx: 2.1.1 prop-types: 15.8.1 @@ -16676,11 +16726,13 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@mui/x-internals@7.29.0(@types/react@17.0.80)(react@17.0.2)': + '@mui/x-internals@8.11.3(@types/react@17.0.80)(react@17.0.2)': dependencies: '@babel/runtime': 7.28.2 - '@mui/utils': 7.3.0(@types/react@17.0.80)(react@17.0.2) + '@mui/utils': 7.3.2(@types/react@17.0.80)(react@17.0.2) react: 17.0.2 + reselect: 5.1.1 + use-sync-external-store: 1.5.0(react@17.0.2) transitivePeerDependencies: - '@types/react' @@ -19234,7 +19286,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.28.2 cosmiconfig: 7.1.0 resolve: 1.22.8 @@ -26548,7 +26600,7 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.28.2 regex-parser@2.3.0: {} @@ -26652,6 +26704,8 @@ snapshots: requires-port@1.0.0: {} + reselect@5.1.1: {} + resize-observer-polyfill@1.5.1: {} resolve-cwd@3.0.0: @@ -28256,6 +28310,10 @@ snapshots: punycode: 1.4.1 qs: 6.13.0 + use-sync-external-store@1.5.0(react@17.0.2): + dependencies: + react: 17.0.2 + util-deprecate@1.0.2: {} util@0.12.5: