From e46059e45204a5b08718af1ff2bb398540f41a77 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 12:57:39 +0200 Subject: [PATCH 01/80] Bump moment-jalaali to v0.10.0 (#8641) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/x-date-pickers-pro/package.json | 2 +- packages/x-date-pickers/package.json | 4 ++-- yarn.lock | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index c53b1cff768f9..083721b37fd99 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -64,7 +64,7 @@ "luxon": "^3.0.2", "moment": "^2.29.4", "moment-hijri": "^2.1.2", - "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0" }, diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 88bfd785c2677..5189705ee9d63 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -69,7 +69,7 @@ "luxon": "^3.0.2", "moment": "^2.29.4", "moment-hijri": "^2.1.2", - "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0" }, @@ -110,7 +110,7 @@ "luxon": "^3.3.0", "moment": "^2.29.4", "moment-hijri": "^2.1.2", - "moment-jalaali": "^0.9.6" + "moment-jalaali": "^0.10.0" }, "setupFiles": [ "/src/setupTests.js" diff --git a/yarn.lock b/yarn.lock index cfec8b05d7d8e..5bd33796cc926 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10083,10 +10083,10 @@ moment-hijri@^2.1.2: dependencies: moment "^2.24.0" -moment-jalaali@^0.9.6: - version "0.9.6" - resolved "https://registry.yarnpkg.com/moment-jalaali/-/moment-jalaali-0.9.6.tgz#6c460aedee08a1e45d3f658c7cffd66cd4dca24c" - integrity sha512-v8wXjQplvk5ez+sUqgsWIrafwIf1BEXXvzTYwsg1wHcqh27nSgKPCJ6FnZRrCz03MoNyB9N31L0oms+vE8Rq7g== +moment-jalaali@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/moment-jalaali/-/moment-jalaali-0.10.0.tgz#4e5de7605bc9597e01f7c62c3cc70268ae4ddf8d" + integrity sha512-XICH1+UHd3zyaE1bXWQBlhoXBqbzEyFfT0TrifNobHxLALRuTSwXn376bX8FmkYg9mZimevwwdd6EB57s5wuFA== dependencies: jalaali-js "^1.1.0" moment "^2.22.2" From 9e27de38734a8aa5b7e96cbc4e023471483e4c98 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 12:57:55 +0200 Subject: [PATCH 02/80] Bump styled-components to ^5.3.10 (#8714) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/package.json b/docs/package.json index dfe4d855f5775..2cf6ccbeea23d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -83,7 +83,7 @@ "react-simple-code-editor": "^0.13.1", "recast": "^0.22.0", "rimraf": "^4.4.1", - "styled-components": "^5.3.9", + "styled-components": "^5.3.10", "stylis": "^4.1.3", "stylis-plugin-rtl": "^2.1.1", "stylis-plugin-rtl-sc": "npm:stylis-plugin-rtl@^2.1.1", diff --git a/yarn.lock b/yarn.lock index 5bd33796cc926..1e955994908db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13245,10 +13245,10 @@ style-loader@^3.3.2: resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.2.tgz#eaebca714d9e462c19aa1e3599057bc363924899" integrity sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw== -styled-components@^5.3.9: - version "5.3.9" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.9.tgz#641af2a8bb89904de708c71b439caa9633e8f0ba" - integrity sha512-Aj3kb13B75DQBo2oRwRa/APdB5rSmwUfN5exyarpX+x/tlM/rwZA2vVk2vQgVSP6WKaZJHWwiFrzgHt+CLtB4A== +styled-components@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.10.tgz#42f7245f58fe960362a63f543dda23c0ac107c0f" + integrity sha512-3kSzSBN0TiCnGJM04UwO1HklIQQSXW7rCARUk+VyMR7clz8XVlA3jijtf5ypqoDIdNMKx3la4VvaPFR855SFcg== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/traverse" "^7.4.5" From a4454b21ccf0289b0f14f6463cd14ce5e5a61024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rodolfo=20Freitas?= Date: Tue, 25 Apr 2023 17:01:01 +0200 Subject: [PATCH 03/80] [data grid] Fix aggregation label alignment (#8694) Co-authored-by: Andrew Cherniavskyi --- .../src/components/GridAggregationHeader.tsx | 3 ++- .../x-data-grid/src/components/containers/GridRootStyles.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/grid/x-data-grid-premium/src/components/GridAggregationHeader.tsx b/packages/grid/x-data-grid-premium/src/components/GridAggregationHeader.tsx index f1dc9207ee264..59dff478d8afc 100644 --- a/packages/grid/x-data-grid-premium/src/components/GridAggregationHeader.tsx +++ b/packages/grid/x-data-grid-premium/src/components/GridAggregationHeader.tsx @@ -42,7 +42,8 @@ const GridAggregationFunctionLabel = styled('div', { return { fontSize: theme.typography.caption.fontSize, lineHeight: theme.typography.caption.fontSize, - marginTop: `calc(-2px - ${theme.typography.caption.fontSize})`, + position: 'absolute', + bottom: 4, fontWeight: theme.typography.fontWeightMedium, color: (theme.vars || theme).palette.primary.dark, textTransform: 'uppercase', diff --git a/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts b/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts index 87b16bf4654ec..afdf105b587ae 100644 --- a/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts +++ b/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts @@ -178,6 +178,8 @@ export const GridRootStyles = styled('div', { flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', + // to anchor the aggregation label + position: 'relative', }, [`& .${gridClasses.columnHeaderTitleContainerContent}`]: { overflow: 'hidden', From e5f42a083431ca9b400b887db6f08676a085d1c8 Mon Sep 17 00:00:00 2001 From: Siriwat K Date: Wed, 26 Apr 2023 10:30:31 +0700 Subject: [PATCH 04/80] [docs] Update custom field pickers using theme scoping (#8609) --- .../custom-field/PickerWithJoyField.js | 154 ++++-------------- .../custom-field/PickerWithJoyField.tsx | 153 ++++------------- .../PickerWithJoyField.tsx.preview | 20 ++- 3 files changed, 75 insertions(+), 252 deletions(-) diff --git a/docs/data/date-pickers/custom-field/PickerWithJoyField.js b/docs/data/date-pickers/custom-field/PickerWithJoyField.js index ee3a480ea432c..963b0cbfa3439 100644 --- a/docs/data/date-pickers/custom-field/PickerWithJoyField.js +++ b/docs/data/date-pickers/custom-field/PickerWithJoyField.js @@ -1,15 +1,18 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { deepmerge } from '@mui/utils'; -import { blue, grey } from '@mui/material/colors'; import { - Experimental_CssVarsProvider as CssVarsProvider, - experimental_extendTheme as extendMuiTheme, - useTheme, - useColorScheme, + useTheme as useMaterialTheme, + useColorScheme as useMaterialColorScheme, + Experimental_CssVarsProvider as MaterialCssVarsProvider, } from '@mui/material/styles'; -import { extendTheme as extendJoyTheme, styled } from '@mui/joy/styles'; +import { + extendTheme as extendJoyTheme, + useColorScheme, + styled, + CssVarsProvider, + THEME_ID, +} from '@mui/joy/styles'; import { useSlotProps } from '@mui/base/utils'; import Input from '@mui/joy/Input'; import Stack from '@mui/joy/Stack'; @@ -24,107 +27,7 @@ import { unstable_useMultiInputDateRangeField as useMultiInputDateRangeField } f import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { unstable_useDateField as useDateField } from '@mui/x-date-pickers/DateField'; -const muiTheme = extendMuiTheme(); - -const joyTheme = extendJoyTheme({ - cssVarPrefix: 'mui', - colorSchemes: { - light: { - palette: { - primary: { - ...blue, - solidColor: 'var(--mui-palette-primary-contrastText)', - solidBg: 'var(--mui-palette-primary-main)', - solidHoverBg: 'var(--mui-palette-primary-dark)', - plainColor: 'var(--mui-palette-primary-main)', - plainHoverBg: - 'rgba(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-hoverOpacity))', - plainActiveBg: 'rgba(var(--mui-palette-primary-mainChannel) / 0.3)', - outlinedBorder: 'rgba(var(--mui-palette-primary-mainChannel) / 0.5)', - outlinedColor: 'var(--mui-palette-primary-main)', - outlinedHoverBg: - 'rgba(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-hoverOpacity))', - outlinedHoverBorder: 'var(--mui-palette-primary-main)', - outlinedActiveBg: 'rgba(var(--mui-palette-primary-mainChannel) / 0.3)', - }, - neutral: { - ...grey, - }, - divider: 'var(--mui-palette-divider)', - text: { - tertiary: 'rgba(0 0 0 / 0.56)', - }, - }, - }, - dark: { - palette: { - primary: { - ...blue, - solidColor: 'var(--mui-palette-primary-contrastText)', - solidBg: 'var(--mui-palette-primary-main)', - solidHoverBg: 'var(--mui-palette-primary-dark)', - plainColor: 'var(--mui-palette-primary-main)', - plainHoverBg: - 'rgba(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-hoverOpacity))', - plainActiveBg: 'rgba(var(--mui-palette-primary-mainChannel) / 0.3)', - outlinedBorder: 'rgba(var(--mui-palette-primary-mainChannel) / 0.5)', - outlinedColor: 'var(--mui-palette-primary-main)', - outlinedHoverBg: - 'rgba(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-hoverOpacity))', - outlinedHoverBorder: 'var(--mui-palette-primary-main)', - outlinedActiveBg: 'rgba(var(--mui-palette-primary-mainChannel) / 0.3)', - }, - neutral: { - ...grey, - }, - divider: 'var(--mui-palette-divider)', - text: { - tertiary: 'rgba(255 255 255 / 0.5)', - }, - }, - }, - }, - fontFamily: { - display: '"Roboto","Helvetica","Arial",sans-serif', - body: '"Roboto","Helvetica","Arial",sans-serif', - }, - shadow: { - xs: `var(--mui-shadowRing), ${muiTheme.shadows[1]}`, - sm: `var(--mui-shadowRing), ${muiTheme.shadows[2]}`, - md: `var(--mui-shadowRing), ${muiTheme.shadows[4]}`, - lg: `var(--mui-shadowRing), ${muiTheme.shadows[8]}`, - xl: `var(--mui-shadowRing), ${muiTheme.shadows[12]}`, - }, -}); - -const mergedTheme = { - ...joyTheme, - ...muiTheme, - colorSchemes: deepmerge(joyTheme.colorSchemes, muiTheme.colorSchemes), - typography: { - ...joyTheme.typography, - ...muiTheme.typography, - }, - zIndex: { - ...joyTheme.zIndex, - ...muiTheme.zIndex, - }, -}; - -mergedTheme.generateCssVars = (colorScheme) => ({ - css: { - ...joyTheme.generateCssVars(colorScheme).css, - ...muiTheme.generateCssVars(colorScheme).css, - }, - vars: deepmerge( - joyTheme.generateCssVars(colorScheme).vars, - muiTheme.generateCssVars(colorScheme).vars, - ), -}); -mergedTheme.unstable_sxConfig = { - ...muiTheme.unstable_sxConfig, - ...joyTheme.unstable_sxConfig, -}; +const joyTheme = extendJoyTheme(); const JoyField = React.forwardRef((props, inputRef) => { const { @@ -140,7 +43,12 @@ const JoyField = React.forwardRef((props, inputRef) => { {label} @@ -305,25 +213,27 @@ JoyDatePicker.propTypes = { function SyncThemeMode({ mode }) { const { setMode } = useColorScheme(); + const { setMode: setMaterialMode } = useMaterialColorScheme(); React.useEffect(() => { setMode(mode); - }, [mode, setMode]); + setMaterialMode(mode); + }, [mode, setMode, setMaterialMode]); return null; } export default function PickerWithJoyField() { - const { - palette: { mode }, - } = useTheme(); + const materialTheme = useMaterialTheme(); return ( - - - - - - - - - + + + + + + + + + + + ); } diff --git a/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx b/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx index 45c3678da9cfe..ccc0e768af093 100644 --- a/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx +++ b/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx @@ -1,14 +1,17 @@ import * as React from 'react'; import { Dayjs } from 'dayjs'; -import { deepmerge } from '@mui/utils'; -import { blue, grey } from '@mui/material/colors'; import { - Experimental_CssVarsProvider as CssVarsProvider, - experimental_extendTheme as extendMuiTheme, - useTheme, - useColorScheme, + useTheme as useMaterialTheme, + useColorScheme as useMaterialColorScheme, + Experimental_CssVarsProvider as MaterialCssVarsProvider, } from '@mui/material/styles'; -import { extendTheme as extendJoyTheme, styled } from '@mui/joy/styles'; +import { + extendTheme as extendJoyTheme, + useColorScheme, + styled, + CssVarsProvider, + THEME_ID, +} from '@mui/joy/styles'; import { useSlotProps } from '@mui/base/utils'; import Input, { InputProps } from '@mui/joy/Input'; import Stack, { StackProps } from '@mui/joy/Stack'; @@ -40,107 +43,8 @@ import { FieldSection, } from '@mui/x-date-pickers-pro'; -const muiTheme = extendMuiTheme(); - -const joyTheme = extendJoyTheme({ - cssVarPrefix: 'mui', - colorSchemes: { - light: { - palette: { - primary: { - ...blue, - solidColor: 'var(--mui-palette-primary-contrastText)', - solidBg: 'var(--mui-palette-primary-main)', - solidHoverBg: 'var(--mui-palette-primary-dark)', - plainColor: 'var(--mui-palette-primary-main)', - plainHoverBg: - 'rgba(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-hoverOpacity))', - plainActiveBg: 'rgba(var(--mui-palette-primary-mainChannel) / 0.3)', - outlinedBorder: 'rgba(var(--mui-palette-primary-mainChannel) / 0.5)', - outlinedColor: 'var(--mui-palette-primary-main)', - outlinedHoverBg: - 'rgba(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-hoverOpacity))', - outlinedHoverBorder: 'var(--mui-palette-primary-main)', - outlinedActiveBg: 'rgba(var(--mui-palette-primary-mainChannel) / 0.3)', - }, - neutral: { - ...grey, - }, - divider: 'var(--mui-palette-divider)', - text: { - tertiary: 'rgba(0 0 0 / 0.56)', - }, - }, - }, - dark: { - palette: { - primary: { - ...blue, - solidColor: 'var(--mui-palette-primary-contrastText)', - solidBg: 'var(--mui-palette-primary-main)', - solidHoverBg: 'var(--mui-palette-primary-dark)', - plainColor: 'var(--mui-palette-primary-main)', - plainHoverBg: - 'rgba(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-hoverOpacity))', - plainActiveBg: 'rgba(var(--mui-palette-primary-mainChannel) / 0.3)', - outlinedBorder: 'rgba(var(--mui-palette-primary-mainChannel) / 0.5)', - outlinedColor: 'var(--mui-palette-primary-main)', - outlinedHoverBg: - 'rgba(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-hoverOpacity))', - outlinedHoverBorder: 'var(--mui-palette-primary-main)', - outlinedActiveBg: 'rgba(var(--mui-palette-primary-mainChannel) / 0.3)', - }, - neutral: { - ...grey, - }, - divider: 'var(--mui-palette-divider)', - text: { - tertiary: 'rgba(255 255 255 / 0.5)', - }, - }, - }, - }, - fontFamily: { - display: '"Roboto","Helvetica","Arial",sans-serif', - body: '"Roboto","Helvetica","Arial",sans-serif', - }, - shadow: { - xs: `var(--mui-shadowRing), ${muiTheme.shadows[1]}`, - sm: `var(--mui-shadowRing), ${muiTheme.shadows[2]}`, - md: `var(--mui-shadowRing), ${muiTheme.shadows[4]}`, - lg: `var(--mui-shadowRing), ${muiTheme.shadows[8]}`, - xl: `var(--mui-shadowRing), ${muiTheme.shadows[12]}`, - }, -}); - -const mergedTheme = { - ...joyTheme, - ...muiTheme, - colorSchemes: deepmerge(joyTheme.colorSchemes, muiTheme.colorSchemes), - typography: { - ...joyTheme.typography, - ...muiTheme.typography, - }, - zIndex: { - ...joyTheme.zIndex, - ...muiTheme.zIndex, - }, -} as unknown as ReturnType; +const joyTheme = extendJoyTheme(); -mergedTheme.generateCssVars = (colorScheme) => ({ - css: { - ...joyTheme.generateCssVars(colorScheme).css, - ...muiTheme.generateCssVars(colorScheme).css, - }, - vars: deepmerge( - joyTheme.generateCssVars(colorScheme).vars, - muiTheme.generateCssVars(colorScheme).vars, - ) as any, -}); -mergedTheme.unstable_sxConfig = { - ...muiTheme.unstable_sxConfig, - ...joyTheme.unstable_sxConfig, -}; interface JoyFieldProps extends InputProps { label?: React.ReactNode; InputProps?: { @@ -170,7 +74,12 @@ const JoyField = React.forwardRef( {label} @@ -336,25 +245,27 @@ function JoyDatePicker(props: DatePickerProps) { */ function SyncThemeMode({ mode }: { mode: 'light' | 'dark' }) { const { setMode } = useColorScheme(); + const { setMode: setMaterialMode } = useMaterialColorScheme(); React.useEffect(() => { setMode(mode); - }, [mode, setMode]); + setMaterialMode(mode); + }, [mode, setMode, setMaterialMode]); return null; } export default function PickerWithJoyField() { - const { - palette: { mode }, - } = useTheme(); + const materialTheme = useMaterialTheme(); return ( - - - - - - - - - + + + + + + + + + + + ); } diff --git a/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx.preview b/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx.preview index b09f829e34013..1d5a3e61ad9af 100644 --- a/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx.preview +++ b/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx.preview @@ -1,9 +1,11 @@ - - - - - - - - - \ No newline at end of file + + + + + + + + + + + \ No newline at end of file From fca72bf68577825e810082bb61b8f5ba9ddae040 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Wed, 26 Apr 2023 07:54:50 +0200 Subject: [PATCH 05/80] [charts] Link the Gantt Charts issue in the docs (#8739) --- docs/data/charts/gantt/gantt.md | 15 +++++++++++++++ docs/data/charts/lines/lines.md | 2 +- docs/data/pages.ts | 1 + docs/pages/x/react-charts/gantt.js | 7 +++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 docs/data/charts/gantt/gantt.md create mode 100644 docs/pages/x/react-charts/gantt.js diff --git a/docs/data/charts/gantt/gantt.md b/docs/data/charts/gantt/gantt.md new file mode 100644 index 0000000000000..bf9963b1807dc --- /dev/null +++ b/docs/data/charts/gantt/gantt.md @@ -0,0 +1,15 @@ +--- +product: charts +title: Charts - Gantt +--- + +# Charts - Sankey + +

Gantt charts can illustrate a product schedule and the relationships between its various activities.

+ +> ⚠️ This feature isn't implemented yet. It's coming. +> +> 👍 Upvote [issue #8732](https://github.com/mui/mui-x/issues/8732) if you want to see it land faster. +> +> 💬 To have a solution that meets your needs, leave a comment on the [same issue](https://github.com/mui/mui-x/issues/8732). +> If you already have a use case for this component, or if you are facing a pain-point with your current solution. diff --git a/docs/data/charts/lines/lines.md b/docs/data/charts/lines/lines.md index 9e1020a2d3a87..bda12beb154c6 100644 --- a/docs/data/charts/lines/lines.md +++ b/docs/data/charts/lines/lines.md @@ -5,7 +5,7 @@ title: Charts - Lines # Charts - Lines -

Chart lines can express qualities about data, such as hierarchy, highlights, and comparisons.

+

Line charts can express qualities about data, such as hierarchy, highlights, and comparisons.

## Overview diff --git a/docs/data/pages.ts b/docs/data/pages.ts index 9c4a3d39c3c8b..df4a29b854a21 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -447,6 +447,7 @@ const pages: MuiPage[] = [ { pathname: '/x/react-charts/scatter', title: '🚧 Scatter' }, { pathname: '/x/react-charts/heat-map', title: '🚧 Heat map' }, { pathname: '/x/react-charts/funnel', title: '🚧 Funnel' }, + { pathname: '/x/react-charts/gantt', title: '🚧 Gantt' }, { pathname: '/x/react-charts/radar', title: '🚧 Radar' }, { pathname: '/x/react-charts/sankey', title: '🚧 Sankey' }, { pathname: '/x/react-charts/tree-map', title: '🚧 Tree map' }, diff --git a/docs/pages/x/react-charts/gantt.js b/docs/pages/x/react-charts/gantt.js new file mode 100644 index 0000000000000..14b3b86973e76 --- /dev/null +++ b/docs/pages/x/react-charts/gantt.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/charts/gantt/gantt.md?@mui/markdown'; + +export default function Page() { + return ; +} From 702b01ccab2ed9c1fd5bf96c1144b2110188332a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 08:46:36 +0200 Subject: [PATCH 06/80] Bump rimraf to ^5.0.0 (#8645) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/package.json | 2 +- yarn.lock | 86 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/docs/package.json b/docs/package.json index 2cf6ccbeea23d..04ae8fea3cbf1 100644 --- a/docs/package.json +++ b/docs/package.json @@ -82,7 +82,7 @@ "react-runner": "^1.0.3", "react-simple-code-editor": "^0.13.1", "recast": "^0.22.0", - "rimraf": "^4.4.1", + "rimraf": "^5.0.0", "styled-components": "^5.3.10", "stylis": "^4.1.3", "stylis-plugin-rtl": "^2.1.1", diff --git a/yarn.lock b/yarn.lock index 1e955994908db..47adb88ef3c3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2595,6 +2595,11 @@ dependencies: esquery "^1.0.1" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@playwright/test@1.32.3": version "1.32.3" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.32.3.tgz#75be8346d4ef289896835e1d2a86fdbe3d9be92a" @@ -4820,7 +4825,7 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -cliui@^7.0.2: +cliui@^7.0.2, cliui@^7.0.4: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== @@ -7133,6 +7138,14 @@ foreground-child@^2.0.0: cross-spawn "^7.0.0" signal-exit "^3.0.2" +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -7519,6 +7532,17 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.0.0: + version "10.2.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.2.2.tgz#ce2468727de7e035e8ecf684669dc74d0526ab75" + integrity sha512-Xsa0BcxIC6th9UwNjZkhrMtNo/MnyRL8jGCP+uEwhA5oFOCY1f2s1/oNKY47xQ0Bg5nkjsfAEIej1VeH62bDDQ== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.0.3" + minimatch "^9.0.0" + minipass "^5.0.0" + path-scurry "^1.7.0" + glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7, glob@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -8611,6 +8635,15 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jackspeak@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.1.0.tgz#69831fe5346532888f279102f39fc4452ebbe6c2" + integrity sha512-DiEwVPqsieUzZBNxQ2cxznmFzfg/AMgJUjYw5xl6rSmCxAQXECcbSdwcLM6Ds6T09+SBfSNCGPhYUoQ96P4h7A== + dependencies: + cliui "^7.0.4" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -9540,10 +9573,10 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lru-cache@*, lru-cache@^7.14.1, lru-cache@^7.18.3, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: - version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +lru-cache@*, lru-cache@^9.0.0: + version "9.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.1.tgz#c58a93de58630b688de39ad04ef02ef26f1902f1" + integrity sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A== lru-cache@^4.0.1: version "4.1.5" @@ -9567,6 +9600,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.18.3, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + luxon@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.3.0.tgz#d73ab5b5d2b49a461c47cedbc7e73309b4805b48" @@ -9918,6 +9956,13 @@ minimatch@^7.4.1, minimatch@^7.4.2: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.0.tgz#bfc8e88a1c40ffd40c172ddac3decb8451503b56" + integrity sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -9997,11 +10042,16 @@ minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: dependencies: yallist "^4.0.0" -minipass@^4.0.0, minipass@^4.0.2, minipass@^4.2.4: +minipass@^4.0.0, minipass@^4.2.4: version "4.2.5" resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -11317,13 +11367,13 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" - integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== +path-scurry@^1.6.1, path-scurry@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.7.0.tgz#99c741a2cfbce782294a39994d63748b5a24f6db" + integrity sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg== dependencies: - lru-cache "^7.14.1" - minipass "^4.0.2" + lru-cache "^9.0.0" + minipass "^5.0.0" path-to-regexp@0.1.7: version "0.1.7" @@ -12429,6 +12479,13 @@ rimraf@^4.4.1: dependencies: glob "^9.2.0" +rimraf@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.0.tgz#5bda14e410d7e4dd522154891395802ce032c2cb" + integrity sha512-Jf9llaP+RvaEVS5nPShYFhtXIrb3LRKP281ib3So0KkeZKo2wIKyq0Re7TOSwanasA423PSr6CCIL4bP6T040g== + dependencies: + glob "^10.0.0" + rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -12747,6 +12804,11 @@ signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, s resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.1.tgz#96a61033896120ec9335d96851d902cc98f0ba2a" + integrity sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw== + sigstore@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.2.0.tgz#ae5b31dac75c2d31e7873897e2862f0d0b205bce" From f77a107343e71332e83486bdf42529364b65e411 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 08:46:54 +0200 Subject: [PATCH 07/80] Bump karma-chrome-launcher to ^3.2.0 (#8720) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index feec5b1db646b..0d87b6c0ada2a 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "jss-plugin-template": "^10.10.0", "jss-rtl": "^0.3.0", "karma": "^6.4.2", - "karma-chrome-launcher": "^3.1.1", + "karma-chrome-launcher": "^3.2.0", "karma-mocha": "^2.0.1", "karma-sourcemap-loader": "^0.4.0", "karma-webpack": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index 47adb88ef3c3b..7727196b4cdb5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9020,10 +9020,10 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" -karma-chrome-launcher@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz#baca9cc071b1562a1db241827257bfe5cab597ea" - integrity sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ== +karma-chrome-launcher@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" + integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== dependencies: which "^1.2.1" From 15a1a96a1f9dcb0a468bea08cf681808e7bb0db0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 09:29:41 +0200 Subject: [PATCH 08/80] Bump MUI Core (#8554) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: delangle --- benchmark/package.json | 2 +- docs/package.json | 10 +-- .../multi-input-date-range-field.json | 2 +- .../multi-input-date-time-range-field.json | 2 +- .../multi-input-time-range-field.json | 2 +- package.json | 4 +- .../grid/x-data-grid-generator/package.json | 2 +- .../grid/x-data-grid-premium/package.json | 2 +- packages/grid/x-data-grid-pro/package.json | 2 +- packages/grid/x-data-grid/package.json | 2 +- packages/x-date-pickers-pro/package.json | 2 +- .../MultiInputDateRangeField.tsx | 2 +- .../MultiInputDateTimeRangeField.tsx | 2 +- .../MultiInputTimeRangeField.tsx | 2 +- packages/x-date-pickers/package.json | 2 +- packages/x-license-pro/package.json | 2 +- yarn.lock | 80 +++++++++---------- 17 files changed, 61 insertions(+), 61 deletions(-) diff --git a/benchmark/package.json b/benchmark/package.json index d0af3a6107ff5..58009b1244bf5 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -10,7 +10,7 @@ "@emotion/styled": "^11.10.6", "@material-ui/core": "^5.0.0-beta.5", "@material-ui/icons": "^5.0.0-beta.5", - "@mui/material": "^5.12.0", + "@mui/material": "^5.12.2", "@mui/x-data-grid": "^4.0.0", "ag-grid-community": "^29.3.2", "ag-grid-react": "^29.3.2", diff --git a/docs/package.json b/docs/package.json index 04ae8fea3cbf1..0c177c7cfde06 100644 --- a/docs/package.json +++ b/docs/package.json @@ -29,11 +29,11 @@ "@emotion/react": "^11.10.6", "@emotion/server": "^11.10.0", "@emotion/styled": "^11.10.6", - "@mui/icons-material": "^5.11.11", - "@mui/joy": "^5.0.0-alpha.75", - "@mui/material": "^5.12.0", - "@mui/styles": "^5.11.16", - "@mui/utils": "^5.11.13", + "@mui/icons-material": "^5.11.16", + "@mui/joy": "^5.0.0-alpha.77", + "@mui/material": "^5.12.2", + "@mui/styles": "^5.12.0", + "@mui/utils": "^5.12.0", "@trendmicro/react-interpolate": "^0.5.5", "@types/lodash": "^4.14.194", "@types/moment-hijri": "^2.1.0", diff --git a/docs/translations/api-docs/date-pickers/multi-input-date-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-date-range-field.json index d7795d73f2263..532aac004c5b8 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-date-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-date-range-field.json @@ -24,7 +24,7 @@ "slots": "Overridable component slots.", "spacing": "Defines the space between immediate children.", "sx": "The system prop, which allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", - "useFlexGap": "If true, the CSS flexbox gap is used instead of applying margin to children.
While CSS gap removes the known limitations, it is not fully supported in some browsers. We recommend checking https://caniuse.com/?search=flex%20gap before using this flag.
To enable this flag globally, follow the theme's default props configuration.", + "useFlexGap": "If true, the CSS flexbox gap is used instead of applying margin to children.
While CSS gap removes the known limitations, it is not fully supported in some browsers. We recommend checking https://caniuse.com/?search=flex%20gap before using this flag.
To enable this flag globally, follow the theme's default props configuration.", "value": "The selected value. Used when the component is controlled." }, "classDescriptions": {}, diff --git a/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field.json index e916c6f37835c..a12f3d48611b7 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field.json @@ -33,7 +33,7 @@ "slots": "Overridable component slots.", "spacing": "Defines the space between immediate children.", "sx": "The system prop, which allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", - "useFlexGap": "If true, the CSS flexbox gap is used instead of applying margin to children.
While CSS gap removes the known limitations, it is not fully supported in some browsers. We recommend checking https://caniuse.com/?search=flex%20gap before using this flag.
To enable this flag globally, follow the theme's default props configuration.", + "useFlexGap": "If true, the CSS flexbox gap is used instead of applying margin to children.
While CSS gap removes the known limitations, it is not fully supported in some browsers. We recommend checking https://caniuse.com/?search=flex%20gap before using this flag.
To enable this flag globally, follow the theme's default props configuration.", "value": "The selected value. Used when the component is controlled." }, "classDescriptions": {}, diff --git a/docs/translations/api-docs/date-pickers/multi-input-time-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-time-range-field.json index cf748a9c4eb29..944b30e82ad0b 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-time-range-field.json @@ -28,7 +28,7 @@ "slots": "Overridable slots.", "spacing": "Defines the space between immediate children.", "sx": "The system prop, which allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", - "useFlexGap": "If true, the CSS flexbox gap is used instead of applying margin to children.
While CSS gap removes the known limitations, it is not fully supported in some browsers. We recommend checking https://caniuse.com/?search=flex%20gap before using this flag.
To enable this flag globally, follow the theme's default props configuration.", + "useFlexGap": "If true, the CSS flexbox gap is used instead of applying margin to children.
While CSS gap removes the known limitations, it is not fully supported in some browsers. We recommend checking https://caniuse.com/?search=flex%20gap before using this flag.
To enable this flag globally, follow the theme's default props configuration.", "value": "The selected value. Used when the component is controlled." }, "classDescriptions": {}, diff --git a/package.json b/package.json index 0d87b6c0ada2a..4f289fc438e60 100644 --- a/package.json +++ b/package.json @@ -85,8 +85,8 @@ "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", "@mnajdova/enzyme-adapter-react-18": "^0.2.0", - "@mui/icons-material": "^5.11.11", - "@mui/material": "^5.12.0", + "@mui/icons-material": "^5.11.16", + "@mui/material": "^5.12.2", "@mui/monorepo": "https://github.com/mui/material-ui.git#master", "@mui/utils": "^5.12.0", "@octokit/plugin-retry": "^4.1.3", diff --git a/packages/grid/x-data-grid-generator/package.json b/packages/grid/x-data-grid-generator/package.json index 055b1f9782db1..b437541340943 100644 --- a/packages/grid/x-data-grid-generator/package.json +++ b/packages/grid/x-data-grid-generator/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/base": "^5.0.0-alpha.123", + "@mui/base": "^5.0.0-alpha.127", "@mui/x-data-grid-premium": "6.2.1", "chance": "^1.1.11", "clsx": "^1.2.1", diff --git a/packages/grid/x-data-grid-premium/package.json b/packages/grid/x-data-grid-premium/package.json index cc0f6d4f06cbc..ee323efb74c24 100644 --- a/packages/grid/x-data-grid-premium/package.json +++ b/packages/grid/x-data-grid-premium/package.json @@ -43,7 +43,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.11.13", + "@mui/utils": "^5.12.0", "@mui/x-data-grid": "6.2.1", "@mui/x-data-grid-pro": "6.2.1", "@mui/x-license-pro": "6.0.4", diff --git a/packages/grid/x-data-grid-pro/package.json b/packages/grid/x-data-grid-pro/package.json index e372c94c46d44..05738da8208c3 100644 --- a/packages/grid/x-data-grid-pro/package.json +++ b/packages/grid/x-data-grid-pro/package.json @@ -43,7 +43,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.11.13", + "@mui/utils": "^5.12.0", "@mui/x-data-grid": "6.2.1", "@mui/x-license-pro": "6.0.4", "@types/format-util": "^1.0.2", diff --git a/packages/grid/x-data-grid/package.json b/packages/grid/x-data-grid/package.json index af26545c62df2..347e113c143bd 100644 --- a/packages/grid/x-data-grid/package.json +++ b/packages/grid/x-data-grid/package.json @@ -47,7 +47,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.11.13", + "@mui/utils": "^5.12.0", "clsx": "^1.2.1", "prop-types": "^15.8.1", "reselect": "^4.1.8" diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index 083721b37fd99..cc4da3e02b38f 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -45,7 +45,7 @@ "@date-io/date-fns": "^2.16.0", "@date-io/luxon": "^2.16.1", "@date-io/moment": "^2.16.1", - "@mui/utils": "^5.11.13", + "@mui/utils": "^5.12.0", "@mui/x-date-pickers": "6.2.1", "@mui/x-license-pro": "6.0.4", "clsx": "^1.2.1", diff --git a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx index 69a1ac6c1a49f..fc21f8ce9c64b 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx @@ -359,7 +359,7 @@ MultiInputDateRangeField.propTypes = { /** * If `true`, the CSS flexbox `gap` is used instead of applying `margin` to children. * - * While CSS `gap` removes the [known limitations](https://mui.com/joy-ui/react-stack#limitations), + * While CSS `gap` removes the [known limitations](https://mui.com/joy-ui/react-stack/#limitations), * it is not fully supported in some browsers. We recommend checking https://caniuse.com/?search=flex%20gap before using this flag. * * To enable this flag globally, follow the [theme's default props](https://mui.com/material-ui/customization/theme-components/#default-props) configuration. diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx index 9d06af9c7bf95..77d8447e86a52 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx @@ -426,7 +426,7 @@ MultiInputDateTimeRangeField.propTypes = { /** * If `true`, the CSS flexbox `gap` is used instead of applying `margin` to children. * - * While CSS `gap` removes the [known limitations](https://mui.com/joy-ui/react-stack#limitations), + * While CSS `gap` removes the [known limitations](https://mui.com/joy-ui/react-stack/#limitations), * it is not fully supported in some browsers. We recommend checking https://caniuse.com/?search=flex%20gap before using this flag. * * To enable this flag globally, follow the [theme's default props](https://mui.com/material-ui/customization/theme-components/#default-props) configuration. diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx index c4d6ff1664fd0..9962e77438b6d 100644 --- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx @@ -393,7 +393,7 @@ MultiInputTimeRangeField.propTypes = { /** * If `true`, the CSS flexbox `gap` is used instead of applying `margin` to children. * - * While CSS `gap` removes the [known limitations](https://mui.com/joy-ui/react-stack#limitations), + * While CSS `gap` removes the [known limitations](https://mui.com/joy-ui/react-stack/#limitations), * it is not fully supported in some browsers. We recommend checking https://caniuse.com/?search=flex%20gap before using this flag. * * To enable this flag globally, follow the [theme's default props](https://mui.com/material-ui/customization/theme-components/#default-props) configuration. diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 5189705ee9d63..24402135234d1 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -51,7 +51,7 @@ "@date-io/hijri": "^2.16.1", "@date-io/jalaali": "^2.16.1", "@date-io/moment": "^2.16.1", - "@mui/utils": "^5.11.13", + "@mui/utils": "^5.12.0", "@types/react-transition-group": "^4.4.5", "clsx": "^1.2.1", "prop-types": "^15.8.1", diff --git a/packages/x-license-pro/package.json b/packages/x-license-pro/package.json index 42a9ae5185fcf..966987cfe5c35 100644 --- a/packages/x-license-pro/package.json +++ b/packages/x-license-pro/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.11.13" + "@mui/utils": "^5.12.0" }, "peerDependencies": { "react": "^17.0.2 || ^18.0.0" diff --git a/yarn.lock b/yarn.lock index 7727196b4cdb5..d89904188de97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1831,10 +1831,10 @@ react-test-renderer "^18.0.0" semver "^5.7.0" -"@mui/base@5.0.0-alpha.125", "@mui/base@^5.0.0-alpha.123": - version "5.0.0-alpha.125" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.125.tgz#6bb3df0760d87aa9186ed9add29b3ae96dee4aa8" - integrity sha512-hAHJJ97SATu6SrkLH/HsAayK1zMZt89lrWyKuAInBKVyn363H78d1MnwyZwre9vDK5MrPoDL/NnZxtAXhwTnBA== +"@mui/base@5.0.0-alpha.127", "@mui/base@^5.0.0-alpha.127": + version "5.0.0-alpha.127" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.127.tgz#bc61eaf1fd31094c939b6521cfea21643207c3b4" + integrity sha512-FoRQd0IOH9MnfyL5yXssyQRnC4vXI+1bwkU1idr+wNkP1ZfxE+JsThHcfl1dy5azLssVUGTtQFD9edQLdbyJog== dependencies: "@babel/runtime" "^7.21.0" "@emotion/is-prop-valid" "^1.2.0" @@ -1845,27 +1845,27 @@ prop-types "^15.8.1" react-is "^18.2.0" -"@mui/core-downloads-tracker@^5.12.0": - version "5.12.0" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.12.0.tgz#2f0dcbf07d4b032bc6f743fdb96fc2c10681da6e" - integrity sha512-1hoFIdlLI0sG+mkJgm70FjgIVpfLcE1vxPtNolg1tLFXrvbXGUYp9NHy3d6c41nDkg2OajuVS+Mn6A8UirFuMw== +"@mui/core-downloads-tracker@^5.12.2": + version "5.12.2" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.12.2.tgz#4a0186d25b01d693171366e1c00de0e7c8c35f6a" + integrity sha512-Qn7dy8tql6T0hY6gTFPkpWlnqVVFGu5Z6QzEzUSzzmLZpfAx4kf8sFz0PHiB7gU5yrqcZF9picMx1shpRY/rXw== -"@mui/icons-material@^5.11.11": - version "5.11.11" - resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.11.11.tgz#d4e01bd405b0dac779f5e060586277f91f3acb6e" - integrity sha512-Eell3ADmQVE8HOpt/LZ3zIma8JSvPh3XgnhwZLT0k5HRqZcd6F/QDHc7xsWtgz09t+UEFvOYJXjtrwKmLdwwpw== +"@mui/icons-material@^5.11.16": + version "5.11.16" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.11.16.tgz#417fa773c56672e39d6ccfed9ac55591985f0d38" + integrity sha512-oKkx9z9Kwg40NtcIajF9uOXhxiyTZrrm9nmIJ4UjkU2IdHpd4QVLbCc/5hZN/y0C6qzi2Zlxyr9TGddQx2vx2A== dependencies: "@babel/runtime" "^7.21.0" -"@mui/joy@^5.0.0-alpha.75": - version "5.0.0-alpha.75" - resolved "https://registry.yarnpkg.com/@mui/joy/-/joy-5.0.0-alpha.75.tgz#8e49e4e9dcb764314bd23eea66f378d1f71d6c03" - integrity sha512-hiMA/9APUIs7yCq5J9hzHvbdGIBs2kBq3oO9cI9g8sz5VpVn4On4znjTSKFlphQ10udvCAa49EFqebfKSOsxCQ== +"@mui/joy@^5.0.0-alpha.77": + version "5.0.0-alpha.77" + resolved "https://registry.yarnpkg.com/@mui/joy/-/joy-5.0.0-alpha.77.tgz#d1af526866366a63ed30e7dc893657a4195bb172" + integrity sha512-X0XbswA90mmwJwOaJNxm3sQgA24NOwMEoCxQ5VbwO/O+8ZoDQ5Ngb6l/VI0FfXJJDL52p793QMAuiDHLef52hQ== dependencies: "@babel/runtime" "^7.21.0" - "@mui/base" "5.0.0-alpha.125" - "@mui/core-downloads-tracker" "^5.12.0" - "@mui/system" "^5.12.0" + "@mui/base" "5.0.0-alpha.127" + "@mui/core-downloads-tracker" "^5.12.2" + "@mui/system" "^5.12.1" "@mui/types" "^7.2.4" "@mui/utils" "^5.12.0" clsx "^1.2.1" @@ -1873,15 +1873,15 @@ prop-types "^15.8.1" react-is "^18.2.0" -"@mui/material@^5.12.0": - version "5.12.0" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.12.0.tgz#ab553e61f4446cf3325667adfbd4effb3c1e1237" - integrity sha512-IMellv153zJ6+xfhLWgXpAm/9hsX8qE6gP66xWcW/Pf2B8ubyVhmkTXsp8pAJxk81D6p/EyYcnAjo5DiDVkj9g== +"@mui/material@^5.12.2": + version "5.12.2" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.12.2.tgz#c3fcc94e523d9e673e2e045dfad04d12ab454a80" + integrity sha512-XOVy6fVC0rI2dEwDq/1s4Te2hewTUe6lznzeVnruyATGkdmM06WnHqkZOoLVIWo9hWwAxpcgTDcAIVpFtt1nrw== dependencies: "@babel/runtime" "^7.21.0" - "@mui/base" "5.0.0-alpha.125" - "@mui/core-downloads-tracker" "^5.12.0" - "@mui/system" "^5.12.0" + "@mui/base" "5.0.0-alpha.127" + "@mui/core-downloads-tracker" "^5.12.2" + "@mui/system" "^5.12.1" "@mui/types" "^7.2.4" "@mui/utils" "^5.12.0" "@types/react-transition-group" "^4.4.5" @@ -1895,7 +1895,7 @@ version "5.12.1" resolved "https://github.com/mui/material-ui.git#710dc38963b965f1aad4d58dfd325dd08b7d1592" -"@mui/private-theming@^5.11.13", "@mui/private-theming@^5.12.0": +"@mui/private-theming@^5.12.0": version "5.12.0" resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.12.0.tgz#5f1e6fd09b1447c387fdac1eef7f23efca5c6d69" integrity sha512-w5dwMen1CUm1puAtubqxY9BIzrBxbOThsg2iWMvRJmWyJAPdf3Z583fPXpqeA2lhTW79uH2jajk5Ka4FuGlTPg== @@ -1914,16 +1914,16 @@ csstype "^3.1.2" prop-types "^15.8.1" -"@mui/styles@^5.11.16": - version "5.11.16" - resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.11.16.tgz#66664b52d15c82b2f7899c8beb2dee2cd8790233" - integrity sha512-KoJubDToD4jqslY4f2K7dzLQoEOWHWnh0qGp8ybFeQBAyffIcuBGEOYqe0YbsJKgU7/Qv+nTHtgvl/y6OS1w3w== +"@mui/styles@^5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.12.0.tgz#a41a4337996db2cd23a828bb28721ea183819e87" + integrity sha512-X7obkgZTd9X+7igqwKKe8pEncyXYdUCNmyJfHruV9TSc6LThoI29OYs6hkN6n+7ueNli+YDKdZ+TCoC1GpJuOw== dependencies: "@babel/runtime" "^7.21.0" "@emotion/hash" "^0.9.0" - "@mui/private-theming" "^5.11.13" - "@mui/types" "^7.2.3" - "@mui/utils" "^5.11.13" + "@mui/private-theming" "^5.12.0" + "@mui/types" "^7.2.4" + "@mui/utils" "^5.12.0" clsx "^1.2.1" csstype "^3.1.2" hoist-non-react-statics "^3.3.2" @@ -1937,10 +1937,10 @@ jss-plugin-vendor-prefixer "^10.10.0" prop-types "^15.8.1" -"@mui/system@^5.12.0": - version "5.12.0" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.12.0.tgz#b47e73917d28db00535c79c043ad04edc5906475" - integrity sha512-Zi+WHuiJfK1ya+9+oeJQ1rLIBdY8CGDYT5oVlQg/6kIuyiCaE6SnN9PVzxBxfY77wHuOPwz4kxcPe9srdZc12Q== +"@mui/system@^5.12.1": + version "5.12.1" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.12.1.tgz#8452bc03159f0a6725b96bde1dee1316e308231b" + integrity sha512-Po+sicdV3bbRYXdU29XZaHPZrW7HUYUqU1qCu77GCCEMbahC756YpeyefdIYuPMUg0OdO3gKIUfDISBrkjJL+w== dependencies: "@babel/runtime" "^7.21.0" "@mui/private-theming" "^5.12.0" @@ -1951,12 +1951,12 @@ csstype "^3.1.2" prop-types "^15.8.1" -"@mui/types@^7.2.3", "@mui/types@^7.2.4": +"@mui/types@^7.2.4": version "7.2.4" resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328" integrity sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA== -"@mui/utils@^5.11.13", "@mui/utils@^5.12.0": +"@mui/utils@^5.12.0": version "5.12.0" resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.12.0.tgz#284db48b36ac26f3d34076379072c1dc8aed1ad0" integrity sha512-RmQwgzF72p7Yr4+AAUO6j1v2uzt6wr7SWXn68KBsnfVpdOHyclCzH2lr/Xu6YOw9su4JRtdAIYfJFXsS6Cjkmw== From 6025b2e817f49b08ac4ce9bb5453d56eb7c637e7 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Wed, 26 Apr 2023 09:51:53 +0200 Subject: [PATCH 09/80] [docs] Add icons for charts menu (#8752) --- docs/.link-check-errors.txt | 3 ++- docs/data/pages.ts | 16 ++++++++++------ yarn.lock | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/.link-check-errors.txt b/docs/.link-check-errors.txt index e231a14bb5d13..d98a314ee5141 100644 --- a/docs/.link-check-errors.txt +++ b/docs/.link-check-errors.txt @@ -1,6 +1,7 @@ Broken links found by `yarn docs:link-check` that exist: -- https://mui.com/base/api/portal/#props +- https://mui.com/base/react-portal/components-api/#portal - https://mui.com/blog/material-ui-v4-is-out/#premium-themes-store-✨ +- https://mui.com/material-ui/guides/minimizing-bundle-size/#legacy-bundle - https://mui.com/size-snapshot - https://mui.com/x/react-data-grid/migration-v4 diff --git a/docs/data/pages.ts b/docs/data/pages.ts index df4a29b854a21..35c8373b80645 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -1,4 +1,8 @@ import type { MuiPage } from '@mui/monorepo/docs/src/MuiPage'; +import standardNavIcons from '@mui/monorepo/docs/src/modules/components/AppNavIcons'; +import ChartIcon from '@mui/icons-material/BarChartRounded'; +import TableViewIcon from '@mui/icons-material/TableViewRounded'; +import DatePickerIcon from '@mui/icons-material/DateRangeRounded'; const isPreview = process.env.NODE_ENV === 'development' || @@ -9,12 +13,12 @@ const pages: MuiPage[] = [ { pathname: '/blog/mui-x-v6/', title: "✨ What's new in v6? ✨", - icon: 'VisibilityIcon', + icon: standardNavIcons.VisibilityIcon, }, { pathname: '/x/introduction-group', title: 'Introduction', - icon: 'DescriptionIcon', + icon: standardNavIcons.DescriptionIcon, children: [ { pathname: `/x/introduction`, title: 'Overview' }, { pathname: `/x/introduction/installation` }, @@ -26,7 +30,7 @@ const pages: MuiPage[] = [ { pathname: '/x/react-data-grid-group', title: 'Data Grid', - icon: 'TableViewIcon', + icon: TableViewIcon, children: [ { pathname: '/x/react-data-grid', title: 'Overview' }, { pathname: '/x/react-data-grid/demo' }, @@ -174,7 +178,7 @@ const pages: MuiPage[] = [ { pathname: '/x/react-date-pickers-group', title: 'Date and Time Pickers', - icon: 'DatePickerIcon', + icon: DatePickerIcon, children: [ { pathname: '/x/react-date-pickers', title: 'Overview' }, { pathname: '/x/react-date-pickers/getting-started' }, @@ -404,7 +408,7 @@ const pages: MuiPage[] = [ { pathname: '/x/migration-group', title: 'Migration', - icon: 'BookIcon', + icon: standardNavIcons.BookIcon, children: [ { pathname: '/x/migration-v6', @@ -438,7 +442,7 @@ const pages: MuiPage[] = [ { pathname: '/x/react-charts-group', title: 'Charts 🚧', - icon: 'ChartIcon', + icon: ChartIcon, children: [ { pathname: '/x/react-charts', title: '🚧 Overview' }, { pathname: '/x/react-charts/bars', title: '🚧 Bars' }, diff --git a/yarn.lock b/yarn.lock index d89904188de97..24659187c7753 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1893,7 +1893,7 @@ "@mui/monorepo@https://github.com/mui/material-ui.git#master": version "5.12.1" - resolved "https://github.com/mui/material-ui.git#710dc38963b965f1aad4d58dfd325dd08b7d1592" + resolved "https://github.com/mui/material-ui.git#8e8442bc280321ac5fd4ee049e4def51c3dd7a70" "@mui/private-theming@^5.12.0": version "5.12.0" From 7054fe4ca4424cb038a8ebef17ed25ce55c1ef6b Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Wed, 26 Apr 2023 09:58:16 +0200 Subject: [PATCH 10/80] [charts] Adapt line and scatter the band scale (#8701) --- packages/x-charts/src/Highlight/Highlight.tsx | 8 +-- packages/x-charts/src/LineChart/LinePlot.tsx | 15 +++--- .../x-charts/src/ScatterChart/Scatter.tsx | 6 ++- packages/x-charts/src/hooks/useScale.ts | 13 +++++ packages/x-charts/src/internals/getCurve.ts | 51 +++++++++++++++++++ .../x-charts/src/models/seriesType/line.ts | 2 + 6 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 packages/x-charts/src/internals/getCurve.ts diff --git a/packages/x-charts/src/Highlight/Highlight.tsx b/packages/x-charts/src/Highlight/Highlight.tsx index 8f7b8d768f4c9..1b4ab186a6fca 100644 --- a/packages/x-charts/src/Highlight/Highlight.tsx +++ b/packages/x-charts/src/Highlight/Highlight.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { InteractionContext } from '../context/InteractionProvider'; import { CartesianContext } from '../context/CartesianContextProvider'; -import { isBandScale } from '../hooks/useScale'; +import { getValueToPositionMapper, isBandScale } from '../hooks/useScale'; export type HighlightProps = { highlight: { @@ -32,7 +32,7 @@ export const Highlight = React.forwardRef(functi // interactionApi?.listenYAxis(USED_Y_AXIS_ID); // }, [USED_X_AXIS_ID, USED_Y_AXIS_ID, interactionApi]); - if (isBandScale(xScale)) { + if (isBandScale(xScale) && !xHighlight) { if (axis.x === null) { return null; } @@ -51,11 +51,13 @@ export const Highlight = React.forwardRef(functi /> ); } + + const getXPosition = getValueToPositionMapper(xScale); return ( {xHighlight && axis.x !== null && ( { const [xAxisKey, yAxisKey] = key.split('-'); - const xScale = xAxis[xAxisKey].scale; + const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); const yScale = yAxis[yAxisKey].scale; const xData = xAxis[xAxisKey].data; @@ -58,10 +60,10 @@ export function LinePlot() { .x((d) => xScale(d.x)) .y0((d) => yScale(d.y[0])) .y1((d) => yScale(d.y[1])); - return stackingGroups.flatMap((groupIds) => { return groupIds.flatMap((seriesId) => { const stackedData = series[seriesId].stackedData; + const curve = getCurveFactory(series[seriesId].curve); const d3Data = xData?.map((x, index) => ({ x, y: stackedData[index] })); return ( @@ -69,7 +71,7 @@ export function LinePlot() { @@ -83,7 +85,7 @@ export function LinePlot() { {Object.keys(seriesPerAxis).flatMap((key) => { const [xAxisKey, yAxisKey] = key.split('-'); - const xScale = xAxis[xAxisKey].scale; + const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); const yScale = yAxis[yAxisKey].scale; const xData = xAxis[xAxisKey].data; @@ -103,13 +105,14 @@ export function LinePlot() { return stackingGroups.flatMap((groupIds) => { return groupIds.flatMap((seriesId) => { const stackedData = series[seriesId].stackedData; + const curve = getCurveFactory(series[seriesId].curve); const d3Data = xData?.map((x, index) => ({ x, y: stackedData[index] })); return ( @@ -122,7 +125,7 @@ export function LinePlot() { {Object.keys(seriesPerAxis).flatMap((key) => { const [xAxisKey, yAxisKey] = key.split('-'); - const xScale = xAxis[xAxisKey].scale; + const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); const yScale = yAxis[yAxisKey].scale; const xData = xAxis[xAxisKey].data; diff --git a/packages/x-charts/src/ScatterChart/Scatter.tsx b/packages/x-charts/src/ScatterChart/Scatter.tsx index 984ad55fb3e0c..8e91b7895bb40 100644 --- a/packages/x-charts/src/ScatterChart/Scatter.tsx +++ b/packages/x-charts/src/ScatterChart/Scatter.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { ScatterSeriesType } from '../models/seriesType/scatter'; -import { D3Scale } from '../hooks/useScale'; +import { D3Scale, getValueToPositionMapper } from '../hooks/useScale'; import { useInteractionItemProps } from '../hooks/useInteractionItemProps'; export interface ScatterProps { @@ -14,6 +14,8 @@ export interface ScatterProps { export function Scatter(props: ScatterProps) { const { series, xScale, yScale, color, markerSize } = props; + const getXPosition = getValueToPositionMapper(xScale); + const getYPosition = getValueToPositionMapper(yScale); const getInteractionItemProps = useInteractionItemProps(); return ( @@ -24,7 +26,7 @@ export function Scatter(props: ScatterProps) { cx={0} cy={0} r={markerSize} - transform={`translate(${xScale(x as number)}, ${yScale(y as number)})`} + transform={`translate(${getXPosition(x)}, ${getYPosition(y)})`} fill={color} {...getInteractionItemProps({ type: 'scatter', seriesId: series.id, dataIndex })} /> diff --git a/packages/x-charts/src/hooks/useScale.ts b/packages/x-charts/src/hooks/useScale.ts index cfb09fcdea433..bddd2ac03b321 100644 --- a/packages/x-charts/src/hooks/useScale.ts +++ b/packages/x-charts/src/hooks/useScale.ts @@ -50,3 +50,16 @@ export function getScale(scaleName: ScaleName | undefined): D3Scale { export function isBandScale(scale: D3Scale): scale is ScaleBand | ScalePoint { return (scale as ScaleBand | ScalePoint).bandwidth !== undefined; } + +/** + * For a given scale return a function that map value to their position. + * Usefull when dealing with specific scale such as band. + * @param scale The scale to use + * @returns (value: any) => number + */ +export function getValueToPositionMapper(scale: D3Scale) { + if (isBandScale(scale)) { + return (value: any) => scale(value)! + scale.bandwidth() / 2; + } + return (value: any) => scale(value) as number; +} diff --git a/packages/x-charts/src/internals/getCurve.ts b/packages/x-charts/src/internals/getCurve.ts new file mode 100644 index 0000000000000..fa4b8cb3f8b3c --- /dev/null +++ b/packages/x-charts/src/internals/getCurve.ts @@ -0,0 +1,51 @@ +import { + curveCatmullRom, + curveLinear, + curveMonotoneX, + curveMonotoneY, + curveNatural, + curveStep, + curveStepAfter, + curveStepBefore, +} from 'd3-shape'; + +export type CurveType = + | 'catmullRom' + | 'linear' + | 'monotoneX' + | 'monotoneY' + | 'natural' + | 'step' + | 'stepBefore' + | 'stepAfter'; + +export default function getCurveFactory(curveType?: CurveType) { + switch (curveType) { + case 'catmullRom': { + return curveCatmullRom.alpha(0.5); + } + case 'linear': { + return curveLinear; + } + case 'monotoneX': { + return curveMonotoneX; + } + case 'monotoneY': { + return curveMonotoneY; + } + case 'natural': { + return curveNatural; + } + case 'step': { + return curveStep; + } + case 'stepBefore': { + return curveStepBefore; + } + case 'stepAfter': { + return curveStepAfter; + } + default: + return curveMonotoneX; + } +} diff --git a/packages/x-charts/src/models/seriesType/line.ts b/packages/x-charts/src/models/seriesType/line.ts index a4d15cab820a0..146b69de25622 100644 --- a/packages/x-charts/src/models/seriesType/line.ts +++ b/packages/x-charts/src/models/seriesType/line.ts @@ -1,3 +1,4 @@ +import { CurveType } from '../../internals/getCurve'; import { DefaultizedProps } from '../helpers'; import { CartesianSeriesType, CommonSeriesType, DefaultizedCommonSeriesType } from './common'; @@ -6,6 +7,7 @@ export interface LineSeriesType extends CommonSeriesType, CartesianSeriesType { data: number[]; stack?: string; area?: any; + curve?: CurveType; } /** From 9a57d9b930f868468a79b45f3bcb48714a2ac232 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Wed, 26 Apr 2023 10:26:15 +0200 Subject: [PATCH 11/80] [core] Cleanup picker tests (#8652) --- .../x/api/date-pickers/date-time-field.json | 2 + .../pages/x/api/date-pickers/time-picker.json | 1 + .../DateRangeCalendar.test.tsx | 16 +- .../DesktopDateRangePicker.test.tsx | 80 +------- .../describes.DesktopDateRangePicker.test.tsx | 26 ++- .../MobileDateRangePicker.test.tsx | 51 +----- .../describes.MobileDateRangePicker.test.tsx | 31 +++- .../StaticDateRangePicker.test.tsx | 8 - .../tests/describes.DateField.test.tsx | 14 +- .../src/DatePicker/DatePicker.test.tsx | 60 ------ .../src/DatePicker/tests/DatePicker.test.tsx | 20 ++ .../tests/describes.DatePicker.test.tsx | 14 -- .../tests/describes.DateTimeField.test.tsx | 23 ++- .../tests/timezone.DateTimeField.test.tsx | 2 +- .../{ => tests}/DateTimePicker.test.tsx | 10 +- .../{ => tests}/DesktopDatePicker.test.tsx | 164 ++--------------- .../describes.DesktopDatePicker.test.tsx | 3 + .../DesktopDateTimePicker.test.tsx | 156 ---------------- .../tests/DesktopDateTimePicker.test.tsx | 57 ++++++ .../describes.DesktopDateTimePicker.test.tsx | 3 + .../describes.DesktopTimePicker.test.tsx | 8 + .../{ => tests}/MobileDatePicker.test.tsx | 94 +--------- .../tests/describes.MobileDatePicker.test.tsx | 3 + .../{ => tests}/MobileDateTimePicker.test.tsx | 45 +---- .../describes.MobileDateTimePicker.test.tsx | 3 + .../{ => tests}/MobileTimePicker.test.tsx | 9 - .../tests/describes.MobileTimePicker.test.tsx | 3 + .../tests/MonthCalendar.test.tsx | 2 +- .../{ => tests}/StaticDatePicker.test.tsx | 18 +- .../StaticDatePickerKeyboard.test.tsx | 0 .../tests/describes.StaticDatePicker.test.tsx | 17 ++ .../StaticDateTimePicker.test.tsx | 165 ----------------- .../tests/StaticDateTimePicker.test.tsx | 72 ++++++++ .../tests/describes.StaticDatePicker.test.tsx | 17 ++ .../StaticTimePicker.test.tsx | 8 - .../src/TimePicker/tests/TimePicker.test.tsx | 20 ++ .../tests/describePicker/describePicker.tsx | 171 ++++++++++++++++++ .../describePicker/describePicker.types.ts | 9 + .../src/tests/describePicker/index.ts | 1 + ...beValidation.tsx => describeValidation.ts} | 0 .../src/tests/describeValue/describeValue.tsx | 2 +- .../describeValue/testPickerActionBar.tsx | 57 +++++- 42 files changed, 573 insertions(+), 892 deletions(-) rename packages/x-date-pickers-pro/src/DesktopDateRangePicker/{ => tests}/DesktopDateRangePicker.test.tsx (90%) rename packages/x-date-pickers-pro/src/DesktopDateRangePicker/{ => tests}/describes.DesktopDateRangePicker.test.tsx (85%) rename packages/x-date-pickers-pro/src/MobileDateRangePicker/{ => tests}/MobileDateRangePicker.test.tsx (87%) rename packages/x-date-pickers-pro/src/MobileDateRangePicker/{ => tests}/describes.MobileDateRangePicker.test.tsx (77%) delete mode 100644 packages/x-date-pickers/src/DatePicker/DatePicker.test.tsx create mode 100644 packages/x-date-pickers/src/DatePicker/tests/DatePicker.test.tsx delete mode 100644 packages/x-date-pickers/src/DatePicker/tests/describes.DatePicker.test.tsx rename packages/x-date-pickers/src/DateTimePicker/{ => tests}/DateTimePicker.test.tsx (67%) rename packages/x-date-pickers/src/DesktopDatePicker/{ => tests}/DesktopDatePicker.test.tsx (71%) delete mode 100644 packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.test.tsx create mode 100644 packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx rename packages/x-date-pickers/src/MobileDatePicker/{ => tests}/MobileDatePicker.test.tsx (68%) rename packages/x-date-pickers/src/MobileDateTimePicker/{ => tests}/MobileDateTimePicker.test.tsx (80%) rename packages/x-date-pickers/src/MobileTimePicker/{ => tests}/MobileTimePicker.test.tsx (90%) rename packages/x-date-pickers/src/StaticDatePicker/{ => tests}/StaticDatePicker.test.tsx (78%) rename packages/x-date-pickers/src/StaticDatePicker/{ => tests}/StaticDatePickerKeyboard.test.tsx (100%) create mode 100644 packages/x-date-pickers/src/StaticDatePicker/tests/describes.StaticDatePicker.test.tsx delete mode 100644 packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.test.tsx create mode 100644 packages/x-date-pickers/src/StaticDateTimePicker/tests/StaticDateTimePicker.test.tsx create mode 100644 packages/x-date-pickers/src/StaticDateTimePicker/tests/describes.StaticDatePicker.test.tsx create mode 100644 packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx create mode 100644 packages/x-date-pickers/src/tests/describePicker/describePicker.tsx create mode 100644 packages/x-date-pickers/src/tests/describePicker/describePicker.types.ts create mode 100644 packages/x-date-pickers/src/tests/describePicker/index.ts rename packages/x-date-pickers/src/tests/describeValidation/{describeValidation.tsx => describeValidation.ts} (100%) diff --git a/docs/pages/x/api/date-pickers/date-time-field.json b/docs/pages/x/api/date-pickers/date-time-field.json index 79b0d1a85f985..c3e3f23f0fa4f 100644 --- a/docs/pages/x/api/date-pickers/date-time-field.json +++ b/docs/pages/x/api/date-pickers/date-time-field.json @@ -106,6 +106,8 @@ }, "name": "DateTimeField", "styles": { "classes": ["root"], "globalClasses": {}, "name": "MuiDateTimeField" }, + "spread": true, + "forwardsRefTo": "HTMLDivElement", "filename": "/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx", "inheritance": null, "demos": "", diff --git a/docs/pages/x/api/date-pickers/time-picker.json b/docs/pages/x/api/date-pickers/time-picker.json index 47d77d8421549..40dac8133db4e 100644 --- a/docs/pages/x/api/date-pickers/time-picker.json +++ b/docs/pages/x/api/date-pickers/time-picker.json @@ -128,6 +128,7 @@ "name": "TimePicker", "styles": { "classes": [], "globalClasses": {}, "name": "MuiTimePicker" }, "filename": "/packages/x-date-pickers/src/TimePicker/TimePicker.tsx", + "inheritance": null, "demos": "", "packages": [ { "packageName": "@mui/x-date-pickers-pro", "componentName": "TimePicker" }, diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.test.tsx b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.test.tsx index 594d4f195a56c..58c5c9c474f7e 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.test.tsx +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.test.tsx @@ -457,7 +457,7 @@ describe('', () => { }); }); - describe('Component slots: Day', () => { + describe('Component slot: Day', () => { it('should render custom day component', () => { render( ', () => { }); }); - describe('Slots: day', () => { - it('should render custom day component', () => { - render( -
, - }} - />, - ); - - expect(screen.getAllByTestId('slot used')).not.to.have.length(0); - }); - }); - describe('prop: disableAutoMonthSwitching', () => { it('should go to the month of the end date when changing the start date', () => { render( diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx similarity index 90% rename from packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.test.tsx rename to packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx index 4e155a82b18f5..da5fd8b4e3179 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx @@ -1,20 +1,11 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { - describeConformance, - screen, - fireEvent, - userEvent, - act, - getByRole, -} from '@mui/monorepo/test/utils'; +import { screen, fireEvent, userEvent, act, getByRole } from '@mui/monorepo/test/utils'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import { DesktopDateRangePicker } from '@mui/x-date-pickers-pro/DesktopDateRangePicker'; import { DateRange, LocalizationProvider } from '@mui/x-date-pickers-pro'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; import { - wrapPickerMount, createPickerRenderer, adapterToUse, AdapterClassToUse, @@ -30,32 +21,6 @@ describe('', () => { clockConfig: new Date(2018, 0, 10), }); - describeConformance(, () => ({ - classes: {} as any, - render, - muiName: 'MuiDesktopDateRangePicker', - wrapMount: wrapPickerMount, - refInstanceof: window.HTMLDivElement, - skip: [ - 'componentProp', - 'componentsProp', - 'themeDefaultProps', - 'themeStyleOverrides', - 'themeVariants', - 'mergeClassName', - 'propsSpread', - 'rootClass', - 'reactTestRenderer', - ], - })); - - describeRangeValidation(DesktopDateRangePicker, () => ({ - render, - clock, - componentFamily: 'picker', - views: ['day'], - })); - it('should scroll current month to the active selection when focusing appropriate field', () => { render( ', () => { expect(screen.queryByText('Fim')).not.to.equal(null); }); - describe('Component slots: Popper', () => { - it('should forward onClick and onTouchStart', () => { - const handleClick = spy(); - const handleTouchStart = spy(); - render( - , - ); - const popper = screen.getByTestId('popper'); - - fireEvent.click(popper); - fireEvent.touchStart(popper); - - expect(handleClick.callCount).to.equal(1); - expect(handleTouchStart.callCount).to.equal(1); - }); - }); - - describe('Slots: Popper', () => { + describe('Component slot: Popper', () => { it('should forward onClick and onTouchStart', () => { const handleClick = spy(); const handleTouchStart = spy(); @@ -673,18 +611,4 @@ describe('', () => { expect(getPickerDay('17')).to.have.attribute('disabled'); }); }); - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render( - , - ); - openPicker({ type: 'date-range', variant: 'desktop', initialFocus: 'start' }); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); }); diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/describes.DesktopDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx similarity index 85% rename from packages/x-date-pickers-pro/src/DesktopDateRangePicker/describes.DesktopDateRangePicker.test.tsx rename to packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx index eebf25781b92a..19011ec343f4a 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/describes.DesktopDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx @@ -1,4 +1,4 @@ -import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { describeConformance, screen, userEvent } from '@mui/monorepo/test/utils'; import { DesktopDateRangePicker } from '@mui/x-date-pickers-pro/DesktopDateRangePicker'; import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; @@ -8,8 +8,11 @@ import { createPickerRenderer, expectInputPlaceholder, expectInputValue, + wrapPickerMount, } from 'test/utils/pickers-utils'; import { SingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; +import * as React from 'react'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ @@ -23,6 +26,8 @@ describe(' - Describes', () => { Component: DesktopDateRangePicker, }); + describePicker(DesktopDateRangePicker, { render, fieldType: 'multi-input', variant: 'desktop' }); + describeRangeValidation(DesktopDateRangePicker, () => ({ render, clock, @@ -30,6 +35,25 @@ describe(' - Describes', () => { views: ['day'], })); + describeConformance(, () => ({ + classes: {} as any, + render, + muiName: 'MuiDesktopDateRangePicker', + wrapMount: wrapPickerMount, + refInstanceof: window.HTMLDivElement, + skip: [ + 'componentProp', + 'componentsProp', + 'themeDefaultProps', + 'themeStyleOverrides', + 'themeVariants', + 'mergeClassName', + 'propsSpread', + 'rootClass', + 'reactTestRenderer', + ], + })); + describeValue(DesktopDateRangePicker, () => ({ render, componentFamily: 'picker', diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx similarity index 87% rename from packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.test.tsx rename to packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx index 31dccbfa3fc14..2a1bdaa80e3d3 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx @@ -1,49 +1,13 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { describeConformance, screen, userEvent, fireEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent, fireEvent } from '@mui/monorepo/test/utils'; import { MobileDateRangePicker } from '@mui/x-date-pickers-pro/MobileDateRangePicker'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; -import { - wrapPickerMount, - createPickerRenderer, - adapterToUse, - openPicker, -} from 'test/utils/pickers-utils'; +import { createPickerRenderer, adapterToUse, openPicker } from 'test/utils/pickers-utils'; import { DateRange } from '@mui/x-date-pickers-pro'; describe('', () => { - const { render, clock } = createPickerRenderer({ - clock: 'fake', - clockConfig: new Date(2018, 0, 1, 0, 0, 0, 0), - }); - - describeConformance(, () => ({ - classes: {} as any, - render, - muiName: 'MuiMobileDateRangePicker', - wrapMount: wrapPickerMount, - refInstanceof: window.HTMLDivElement, - skip: [ - 'componentProp', - 'componentsProp', - 'themeDefaultProps', - 'themeStyleOverrides', - 'themeVariants', - 'mergeClassName', - 'propsSpread', - 'rootClass', - 'reactTestRenderer', - ], - })); - - describeRangeValidation(MobileDateRangePicker, () => ({ - render, - clock, - componentFamily: 'picker', - views: ['day'], - variant: 'mobile', - })); + const { render } = createPickerRenderer(); describe('picker state', () => { it('should open when focusing the start input', () => { @@ -309,13 +273,4 @@ describe('', () => { // TODO: Write test // it('should call onClose and onAccept with the live value when clicking outside of the picker', () => { // }) - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render(); - openPicker({ type: 'date-range', variant: 'mobile', initialFocus: 'start' }); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); }); diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/describes.MobileDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx similarity index 77% rename from packages/x-date-pickers-pro/src/MobileDateRangePicker/describes.MobileDateRangePicker.test.tsx rename to packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx index 039bc4bc4c626..ed2daa6f82c8f 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/describes.MobileDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx @@ -1,4 +1,9 @@ -import { screen, userEvent, fireDiscreteEvent } from '@mui/monorepo/test/utils'; +import { + describeConformance, + screen, + userEvent, + fireDiscreteEvent, +} from '@mui/monorepo/test/utils'; import { MobileDateRangePicker } from '@mui/x-date-pickers-pro/MobileDateRangePicker'; import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; @@ -8,7 +13,10 @@ import { expectInputPlaceholder, expectInputValue, openPicker, + wrapPickerMount, } from 'test/utils/pickers-utils'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; +import * as React from 'react'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ @@ -16,6 +24,8 @@ describe(' - Describes', () => { clockConfig: new Date(2018, 0, 1, 0, 0, 0, 0), }); + describePicker(MobileDateRangePicker, { render, fieldType: 'multi-input', variant: 'mobile' }); + describeRangeValidation(MobileDateRangePicker, () => ({ render, clock, @@ -24,6 +34,25 @@ describe(' - Describes', () => { variant: 'mobile', })); + describeConformance(, () => ({ + classes: {} as any, + render, + muiName: 'MuiMobileDateRangePicker', + wrapMount: wrapPickerMount, + refInstanceof: window.HTMLDivElement, + skip: [ + 'componentProp', + 'componentsProp', + 'themeDefaultProps', + 'themeStyleOverrides', + 'themeVariants', + 'mergeClassName', + 'propsSpread', + 'rootClass', + 'reactTestRenderer', + ], + })); + describeValue(MobileDateRangePicker, () => ({ render, componentFamily: 'picker', diff --git a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.test.tsx index 198257a05d881..4ad17373be243 100644 --- a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.test.tsx @@ -76,12 +76,4 @@ describe('', () => { ), ).to.have.text('1'); }); - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render(); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); }); diff --git a/packages/x-date-pickers/src/DateField/tests/describes.DateField.test.tsx b/packages/x-date-pickers/src/DateField/tests/describes.DateField.test.tsx index 75f2cad93c46c..aedd89c0b6b97 100644 --- a/packages/x-date-pickers/src/DateField/tests/describes.DateField.test.tsx +++ b/packages/x-date-pickers/src/DateField/tests/describes.DateField.test.tsx @@ -19,6 +19,13 @@ describe(' - Describes', () => { const { clickOnInput } = buildFieldInteractions({ clock, render, Component: DateField }); + describeValidation(DateField, () => ({ + render, + clock, + views: ['year', 'month', 'day'], + componentFamily: 'field', + })); + describeConformance(, () => ({ classes: {} as any, inheritComponent: TextField, @@ -37,13 +44,6 @@ describe(' - Describes', () => { ], })); - describeValidation(DateField, () => ({ - render, - clock, - views: ['year', 'month', 'day'], - componentFamily: 'field', - })); - describeValue(DateField, () => ({ render, componentFamily: 'field', diff --git a/packages/x-date-pickers/src/DatePicker/DatePicker.test.tsx b/packages/x-date-pickers/src/DatePicker/DatePicker.test.tsx deleted file mode 100644 index 15b9838e39b66..0000000000000 --- a/packages/x-date-pickers/src/DatePicker/DatePicker.test.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { DatePicker } from '@mui/x-date-pickers/DatePicker'; -import { fireEvent, screen } from '@mui/monorepo/test/utils/createRenderer'; -import { - createPickerRenderer, - openPicker, - stubMatchMedia, - expectInputValue, - getTextbox, -} from 'test/utils/pickers-utils'; - -const isJSDOM = /jsdom/.test(window.navigator.userAgent); - -describe('', () => { - const { render } = createPickerRenderer(); - - describe('prop: inputRef', () => { - it('should forward ref to the text box', () => { - const inputRef = React.createRef(); - render(); - - expect(inputRef.current).to.have.tagName('input'); - }); - }); - - describe('rendering', () => { - it('should handle controlled `onChange` in desktop mode', () => { - render(); - const input = getTextbox(); - - fireEvent.change(input, { target: { value: '02/22/2022' } }); - expectInputValue(input, '02/22/2022'); - }); - - it('should render in mobile mode when `useMediaQuery` returns `false`', () => { - const originalMatchMedia = window.matchMedia; - window.matchMedia = stubMatchMedia(false); - - render(); - - expect(screen.getByLabelText(/Choose date/)).to.have.tagName('input'); - - window.matchMedia = originalMatchMedia; - }); - - it('should keep focus when switching views', function test() { - if (isJSDOM) { - this.skip(); - } - render(); - - openPicker({ type: 'date', variant: 'desktop' }); - expect(document.activeElement).to.have.text('2019'); - - fireEvent.click(screen.getByText('2020')); - expect(document.activeElement).to.have.text('5'); - }); - }); -}); diff --git a/packages/x-date-pickers/src/DatePicker/tests/DatePicker.test.tsx b/packages/x-date-pickers/src/DatePicker/tests/DatePicker.test.tsx new file mode 100644 index 0000000000000..90de4826bbd5c --- /dev/null +++ b/packages/x-date-pickers/src/DatePicker/tests/DatePicker.test.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { createPickerRenderer, stubMatchMedia } from 'test/utils/pickers-utils'; + +describe('', () => { + const { render } = createPickerRenderer(); + + it('should render in mobile mode when `useMediaQuery` returns `false`', () => { + const originalMatchMedia = window.matchMedia; + window.matchMedia = stubMatchMedia(false); + + render(); + + expect(screen.getByLabelText(/Choose date/)).to.have.tagName('input'); + + window.matchMedia = originalMatchMedia; + }); +}); diff --git a/packages/x-date-pickers/src/DatePicker/tests/describes.DatePicker.test.tsx b/packages/x-date-pickers/src/DatePicker/tests/describes.DatePicker.test.tsx deleted file mode 100644 index adb25ee54a364..0000000000000 --- a/packages/x-date-pickers/src/DatePicker/tests/describes.DatePicker.test.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { createPickerRenderer } from 'test/utils/pickers-utils'; -import { DatePicker } from '@mui/x-date-pickers/DatePicker'; - -describe(' - Describes', () => { - const { render, clock } = createPickerRenderer({ clock: 'fake' }); - - describeValidation(DatePicker, () => ({ - render, - clock, - views: ['year', 'month', 'day'], - componentFamily: 'picker', - })); -}); diff --git a/packages/x-date-pickers/src/DateTimeField/tests/describes.DateTimeField.test.tsx b/packages/x-date-pickers/src/DateTimeField/tests/describes.DateTimeField.test.tsx index fe98a308426d8..b45ea2be77904 100644 --- a/packages/x-date-pickers/src/DateTimeField/tests/describes.DateTimeField.test.tsx +++ b/packages/x-date-pickers/src/DateTimeField/tests/describes.DateTimeField.test.tsx @@ -1,5 +1,7 @@ +import * as React from 'react'; import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { userEvent } from '@mui/monorepo/test/utils'; +import TextField from '@mui/material/TextField'; +import { describeConformance, userEvent } from '@mui/monorepo/test/utils'; import { DateTimeField } from '@mui/x-date-pickers/DateTimeField'; import { adapterToUse, @@ -8,6 +10,7 @@ import { expectInputPlaceholder, expectInputValue, getTextbox, + wrapPickerMount, } from 'test/utils/pickers-utils'; import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; @@ -23,6 +26,24 @@ describe(' - Describes', () => { componentFamily: 'field', })); + describeConformance(, () => ({ + classes: {} as any, + inheritComponent: TextField, + render, + muiName: 'MuiDateTimeField', + wrapMount: wrapPickerMount, + refInstanceof: window.HTMLDivElement, + // cannot test reactTestRenderer because of required context + skip: [ + 'reactTestRenderer', + 'componentProp', + 'componentsProp', + 'themeDefaultProps', + 'themeStyleOverrides', + 'themeVariants', + ], + })); + describeValue(DateTimeField, () => ({ render, componentFamily: 'field', diff --git a/packages/x-date-pickers/src/DateTimeField/tests/timezone.DateTimeField.test.tsx b/packages/x-date-pickers/src/DateTimeField/tests/timezone.DateTimeField.test.tsx index ae79542fd3f28..8ab92fc934db7 100644 --- a/packages/x-date-pickers/src/DateTimeField/tests/timezone.DateTimeField.test.tsx +++ b/packages/x-date-pickers/src/DateTimeField/tests/timezone.DateTimeField.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { DateTimeField } from '@mui/x-date-pickers/DateTimeField'; import { createPickerRenderer, expectInputValue, getTextbox } from 'test/utils/pickers-utils'; -describe(' - TimeZone', () => { +describe(' - Timezone', () => { describe('Value time-zone modification - Luxon', () => { const { render, adapter } = createPickerRenderer({ clock: 'fake', adapterName: 'luxon' }); it('should update the field when time zone changes (timestamp remains the same)', () => { diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.test.tsx b/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx similarity index 67% rename from packages/x-date-pickers/src/DateTimePicker/DateTimePicker.test.tsx rename to packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx index dc62eb2497e1d..f9db4dab70883 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx @@ -3,17 +3,9 @@ import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { screen } from '@mui/monorepo/test/utils/createRenderer'; import { expect } from 'chai'; import { createPickerRenderer, stubMatchMedia } from 'test/utils/pickers-utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; describe('', () => { - const { render, clock } = createPickerRenderer({ clock: 'fake' }); - - describeValidation(DateTimePicker, () => ({ - render, - clock, - views: ['year', 'month', 'day', 'hours', 'minutes'], - componentFamily: 'picker', - })); + const { render } = createPickerRenderer(); it('should render in mobile mode when `useMediaQuery` returns `false`', () => { const originalMatchMedia = window.matchMedia; diff --git a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.test.tsx b/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx similarity index 71% rename from packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.test.tsx rename to packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx index 4187cdbbe3c80..611efbae513b7 100644 --- a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx @@ -3,7 +3,6 @@ import { expect } from 'chai'; import { spy } from 'sinon'; import { TransitionProps } from '@mui/material/transitions'; import { inputBaseClasses } from '@mui/material/InputBase'; -import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; import { fireEvent, screen, userEvent } from '@mui/monorepo/test/utils'; import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker'; import { @@ -13,56 +12,13 @@ import { expectInputValue, getTextbox, } from 'test/utils/pickers-utils'; +import { DatePicker } from '@mui/x-date-pickers'; + +const isJSDOM = /jsdom/.test(window.navigator.userAgent); describe('', () => { const { render } = createPickerRenderer({ clock: 'fake' }); - describe('Component slots: OpenPickerIcon', () => { - it('should render custom component', () => { - function HomeIcon(props: SvgIconProps) { - return ( - - - - ); - } - - const { getByTestId } = render( - , - ); - - expect(getByTestId('component-test')).not.to.equal(null); - }); - }); - - describe('Slots: openPickerIcon', () => { - it('should render custom component', () => { - function HomeIcon(props: SvgIconProps) { - return ( - - - - ); - } - - const { getByTestId } = render( - , - ); - - expect(getByTestId('component-test')).not.to.equal(null); - }); - }); - it('allows to change selected date from the field according to `format`', () => { const handleChange = spy(); @@ -79,20 +35,6 @@ describe('', () => { expect(handleChange.callCount).to.equal(1); }); - describe('Component slots: Toolbar', () => { - it('should render toolbar in desktop mode when `hidden` is `false`', () => { - render( - , - ); - - expect(screen.getByMuiTest('picker-toolbar')).toBeVisible(); - }); - }); - describe('Views', () => { it('should switch between views uncontrolled', () => { const handleViewChange = spy(); @@ -158,86 +100,18 @@ describe('', () => { expect(handleViewChange.callCount).to.equal(2); expect(handleViewChange.lastCall.firstArg).to.equal('month'); }); - }); - - describe('Component slots: Popper', () => { - it('should forward onClick and onTouchStart', () => { - const handleClick = spy(); - const handleTouchStart = spy(); - render( - , - ); - const popper = screen.getByTestId('popper'); - - fireEvent.click(popper); - fireEvent.touchStart(popper); - - expect(handleClick.callCount).to.equal(1); - expect(handleTouchStart.callCount).to.equal(1); - }); - }); - - describe('Slots: Popper', () => { - it('should forward onClick and onTouchStart', () => { - const handleClick = spy(); - const handleTouchStart = spy(); - render( - , - ); - const popper = screen.getByTestId('popper'); - - fireEvent.click(popper); - fireEvent.touchStart(popper); - - expect(handleClick.callCount).to.equal(1); - expect(handleTouchStart.callCount).to.equal(1); - }); - }); - describe('Component slots: DesktopPaper', () => { - it('forwards onClick and onTouchStart', () => { - const handleClick = spy(); - const handleTouchStart = spy(); - render( - , - ); - const paper = screen.getByTestId('paper'); + it('should move the focus to the newly opened views', function test() { + if (isJSDOM) { + this.skip(); + } + render(); - fireEvent.click(paper); - fireEvent.touchStart(paper); + openPicker({ type: 'date', variant: 'desktop' }); + expect(document.activeElement).to.have.text('2019'); - expect(handleClick.callCount).to.equal(1); - expect(handleTouchStart.callCount).to.equal(1); + fireEvent.click(screen.getByText('2020')); + expect(document.activeElement).to.have.text('5'); }); }); @@ -467,18 +341,4 @@ describe('', () => { openPicker({ type: 'date', variant: 'desktop' }); }).toWarnDev('MUI: `openTo="month"` is not a valid prop.'); }); - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render( - , - ); - openPicker({ type: 'date', variant: 'desktop' }); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); }); diff --git a/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx b/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx index 7caa1f957bbc4..a2bae11dbff95 100644 --- a/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx @@ -10,12 +10,15 @@ import { expectInputPlaceholder, } from 'test/utils/pickers-utils'; import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); const { clickOnInput } = buildFieldInteractions({ clock, render, Component: DesktopDatePicker }); + describePicker(DesktopDatePicker, { render, fieldType: 'single-input', variant: 'desktop' }); + describeValidation(DesktopDatePicker, () => ({ render, clock, diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.test.tsx deleted file mode 100644 index 9ff459a163835..0000000000000 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.test.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { fireEvent, screen, userEvent } from '@mui/monorepo/test/utils'; -import { DesktopDateTimePicker } from '@mui/x-date-pickers/DesktopDateTimePicker'; -import { adapterToUse, createPickerRenderer, openPicker } from 'test/utils/pickers-utils'; - -describe('', () => { - const { render } = createPickerRenderer({ - clock: 'fake', - clockConfig: new Date('2018-01-01T10:05:05.000'), - }); - - describe('Component slots: Popper', () => { - it('should forward onClick and onTouchStart', () => { - const handleClick = spy(); - const handleTouchStart = spy(); - render( - , - ); - const popper = screen.getByTestId('popper'); - - fireEvent.click(popper); - fireEvent.touchStart(popper); - - expect(handleClick.callCount).to.equal(1); - expect(handleTouchStart.callCount).to.equal(1); - }); - }); - - describe('Slots: Popper', () => { - it('should forward onClick and onTouchStart', () => { - const handleClick = spy(); - const handleTouchStart = spy(); - render( - , - ); - const popper = screen.getByTestId('popper'); - - fireEvent.click(popper); - fireEvent.touchStart(popper); - - expect(handleClick.callCount).to.equal(1); - expect(handleTouchStart.callCount).to.equal(1); - }); - }); - - describe('Component slots: DesktopPaper', () => { - it('should forward onClick and onTouchStart', () => { - const handleClick = spy(); - const handleTouchStart = spy(); - render( - , - ); - const paper = screen.getByTestId('paper'); - - fireEvent.click(paper); - fireEvent.touchStart(paper); - - expect(handleClick.callCount).to.equal(1); - expect(handleTouchStart.callCount).to.equal(1); - }); - }); - - describe('picker state', () => { - it('should open when clicking "Choose date"', () => { - const onOpen = spy(); - - render(); - - userEvent.mousePress(screen.getByLabelText(/Choose date/)); - - expect(onOpen.callCount).to.equal(1); - expect(screen.queryByRole('dialog')).toBeVisible(); - }); - - it('should call onAccept when selecting the same date and time after changing the year', () => { - const onChange = spy(); - const onAccept = spy(); - const onClose = spy(); - - render( - , - ); - - openPicker({ type: 'date', variant: 'desktop' }); - - // Select year - userEvent.mousePress(screen.getByRole('button', { name: '2025' })); - expect(onChange.callCount).to.equal(1); - expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2025, 0, 1, 11, 53)); - expect(onAccept.callCount).to.equal(0); - expect(onClose.callCount).to.equal(0); - - // Change the date (same value) - userEvent.mousePress(screen.getByRole('gridcell', { name: '1' })); - expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change - expect(onAccept.callCount).to.equal(0); - expect(onClose.callCount).to.equal(1); - }); - }); - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render( - , - ); - openPicker({ type: 'date', variant: 'desktop' }); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); -}); diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx new file mode 100644 index 0000000000000..a93e76a74a5a4 --- /dev/null +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx @@ -0,0 +1,57 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { spy } from 'sinon'; +import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { DesktopDateTimePicker } from '@mui/x-date-pickers/DesktopDateTimePicker'; +import { adapterToUse, createPickerRenderer, openPicker } from 'test/utils/pickers-utils'; + +describe('', () => { + const { render } = createPickerRenderer({ + clock: 'fake', + clockConfig: new Date('2018-01-01T10:05:05.000'), + }); + + describe('picker state', () => { + it('should open when clicking "Choose date"', () => { + const onOpen = spy(); + + render(); + + userEvent.mousePress(screen.getByLabelText(/Choose date/)); + + expect(onOpen.callCount).to.equal(1); + expect(screen.queryByRole('dialog')).toBeVisible(); + }); + + it('should call onAccept when selecting the same date and time after changing the year', () => { + const onChange = spy(); + const onAccept = spy(); + const onClose = spy(); + + render( + , + ); + + openPicker({ type: 'date', variant: 'desktop' }); + + // Select year + userEvent.mousePress(screen.getByRole('button', { name: '2025' })); + expect(onChange.callCount).to.equal(1); + expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2025, 0, 1, 11, 53)); + expect(onAccept.callCount).to.equal(0); + expect(onClose.callCount).to.equal(0); + + // Change the date (same value) + userEvent.mousePress(screen.getByRole('gridcell', { name: '1' })); + expect(onChange.callCount).to.equal(1); // Don't call onChange again since the value did not change + expect(onAccept.callCount).to.equal(0); + expect(onClose.callCount).to.equal(1); + }); + }); +}); diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx index 7e7ecd40371b4..37ad17ccd4aac 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx @@ -10,6 +10,7 @@ import { expectInputPlaceholder, } from 'test/utils/pickers-utils'; import { DesktopDateTimePicker } from '@mui/x-date-pickers/DesktopDateTimePicker'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); @@ -20,6 +21,8 @@ describe(' - Describes', () => { Component: DesktopDateTimePicker, }); + describePicker(DesktopDateTimePicker, { render, fieldType: 'single-input', variant: 'desktop' }); + describeValidation(DesktopDateTimePicker, () => ({ render, clock, diff --git a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx index 5c79abbc01da1..87edb3d164640 100644 --- a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx @@ -12,12 +12,20 @@ import { expectInputPlaceholder, } from 'test/utils/pickers-utils'; import { DesktopTimePicker } from '@mui/x-date-pickers/DesktopTimePicker'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); const { clickOnInput } = buildFieldInteractions({ clock, render, Component: DesktopTimePicker }); + describePicker(DesktopTimePicker, { + render, + fieldType: 'single-input', + variant: 'desktop', + hasNoView: true, + }); + describeValidation(DesktopTimePicker, () => ({ render, clock, diff --git a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.test.tsx b/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx similarity index 68% rename from packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.test.tsx rename to packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx index 638eca7651623..9a330dbe90860 100644 --- a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx @@ -8,7 +8,6 @@ import { MobileDatePicker } from '@mui/x-date-pickers/MobileDatePicker'; import { createPickerRenderer, adapterToUse, - openPicker, getTextbox, expectInputValue, } from 'test/utils/pickers-utils'; @@ -76,7 +75,7 @@ describe('', () => { expect(screen.getByTestId('custom-loading')).toBeVisible(); }); - describe('Component slots: Toolbar', () => { + describe('Component slot: Toolbar', () => { it('should render custom toolbar component', () => { render( ', () => { }); }); - describe('Slots: Toolbar', () => { - it('should render custom toolbar component', () => { - render( -
, - }} - />, - ); - - expect(screen.getByTestId('custom-toolbar')).toBeVisible(); - }); - - it('should format toolbar according to `toolbarFormat` prop', () => { - render( - , - ); - - expect(screen.getByMuiTest('datepicker-toolbar-date').textContent).to.equal('January'); - }); - }); - - describe('Component slots: Day', () => { - it('should render custom day', () => { - render( - , - }} - />, - ); - - expect(screen.getAllByTestId('test-day')).to.have.length(31); - }); - }); - - describe('Slots: Day', () => { + describe('Component slot: Day', () => { it('should render custom day', () => { render( ', () => { expect(screen.getByText('July 2018')).toBeVisible(); }); - it('prop `showTodayButton` – should accept current date when "today" button is clicked', () => { - const handleClose = spy(); - const handleChange = spy(); - render( - , - ); - userEvent.mousePress(screen.getByRole('textbox')); - fireEvent.click(screen.getByText(/today/i)); - - expect(handleClose.callCount).to.equal(1); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.args[0][0]).toEqualDateTime(adapterToUse.startOfDay(adapterToUse.date())); - }); - describe('picker state', () => { it('should open when clicking "Choose date"', () => { const onOpen = spy(); @@ -255,27 +188,4 @@ describe('', () => { expectInputValue(getTextbox(), ''); }); }); - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render(); - openPicker({ type: 'date', variant: 'mobile' }); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - - it('should render title from the `toolbarTitle` locale key', () => { - render( - , - ); - - expect(screen.getByMuiTest('picker-toolbar-title').textContent).to.equal('test'); - }); - }); }); diff --git a/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx b/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx index 81dbaacb87905..7c3143a74ab58 100644 --- a/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx @@ -10,10 +10,13 @@ import { getTextbox, } from 'test/utils/pickers-utils'; import { MobileDatePicker } from '@mui/x-date-pickers/MobileDatePicker'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); + describePicker(MobileDatePicker, { render, fieldType: 'single-input', variant: 'mobile' }); + describeValidation(MobileDatePicker, () => ({ render, clock, diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.test.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx similarity index 80% rename from packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.test.tsx rename to packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx index c18b86875e505..289b31f1976ba 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx @@ -10,21 +10,9 @@ import { openPicker, getClockTouchEvent, } from 'test/utils/pickers-utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; describe('', () => { - const { render, clock } = createPickerRenderer({ - clock: 'fake', - clockConfig: new Date(2018, 2, 12, 8, 16, 0), - }); - - describeValidation(MobileDateTimePicker, () => ({ - render, - clock, - views: ['year', 'month', 'day', 'hours', 'minutes'], - componentFamily: 'picker', - variant: 'mobile', - })); + const { render } = createPickerRenderer({ clock: 'fake' }); it('should render date and time by default', () => { render( @@ -68,24 +56,7 @@ describe('', () => { expect(screen.getByMuiTest('seconds')).to.have.text('22'); }); - describe('Component slots: Tabs', () => { - it('should not render tabs when `hidden` is `true`', () => { - render( - , - ); - - expect(screen.queryByMuiTest('picker-toolbar-title')).not.to.equal(null); - expect(screen.queryByRole('tab', { name: 'pick date' })).to.equal(null); - }); - }); - - describe('Slots: Tabs', () => { + describe('Component slot: Tabs', () => { it('should not render tabs when `hidden` is `true`', () => { render( ', () => { }); }); - describe('Component slots: Toolbar', () => { + describe('Component slot: Toolbar', () => { it('should not render only toolbar when `hidden` is `true`', () => { render( ', () => { // Change the year view userEvent.mousePress(screen.getByLabelText(/switch to year view/)); userEvent.mousePress(screen.getByText('2010', { selector: 'button' })); + expect(onChange.callCount).to.equal(1); expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2010, 0, 1)); @@ -186,13 +158,4 @@ describe('', () => { expect(onClose.callCount).to.equal(0); }); }); - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render(); - openPicker({ type: 'date-time', variant: 'mobile' }); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); }); diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx index bb484eab76c4b..ba4590ae84c72 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx @@ -11,6 +11,7 @@ import { getTextbox, } from 'test/utils/pickers-utils'; import { MobileDateTimePicker } from '@mui/x-date-pickers/MobileDateTimePicker'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ @@ -18,6 +19,8 @@ describe(' - Describes', () => { clockConfig: new Date(2018, 2, 12, 8, 16, 0), }); + describePicker(MobileDateTimePicker, { render, fieldType: 'single-input', variant: 'mobile' }); + describeValidation(MobileDateTimePicker, () => ({ render, clock, diff --git a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.test.tsx b/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx similarity index 90% rename from packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.test.tsx rename to packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx index 2552f632eae48..22c9780dab6c1 100644 --- a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx @@ -88,13 +88,4 @@ describe('', () => { expect(onClose.callCount).to.equal(0); }); }); - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render(); - openPicker({ type: 'time', variant: 'mobile' }); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); }); diff --git a/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx b/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx index 0d3fd013572ec..2fa25b2e11148 100644 --- a/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx @@ -18,6 +18,7 @@ import { getTextbox, } from 'test/utils/pickers-utils'; import { MobileTimePicker } from '@mui/x-date-pickers/MobileTimePicker'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ @@ -25,6 +26,8 @@ describe(' - Describes', () => { clockConfig: new Date(2018, 2, 12, 8, 16, 0), }); + describePicker(MobileTimePicker, { render, fieldType: 'single-input', variant: 'mobile' }); + describeValidation(MobileTimePicker, () => ({ render, clock, diff --git a/packages/x-date-pickers/src/MonthCalendar/tests/MonthCalendar.test.tsx b/packages/x-date-pickers/src/MonthCalendar/tests/MonthCalendar.test.tsx index 4a5e92a8b902e..d32625a7fb47c 100644 --- a/packages/x-date-pickers/src/MonthCalendar/tests/MonthCalendar.test.tsx +++ b/packages/x-date-pickers/src/MonthCalendar/tests/MonthCalendar.test.tsx @@ -8,7 +8,7 @@ import { adapterToUse, createPickerRenderer } from 'test/utils/pickers-utils'; describe('', () => { const { render } = createPickerRenderer({ clock: 'fake', clockConfig: new Date(2019, 0, 1) }); - it('allows to pick month standalone by click, `Enter` and `Space`', () => { + it('should allow to pick month standalone by click, `Enter` and `Space`', () => { const onChange = spy(); render(); const targetMonth = screen.getByRole('button', { name: 'Feb' }); diff --git a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.test.tsx b/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePicker.test.tsx similarity index 78% rename from packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.test.tsx rename to packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePicker.test.tsx index 2b9836caee14e..e52a04353c79f 100644 --- a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.test.tsx +++ b/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePicker.test.tsx @@ -3,19 +3,11 @@ import { expect } from 'chai'; import { fireEvent, screen } from '@mui/monorepo/test/utils'; import { StaticDatePicker } from '@mui/x-date-pickers/StaticDatePicker'; import { createPickerRenderer, adapterToUse } from 'test/utils/pickers-utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); describe('', () => { - const { render, clock } = createPickerRenderer({ clock: 'fake' }); - - describeValidation(StaticDatePicker, () => ({ - render, - clock, - views: ['year', 'month', 'day'], - componentFamily: 'static-picker', - })); + const { render } = createPickerRenderer({ clock: 'fake' }); it('render proper month', () => { render(); @@ -43,14 +35,6 @@ describe('', () => { expect(screen.getByMuiTest('calendar-month-and-year-text')).to.have.text('December 2018'); }); - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render(); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); - describe('props - autoFocus', () => { function Test(props) { return ( diff --git a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePickerKeyboard.test.tsx b/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePickerKeyboard.test.tsx similarity index 100% rename from packages/x-date-pickers/src/StaticDatePicker/StaticDatePickerKeyboard.test.tsx rename to packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePickerKeyboard.test.tsx diff --git a/packages/x-date-pickers/src/StaticDatePicker/tests/describes.StaticDatePicker.test.tsx b/packages/x-date-pickers/src/StaticDatePicker/tests/describes.StaticDatePicker.test.tsx new file mode 100644 index 0000000000000..4a41ca3755929 --- /dev/null +++ b/packages/x-date-pickers/src/StaticDatePicker/tests/describes.StaticDatePicker.test.tsx @@ -0,0 +1,17 @@ +import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; +import { createPickerRenderer } from 'test/utils/pickers-utils'; +import { StaticDatePicker } from '@mui/x-date-pickers/StaticDatePicker'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; + +describe(' - Describes', () => { + const { render, clock } = createPickerRenderer({ clock: 'fake' }); + + describePicker(StaticDatePicker, { render, fieldType: 'single-input', variant: 'static' }); + + describeValidation(StaticDatePicker, () => ({ + render, + clock, + views: ['year', 'month', 'day'], + componentFamily: 'static-picker', + })); +}); diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.test.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.test.tsx deleted file mode 100644 index ab0a9ec0cd9e0..0000000000000 --- a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.test.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { fireEvent, screen } from '@mui/monorepo/test/utils'; -import { StaticDateTimePicker } from '@mui/x-date-pickers/StaticDateTimePicker'; -import { adapterToUse, createPickerRenderer } from 'test/utils/pickers-utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { DateTimePickerTabs, DateTimePickerTabsProps } from '../DateTimePicker'; - -describe('', () => { - const { render, clock } = createPickerRenderer({ - clock: 'fake', - clockConfig: new Date(2018, 2, 12, 8, 16, 0), - }); - - describeValidation(StaticDateTimePicker, () => ({ - render, - clock, - views: ['year', 'month', 'day', 'hours', 'minutes'], - componentFamily: 'static-picker', - })); - - it('should allow to select the same day and move to the next view', () => { - const onChange = spy(); - render( - , - ); - - fireEvent.click(screen.getByRole('gridcell', { name: '1' })); - expect(onChange.callCount).to.equal(1); - - expect(screen.getByLabelText(/Selected time/)).toBeVisible(); - }); - - it('should render toolbar and tabs by default', () => { - render(); - - expect(screen.queryByMuiTest('picker-toolbar-title')).not.to.equal(null); - expect(screen.getByRole('tab', { name: 'pick date' })).not.to.equal(null); - }); - - describe('prop: displayStaticWrapperAs', () => { - describe('Component slots: Toolbar', () => { - it('should render toolbar when `hidden` is `false`', () => { - render( - , - ); - - expect(screen.queryByMuiTest('picker-toolbar-title')).not.to.equal(null); - }); - }); - }); - - describe('Components slots: Tabs', () => { - it('should not render tabs when `hidden` is `true`', () => { - render( - , - ); - - expect(screen.queryByMuiTest('picker-toolbar-title')).not.to.equal(null); - expect(screen.queryByRole('tab', { name: 'pick date' })).to.equal(null); - }); - - it('should render tabs when `hidden` is `false`', () => { - render( - , - ); - - expect(screen.queryByMuiTest('picker-toolbar-title')).to.equal(null); - expect(screen.getByRole('tab', { name: 'pick date' })).not.to.equal(null); - }); - - it('should render custom component', () => { - function CustomPickerTabs(props: DateTimePickerTabsProps) { - return ( - - - test-custom-picker-tabs - - ); - } - render( - , - ); - - expect(screen.getByRole('tab', { name: 'pick date' })).not.to.equal(null); - expect(screen.getByText('test-custom-picker-tabs')).not.to.equal(null); - }); - }); - - describe('Slots: Tabs', () => { - it('should not render tabs when `hidden` is `true`', () => { - render( - , - ); - - expect(screen.queryByMuiTest('picker-toolbar-title')).not.to.equal(null); - expect(screen.queryByRole('tab', { name: 'pick date' })).to.equal(null); - }); - - it('should render tabs when `hidden` is `false`', () => { - render( - , - ); - - expect(screen.queryByMuiTest('picker-toolbar-title')).to.equal(null); - expect(screen.getByRole('tab', { name: 'pick date' })).not.to.equal(null); - }); - - it('should render custom component', () => { - function CustomPickerTabs(props: DateTimePickerTabsProps) { - return ( - - - test-custom-picker-tabs - - ); - } - render( - , - ); - - expect(screen.getByRole('tab', { name: 'pick date' })).not.to.equal(null); - expect(screen.getByText('test-custom-picker-tabs')).not.to.equal(null); - }); - }); - - describe('Component slots: Toolbar', () => { - it('should not render only toolbar when `hidden` is `true`', () => { - render(); - }); - }); - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render(); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); -}); diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/tests/StaticDateTimePicker.test.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/tests/StaticDateTimePicker.test.tsx new file mode 100644 index 0000000000000..777ac7acbf2e6 --- /dev/null +++ b/packages/x-date-pickers/src/StaticDateTimePicker/tests/StaticDateTimePicker.test.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { spy } from 'sinon'; +import { fireEvent, screen } from '@mui/monorepo/test/utils'; +import { StaticDateTimePicker } from '@mui/x-date-pickers/StaticDateTimePicker'; +import { adapterToUse, createPickerRenderer } from 'test/utils/pickers-utils'; +import { DateTimePickerTabs, DateTimePickerTabsProps } from '../../DateTimePicker'; + +describe('', () => { + const { render } = createPickerRenderer(); + + it('should allow to select the same day and move to the next view', () => { + const onChange = spy(); + render( + , + ); + + fireEvent.click(screen.getByRole('gridcell', { name: '1' })); + expect(onChange.callCount).to.equal(1); + + expect(screen.getByLabelText(/Selected time/)).toBeVisible(); + }); + + describe('Component slot: Tabs', () => { + it('should not render tabs when `hidden` is `true`', () => { + render( + , + ); + + expect(screen.queryByMuiTest('picker-toolbar-title')).not.to.equal(null); + expect(screen.queryByRole('tab', { name: 'pick date' })).to.equal(null); + }); + + it('should render tabs when `hidden` is `false`', () => { + render( + , + ); + + expect(screen.queryByMuiTest('picker-toolbar-title')).to.equal(null); + expect(screen.getByRole('tab', { name: 'pick date' })).not.to.equal(null); + }); + + it('should render custom component', () => { + function CustomPickerTabs(props: DateTimePickerTabsProps) { + return ( + + + test-custom-picker-tabs + + ); + } + render( + , + ); + + expect(screen.getByRole('tab', { name: 'pick date' })).not.to.equal(null); + expect(screen.getByText('test-custom-picker-tabs')).not.to.equal(null); + }); + }); +}); diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/tests/describes.StaticDatePicker.test.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/tests/describes.StaticDatePicker.test.tsx new file mode 100644 index 0000000000000..2b5f1f3a9f995 --- /dev/null +++ b/packages/x-date-pickers/src/StaticDateTimePicker/tests/describes.StaticDatePicker.test.tsx @@ -0,0 +1,17 @@ +import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; +import { createPickerRenderer } from 'test/utils/pickers-utils'; +import { StaticDateTimePicker } from '@mui/x-date-pickers/StaticDateTimePicker'; +import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; + +describe(' - Describes', () => { + const { render, clock } = createPickerRenderer({ clock: 'fake' }); + + describePicker(StaticDateTimePicker, { render, fieldType: 'single-input', variant: 'static' }); + + describeValidation(StaticDateTimePicker, () => ({ + render, + clock, + views: ['year', 'month', 'day', 'hours', 'minutes'], + componentFamily: 'static-picker', + })); +}); diff --git a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.test.tsx b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.test.tsx index 80802779d8009..25f2fb9409c85 100644 --- a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.test.tsx +++ b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.test.tsx @@ -148,12 +148,4 @@ describe('', () => { expect(screen.getByRole('button', { name: /AM/i })).to.have.attribute('disabled'); expect(screen.getByRole('button', { name: /PM/i })).to.have.attribute('disabled'); }); - - describe('localization', () => { - it('should respect the `localeText` prop', () => { - render(); - - expect(screen.queryByText('Custom cancel')).not.to.equal(null); - }); - }); }); diff --git a/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx b/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx new file mode 100644 index 0000000000000..f3cd9ff774240 --- /dev/null +++ b/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import { TimePicker } from '@mui/x-date-pickers/TimePicker'; +import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { expect } from 'chai'; +import { createPickerRenderer, stubMatchMedia } from 'test/utils/pickers-utils'; + +describe('', () => { + const { render } = createPickerRenderer(); + + it('should render in mobile mode when `useMediaQuery` returns `false`', () => { + const originalMatchMedia = window.matchMedia; + window.matchMedia = stubMatchMedia(false); + + render(); + + expect(screen.getByLabelText(/Choose time/)).to.have.tagName('input'); + + window.matchMedia = originalMatchMedia; + }); +}); diff --git a/packages/x-date-pickers/src/tests/describePicker/describePicker.tsx b/packages/x-date-pickers/src/tests/describePicker/describePicker.tsx new file mode 100644 index 0000000000000..418b003a4cea3 --- /dev/null +++ b/packages/x-date-pickers/src/tests/describePicker/describePicker.tsx @@ -0,0 +1,171 @@ +/* eslint-env mocha */ +import * as React from 'react'; +import { expect } from 'chai'; +import { spy } from 'sinon'; +import { screen, fireEvent, createDescribe } from '@mui/monorepo/test/utils'; +import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; +import { DescribePickerOptions } from './describePicker.types'; + +function innerDescribePicker(ElementToTest: React.ElementType, options: DescribePickerOptions) { + const { render, fieldType, hasNoView, variant } = options; + + const propsToOpen = variant === 'static' ? {} : { open: true }; + + it('should forward the `inputRef` prop to the text field', function test() { + if (fieldType === 'multi-input' || variant === 'static') { + this.skip(); + } + + const inputRef = React.createRef(); + render(); + + expect(inputRef.current).to.have.tagName('input'); + }); + + describe('Localization', () => { + it('should respect the `localeText` prop', function test() { + if (hasNoView) { + this.skip(); + } + + render( + , + ); + + expect(screen.queryByText('Custom cancel')).not.to.equal(null); + }); + }); + + describe('Component slot: OpenPickerIcon', () => { + it('should render custom component', function test() { + if (variant === 'static' || fieldType === 'multi-input') { + this.skip(); + } + + function HomeIcon(props: SvgIconProps) { + return ( + + + + ); + } + + const { queryAllByTestId } = render( + , + ); + + const shouldRenderOpenPickerIcon = !hasNoView && variant !== 'mobile'; + + expect(queryAllByTestId('component-test')).to.have.length(shouldRenderOpenPickerIcon ? 1 : 0); + }); + }); + + describe('Component slot: DesktopPaper', () => { + it('should forward onClick and onTouchStart', function test() { + if (hasNoView || variant !== 'desktop') { + this.skip(); + } + + const handleClick = spy(); + const handleTouchStart = spy(); + render( + , + ); + const paper = screen.getByTestId('paper'); + + fireEvent.click(paper); + fireEvent.touchStart(paper); + + expect(handleClick.callCount).to.equal(1); + expect(handleTouchStart.callCount).to.equal(1); + }); + }); + + describe('Component slot: Popper', () => { + it('should forward onClick and onTouchStart', function test() { + if (hasNoView || variant !== 'desktop') { + this.skip(); + } + + const handleClick = spy(); + const handleTouchStart = spy(); + render( + , + ); + const popper = screen.getByTestId('popper'); + + fireEvent.click(popper); + fireEvent.touchStart(popper); + + expect(handleClick.callCount).to.equal(1); + expect(handleTouchStart.callCount).to.equal(1); + }); + }); + + describe('Component slot: Toolbar', () => { + it('should render toolbar on mobile but not on desktop when `hidden` is not defined', function test() { + if (hasNoView) { + this.skip(); + } + + render(); + + if (variant === 'desktop') { + expect(screen.queryByMuiTest('picker-toolbar')).to.equal(null); + } else { + expect(screen.getByMuiTest('picker-toolbar')).toBeVisible(); + } + }); + + it('should render toolbar when `hidden` is `false`', function test() { + if (hasNoView) { + this.skip(); + } + + render(); + + expect(screen.getByMuiTest('picker-toolbar')).toBeVisible(); + }); + + it('should not render toolbar when `hidden` is `true`', function test() { + if (hasNoView) { + this.skip(); + } + + render(); + + expect(screen.queryByMuiTest('picker-toolbar')).to.equal(null); + }); + }); +} + +/** + * Test behaviors shared across all pickers. + */ +export const describePicker = createDescribe('Pickers shared APIs', innerDescribePicker); diff --git a/packages/x-date-pickers/src/tests/describePicker/describePicker.types.ts b/packages/x-date-pickers/src/tests/describePicker/describePicker.types.ts new file mode 100644 index 0000000000000..8b6beec38800f --- /dev/null +++ b/packages/x-date-pickers/src/tests/describePicker/describePicker.types.ts @@ -0,0 +1,9 @@ +import * as React from 'react'; +import { MuiRenderResult } from '@mui/monorepo/test/utils/createRenderer'; + +export interface DescribePickerOptions { + fieldType: 'single-input' | 'multi-input'; + variant: 'mobile' | 'desktop' | 'static'; + hasNoView?: boolean; + render: (node: React.ReactElement) => MuiRenderResult; +} diff --git a/packages/x-date-pickers/src/tests/describePicker/index.ts b/packages/x-date-pickers/src/tests/describePicker/index.ts new file mode 100644 index 0000000000000..4dea94b5b0495 --- /dev/null +++ b/packages/x-date-pickers/src/tests/describePicker/index.ts @@ -0,0 +1 @@ +export { describePicker } from './describePicker'; diff --git a/packages/x-date-pickers/src/tests/describeValidation/describeValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/describeValidation.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeValidation/describeValidation.tsx rename to packages/x-date-pickers/src/tests/describeValidation/describeValidation.ts diff --git a/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx b/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx index ff6d8216d474b..64096b872d725 100644 --- a/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx @@ -40,6 +40,6 @@ type DescribeValue = { }; /** - * Tests various aspects of the picker value. + * Tests various aspects of component value. */ export const describeValue = createDescribe('Value API', innerDescribeValue) as DescribeValue; diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx b/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx index dd5a985975bb0..11ac3cbd7644f 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { adapterToUse } from 'test/utils/pickers-utils'; import { DescribeValueTestSuite } from './describeValue.types'; export const testPickerActionBar: DescribeValueTestSuite = ( @@ -33,7 +34,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( onClose={onClose} defaultValue={values[0]} open - componentsProps={{ actionBar: { actions: ['clear'] } }} + slotProps={{ actionBar: { actions: ['clear'] } }} />, ); @@ -69,7 +70,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( onAccept={onAccept} onClose={onClose} open - componentsProps={{ actionBar: { actions: ['clear'] } }} + slotProps={{ actionBar: { actions: ['clear'] } }} value={emptyValue} />, ); @@ -95,7 +96,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( onClose={onClose} open value={values[0]} - componentsProps={{ actionBar: { actions: ['cancel'] } }} + slotProps={{ actionBar: { actions: ['cancel'] } }} closeOnSelect={false} />, ); @@ -129,7 +130,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( onClose={onClose} open value={values[0]} - componentsProps={{ actionBar: { actions: ['cancel'] } }} + slotProps={{ actionBar: { actions: ['cancel'] } }} closeOnSelect={false} />, ); @@ -155,7 +156,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( onClose={onClose} open defaultValue={values[0]} - componentsProps={{ actionBar: { actions: ['accept'] } }} + slotProps={{ actionBar: { actions: ['accept'] } }} closeOnSelect={false} />, ); @@ -182,7 +183,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( onClose={onClose} open defaultValue={values[0]} - componentsProps={{ actionBar: { actions: ['accept'] } }} + slotProps={{ actionBar: { actions: ['accept'] } }} closeOnSelect={false} />, ); @@ -206,7 +207,7 @@ export const testPickerActionBar: DescribeValueTestSuite = ( onClose={onClose} open value={values[0]} - componentsProps={{ actionBar: { actions: ['accept'] } }} + slotProps={{ actionBar: { actions: ['accept'] } }} closeOnSelect={false} />, ); @@ -218,5 +219,47 @@ export const testPickerActionBar: DescribeValueTestSuite = ( expect(onClose.callCount).to.equal(1); }); }); + + describe('today action', () => { + it("should call onClose, onChange with today's value and onAccept with today's value", () => { + const onChange = spy(); + const onAccept = spy(); + const onClose = spy(); + + render( + , + ); + + userEvent.mousePress(screen.getByText(/today/i)); + + const startOfToday = + type === 'date' ? adapterToUse.startOfDay(adapterToUse.date()) : adapterToUse.date(); + + expect(onChange.callCount).to.equal(1); + if (type === 'date-range') { + onChange.lastCall.args[0].forEach((value) => { + expect(value).toEqualDateTime(startOfToday); + }); + } else { + expect(onChange.lastCall.args[0]).toEqualDateTime(startOfToday); + } + expect(onAccept.callCount).to.equal(1); + if (type === 'date-range') { + onAccept.lastCall.args[0].forEach((value) => { + expect(value).toEqualDateTime(startOfToday); + }); + } else { + expect(onAccept.lastCall.args[0]).toEqualDateTime(startOfToday); + } + expect(onClose.callCount).to.equal(1); + }); + }); }); }; From a34ae2b95e17c1ed910b137da07a4663fe2833d5 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Wed, 26 Apr 2023 10:28:00 +0200 Subject: [PATCH 12/80] [docs] Use new `slots` / `slotProps` props in the pickers migration guide (#8341) --- .../migration-pickers-v5.md | 153 ++++++++++++------ 1 file changed, 107 insertions(+), 46 deletions(-) diff --git a/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md b/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md index bdf551fe38e8b..beb6d4eeb51b3 100644 --- a/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md +++ b/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md @@ -453,7 +453,7 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff } - + componentsProps={{ textField: { variant: 'outlined' } }} + + slotProps={{ textField: { variant: 'outlined' } }} /> - - )} - + componentsProps={{ textField: { variant: 'outlined' } }} + + slotProps={{ textField: { variant: 'outlined' } }} /> ``` @@ -479,18 +479,18 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s - - - )} - + componentsProps={{ fieldSeparator: { children: 'to' } }} + + slotProps={{ fieldSeparator: { children: 'to' } }} /> ``` ### Toolbar (`ToolbarComponent`) -- ✅ The `ToolbarComponent` has been replaced by a `Toolbar` component slot: +- ✅ The `ToolbarComponent` has been replaced by a `toolbar` component slot: ```diff ``` @@ -501,7 +501,7 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s - toolbarPlaceholder="__" - toolbarFormat="DD / MM / YYYY" - showToolbar - + componentsProps={{ + + slotProps={{ + toolbar: { + toolbarPlaceholder: '__', + toolbarFormat: 'DD / MM / YYYY', @@ -554,8 +554,8 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ``` @@ -573,8 +573,8 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ) ``` @@ -589,7 +589,7 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s - hideTabs={false} - dateRangeIcon={} - timeIcon={} - + componentsProps={{ + + slotProps={{ + tabs: { + hidden: false, + dateIcon: , @@ -616,8 +616,8 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s
) ``` @@ -629,25 +629,33 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff (variant === 'desktop' ? [] : ['clear']), - }, + - }} + + componentsProps={{ + actionBar: ({ wrapperVariant }) => ({ + actions: wrapperVariant === 'desktop' ? [] : ['clear'], + }), - }} + + }} + // or using the new `slots` prop + + slotProps={{ + + actionBar: ({ wrapperVariant }) => ({ + + actions: wrapperVariant === 'desktop' ? [] : ['clear'], + + }), + + }} /> ``` ### Day (`renderDay`) -- The `renderDay` prop has been replaced by a `Day` component slot: +- The `renderDay` prop has been replaced by a `day` component slot: ```diff } - + components={{ Day: CustomDay }} + + slots={{ day: CustomDay }} /> ``` @@ -667,8 +675,8 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s setValue(newValue)} - components={{ Day: CustomDay }} - componentsProps={{ + slots={{ day: CustomDay }} + slotProps={{ day: { selectedDay: value }, }} /> @@ -683,18 +691,18 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff ``` ### ✅ Desktop transition (`TransitionComponent`) -- The `TransitionComponent` prop has been replaced by a `DesktopTransition` component slot: +- The `TransitionComponent` prop has been replaced by a `desktopTransition` component slot: ```diff ``` @@ -705,7 +713,7 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff ``` @@ -716,7 +724,7 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff ``` @@ -727,7 +735,7 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff false }} - + componentsProps={{ desktopTrapFocus: { isEnabled: () => false } }} + + slotProps={{ desktopTrapFocus: { isEnabled: () => false } }} /> ``` @@ -765,10 +773,16 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s function App() { return ( ); } @@ -778,19 +792,31 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s - The component slot `LeftArrowButton` has been renamed to `PreviousIconButton`: -```diff - -``` + - componentsProps={{ + - leftArrowButton: {}, + - }} + + componentsProps={{ + + previousIconButton: {}, + + }} + // or using the new `slotProps` prop + + slotProps={{ + + previousIconButton: {}, + + }} + /> + ``` ### ✅ Right arrow button @@ -798,15 +824,27 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff ``` @@ -818,7 +856,7 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff ``` @@ -829,7 +867,7 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff ``` @@ -840,7 +878,7 @@ For example, the `ToolbarComponent` has been replaced by a `Toolbar` component s ```diff ``` @@ -908,3 +946,26 @@ npx @mui/x-codemod v6.0.0/pickers/rename-components-to-slots ``` Take a look at [the RFC](https://github.com/mui/material-ui/issues/33416) for more information. + +:::warning +If this codemod is applied on a component with both a `slots` and a `components` prop, the output will contain two `slots` props. +You are then responsible for merging those two props manually. + +For example: + +```tsx +// Before running the codemod + + +// After running the codemod + +``` + +The same applies to `slotProps` and `componentsProps`. +::: From 148ba8cb67851504a0057648ccc1dc4246e0f519 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Wed, 26 Apr 2023 10:42:10 +0200 Subject: [PATCH 13/80] [pickers] Migrate `AdapterDateFns` to our repository (#8736) --- codecov.yml | 1 + .../AdapterDateFns/AdapterDateFns.test.tsx | 150 +++++ .../src/AdapterDateFns/AdapterDateFns.ts | 566 ++++++++++++++++++ .../src/AdapterDateFns/index.ts | 109 +--- .../src/AdapterDateFns/localization.test.tsx | 62 -- .../src/AdapterDayjs/AdapterDayjs.test.tsx | 2 +- .../src/AdapterDayjs/AdapterDayjs.ts | 22 +- .../src/AdapterLuxon/AdapterLuxon.test.tsx | 2 +- .../src/AdapterMoment/AdapterMoment.test.tsx | 2 +- .../tests/editing.TimeField.test.tsx | 44 +- .../describeGregorianAdapter.types.ts | 3 +- .../testCalculations.ts | 4 +- 12 files changed, 754 insertions(+), 213 deletions(-) create mode 100644 packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx create mode 100644 packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts delete mode 100644 packages/x-date-pickers/src/AdapterDateFns/localization.test.tsx diff --git a/codecov.yml b/codecov.yml index 3fd29d06db02d..d0b9a4c709923 100644 --- a/codecov.yml +++ b/codecov.yml @@ -10,6 +10,7 @@ coverage: - 'packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts' - 'packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts' - 'packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts' + - 'packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts' patch: off comment: false diff --git a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx new file mode 100644 index 0000000000000..c2f2f071fbcce --- /dev/null +++ b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx @@ -0,0 +1,150 @@ +import * as React from 'react'; +import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import { AdapterFormats } from '@mui/x-date-pickers/models'; +import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { expect } from 'chai'; +import { + createPickerRenderer, + expectInputPlaceholder, + expectInputValue, +} from 'test/utils/pickers-utils'; +import enUS from 'date-fns/locale/en-US'; +import fr from 'date-fns/locale/fr'; +import de from 'date-fns/locale/de'; +import ru from 'date-fns/locale/ru'; +import { + describeGregorianAdapter, + TEST_DATE_ISO, +} from '@mui/x-date-pickers/tests/describeGregorianAdapter'; + +const testDate = new Date(2018, 4, 15, 9, 35); +const localizedTexts = { + undefined: { + placeholder: 'MM/DD/YYYY hh:mm aa', + value: '05/15/2018 09:35 AM', + }, + fr: { + placeholder: 'DD/MM/YYYY hh:mm', + value: '15/05/2018 09:35', + }, + de: { + placeholder: 'DD.MM.YYYY hh:mm', + value: '15.05.2018 09:35', + }, +}; +describe('', () => { + describeGregorianAdapter(AdapterDateFns, { formatDateTime: 'yyyy-MM-dd HH:mm:ss', locale: enUS }); + + describe('Adapter localization', () => { + describe('English', () => { + const adapter = new AdapterDateFns({ locale: enUS }); + const date = adapter.date(TEST_DATE_ISO)!; + + it('getWeekdays: should start on Sunday', () => { + const result = adapter.getWeekdays(); + expect(result).to.deep.equal(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']); + }); + + it('getWeekArray: should start on Sunday', () => { + const result = adapter.getWeekArray(date); + expect(adapter.formatByString(result[0][0], 'EEEEEE')).to.equal('Su'); + }); + + it('is12HourCycleInCurrentLocale: should have meridiem', () => { + expect(adapter.is12HourCycleInCurrentLocale()).to.equal(true); + }); + }); + + describe('Russian', () => { + const adapter = new AdapterDateFns({ locale: ru }); + + it('getWeekDays: should start on Monday', () => { + const result = adapter.getWeekdays(); + expect(result).to.deep.equal(['пн', 'вт', 'ср', 'чт', 'пт', 'сб', 'вс']); + }); + + it('getWeekArray: should start on Monday', () => { + const date = adapter.date(TEST_DATE_ISO)!; + const result = adapter.getWeekArray(date); + expect(adapter.formatByString(result[0][0], 'EEEEEE')).to.equal('пн'); + }); + + it('is12HourCycleInCurrentLocale: should not have meridiem', () => { + expect(adapter.is12HourCycleInCurrentLocale()).to.equal(false); + }); + + it('getCurrentLocaleCode: should return locale code', () => { + expect(adapter.getCurrentLocaleCode()).to.equal('ru'); + }); + }); + + it('Formatting', () => { + const adapter = new AdapterDateFns({ locale: enUS }); + const adapterRu = new AdapterDateFns({ locale: ru }); + + const expectDate = ( + format: keyof AdapterFormats, + expectedWithEn: string, + expectedWithRu: string, + ) => { + const date = adapter.date('2020-02-01T23:44:00.000Z')!; + + expect(adapter.format(date, format)).to.equal(expectedWithEn); + expect(adapterRu.format(date, format)).to.equal(expectedWithRu); + }; + + expectDate('fullDate', 'Feb 1, 2020', '1 фев. 2020 г.'); + expectDate( + 'fullDateWithWeekday', + 'Saturday, February 1st, 2020', + 'суббота, 1 февраля 2020 г.', + ); + expectDate('fullDateTime', 'Feb 1, 2020 11:44 PM', '1 фев. 2020 г. 23:44'); + expectDate('fullDateTime12h', 'Feb 1, 2020 11:44 PM', '1 фев. 2020 г. 11:44 ПП'); + expectDate('fullDateTime24h', 'Feb 1, 2020 23:44', '1 фев. 2020 г. 23:44'); + expectDate('keyboardDate', '02/01/2020', '01.02.2020'); + expectDate('keyboardDateTime', '02/01/2020 11:44 PM', '01.02.2020 23:44'); + expectDate('keyboardDateTime12h', '02/01/2020 11:44 PM', '01.02.2020 11:44 ПП'); + expectDate('keyboardDateTime24h', '02/01/2020 23:44', '01.02.2020 23:44'); + }); + }); + + describe('Picker localization', () => { + Object.keys(localizedTexts).forEach((localeKey) => { + const localeName = localeKey === 'undefined' ? 'default' : `"${localeKey}"`; + const localeObject = localeKey === 'undefined' ? undefined : { fr, de }[localeKey]; + + describe(`test with the ${localeName} locale`, () => { + const { render, adapter } = createPickerRenderer({ + clock: 'fake', + adapterName: 'date-fns', + locale: localeObject, + }); + + it('should have correct placeholder', () => { + render(); + + expectInputPlaceholder( + screen.getByRole('textbox'), + localizedTexts[localeKey].placeholder, + ); + }); + + it('should have well formatted value', () => { + render(); + + expectInputValue(screen.getByRole('textbox'), localizedTexts[localeKey].value); + }); + }); + }); + + it('should return the correct week number', () => { + const adapter = new AdapterDateFns({ locale: fr }); + + const dateToTest = adapter.date(new Date(2022, 10, 10))!; + + expect(adapter.getWeekNumber!(dateToTest)).to.equal(45); + }); + }); +}); diff --git a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts new file mode 100644 index 0000000000000..0e819ad48fd64 --- /dev/null +++ b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts @@ -0,0 +1,566 @@ +/* eslint-disable class-methods-use-this */ +import addDays from 'date-fns/addDays'; +import addSeconds from 'date-fns/addSeconds'; +import addMinutes from 'date-fns/addMinutes'; +import addHours from 'date-fns/addHours'; +import addWeeks from 'date-fns/addWeeks'; +import addMonths from 'date-fns/addMonths'; +import addYears from 'date-fns/addYears'; +import differenceInYears from 'date-fns/differenceInYears'; +import differenceInQuarters from 'date-fns/differenceInQuarters'; +import differenceInMonths from 'date-fns/differenceInMonths'; +import differenceInWeeks from 'date-fns/differenceInWeeks'; +import differenceInDays from 'date-fns/differenceInDays'; +import differenceInHours from 'date-fns/differenceInHours'; +import differenceInMinutes from 'date-fns/differenceInMinutes'; +import differenceInSeconds from 'date-fns/differenceInSeconds'; +import differenceInMilliseconds from 'date-fns/differenceInMilliseconds'; +import eachDayOfInterval from 'date-fns/eachDayOfInterval'; +import endOfDay from 'date-fns/endOfDay'; +import endOfWeek from 'date-fns/endOfWeek'; +import endOfYear from 'date-fns/endOfYear'; +import dateFnsFormat from 'date-fns/format'; +import getDate from 'date-fns/getDate'; +import getDay from 'date-fns/getDay'; +import getDaysInMonth from 'date-fns/getDaysInMonth'; +import getHours from 'date-fns/getHours'; +import getMinutes from 'date-fns/getMinutes'; +import getMonth from 'date-fns/getMonth'; +import getSeconds from 'date-fns/getSeconds'; +import getWeek from 'date-fns/getWeek'; +import getYear from 'date-fns/getYear'; +import isAfter from 'date-fns/isAfter'; +import isBefore from 'date-fns/isBefore'; +import isEqual from 'date-fns/isEqual'; +import isSameDay from 'date-fns/isSameDay'; +import isSameYear from 'date-fns/isSameYear'; +import isSameMonth from 'date-fns/isSameMonth'; +import isSameHour from 'date-fns/isSameHour'; +import isValid from 'date-fns/isValid'; +import dateFnsParse from 'date-fns/parse'; +import setDate from 'date-fns/setDate'; +import setHours from 'date-fns/setHours'; +import setMinutes from 'date-fns/setMinutes'; +import setMonth from 'date-fns/setMonth'; +import setSeconds from 'date-fns/setSeconds'; +import setYear from 'date-fns/setYear'; +import startOfDay from 'date-fns/startOfDay'; +import startOfMonth from 'date-fns/startOfMonth'; +import endOfMonth from 'date-fns/endOfMonth'; +import startOfWeek from 'date-fns/startOfWeek'; +import startOfYear from 'date-fns/startOfYear'; +import parseISO from 'date-fns/parseISO'; +import formatISO from 'date-fns/formatISO'; +import isWithinInterval from 'date-fns/isWithinInterval'; +import defaultLocale from 'date-fns/locale/en-US'; +// @ts-ignore +import longFormatters from 'date-fns/_lib/format/longFormatters'; +import { AdapterFormats, AdapterUnits, FieldFormatTokenMap, MuiPickersAdapter } from '../models'; + +type DateFnsLocale = typeof defaultLocale; + +interface AdapterDateFnsOptions { + locale?: DateFnsLocale; + formats?: Partial; +} + +const formatTokenMap: FieldFormatTokenMap = { + // Year + y: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, + yy: 'year', + yyy: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, + yyyy: 'year', + + // Month + M: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, + MM: 'month', + MMMM: { sectionType: 'month', contentType: 'letter' }, + MMM: { sectionType: 'month', contentType: 'letter' }, + L: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, + LL: 'month', + LLL: { sectionType: 'month', contentType: 'letter' }, + LLLL: { sectionType: 'month', contentType: 'letter' }, + + // Day of the month + d: { sectionType: 'day', contentType: 'digit', maxLength: 2 }, + dd: 'day', + do: { sectionType: 'day', contentType: 'digit-with-letter' }, + + // Day of the week + E: { sectionType: 'weekDay', contentType: 'letter' }, + EE: { sectionType: 'weekDay', contentType: 'letter' }, + EEE: { sectionType: 'weekDay', contentType: 'letter' }, + EEEE: { sectionType: 'weekDay', contentType: 'letter' }, + EEEEE: { sectionType: 'weekDay', contentType: 'letter' }, + i: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, + ii: 'weekDay', + iii: { sectionType: 'weekDay', contentType: 'letter' }, + iiii: { sectionType: 'weekDay', contentType: 'letter' }, + e: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, + ee: 'weekDay', + eee: { sectionType: 'weekDay', contentType: 'letter' }, + eeee: { sectionType: 'weekDay', contentType: 'letter' }, + eeeee: { sectionType: 'weekDay', contentType: 'letter' }, + eeeeee: { sectionType: 'weekDay', contentType: 'letter' }, + c: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, + cc: 'weekDay', + ccc: { sectionType: 'weekDay', contentType: 'letter' }, + cccc: { sectionType: 'weekDay', contentType: 'letter' }, + ccccc: { sectionType: 'weekDay', contentType: 'letter' }, + cccccc: { sectionType: 'weekDay', contentType: 'letter' }, + + // Meridiem + a: 'meridiem', + aa: 'meridiem', + aaa: 'meridiem', + + // Hours + H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, + HH: 'hours', + h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, + hh: 'hours', + + // Minutes + m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, + mm: 'minutes', + + // Seconds + s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 }, + ss: 'seconds', +}; + +const defaultFormats: AdapterFormats = { + dayOfMonth: 'd', + fullDate: 'PP', + fullDateWithWeekday: 'PPPP', + fullDateTime: 'PP p', + fullDateTime12h: 'PP hh:mm aa', + fullDateTime24h: 'PP HH:mm', + fullTime: 'p', + fullTime12h: 'hh:mm aa', + fullTime24h: 'HH:mm', + hours12h: 'hh', + hours24h: 'HH', + keyboardDate: 'P', + keyboardDateTime: 'P p', + keyboardDateTime12h: 'P hh:mm aa', + keyboardDateTime24h: 'P HH:mm', + minutes: 'mm', + month: 'LLLL', + monthAndDate: 'MMMM d', + monthAndYear: 'LLLL yyyy', + monthShort: 'MMM', + weekday: 'EEEE', + weekdayShort: 'EEE', + normalDate: 'd MMMM', + normalDateWithWeekday: 'EEE, MMM d', + seconds: 'ss', + shortDate: 'MMM d', + year: 'yyyy', +}; + +/** + * Based on `@date-io/date-fns` + * + * MIT License + * + * Copyright (c) 2017 Dmitriy Kovalenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +export class AdapterDateFns implements MuiPickersAdapter { + public isMUIAdapter = true; + + public lib = 'date-fns'; + + public locale?: DateFnsLocale; + + public formats: AdapterFormats; + + public formatTokenMap = formatTokenMap; + + public escapedCharacters = { start: "'", end: "'" }; + + constructor({ locale, formats }: AdapterDateFnsOptions = {}) { + this.locale = locale; + this.formats = { ...defaultFormats, ...formats }; + } + + public date = (value?: any) => { + if (typeof value === 'undefined') { + return new Date(); + } + + if (value === null) { + return null; + } + + return new Date(value); + }; + + public toJsDate = (value: Date) => { + return value; + }; + + public parseISO = (isoString: string) => { + return parseISO(isoString); + }; + + public toISO = (value: Date) => { + return formatISO(value, { format: 'extended' }); + }; + + public parse = (value: string, format: string) => { + if (value === '') { + return null; + } + + return dateFnsParse(value, format, new Date(), { locale: this.locale }); + }; + + public getCurrentLocaleCode = () => { + return this.locale?.code || 'en-US'; + }; + + // Note: date-fns input types are more lenient than this adapter, so we need to expose our more + // strict signature and delegate to the more lenient signature. Otherwise, we have downstream type errors upon usage. + public is12HourCycleInCurrentLocale = () => { + if (this.locale) { + return /a/.test(this.locale.formatLong!.time()); + } + + // By default, date-fns is using en-US locale with am/pm enabled + return true; + }; + + public expandFormat = (format: string) => { + const longFormatRegexp = /P+p+|P+|p+|''|'(''|[^'])+('|$)|./g; + + // @see https://github.com/date-fns/date-fns/blob/master/src/format/index.js#L31 + return format + .match(longFormatRegexp)! + .map((token: string) => { + const firstCharacter = token[0]; + if (firstCharacter === 'p' || firstCharacter === 'P') { + const longFormatter = longFormatters[firstCharacter]; + const locale = this.locale || defaultLocale; + return longFormatter(token, locale.formatLong, {}); + } + return token; + }) + .join(''); + }; + + // Redefined here just to show how it can be written using expandFormat + public getFormatHelperText = (format: string) => { + return this.expandFormat(format) + .replace(/(aaa|aa|a)/g, '(a|p)m') + .toLocaleLowerCase(); + }; + + public isNull = (value: Date | null) => { + return value === null; + }; + + public isValid = (value: any) => { + return isValid(this.date(value)); + }; + + public format = (value: Date, formatKey: keyof AdapterFormats) => { + return this.formatByString(value, this.formats[formatKey]); + }; + + public formatByString = (value: Date, formatString: string) => { + return dateFnsFormat(value, formatString, { locale: this.locale }); + }; + + public formatNumber = (numberToFormat: string) => { + return numberToFormat; + }; + + public getDiff = (value: Date, comparing: Date | string, unit?: AdapterUnits) => { + switch (unit) { + case 'years': + return differenceInYears(value, this.date(comparing)!); + case 'quarters': + return differenceInQuarters(value, this.date(comparing)!); + case 'months': + return differenceInMonths(value, this.date(comparing)!); + case 'weeks': + return differenceInWeeks(value, this.date(comparing)!); + case 'days': + return differenceInDays(value, this.date(comparing)!); + case 'hours': + return differenceInHours(value, this.date(comparing)!); + case 'minutes': + return differenceInMinutes(value, this.date(comparing)!); + case 'seconds': + return differenceInSeconds(value, this.date(comparing)!); + default: { + return differenceInMilliseconds(value, this.date(comparing)!); + } + } + }; + + public isEqual = (value: any, comparing: any) => { + if (value === null && comparing === null) { + return true; + } + + return isEqual(value, comparing); + }; + + public isSameYear = (value: Date, comparing: Date) => { + return isSameYear(value, comparing); + }; + + public isSameMonth = (value: Date, comparing: Date) => { + return isSameMonth(value, comparing); + }; + + public isSameDay = (value: Date, comparing: Date) => { + return isSameDay(value, comparing); + }; + + public isSameHour = (value: Date, comparing: Date) => { + return isSameHour(value, comparing); + }; + + public isAfter = (value: Date, comparing: Date) => { + return isAfter(value, comparing); + }; + + public isAfterYear = (value: Date, comparing: Date) => { + return isAfter(value, endOfYear(comparing)); + }; + + public isAfterDay = (value: Date, comparing: Date) => { + return isAfter(value, endOfDay(comparing)); + }; + + public isBefore = (value: Date, comparing: Date) => { + return isBefore(value, comparing); + }; + + public isBeforeYear = (value: Date, comparing: Date) => { + return isBefore(value, startOfYear(comparing)); + }; + + public isBeforeDay = (value: Date, comparing: Date) => { + return isBefore(value, startOfDay(comparing)); + }; + + public isWithinRange = (value: Date, [start, end]: [Date, Date]) => { + return isWithinInterval(value, { start, end }); + }; + + public startOfYear = (value: Date) => { + return startOfYear(value); + }; + + public startOfMonth = (value: Date) => { + return startOfMonth(value); + }; + + public startOfWeek = (value: Date) => { + return startOfWeek(value, { locale: this.locale }); + }; + + public startOfDay = (value: Date) => { + return startOfDay(value); + }; + + public endOfYear = (value: Date) => { + return endOfYear(value); + }; + + public endOfMonth = (value: Date) => { + return endOfMonth(value); + }; + + public endOfWeek = (value: Date) => { + return endOfWeek(value, { locale: this.locale }); + }; + + public endOfDay = (value: Date) => { + return endOfDay(value); + }; + + public addYears = (value: Date, amount: number) => { + return addYears(value, amount); + }; + + public addMonths = (value: Date, amount: number) => { + return addMonths(value, amount); + }; + + public addWeeks = (value: Date, amount: number) => { + return addWeeks(value, amount); + }; + + public addDays = (value: Date, amount: number) => { + return addDays(value, amount); + }; + + public addHours = (value: Date, amount: number) => { + return addHours(value, amount); + }; + + public addMinutes = (value: Date, amount: number) => { + return addMinutes(value, amount); + }; + + public addSeconds = (value: Date, amount: number) => { + return addSeconds(value, amount); + }; + + public getYear = (value: Date) => { + return getYear(value); + }; + + public getMonth = (value: Date) => { + return getMonth(value); + }; + + public getDate = (value: Date) => { + return getDate(value); + }; + + public getHours = (value: Date) => { + return getHours(value); + }; + + public getMinutes = (value: Date) => { + return getMinutes(value); + }; + + public getSeconds = (value: Date) => { + return getSeconds(value); + }; + + public setYear = (value: Date, year: number) => { + return setYear(value, year); + }; + + public setMonth = (value: Date, month: number) => { + return setMonth(value, month); + }; + + public setDate = (value: Date, date: number) => { + return setDate(value, date); + }; + + public setHours = (value: Date, hours: number) => { + return setHours(value, hours); + }; + + public setMinutes = (value: Date, minutes: number) => { + return setMinutes(value, minutes); + }; + + public setSeconds = (value: Date, seconds: number) => { + return setSeconds(value, seconds); + }; + + public getDaysInMonth = (value: Date) => { + return getDaysInMonth(value); + }; + + public getNextMonth = (value: Date) => { + return addMonths(value, 1); + }; + + public getPreviousMonth = (value: Date) => { + return addMonths(value, -1); + }; + + public getMonthArray = (value: Date) => { + const firstMonth = startOfYear(value); + const monthArray = [firstMonth]; + + while (monthArray.length < 12) { + const prevMonth = monthArray[monthArray.length - 1]; + monthArray.push(this.getNextMonth(prevMonth)); + } + + return monthArray; + }; + + public mergeDateAndTime = (dateParam: Date, timeParam: Date) => { + return this.setSeconds( + this.setMinutes( + this.setHours(dateParam, this.getHours(timeParam)), + this.getMinutes(timeParam), + ), + this.getSeconds(timeParam), + ); + }; + + public getWeekdays = () => { + const now = new Date(); + return eachDayOfInterval({ + start: startOfWeek(now, { locale: this.locale }), + end: endOfWeek(now, { locale: this.locale }), + }).map((day) => this.formatByString(day, 'EEEEEE')); + }; + + public getWeekArray = (value: Date) => { + const start = startOfWeek(startOfMonth(value), { locale: this.locale }); + const end = endOfWeek(endOfMonth(value), { locale: this.locale }); + + let count = 0; + let current = start; + const nestedWeeks: Date[][] = []; + let lastDay: number | null = null; + while (isBefore(current, end)) { + const weekNumber = Math.floor(count / 7); + nestedWeeks[weekNumber] = nestedWeeks[weekNumber] || []; + const day = getDay(current); + if (lastDay !== day) { + lastDay = day; + nestedWeeks[weekNumber].push(current); + count += 1; + } + current = addDays(current, 1); + } + return nestedWeeks; + }; + + public getWeekNumber = (value: Date) => { + return getWeek(value, { locale: this.locale }); + }; + + public getYearRange = (start: Date, end: Date) => { + const startDate = startOfYear(start); + const endDate = endOfYear(end); + const years: Date[] = []; + + let current = startDate; + while (isBefore(current, endDate)) { + years.push(current); + current = addYears(current, 1); + } + + return years; + }; + + public getMeridiemText = (ampm: 'am' | 'pm') => { + return ampm === 'am' ? 'AM' : 'PM'; + }; +} diff --git a/packages/x-date-pickers/src/AdapterDateFns/index.ts b/packages/x-date-pickers/src/AdapterDateFns/index.ts index d27f40588160c..cfb486c23b24a 100644 --- a/packages/x-date-pickers/src/AdapterDateFns/index.ts +++ b/packages/x-date-pickers/src/AdapterDateFns/index.ts @@ -1,108 +1 @@ -import BaseAdapterDateFns from '@date-io/date-fns'; -import defaultLocale from 'date-fns/locale/en-US'; -// @ts-ignore -import longFormatters from 'date-fns/_lib/format/longFormatters'; -import getWeek from 'date-fns/getWeek'; -import { FieldFormatTokenMap, MuiPickersAdapter } from '../models'; - -const formatTokenMap: FieldFormatTokenMap = { - // Year - y: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, - yy: 'year', - yyy: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, - yyyy: 'year', - - // Month - M: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, - MM: 'month', - MMMM: { sectionType: 'month', contentType: 'letter' }, - MMM: { sectionType: 'month', contentType: 'letter' }, - L: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, - LL: 'month', - LLL: { sectionType: 'month', contentType: 'letter' }, - LLLL: { sectionType: 'month', contentType: 'letter' }, - - // Day of the month - d: { sectionType: 'day', contentType: 'digit', maxLength: 2 }, - dd: 'day', - do: { sectionType: 'day', contentType: 'digit-with-letter' }, - - // Day of the week - E: { sectionType: 'weekDay', contentType: 'letter' }, - EE: { sectionType: 'weekDay', contentType: 'letter' }, - EEE: { sectionType: 'weekDay', contentType: 'letter' }, - EEEE: { sectionType: 'weekDay', contentType: 'letter' }, - EEEEE: { sectionType: 'weekDay', contentType: 'letter' }, - i: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, - ii: 'weekDay', - iii: { sectionType: 'weekDay', contentType: 'letter' }, - iiii: { sectionType: 'weekDay', contentType: 'letter' }, - e: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, - ee: 'weekDay', - eee: { sectionType: 'weekDay', contentType: 'letter' }, - eeee: { sectionType: 'weekDay', contentType: 'letter' }, - eeeee: { sectionType: 'weekDay', contentType: 'letter' }, - eeeeee: { sectionType: 'weekDay', contentType: 'letter' }, - c: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, - cc: 'weekDay', - ccc: { sectionType: 'weekDay', contentType: 'letter' }, - cccc: { sectionType: 'weekDay', contentType: 'letter' }, - ccccc: { sectionType: 'weekDay', contentType: 'letter' }, - cccccc: { sectionType: 'weekDay', contentType: 'letter' }, - - // Meridiem - a: 'meridiem', - aa: 'meridiem', - aaa: 'meridiem', - - // Hours - H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - HH: 'hours', - h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - hh: 'hours', - - // Minutes - m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, - mm: 'minutes', - - // Seconds - s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 }, - ss: 'seconds', -}; - -export class AdapterDateFns extends BaseAdapterDateFns implements MuiPickersAdapter { - public isMUIAdapter = true; - - public formatTokenMap = formatTokenMap; - - public escapedCharacters = { start: "'", end: "'" }; - - public expandFormat = (format: string) => { - const longFormatRegexp = /P+p+|P+|p+|''|'(''|[^'])+('|$)|./g; - - // @see https://github.com/date-fns/date-fns/blob/master/src/format/index.js#L31 - return format - .match(longFormatRegexp)! - .map((token: string) => { - const firstCharacter = token[0]; - if (firstCharacter === 'p' || firstCharacter === 'P') { - const longFormatter = longFormatters[firstCharacter]; - const locale = this.locale || defaultLocale; - return longFormatter(token, locale.formatLong, {}); - } - return token; - }) - .join(''); - }; - - // Redefined here just to show how it can be written using expandFormat - public getFormatHelperText = (format: string) => { - return this.expandFormat(format) - .replace(/(aaa|aa|a)/g, '(a|p)m') - .toLocaleLowerCase(); - }; - - public getWeekNumber = (date: Date) => { - return getWeek(date, { locale: this.locale }); - }; -} +export { AdapterDateFns } from './AdapterDateFns'; diff --git a/packages/x-date-pickers/src/AdapterDateFns/localization.test.tsx b/packages/x-date-pickers/src/AdapterDateFns/localization.test.tsx deleted file mode 100644 index 90e99a915337b..0000000000000 --- a/packages/x-date-pickers/src/AdapterDateFns/localization.test.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from 'react'; -import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; -import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; -import { expect } from 'chai'; -import { - createPickerRenderer, - expectInputPlaceholder, - expectInputValue, -} from 'test/utils/pickers-utils'; -import fr from 'date-fns/locale/fr'; -import de from 'date-fns/locale/de'; - -const testDate = new Date(2018, 4, 15, 9, 35); -const localizedTexts = { - undefined: { - placeholder: 'MM/DD/YYYY hh:mm aa', - value: '05/15/2018 09:35 am', - }, - fr: { - placeholder: 'DD/MM/YYYY hh:mm', - value: '15/05/2018 09:35', - }, - de: { - placeholder: 'DD.MM.YYYY hh:mm', - value: '15.05.2018 09:35', - }, -}; -describe('', () => { - Object.keys(localizedTexts).forEach((localeKey) => { - const localeName = localeKey === 'undefined' ? 'default' : `"${localeKey}"`; - const localeObject = localeKey === 'undefined' ? undefined : { fr, de }[localeKey]; - - describe(`test with the ${localeName} locale`, () => { - const { render, adapter } = createPickerRenderer({ - clock: 'fake', - adapterName: 'date-fns', - locale: localeObject, - }); - - it('should have correct placeholder', () => { - render(); - - expectInputPlaceholder(screen.getByRole('textbox'), localizedTexts[localeKey].placeholder); - }); - - it('should have well formatted value', () => { - render(); - - expectInputValue(screen.getByRole('textbox'), localizedTexts[localeKey].value); - }); - }); - }); - - it('should return the correct week number', () => { - const adapter = new AdapterDateFns({ locale: fr }); - - const dateToTest = adapter.date(new Date(2022, 10, 10)); - - expect(adapter.getWeekNumber!(dateToTest)).to.equal(45); - }); -}); diff --git a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx index 6f6cfa68cb4ed..b40436b89676d 100644 --- a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx +++ b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx @@ -5,6 +5,7 @@ import utc from 'dayjs/plugin/utc'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { AdapterFormats } from '@mui/x-date-pickers/models'; import { screen, userEvent } from '@mui/monorepo/test/utils'; import { expect } from 'chai'; import { @@ -22,7 +23,6 @@ import { describeGregorianAdapter, TEST_DATE_ISO, } from 'packages/x-date-pickers/src/tests/describeGregorianAdapter'; -import { AdapterFormats } from '@mui/x-date-pickers'; dayjs.extend(utc); diff --git a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts index 465f5cb15b218..4a667ad4e5e79 100644 --- a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts +++ b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts @@ -250,7 +250,7 @@ export class AdapterDayjs implements MuiPickersAdapter { return numberToFormat; }; - public getDiff = (value: Dayjs, comparing: Dayjs, unit?: AdapterUnits) => { + public getDiff = (value: Dayjs, comparing: Dayjs | string, unit?: AdapterUnits) => { return value.diff(comparing, unit as AdapterUnits); }; @@ -282,24 +282,24 @@ export class AdapterDayjs implements MuiPickersAdapter { return value.isAfter(comparing); }; - public isAfterYear = (date: Dayjs, comparing: Dayjs) => { - return date.isAfter(comparing, 'year'); + public isAfterYear = (value: Dayjs, comparing: Dayjs) => { + return value.isAfter(comparing, 'year'); }; - public isAfterDay = (date: Dayjs, comparing: Dayjs) => { - return date.isAfter(comparing, 'day'); + public isAfterDay = (value: Dayjs, comparing: Dayjs) => { + return value.isAfter(comparing, 'day'); }; - public isBefore = (date: Dayjs, comparing: Dayjs) => { - return date.isBefore(comparing); + public isBefore = (value: Dayjs, comparing: Dayjs) => { + return value.isBefore(comparing); }; - public isBeforeYear = (date: Dayjs, comparing: Dayjs) => { - return date.isBefore(comparing, 'year'); + public isBeforeYear = (value: Dayjs, comparing: Dayjs) => { + return value.isBefore(comparing, 'year'); }; - public isBeforeDay = (date: Dayjs, comparing: Dayjs) => { - return date.isBefore(comparing, 'day'); + public isBeforeDay = (value: Dayjs, comparing: Dayjs) => { + return value.isBefore(comparing, 'day'); }; public isWithinRange = (value: Dayjs, [start, end]: [Dayjs, Dayjs]) => { diff --git a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx index 82820d419fb6f..c884207ac834a 100644 --- a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx +++ b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon'; +import { AdapterFormats } from '@mui/x-date-pickers/models'; import { screen } from '@mui/monorepo/test/utils/createRenderer'; import { expect } from 'chai'; import { @@ -12,7 +13,6 @@ import { describeGregorianAdapter, TEST_DATE_ISO, } from '@mui/x-date-pickers/tests/describeGregorianAdapter'; -import { AdapterFormats } from '@mui/x-date-pickers'; const testDate = new Date(2018, 4, 15, 9, 35); const localizedTexts = { diff --git a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx index 7d71edce2f559..85bf9398e8a59 100644 --- a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx +++ b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import moment from 'moment'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment'; +import { AdapterFormats } from '@mui/x-date-pickers/models'; import { screen } from '@mui/monorepo/test/utils/createRenderer'; import { expect } from 'chai'; import { @@ -16,7 +17,6 @@ import { describeGregorianAdapter, TEST_DATE_ISO, } from '@mui/x-date-pickers/tests/describeGregorianAdapter'; -import { AdapterFormats } from '@mui/x-date-pickers'; const testDate = new Date(2018, 4, 15, 9, 35); const localizedTexts = { diff --git a/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx b/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx index b65e10a6a20b8..b2199fb735992 100644 --- a/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx +++ b/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx @@ -7,7 +7,7 @@ import { expectInputValue, getCleanedSelectedContent, getTextbox } from 'test/ut import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; describe(' - Editing', () => { - describeAdapters('key: ArrowDown', TimeField, ({ adapter, adapterName, testFieldKeyPress }) => { + describeAdapters('key: ArrowDown', TimeField, ({ adapter, testFieldKeyPress }) => { describe('24 hours format (ArrowDown)', () => { it('should set the hour to 23 when no value is provided', () => { testFieldKeyPress({ @@ -77,7 +77,7 @@ describe(' - Editing', () => { format: adapter.formats.fullTime12h, defaultValue: adapter.date(new Date(2022, 5, 15, 0, 12, 25)), key: 'ArrowDown', - expectedValue: adapterName === 'date-fns' ? '11:12 am' : '11:12 AM', + expectedValue: '11:12 AM', }); }); @@ -85,7 +85,7 @@ describe(' - Editing', () => { testFieldKeyPress({ format: adapter.formats.fullTime12h, key: 'ArrowDown', - expectedValue: adapterName === 'date-fns' ? 'hh:mm pm' : 'hh:mm PM', + expectedValue: 'hh:mm PM', valueToSelect: 'aa', }); }); @@ -95,8 +95,8 @@ describe(' - Editing', () => { format: adapter.formats.fullTime12h, defaultValue: adapter.date(new Date(2022, 5, 15, 2, 12, 25)), key: 'ArrowDown', - expectedValue: adapterName === 'date-fns' ? '02:12 pm' : '02:12 PM', - valueToSelect: adapterName === 'date-fns' ? 'am' : 'AM', + expectedValue: '02:12 PM', + valueToSelect: 'AM', }); }); @@ -105,14 +105,14 @@ describe(' - Editing', () => { format: adapter.formats.fullTime12h, defaultValue: adapter.date(new Date(2022, 5, 15, 14, 12, 25)), key: 'ArrowDown', - expectedValue: adapterName === 'date-fns' ? '02:12 am' : '02:12 AM', - valueToSelect: adapterName === 'date-fns' ? 'pm' : 'PM', + expectedValue: '02:12 AM', + valueToSelect: 'PM', }); }); }); }); - describeAdapters('key: ArrowUp', TimeField, ({ adapter, adapterName, testFieldKeyPress }) => { + describeAdapters('key: ArrowUp', TimeField, ({ adapter, testFieldKeyPress }) => { describe('24 hours format (ArrowUp)', () => { it('should set the hour to 0 when no value is provided', () => { testFieldKeyPress({ @@ -173,7 +173,7 @@ describe(' - Editing', () => { testFieldKeyPress({ format: adapter.formats.fullTime12h, key: 'ArrowUp', - expectedValue: adapterName === 'date-fns' ? 'hh:mm am' : 'hh:mm AM', + expectedValue: 'hh:mm AM', cursorPosition: 14, }); }); @@ -183,7 +183,7 @@ describe(' - Editing', () => { format: adapter.formats.fullTime12h, defaultValue: adapter.date(new Date(2022, 5, 15, 2, 12, 25)), key: 'ArrowUp', - expectedValue: adapterName === 'date-fns' ? '02:12 pm' : '02:12 PM', + expectedValue: '02:12 PM', cursorPosition: 14, }); }); @@ -193,7 +193,7 @@ describe(' - Editing', () => { format: adapter.formats.fullTime12h, defaultValue: adapter.date(new Date(2022, 5, 15, 14, 12, 25)), key: 'ArrowUp', - expectedValue: adapterName === 'date-fns' ? '02:12 am' : '02:12 AM', + expectedValue: '02:12 AM', cursorPosition: 14, }); }); @@ -281,7 +281,7 @@ describe(' - Editing', () => { }, ); - describeAdapters('Letter editing', TimeField, ({ adapter, adapterName, testFieldChange }) => { + describeAdapters('Letter editing', TimeField, ({ adapter, testFieldChange }) => { it('should not edit when props.readOnly = true and no value is provided (letter)', () => { testFieldChange({ format: adapter.formats.fullTime12h, @@ -297,9 +297,7 @@ describe(' - Editing', () => { defaultValue: adapter.date(new Date(2022, 5, 15, 14, 12, 25)), readOnly: true, // Press "a" - keyStrokes: [ - { value: '02:12 a', expected: adapterName === 'date-fns' ? '02:12 pm' : '02:12 PM' }, - ], + keyStrokes: [{ value: '02:12 a', expected: '02:12 PM' }], }); }); @@ -308,9 +306,7 @@ describe(' - Editing', () => { format: adapter.formats.fullTime12h, cursorPosition: 17, // Press "a" - keyStrokes: [ - { value: 'hh:mm a', expected: adapterName === 'date-fns' ? 'hh:mm am' : 'hh:mm AM' }, - ], + keyStrokes: [{ value: 'hh:mm a', expected: 'hh:mm AM' }], }); }); @@ -319,9 +315,7 @@ describe(' - Editing', () => { format: adapter.formats.fullTime12h, cursorPosition: 17, // Press "p" - keyStrokes: [ - { value: 'hh:mm p', expected: adapterName === 'date-fns' ? 'hh:mm pm' : 'hh:mm PM' }, - ], + keyStrokes: [{ value: 'hh:mm p', expected: 'hh:mm PM' }], }); }); @@ -331,9 +325,7 @@ describe(' - Editing', () => { defaultValue: adapter.date(new Date(2022, 5, 15, 14, 12, 25)), cursorPosition: 17, // Press "a" - keyStrokes: [ - { value: '02:12 a', expected: adapterName === 'date-fns' ? '02:12 am' : '02:12 AM' }, - ], + keyStrokes: [{ value: '02:12 a', expected: '02:12 AM' }], }); }); @@ -343,9 +335,7 @@ describe(' - Editing', () => { defaultValue: adapter.date(new Date(2022, 5, 15, 14, 12, 25)), cursorPosition: 17, // Press "p" - keyStrokes: [ - { value: '02:12 p', expected: adapterName === 'date-fns' ? '02:12 pm' : '02:12 PM' }, - ], + keyStrokes: [{ value: '02:12 p', expected: '02:12 PM' }], }); }); }); diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.types.ts b/packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.types.ts index a158a32e21551..e9fc6ffe52813 100644 --- a/packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.types.ts +++ b/packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.types.ts @@ -2,7 +2,8 @@ import { MuiPickersAdapter } from '@mui/x-date-pickers/models'; export interface DescribeGregorianAdapterParams { formatDateTime: string; - locale: string; + // TODO: Type once the adapter locale is correctly types + locale: any; } export type DescribeGregorianAdapterTestSuite = (params: { diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts b/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts index 59177e9f30ef0..282865c603a08 100644 --- a/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts +++ b/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts @@ -296,7 +296,9 @@ export const testCalculations: DescribeGregorianAdapterTestSuite = ({ expect(adapter.getDiff(testDate, adapter.date('2018-10-31T11:44:00.000Z')!)).to.equal( -86400000, ); - expect(adapter.getDiff(testDate, '2018-10-31T11:44:00.000Z')).to.equal(-86400000); + expect(adapter.getDiff(testDate, adapter.date('2018-10-31T11:44:00.000Z')!)).to.equal( + -86400000, + ); // With units expect(adapter.getDiff(testDate, adapter.date('2017-09-29T11:44:00.000Z')!, 'years')).to.equal( From 472ba96dec9a58b7542d410e300e8697aec0e1e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 11:07:43 +0200 Subject: [PATCH 14/80] Bump typescript-eslint to ^5.59.1 (#8722) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- .../eslint-plugin-material-ui/package.json | 4 +- yarn.lock | 106 +++++++++--------- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 4f289fc438e60..458dd687fda20 100644 --- a/package.json +++ b/package.json @@ -106,8 +106,8 @@ "@types/requestidlecallback": "^0.3.5", "@types/sinon": "^10.0.14", "@types/yargs": "^17.0.24", - "@typescript-eslint/eslint-plugin": "^5.58.0", - "@typescript-eslint/parser": "^5.58.0", + "@typescript-eslint/eslint-plugin": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1", "axe-core": "4.7.0", "babel-loader": "^9.1.2", "babel-plugin-istanbul": "^6.1.1", diff --git a/packages/eslint-plugin-material-ui/package.json b/packages/eslint-plugin-material-ui/package.json index 745ae358db297..93d1d425beb2f 100644 --- a/packages/eslint-plugin-material-ui/package.json +++ b/packages/eslint-plugin-material-ui/package.json @@ -6,8 +6,8 @@ "main": "src/index.js", "devDependencies": { "@types/eslint": "^8.37.0", - "@typescript-eslint/experimental-utils": "^5.58.0", - "@typescript-eslint/parser": "^5.58.0" + "@typescript-eslint/experimental-utils": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1" }, "scripts": { "test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/eslint-plugin-material-ui/**/*.test.js' --timeout 3000" diff --git a/yarn.lock b/yarn.lock index 24659187c7753..90f90d2c30565 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3191,15 +3191,15 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.58.0.tgz#b1d4b0ad20243269d020ef9bbb036a40b0849829" - integrity sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA== +"@typescript-eslint/eslint-plugin@^5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz#9b09ee1541bff1d2cebdcb87e7ce4a4003acde08" + integrity sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.58.0" - "@typescript-eslint/type-utils" "5.58.0" - "@typescript-eslint/utils" "5.58.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/type-utils" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" @@ -3207,79 +3207,79 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@^5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.58.0.tgz#157af1376add1a945c4559eef25114f0a29f49e1" - integrity sha512-LA/sRPaynZlrlYxdefrZbMx8dqs/1Kc0yNG+XOk5CwwZx7tTv263ix3AJNioF0YBVt7hADpAUR20owl6pv4MIQ== +"@typescript-eslint/experimental-utils@^5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.59.1.tgz#e185c9db6fe0c02ff2e542622db375ae012a0fe2" + integrity sha512-KVtKcHEizCIRx//LC9tBi6xp94ULKbU5StVHBVWURJQOVa2qw6HP28Hu7LmHrQM3p9I3q5Y2VR4wKllCJ3IWrw== dependencies: - "@typescript-eslint/utils" "5.58.0" + "@typescript-eslint/utils" "5.59.1" -"@typescript-eslint/parser@^5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.58.0.tgz#2ac4464cf48bef2e3234cb178ede5af352dddbc6" - integrity sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ== +"@typescript-eslint/parser@^5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.1.tgz#73c2c12127c5c1182d2e5b71a8fa2a85d215cbb4" + integrity sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g== dependencies: - "@typescript-eslint/scope-manager" "5.58.0" - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/typescript-estree" "5.58.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz#5e023a48352afc6a87be6ce3c8e763bc9e2f0bc8" - integrity sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA== +"@typescript-eslint/scope-manager@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz#8a20222719cebc5198618a5d44113705b51fd7fe" + integrity sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA== dependencies: - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/visitor-keys" "5.58.0" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" -"@typescript-eslint/type-utils@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.58.0.tgz#f7d5b3971483d4015a470d8a9e5b8a7d10066e52" - integrity sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w== +"@typescript-eslint/type-utils@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.1.tgz#63981d61684fd24eda2f9f08c0a47ecb000a2111" + integrity sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw== dependencies: - "@typescript-eslint/typescript-estree" "5.58.0" - "@typescript-eslint/utils" "5.58.0" + "@typescript-eslint/typescript-estree" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.58.0.tgz#54c490b8522c18986004df7674c644ffe2ed77d8" - integrity sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g== +"@typescript-eslint/types@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.1.tgz#03f3fedd1c044cb336ebc34cc7855f121991f41d" + integrity sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg== -"@typescript-eslint/typescript-estree@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz#4966e6ff57eaf6e0fce2586497edc097e2ab3e61" - integrity sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q== +"@typescript-eslint/typescript-estree@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz#4aa546d27fd0d477c618f0ca00b483f0ec84c43c" + integrity sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA== dependencies: - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/visitor-keys" "5.58.0" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.58.0.tgz#430d7c95f23ec457b05be5520c1700a0dfd559d5" - integrity sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ== +"@typescript-eslint/utils@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.1.tgz#d89fc758ad23d2157cfae53f0b429bdf15db9473" + integrity sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.58.0" - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/typescript-estree" "5.58.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz#eb9de3a61d2331829e6761ce7fd13061781168b4" - integrity sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA== +"@typescript-eslint/visitor-keys@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz#0d96c36efb6560d7fb8eb85de10442c10d8f6058" + integrity sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA== dependencies: - "@typescript-eslint/types" "5.58.0" + "@typescript-eslint/types" "5.59.1" eslint-visitor-keys "^3.3.0" "@webassemblyjs/ast@1.11.1": From 7227b8a5ef8ec080395e16b5647b1670f0abbe21 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Wed, 26 Apr 2023 11:19:56 +0200 Subject: [PATCH 15/80] [docs] Document the supported formats (#8746) --- .../adapters-locale/adapters-locale.md | 44 +++++++++++++++++++ .../src/AdapterLuxon/AdapterLuxon.ts | 2 - .../src/AdapterMoment/AdapterMoment.ts | 2 - .../src/AdapterMomentHijri/index.ts | 2 - .../src/AdapterMomentJalaali/index.ts | 2 - 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/docs/data/date-pickers/adapters-locale/adapters-locale.md b/docs/data/date-pickers/adapters-locale/adapters-locale.md index bba7a9078a67a..b1bb7ad3be09a 100644 --- a/docs/data/date-pickers/adapters-locale/adapters-locale.md +++ b/docs/data/date-pickers/adapters-locale/adapters-locale.md @@ -151,6 +151,50 @@ This prop is available on all fields and pickers. You can control the field format spacing using the [formatDensity](/x/react-date-pickers/custom-field/#change-the-format-density) prop. ::: +### Field-supported formats + +Some formats might not yet be supported by the fields. +For example, they don't support day of the year or quarter. + +Here is the list of the currently supported formats: + +- The year + - ✅ 2-digits values (e.g: `23`) + - ✅ 4-digits values (e.g: `2023`) + - ❌ Values with ordinal (e.g: `2023th`) +- The month + + - ✅ 1-based digit (e.g: `08`) + - ✅ Multi-letter values (e.g `Aug`, `August`) + - ❌ 1-letter values (e.g: `A`) because several months are represented with the same letter + +- The day of the month + + - ✅ 1-based digit values (e.g: `24`) + - ✅ 1-based digit values with ordinal (e.g: `24th`) + +- The day of the week + + - ✅ 0-based digit values (e.g: `03`) + - ✅ 1-based digit values (e.g: `04`) + - ✅ Multi-letter values (e.g: `Tue`, `Tuesday`) + - ❌ 1-letter values (e.g: `T`) because several days of the week are represented with the same letter + +- The hours + + - ✅ 0-based 12-hours values (e.g: `03`) + - ✅ 0-based 24-hours values (e.g: `15`) + - ❌ 1-based values (e.g: `24` instead of `00`) + +- The minutes + +- The seconds + +- The meridiem + +If you need to use some format that is not yet supported, please [open an issue](https://github.com/mui/mui-x/issues/new/choose) describing what is your exact use case. +Some new formats might be supported in the future, depending on the complexity of the implementation. + ### Respect leading zeros in fields By default, the value rendered in the field always contains digit zeros, even if your format says otherwise. diff --git a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts index b7f1365f87dae..7a797bffec514 100644 --- a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts +++ b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts @@ -31,11 +31,9 @@ const formatTokenMap: FieldFormatTokenMap = { c: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, ccc: { sectionType: 'weekDay', contentType: 'letter' }, cccc: { sectionType: 'weekDay', contentType: 'letter' }, - ccccc: { sectionType: 'weekDay', contentType: 'letter' }, E: { sectionType: 'weekDay', contentType: 'digit', maxLength: 2 }, EEE: { sectionType: 'weekDay', contentType: 'letter' }, EEEE: { sectionType: 'weekDay', contentType: 'letter' }, - EEEEE: { sectionType: 'weekDay', contentType: 'letter' }, // Meridiem a: 'meridiem', diff --git a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts index d29654dfc3632..4d0b0187f9799 100644 --- a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts +++ b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts @@ -43,8 +43,6 @@ const formatTokenMap: FieldFormatTokenMap = { HH: 'hours', h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, hh: 'hours', - k: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - kk: 'hours', // Minutes m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, diff --git a/packages/x-date-pickers/src/AdapterMomentHijri/index.ts b/packages/x-date-pickers/src/AdapterMomentHijri/index.ts index 7beb39755d9f4..2a4817cae496e 100644 --- a/packages/x-date-pickers/src/AdapterMomentHijri/index.ts +++ b/packages/x-date-pickers/src/AdapterMomentHijri/index.ts @@ -30,8 +30,6 @@ const formatTokenMap: FieldFormatTokenMap = { HH: 'hours', h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, hh: 'hours', - k: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - kk: 'hours', // Minutes m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, diff --git a/packages/x-date-pickers/src/AdapterMomentJalaali/index.ts b/packages/x-date-pickers/src/AdapterMomentJalaali/index.ts index dd6f6c90f44a3..18087e84c5d0c 100644 --- a/packages/x-date-pickers/src/AdapterMomentJalaali/index.ts +++ b/packages/x-date-pickers/src/AdapterMomentJalaali/index.ts @@ -30,8 +30,6 @@ const formatTokenMap: FieldFormatTokenMap = { HH: 'hours', h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, hh: 'hours', - k: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - kk: 'hours', // Minutes m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, From 28b31643adb30ee6dce94295b27732ccae619503 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Wed, 26 Apr 2023 16:29:25 +0200 Subject: [PATCH 16/80] [DataGridPremium] Fix infinite loop when updating grouped rows (#8693) --- .../rowGrouping.DataGridPremium.test.tsx | 55 ++++++++++++++++++- .../src/utils/tree/removeDataRowFromTree.ts | 2 +- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx index 18aa8312b182b..ee3463d14c6b5 100644 --- a/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx @@ -1,5 +1,12 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen, act, userEvent } from '@mui/monorepo/test/utils'; +import { + createRenderer, + fireEvent, + screen, + act, + userEvent, + waitFor, +} from '@mui/monorepo/test/utils'; import { getColumnHeaderCell, getColumnHeadersTextContent, @@ -62,7 +69,7 @@ const baselineProps: DataGridPremiumProps = { }; describe(' - Row Grouping', () => { - const { render, clock } = createRenderer({ clock: 'fake' }); + const { render, clock } = createRenderer(); let apiRef: React.MutableRefObject; @@ -77,6 +84,8 @@ describe(' - Row Grouping', () => { } describe('Setting grouping criteria', () => { + clock.withFakeTimers(); + describe('initialState: rowGrouping.model', () => { it('should allow to initialize the row grouping', () => { render( @@ -199,6 +208,8 @@ describe(' - Row Grouping', () => { }); describe('prop: rowGroupingColumnMode', () => { + clock.withFakeTimers(); + it('should gather all the grouping criteria into a single column when rowGroupingColumnMode is not defined', () => { render( - Row Grouping', () => { }); describe('prop: disableRowGrouping', () => { + clock.withFakeTimers(); + it('should disable the row grouping when `prop.disableRowGrouping = true`', () => { render( - Row Grouping', () => { }); describe('prop: defaultGroupingExpansionDepth', () => { + clock.withFakeTimers(); + it('should not expand any row if defaultGroupingExpansionDepth = 0', () => { render( - Row Grouping', () => { }); describe('prop: isGroupExpandedByDefault', () => { + clock.withFakeTimers(); + it('should expand groups according to isGroupExpandedByDefault when defined', () => { const isGroupExpandedByDefault = spy( (node: GridGroupNode) => node.groupingKey === 'Cat A' && node.groupingField === 'category1', @@ -659,6 +676,8 @@ describe(' - Row Grouping', () => { }); describe('prop: groupingColDef when groupingColumnMode = "single"', () => { + clock.withFakeTimers(); + it('should not allow to override the field', () => { render( - Row Grouping', () => { }); describe('prop: groupingColDef when groupingColumnMode = "multiple"', () => { + clock.withFakeTimers(); + it('should not allow to override the field', () => { render( - Row Grouping', () => { }); describe('colDef: groupingValueGetter & valueGetter', () => { + clock.withFakeTimers(); + it('should use groupingValueGetter to group rows when defined', () => { render( - Row Grouping', () => { }); describe('column menu', () => { + clock.withFakeTimers(); + it('should add a "Group by {field}" menu item on ungrouped columns when coLDef.groupable is not defined', () => { render( - Row Grouping', () => { }); describe('sorting', () => { + clock.withFakeTimers(); + describe('prop: rowGroupingColumnMode = "single"', () => { it('should use the top level grouping criteria for sorting if mainGroupingCriteria and leafField are not defined', () => { render( @@ -2435,6 +2462,8 @@ describe(' - Row Grouping', () => { }); describe('apiRef: addRowGroupingCriteria', () => { + clock.withFakeTimers(); + it('should add grouping criteria to model', () => { render(); act(() => apiRef.current.addRowGroupingCriteria('category2')); @@ -2449,6 +2478,8 @@ describe(' - Row Grouping', () => { }); describe('apiRef: removeRowGroupingCriteria', () => { + clock.withFakeTimers(); + it('should remove field from model', () => { render(); act(() => apiRef.current.removeRowGroupingCriteria('category1')); @@ -2457,6 +2488,8 @@ describe(' - Row Grouping', () => { }); describe('apiRef: setRowGroupingCriteriaIndex', () => { + clock.withFakeTimers(); + it('should change the grouping criteria order', () => { render(); act(() => apiRef.current.setRowGroupingCriteriaIndex('category1', 1)); @@ -2465,6 +2498,8 @@ describe(' - Row Grouping', () => { }); describe('apiRef: getRowGroupChildren', () => { + clock.withFakeTimers(); + it('should return the rows in group of depth 0 of length 1 from tree of depth 1', () => { render( - Row Grouping', () => { ]); }); }); + + // See https://github.com/mui/mui-x/issues/8626 + it('should properly update the rows when they change', async () => { + render( + , + ); + + act(() => apiRef.current.updateRows([{ id: 1, group: 'A', username: 'username 2' }])); + + await waitFor(() => expect(getCell(1, 3).textContent).to.equal('username 2')); + }); }); diff --git a/packages/grid/x-data-grid-pro/src/utils/tree/removeDataRowFromTree.ts b/packages/grid/x-data-grid-pro/src/utils/tree/removeDataRowFromTree.ts index 98ed93cf639f2..1affa092e6d78 100644 --- a/packages/grid/x-data-grid-pro/src/utils/tree/removeDataRowFromTree.ts +++ b/packages/grid/x-data-grid-pro/src/utils/tree/removeDataRowFromTree.ts @@ -76,7 +76,7 @@ const removeNodeAndCleanParent = ({ parentNode.id !== GRID_ROOT_GROUP_ID && parentNode.children.length === 0; if (shouldDeleteGroup) { if (parentNode.isAutoGenerated) { - removeNodeAndCleanParent({ node, tree, treeDepths }); + removeNodeAndCleanParent({ node: parentNode, tree, treeDepths }); } else { tree[parentNode.id] = { type: 'leaf', From 425856f9d10b281ad664ecfb025cb1a8d8b4a839 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Wed, 26 Apr 2023 16:14:04 -0300 Subject: [PATCH 17/80] [DataGrid] Fix 'ResizeObserver loop limit exceeded' error (#8744) --- .../grid/x-data-grid/src/components/base/GridBody.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/grid/x-data-grid/src/components/base/GridBody.tsx b/packages/grid/x-data-grid/src/components/base/GridBody.tsx index 5bf1ef8c52da9..d21f4c5d29c5b 100644 --- a/packages/grid/x-data-grid/src/components/base/GridBody.tsx +++ b/packages/grid/x-data-grid/src/components/base/GridBody.tsx @@ -87,8 +87,12 @@ function GridBody(props: GridBodyProps) { return () => {}; } + let animationFrame: number; const observer = new ResizeObserver(() => { - apiRef.current.computeSizeAndPublishResizeEvent(); + // See https://github.com/mui/mui-x/issues/8733 + animationFrame = window.requestAnimationFrame(() => { + apiRef.current.computeSizeAndPublishResizeEvent(); + }); }); if (elementToObserve) { @@ -96,6 +100,10 @@ function GridBody(props: GridBodyProps) { } return () => { + if (animationFrame) { + window.cancelAnimationFrame(animationFrame); + } + if (elementToObserve) { observer.unobserve(elementToObserve); } From de88121384b7e630b0a18022edb9ba660ce8641b Mon Sep 17 00:00:00 2001 From: SakumyZ Date: Thu, 27 Apr 2023 14:22:53 +0800 Subject: [PATCH 18/80] [l10n] Improve Chinese (zh-CN) locale (#8753) Co-authored-by: leipengcheng --- docs/data/data-grid/localization/data.json | 2 +- docs/data/date-pickers/localization/data.json | 2 +- packages/grid/x-data-grid/src/locales/zhCN.ts | 4 +-- packages/x-date-pickers/src/locales/zhCN.ts | 32 +++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/data/data-grid/localization/data.json b/docs/data/data-grid/localization/data.json index 446989c1ca987..2647662315785 100644 --- a/docs/data/data-grid/localization/data.json +++ b/docs/data/data-grid/localization/data.json @@ -27,7 +27,7 @@ "languageTag": "zh-CN", "importName": "zhCN", "localeName": "Chinese (Simplified)", - "missingKeysCount": 2, + "missingKeysCount": 0, "totalKeysCount": 94, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/grid/x-data-grid/src/locales/zhCN.ts" }, diff --git a/docs/data/date-pickers/localization/data.json b/docs/data/date-pickers/localization/data.json index 87387fd33b660..178dcad0c53b8 100644 --- a/docs/data/date-pickers/localization/data.json +++ b/docs/data/date-pickers/localization/data.json @@ -19,7 +19,7 @@ "languageTag": "zh-CN", "importName": "zhCN", "localeName": "Chinese (Simplified)", - "missingKeysCount": 16, + "missingKeysCount": 0, "totalKeysCount": 35, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/zhCN.ts" }, diff --git a/packages/grid/x-data-grid/src/locales/zhCN.ts b/packages/grid/x-data-grid/src/locales/zhCN.ts index f4a172b03aa9b..563e44b11e0b1 100644 --- a/packages/grid/x-data-grid/src/locales/zhCN.ts +++ b/packages/grid/x-data-grid/src/locales/zhCN.ts @@ -46,7 +46,7 @@ const zhCNGrid: Partial = { // Filter panel text filterPanelAddFilter: '添加筛选器', - // filterPanelRemoveAll: 'Remove all', + filterPanelRemoveAll: '清除全部', filterPanelDeleteIconLabel: '删除', filterPanelLogicOperator: '逻辑操作器', filterPanelOperator: '操作器', @@ -79,7 +79,7 @@ const zhCNGrid: Partial = { // Column menu text columnMenuLabel: '菜单', columnMenuShowColumns: '显示', - // columnMenuManageColumns: 'Manage columns', + columnMenuManageColumns: '管理列', columnMenuFilter: '筛选器', columnMenuHideColumn: '隐藏', columnMenuUnsort: '恢复默认', diff --git a/packages/x-date-pickers/src/locales/zhCN.ts b/packages/x-date-pickers/src/locales/zhCN.ts index 9ebac173f4d8c..8a7ac7da2d480 100644 --- a/packages/x-date-pickers/src/locales/zhCN.ts +++ b/packages/x-date-pickers/src/locales/zhCN.ts @@ -29,10 +29,10 @@ const zhCNPickers: Partial> = { todayButtonLabel: '今天', // Toolbar titles - // datePickerToolbarTitle: 'Select date', - // dateTimePickerToolbarTitle: 'Select date & time', - // timePickerToolbarTitle: 'Select time', - // dateRangePickerToolbarTitle: 'Select date range', + datePickerToolbarTitle: '选择日期', + dateTimePickerToolbarTitle: '选择日期和时间', + timePickerToolbarTitle: '选择时间', + dateRangePickerToolbarTitle: '选择时间范围', // Clock labels clockLabelText: (view, time, adapter) => @@ -44,10 +44,10 @@ const zhCNPickers: Partial> = { secondsClockNumberText: (seconds) => `${seconds}秒`, // Calendar labels - // calendarWeekNumberHeaderLabel: 'Week number', - // calendarWeekNumberHeaderText: '#', - // calendarWeekNumberAriaLabelText: weekNumber => `Week ${weekNumber}`, - // calendarWeekNumberText: weekNumber => `${weekNumber}`, + calendarWeekNumberHeaderLabel: '周数', + calendarWeekNumberHeaderText: '#', + calendarWeekNumberAriaLabelText: (weekNumber) => `第${weekNumber}周`, + calendarWeekNumberText: (weekNumber) => `${weekNumber}`, // Open picker labels openDatePickerDialogue: (value, utils) => @@ -64,14 +64,14 @@ const zhCNPickers: Partial> = { dateTableLabel: '选择日期', // Field section placeholders - // fieldYearPlaceholder: params => 'Y'.repeat(params.digitAmount), - // fieldMonthPlaceholder: params => params.contentType === 'letter' ? 'MMMM' : 'MM', - // fieldDayPlaceholder: () => 'DD', - // fieldWeekDayPlaceholder: params => params.contentType === 'letter' ? 'EEEE' : 'EE', - // fieldHoursPlaceholder: () => 'hh', - // fieldMinutesPlaceholder: () => 'mm', - // fieldSecondsPlaceholder: () => 'ss', - // fieldMeridiemPlaceholder: () => 'aa', + fieldYearPlaceholder: (params) => 'Y'.repeat(params.digitAmount), + fieldMonthPlaceholder: (params) => (params.contentType === 'letter' ? 'MMMM' : 'MM'), + fieldDayPlaceholder: () => 'DD', + fieldWeekDayPlaceholder: (params) => (params.contentType === 'letter' ? 'EEEE' : 'EE'), + fieldHoursPlaceholder: () => 'hh', + fieldMinutesPlaceholder: () => 'mm', + fieldSecondsPlaceholder: () => 'ss', + fieldMeridiemPlaceholder: () => 'aa', }; export const zhCN = getPickersLocalization(zhCNPickers); From ac6b4b755f90ee808d5d7738d6a194f43fbe5782 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Apr 2023 08:45:43 +0200 Subject: [PATCH 19/80] Bump webpack to ^5.81.0 (#8723) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- benchmark/package.json | 2 +- package.json | 2 +- yarn.lock | 250 ++++++++++++++++++++--------------------- 3 files changed, 127 insertions(+), 127 deletions(-) diff --git a/benchmark/package.json b/benchmark/package.json index 58009b1244bf5..f256286d15bc2 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -24,7 +24,7 @@ "react-virtualized": "^9.22.5", "serve-handler": "^6.1.5", "style-loader": "^3.3.2", - "webpack": "^5.79.0", + "webpack": "^5.81.0", "webpack-cli": "^5.0.2" } } diff --git a/package.json b/package.json index 458dd687fda20..ce4c83b636e55 100644 --- a/package.json +++ b/package.json @@ -169,7 +169,7 @@ "string-replace-loader": "^3.1.0", "typescript": "^5.0.4", "util": "^0.12.5", - "webpack": "^5.79.0", + "webpack": "^5.81.0", "webpack-cli": "^5.0.2", "yargs": "^17.7.1", "yarn-deduplicate": "^6.0.1" diff --git a/yarn.lock b/yarn.lock index 90f90d2c30565..f53dc3aac505f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3282,125 +3282,125 @@ "@typescript-eslint/types" "5.59.1" eslint-visitor-keys "^3.3.0" -"@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== - dependencies: - "@webassemblyjs/helper-numbers" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - -"@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== - -"@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== - -"@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== - -"@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" +"@webassemblyjs/ast@1.11.5", "@webassemblyjs/ast@^1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.5.tgz#6e818036b94548c1fb53b754b5cae3c9b208281c" + integrity sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + +"@webassemblyjs/floating-point-hex-parser@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz#e85dfdb01cad16b812ff166b96806c050555f1b4" + integrity sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ== + +"@webassemblyjs/helper-api-error@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz#1e82fa7958c681ddcf4eabef756ce09d49d442d1" + integrity sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA== + +"@webassemblyjs/helper-buffer@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz#91381652ea95bb38bbfd270702351c0c89d69fba" + integrity sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg== + +"@webassemblyjs/helper-numbers@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz#23380c910d56764957292839006fecbe05e135a9" + integrity sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.5" + "@webassemblyjs/helper-api-error" "1.11.5" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== +"@webassemblyjs/helper-wasm-bytecode@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz#e258a25251bc69a52ef817da3001863cc1c24b9f" + integrity sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA== -"@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== +"@webassemblyjs/helper-wasm-section@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz#966e855a6fae04d5570ad4ec87fbcf29b42ba78e" + integrity sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-buffer" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/wasm-gen" "1.11.5" -"@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== +"@webassemblyjs/ieee754@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz#b2db1b33ce9c91e34236194c2b5cba9b25ca9d60" + integrity sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== +"@webassemblyjs/leb128@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.5.tgz#482e44d26b6b949edf042a8525a66c649e38935a" + integrity sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== - -"@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/helper-wasm-section" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-opt" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - "@webassemblyjs/wast-printer" "1.11.1" - -"@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - -"@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== - dependencies: - "@webassemblyjs/ast" "1.11.1" +"@webassemblyjs/utf8@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.5.tgz#83bef94856e399f3740e8df9f63bc47a987eae1a" + integrity sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz#93ee10a08037657e21c70de31c47fdad6b522b2d" + integrity sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-buffer" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/helper-wasm-section" "1.11.5" + "@webassemblyjs/wasm-gen" "1.11.5" + "@webassemblyjs/wasm-opt" "1.11.5" + "@webassemblyjs/wasm-parser" "1.11.5" + "@webassemblyjs/wast-printer" "1.11.5" + +"@webassemblyjs/wasm-gen@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz#ceb1c82b40bf0cf67a492c53381916756ef7f0b1" + integrity sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/ieee754" "1.11.5" + "@webassemblyjs/leb128" "1.11.5" + "@webassemblyjs/utf8" "1.11.5" + +"@webassemblyjs/wasm-opt@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz#b52bac29681fa62487e16d3bb7f0633d5e62ca0a" + integrity sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-buffer" "1.11.5" + "@webassemblyjs/wasm-gen" "1.11.5" + "@webassemblyjs/wasm-parser" "1.11.5" + +"@webassemblyjs/wasm-parser@1.11.5", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz#7ba0697ca74c860ea13e3ba226b29617046982e2" + integrity sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-api-error" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/ieee754" "1.11.5" + "@webassemblyjs/leb128" "1.11.5" + "@webassemblyjs/utf8" "1.11.5" + +"@webassemblyjs/wast-printer@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz#7a5e9689043f3eca82d544d7be7a8e6373a6fa98" + integrity sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA== + dependencies: + "@webassemblyjs/ast" "1.11.5" "@xtuc/long" "4.2.2" "@webpack-cli/configtest@^2.0.1": @@ -6131,10 +6131,10 @@ enhanced-resolve@^0.9.1: memory-fs "^0.2.0" tapable "^0.1.8" -enhanced-resolve@^5.10.0: - version "5.10.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" - integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== +enhanced-resolve@^5.13.0: + version "5.13.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275" + integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -12593,10 +12593,10 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.2.tgz#36c10abca6f7577aeae136c804b0c741edeadc99" + integrity sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg== dependencies: "@types/json-schema" "^7.0.8" ajv "^6.12.5" @@ -14217,21 +14217,21 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.79.0: - version "5.79.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.79.0.tgz#8552b5da5a26e4e25842c08a883e08fc7740547a" - integrity sha512-3mN4rR2Xq+INd6NnYuL9RC9GAmc1ROPKJoHhrZ4pAjdMFEkJJWrsPw8o2JjCIyQyTu7rTXYn4VG6OpyB3CobZg== +webpack@^5.81.0: + version "5.81.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.81.0.tgz#27a2e8466c8b4820d800a8d90f06ef98294f9956" + integrity sha512-AAjaJ9S4hYCVODKLQTgG5p5e11hiMawBwV2v8MYLE0C/6UAGLuAF4n1qa9GOwdxnicaP+5k6M5HrLmD4+gIB8Q== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.0" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" acorn "^8.7.1" acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.10.0" + enhanced-resolve "^5.13.0" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" @@ -14241,7 +14241,7 @@ webpack@^5.79.0: loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.0" + schema-utils "^3.1.2" tapable "^2.1.1" terser-webpack-plugin "^5.3.7" watchpack "^2.4.0" From dc07fd4c6882021ccd516f51f3bfd75c2fc780ce Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 27 Apr 2023 10:43:12 +0300 Subject: [PATCH 20/80] [pickers] Fix date calendar `selected` & `disabled` day style (#8773) --- packages/x-date-pickers/src/PickersDay/PickersDay.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/x-date-pickers/src/PickersDay/PickersDay.tsx b/packages/x-date-pickers/src/PickersDay/PickersDay.tsx index 37d1a31b56c52..1b5000c5fece6 100644 --- a/packages/x-date-pickers/src/PickersDay/PickersDay.tsx +++ b/packages/x-date-pickers/src/PickersDay/PickersDay.tsx @@ -162,9 +162,12 @@ const styleArg = ({ theme, ownerState }: { theme: Theme; ownerState: OwnerState backgroundColor: (theme.vars || theme).palette.primary.dark, }, }, - [`&.${pickersDayClasses.disabled}`]: { + [`&.${pickersDayClasses.disabled}:not(.${pickersDayClasses.selected})`]: { color: (theme.vars || theme).palette.text.disabled, }, + [`&.${pickersDayClasses.disabled}&.${pickersDayClasses.selected}`]: { + opacity: 0.6, + }, ...(!ownerState.disableMargin && { margin: `0 ${DAY_MARGIN}px`, }), From d6a2848f40b687d408db163a659e440995b43949 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Thu, 27 Apr 2023 13:12:43 +0200 Subject: [PATCH 21/80] [DataGridPro] Fix error after updating `columns` and `columnGroupingModel` at once (#8730) --- .../columnGrouping/useGridColumnGrouping.ts | 92 ++++++++++--------- .../tests/columnsGrouping.DataGrid.test.tsx | 41 +++++++++ 2 files changed, 90 insertions(+), 43 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/columnGrouping/useGridColumnGrouping.ts b/packages/grid/x-data-grid/src/hooks/features/columnGrouping/useGridColumnGrouping.ts index 9c652e621af4e..f7255263dd7d2 100644 --- a/packages/grid/x-data-grid/src/hooks/features/columnGrouping/useGridColumnGrouping.ts +++ b/packages/grid/x-data-grid/src/hooks/features/columnGrouping/useGridColumnGrouping.ts @@ -2,7 +2,11 @@ import * as React from 'react'; import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { GridStateInitializer } from '../../utils/useGridInitializeState'; -import { GridColumnNode, isLeaf } from '../../../models/gridColumnGrouping'; +import { + GridColumnGroupingModel, + GridColumnNode, + isLeaf, +} from '../../../models/gridColumnGrouping'; import { gridColumnGroupsLookupSelector, gridColumnGroupsUnwrappedModelSelector, @@ -13,12 +17,7 @@ import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { getColumnGroupsHeaderStructure, unwrapGroupingColumnModel } from './gridColumnGroupsUtils'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridEventListener } from '../../../models/events'; -import { - gridColumnFieldsSelector, - // GridColumnsState, - gridVisibleColumnFieldsSelector, -} from '../columns'; -import { useGridSelector } from '../../utils/useGridSelector'; +import { gridColumnFieldsSelector, gridVisibleColumnFieldsSelector } from '../columns'; const createGroupLookup = (columnGroupingModel: GridColumnNode[]): GridColumnGroupLookup => { let groupLookup: GridColumnGroupLookup = {}; @@ -137,46 +136,53 @@ export const useGridColumnGrouping = ( }); }, [apiRef, props.columnGroupingModel]); + const updateColumnGroupingState = React.useCallback( + (columnGroupingModel: GridColumnGroupingModel | undefined) => { + if (!props.experimentalFeatures?.columnGrouping) { + return; + } + const columnFields = gridColumnFieldsSelector(apiRef); + const visibleColumnFields = gridVisibleColumnFieldsSelector(apiRef); + const groupLookup = createGroupLookup(columnGroupingModel ?? []); + const unwrappedGroupingModel = unwrapGroupingColumnModel(columnGroupingModel ?? []); + const columnGroupsHeaderStructure = getColumnGroupsHeaderStructure( + columnFields, + unwrappedGroupingModel, + ); + const maxDepth = + visibleColumnFields.length === 0 + ? 0 + : Math.max( + ...visibleColumnFields.map((field) => unwrappedGroupingModel[field]?.length ?? 0), + ); + + apiRef.current.setState((state) => { + return { + ...state, + columnGrouping: { + lookup: groupLookup, + unwrappedGroupingModel, + headerStructure: columnGroupsHeaderStructure, + maxDepth, + }, + }; + }); + }, + [apiRef, props.experimentalFeatures?.columnGrouping], + ); + useGridApiEventHandler(apiRef, 'columnIndexChange', handleColumnIndexChange); + useGridApiEventHandler(apiRef, 'columnsChange', () => { + updateColumnGroupingState(props.columnGroupingModel); + }); + useGridApiEventHandler(apiRef, 'columnVisibilityModelChange', () => { + updateColumnGroupingState(props.columnGroupingModel); + }); - const columnFields = useGridSelector(apiRef, gridColumnFieldsSelector); - const visibleColumnFields = useGridSelector(apiRef, gridVisibleColumnFieldsSelector); /** * EFFECTS */ React.useEffect(() => { - if (!props.experimentalFeatures?.columnGrouping) { - return; - } - const groupLookup = createGroupLookup(props.columnGroupingModel ?? []); - const unwrappedGroupingModel = unwrapGroupingColumnModel(props.columnGroupingModel ?? []); - const columnGroupsHeaderStructure = getColumnGroupsHeaderStructure( - columnFields, - unwrappedGroupingModel, - ); - const maxDepth = - visibleColumnFields.length === 0 - ? 0 - : Math.max( - ...visibleColumnFields.map((field) => unwrappedGroupingModel[field]?.length ?? 0), - ); - - apiRef.current.setState((state) => { - return { - ...state, - columnGrouping: { - lookup: groupLookup, - unwrappedGroupingModel, - headerStructure: columnGroupsHeaderStructure, - maxDepth, - }, - }; - }); - }, [ - apiRef, - columnFields, - visibleColumnFields, - props.columnGroupingModel, - props.experimentalFeatures?.columnGrouping, - ]); + updateColumnGroupingState(props.columnGroupingModel); + }, [updateColumnGroupingState, props.columnGroupingModel]); }; diff --git a/packages/grid/x-data-grid/src/tests/columnsGrouping.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/columnsGrouping.DataGrid.test.tsx index 838ae420eed89..0e241faaed698 100644 --- a/packages/grid/x-data-grid/src/tests/columnsGrouping.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/columnsGrouping.DataGrid.test.tsx @@ -285,6 +285,47 @@ describe(' - Column grouping', () => { }, }); }); + + // See https://github.com/mui/mui-x/issues/8602 + it('should not throw when both `columns` and `columnGroupingModel` are updated', () => { + const defaultProps = getDefaultProps(2); + const { setProps } = render( + , + ); + + setProps({ + columns: [...defaultProps.columns, { field: 'newColumn' }], + columnGroupingModel: [ + { + groupId: 'testGroup', + children: [{ field: 'col1' }, { field: 'col2' }, { field: 'newColumn' }], + }, + ], + }); + + const row1Headers = document.querySelectorAll( + '[aria-rowindex="1"] [role="columnheader"]', + ); + const row2Headers = document.querySelectorAll( + '[aria-rowindex="2"] [role="columnheader"]', + ); + + expect( + Array.from(row1Headers).map((header) => header.getAttribute('aria-colindex')), + ).to.deep.equal(['1']); + expect( + Array.from(row2Headers).map((header) => header.getAttribute('aria-colindex')), + ).to.deep.equal(['1', '2', '3']); + }); }); // TODO: remove the skip. I failed to test if an error is thrown From 2f2344d7b550c9118483e26f813f1c79c7eb9fa0 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Thu, 27 Apr 2023 13:40:36 +0200 Subject: [PATCH 22/80] [pickers] Migrate `AdapterMomentJalaali` and `AdapterDateFnsJalali` to our repository (#8741) --- docs/package.json | 1 - packages/x-date-pickers-pro/package.json | 1 - packages/x-date-pickers/package.json | 2 +- .../AdapterDateFns/AdapterDateFns.test.tsx | 39 +- .../src/AdapterDateFns/AdapterDateFns.ts | 1 - .../AdapterDateFnsJalali.test.tsx | 96 +++ .../AdapterDateFnsJalali.ts | 554 ++++++++++++++++++ .../src/AdapterDateFnsJalali/index.ts | 111 +--- .../localization.test.tsx | 72 --- .../src/AdapterDayjs/AdapterDayjs.test.tsx | 8 - .../src/AdapterLuxon/AdapterLuxon.test.tsx | 8 - .../src/AdapterLuxon/AdapterLuxon.ts | 1 - .../src/AdapterMoment/AdapterMoment.test.tsx | 40 +- .../AdapterMomentHijri/localization.test.tsx | 1 - .../AdapterMomentJalaali.test.tsx | 102 ++++ .../AdapterMomentJalaali.ts | 281 +++++++++ .../src/AdapterMomentJalaali/index.ts | 116 +--- .../localization.test.tsx | 63 -- .../testCalculations.ts | 4 + .../describeJalaliAdapter.ts | 34 ++ .../describeJalaliAdapter.types.ts | 13 + .../src/tests/describeJalaliAdapter/index.ts | 1 + .../describeJalaliAdapter/testCalculations.ts | 109 ++++ .../describeJalaliAdapter/testLocalization.ts | 14 + patches/@date-io+date-fns+2.16.0.patch | 9 - patches/@date-io+date-fns-jalali+2.16.0.patch | 9 - yarn.lock | 14 +- 27 files changed, 1251 insertions(+), 453 deletions(-) create mode 100644 packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.test.tsx create mode 100644 packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts delete mode 100644 packages/x-date-pickers/src/AdapterDateFnsJalali/localization.test.tsx create mode 100644 packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx create mode 100644 packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts delete mode 100644 packages/x-date-pickers/src/AdapterMomentJalaali/localization.test.tsx create mode 100644 packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.ts create mode 100644 packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.types.ts create mode 100644 packages/x-date-pickers/src/tests/describeJalaliAdapter/index.ts create mode 100644 packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts create mode 100644 packages/x-date-pickers/src/tests/describeJalaliAdapter/testLocalization.ts delete mode 100644 patches/@date-io+date-fns+2.16.0.patch delete mode 100644 patches/@date-io+date-fns-jalali+2.16.0.patch diff --git a/docs/package.json b/docs/package.json index 0c177c7cfde06..578c367eb1a7c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -23,7 +23,6 @@ "@babel/core": "^7.21.4", "@babel/plugin-transform-object-assign": "^7.18.6", "@babel/runtime-corejs2": "^7.21.0", - "@date-io/date-fns-jalali": "^2.16.0", "@docsearch/react": "^3.3.3", "@emotion/cache": "^11.10.7", "@emotion/react": "^11.10.6", diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index cc4da3e02b38f..ad3d00a2a9458 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -42,7 +42,6 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@date-io/date-fns": "^2.16.0", "@date-io/luxon": "^2.16.1", "@date-io/moment": "^2.16.1", "@mui/utils": "^5.12.0", diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 24402135234d1..b8b43c0fafa06 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -49,7 +49,6 @@ "@date-io/date-fns": "^2.16.0", "@date-io/date-fns-jalali": "^2.16.0", "@date-io/hijri": "^2.16.1", - "@date-io/jalaali": "^2.16.1", "@date-io/moment": "^2.16.1", "@mui/utils": "^5.12.0", "@types/react-transition-group": "^4.4.5", @@ -104,6 +103,7 @@ }, "devDependencies": { "@types/luxon": "^3.3.0", + "@types/moment-jalaali": "^0.7.6", "date-fns": "^2.29.3", "date-fns-jalali": "^2.13.0-0", "dayjs": "^1.11.7", diff --git a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx index c2f2f071fbcce..da5a2c9f58ddc 100644 --- a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx +++ b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx @@ -18,21 +18,6 @@ import { TEST_DATE_ISO, } from '@mui/x-date-pickers/tests/describeGregorianAdapter'; -const testDate = new Date(2018, 4, 15, 9, 35); -const localizedTexts = { - undefined: { - placeholder: 'MM/DD/YYYY hh:mm aa', - value: '05/15/2018 09:35 AM', - }, - fr: { - placeholder: 'DD/MM/YYYY hh:mm', - value: '15/05/2018 09:35', - }, - de: { - placeholder: 'DD.MM.YYYY hh:mm', - value: '15.05.2018 09:35', - }, -}; describe('', () => { describeGregorianAdapter(AdapterDateFns, { formatDateTime: 'yyyy-MM-dd HH:mm:ss', locale: enUS }); @@ -111,6 +96,22 @@ describe('', () => { }); describe('Picker localization', () => { + const testDate = new Date(2018, 4, 15, 9, 35); + const localizedTexts = { + undefined: { + placeholder: 'MM/DD/YYYY hh:mm aa', + value: '05/15/2018 09:35 AM', + }, + fr: { + placeholder: 'DD/MM/YYYY hh:mm', + value: '15/05/2018 09:35', + }, + de: { + placeholder: 'DD.MM.YYYY hh:mm', + value: '15.05.2018 09:35', + }, + }; + Object.keys(localizedTexts).forEach((localeKey) => { const localeName = localeKey === 'undefined' ? 'default' : `"${localeKey}"`; const localeObject = localeKey === 'undefined' ? undefined : { fr, de }[localeKey]; @@ -138,13 +139,5 @@ describe('', () => { }); }); }); - - it('should return the correct week number', () => { - const adapter = new AdapterDateFns({ locale: fr }); - - const dateToTest = adapter.date(new Date(2022, 10, 10))!; - - expect(adapter.getWeekNumber!(dateToTest)).to.equal(45); - }); }); }); diff --git a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts index 0e819ad48fd64..8547fe9195cf4 100644 --- a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts +++ b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts @@ -267,7 +267,6 @@ export class AdapterDateFns implements MuiPickersAdapter { .join(''); }; - // Redefined here just to show how it can be written using expandFormat public getFormatHelperText = (format: string) => { return this.expandFormat(format) .replace(/(aaa|aa|a)/g, '(a|p)m') diff --git a/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.test.tsx b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.test.tsx new file mode 100644 index 0000000000000..e3dc8b1938ad7 --- /dev/null +++ b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.test.tsx @@ -0,0 +1,96 @@ +import * as React from 'react'; +import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; +import { AdapterDateFnsJalali } from '@mui/x-date-pickers/AdapterDateFnsJalali'; +import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { + createPickerRenderer, + expectInputPlaceholder, + expectInputValue, +} from 'test/utils/pickers-utils'; +import enUS from 'date-fns/locale/en-US'; +import faIR from 'date-fns-jalali/locale/fa-IR'; +import faJalaliIR from 'date-fns-jalali/locale/fa-jalali-IR'; +import { describeJalaliAdapter } from '@mui/x-date-pickers/tests/describeJalaliAdapter'; +import { AdapterMomentJalaali } from '@mui/x-date-pickers/AdapterMomentJalaali'; +import { AdapterFormats } from '@mui/x-date-pickers'; +import { expect } from 'chai'; + +describe('', () => { + describeJalaliAdapter(AdapterDateFnsJalali, { locale: faJalaliIR }); + + describe('Adapter localization', () => { + it('Formatting', () => { + const adapter = new AdapterMomentJalaali(); + + const expectDate = (format: keyof AdapterFormats, expectedWithFaIR: string) => { + const date = adapter.date('2020-02-01T23:44:00.000Z')!; + + expect(adapter.format(date, format)).to.equal(expectedWithFaIR); + }; + + expectDate('fullDate', '۱۳۹۸، Bahman ۱م'); + expectDate('fullDateWithWeekday', 'شنبه ۱م Bahman ۱۳۹۸'); + expectDate('fullDateTime', '۱۳۹۸، Bahman ۱م، ۱۱:۴۴ بعد از ظهر'); + expectDate('fullDateTime12h', '۱۲ Bahman ۱۱:۴۴ بعد از ظهر'); + expectDate('fullDateTime24h', '۱۲ Bahman ۲۳:۴۴'); + expectDate('keyboardDate', '۱۳۹۸/۱۱/۱۲'); + expectDate('keyboardDateTime', '۱۳۹۸/۱۱/۱۲ ۲۳:۴۴'); + expectDate('keyboardDateTime12h', '۱۳۹۸/۱۱/۱۲ ۱۱:۴۴ بعد از ظهر'); + expectDate('keyboardDateTime24h', '۱۳۹۸/۱۱/۱۲ ۲۳:۴۴'); + }); + }); + + describe('Picker localization', () => { + const testDate = new Date(2018, 4, 15, 9, 35); + const localizedTexts = { + enUS: { + placeholder: 'MM/DD/YYYY hh:mm aa', + value: '02/25/1397 09:35 AM', + }, + faIR: { + placeholder: 'YYYY/MM/DD hh:mm aa', + value: '1397/02/25 09:35 ق.ظ.', + }, + faJalaliIR: { + // Not sure about what's the difference between this and fa-IR + placeholder: 'YYYY/MM/DD hh:mm aa', + value: '1397/02/25 09:35 ق.ظ.', + }, + }; + + Object.keys(localizedTexts).forEach((localeKey) => { + const localeName = localeKey === 'undefined' ? 'default' : `"${localeKey}"`; + const localeObject = + localeKey === 'undefined' + ? undefined + : { + faIR, + faJalaliIR, + enUS, + }[localeKey]; + + describe(`test with the ${localeName} locale`, () => { + const { render, adapter } = createPickerRenderer({ + clock: 'fake', + adapterName: 'date-fns-jalali', + locale: localeObject, + }); + + it('should have correct placeholder', () => { + render(); + + expectInputPlaceholder( + screen.getByRole('textbox'), + localizedTexts[localeKey].placeholder, + ); + }); + + it('should have well formatted value', () => { + render(); + + expectInputValue(screen.getByRole('textbox'), localizedTexts[localeKey].value); + }); + }); + }); + }); +}); diff --git a/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts new file mode 100644 index 0000000000000..7a84543db8759 --- /dev/null +++ b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts @@ -0,0 +1,554 @@ +/* eslint-disable class-methods-use-this */ +import addSeconds from 'date-fns-jalali/addSeconds'; +import addMinutes from 'date-fns-jalali/addMinutes'; +import addHours from 'date-fns-jalali/addHours'; +import addDays from 'date-fns-jalali/addDays'; +import addWeeks from 'date-fns-jalali/addWeeks'; +import addMonths from 'date-fns-jalali/addMonths'; +import addYears from 'date-fns-jalali/addYears'; +import differenceInYears from 'date-fns-jalali/differenceInYears'; +import differenceInQuarters from 'date-fns-jalali/differenceInQuarters'; +import differenceInMonths from 'date-fns-jalali/differenceInMonths'; +import differenceInWeeks from 'date-fns-jalali/differenceInWeeks'; +import differenceInDays from 'date-fns-jalali/differenceInDays'; +import differenceInHours from 'date-fns-jalali/differenceInHours'; +import differenceInMinutes from 'date-fns-jalali/differenceInMinutes'; +import differenceInSeconds from 'date-fns-jalali/differenceInSeconds'; +import differenceInMilliseconds from 'date-fns-jalali/differenceInMilliseconds'; +import eachDayOfInterval from 'date-fns-jalali/eachDayOfInterval'; +import endOfDay from 'date-fns-jalali/endOfDay'; +import endOfWeek from 'date-fns-jalali/endOfWeek'; +import endOfYear from 'date-fns-jalali/endOfYear'; +import dateFnsFormat from 'date-fns-jalali/format'; +import formatISO from 'date-fns-jalali/formatISO'; +import getHours from 'date-fns-jalali/getHours'; +import getSeconds from 'date-fns-jalali/getSeconds'; +import getWeek from 'date-fns-jalali/getWeek'; +import getYear from 'date-fns-jalali/getYear'; +import getMonth from 'date-fns-jalali/getMonth'; +import getDate from 'date-fns-jalali/getDate'; +import getDay from 'date-fns-jalali/getDay'; +import getDaysInMonth from 'date-fns-jalali/getDaysInMonth'; +import getMinutes from 'date-fns-jalali/getMinutes'; +import isAfter from 'date-fns-jalali/isAfter'; +import isBefore from 'date-fns-jalali/isBefore'; +import isEqual from 'date-fns-jalali/isEqual'; +import isSameDay from 'date-fns-jalali/isSameDay'; +import isSameYear from 'date-fns-jalali/isSameYear'; +import isSameMonth from 'date-fns-jalali/isSameMonth'; +import isSameHour from 'date-fns-jalali/isSameHour'; +import isValid from 'date-fns-jalali/isValid'; +import dateFnsParse from 'date-fns-jalali/parse'; +import parseISO from 'date-fns-jalali/parseISO'; +import setDate from 'date-fns-jalali/setDate'; +import setHours from 'date-fns-jalali/setHours'; +import setMinutes from 'date-fns-jalali/setMinutes'; +import setMonth from 'date-fns-jalali/setMonth'; +import setSeconds from 'date-fns-jalali/setSeconds'; +import setYear from 'date-fns-jalali/setYear'; +import startOfDay from 'date-fns-jalali/startOfDay'; +import startOfMonth from 'date-fns-jalali/startOfMonth'; +import endOfMonth from 'date-fns-jalali/endOfMonth'; +import startOfWeek from 'date-fns-jalali/startOfWeek'; +import startOfYear from 'date-fns-jalali/startOfYear'; +import isWithinInterval from 'date-fns-jalali/isWithinInterval'; +import defaultLocale from 'date-fns-jalali/locale/fa-IR'; +// @ts-ignore +import longFormatters from 'date-fns-jalali/_lib/format/longFormatters'; +import { AdapterFormats, AdapterUnits, FieldFormatTokenMap, MuiPickersAdapter } from '../models'; + +type DateFnsLocale = typeof defaultLocale; + +interface AdapterDateFnsJalaliOptions { + locale?: DateFnsLocale; + formats?: Partial; +} + +const formatTokenMap: FieldFormatTokenMap = { + // Year + y: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, + yy: 'year', + yyy: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, + yyyy: 'year', + + // Month + M: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, + MM: 'month', + MMMM: { sectionType: 'month', contentType: 'letter' }, + MMM: { sectionType: 'month', contentType: 'letter' }, + L: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, + LL: 'month', + LLL: { sectionType: 'month', contentType: 'letter' }, + LLLL: { sectionType: 'month', contentType: 'letter' }, + + // Day of the month + d: { sectionType: 'day', contentType: 'digit', maxLength: 2 }, + dd: 'day', + do: { sectionType: 'day', contentType: 'digit-with-letter' }, + + // Day of the week + E: { sectionType: 'weekDay', contentType: 'letter' }, + EE: { sectionType: 'weekDay', contentType: 'letter' }, + EEE: { sectionType: 'weekDay', contentType: 'letter' }, + EEEE: { sectionType: 'weekDay', contentType: 'letter' }, + EEEEE: { sectionType: 'weekDay', contentType: 'letter' }, + i: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, + ii: 'weekDay', + iii: { sectionType: 'weekDay', contentType: 'letter' }, + iiii: { sectionType: 'weekDay', contentType: 'letter' }, + e: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, + ee: 'weekDay', + eee: { sectionType: 'weekDay', contentType: 'letter' }, + eeee: { sectionType: 'weekDay', contentType: 'letter' }, + eeeee: { sectionType: 'weekDay', contentType: 'letter' }, + eeeeee: { sectionType: 'weekDay', contentType: 'letter' }, + c: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, + cc: 'weekDay', + ccc: { sectionType: 'weekDay', contentType: 'letter' }, + cccc: { sectionType: 'weekDay', contentType: 'letter' }, + ccccc: { sectionType: 'weekDay', contentType: 'letter' }, + cccccc: { sectionType: 'weekDay', contentType: 'letter' }, + + // Meridiem + a: 'meridiem', + aa: 'meridiem', + aaa: 'meridiem', + + // Hours + H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, + HH: 'hours', + h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, + hh: 'hours', + + // Minutes + m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, + mm: 'minutes', + + // Seconds + s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 }, + ss: 'seconds', +}; + +const defaultFormats: AdapterFormats = { + dayOfMonth: 'd', + fullDate: 'PPP', + fullDateWithWeekday: 'PPPP', + fullDateTime: 'PPP p', + fullDateTime12h: 'PPP hh:mm aa', + fullDateTime24h: 'PPP HH:mm', + fullTime: 'p', + fullTime12h: 'hh:mm aaa', + fullTime24h: 'HH:mm', + hours12h: 'hh', + hours24h: 'HH', + keyboardDate: 'P', + keyboardDateTime: 'P p', + keyboardDateTime12h: 'P hh:mm aa', + keyboardDateTime24h: 'P HH:mm', + minutes: 'mm', + month: 'LLLL', + monthAndDate: 'd MMMM', + monthAndYear: 'LLLL yyyy', + monthShort: 'MMM', + weekday: 'EEEE', + weekdayShort: 'EEE', + normalDate: 'd MMMM', + normalDateWithWeekday: 'EEE, d MMMM', + seconds: 'ss', + shortDate: 'd MMM', + year: 'yyyy', +}; + +const NUMBER_SYMBOL_MAP = { + '1': '۱', + '2': '۲', + '3': '۳', + '4': '۴', + '5': '۵', + '6': '۶', + '7': '۷', + '8': '۸', + '9': '۹', + '0': '۰', +}; + +export class AdapterDateFnsJalali implements MuiPickersAdapter { + public isMUIAdapter = true; + + public lib = 'date-fns-jalali'; + + public locale?: DateFnsLocale; + + public formats: AdapterFormats; + + public formatTokenMap = formatTokenMap; + + public escapedCharacters = { start: "'", end: "'" }; + + constructor({ locale, formats }: AdapterDateFnsJalaliOptions = {}) { + this.locale = locale; + this.formats = { ...defaultFormats, ...formats }; + } + + public date = (value?: any) => { + if (typeof value === 'undefined') { + return new Date(); + } + + if (value === null) { + return null; + } + + return new Date(value); + }; + + public toJsDate = (value: Date) => { + return value; + }; + + public parseISO = (isoString: string) => { + return parseISO(isoString); + }; + + public toISO = (value: Date) => { + return formatISO(value, { format: 'extended' }); + }; + + public parse = (value: string, format: string) => { + if (value === '') { + return null; + } + + return dateFnsParse(value, format, new Date(), { locale: this.locale }); + }; + + public getCurrentLocaleCode = () => { + return this.locale?.code || 'fa-IR'; + }; + + // Note: date-fns input types are more lenient than this adapter, so we need to expose our more + // strict signature and delegate to the more lenient signature. Otherwise, we have downstream type errors upon usage. + public is12HourCycleInCurrentLocale = () => { + if (this.locale) { + return /a/.test(this.locale.formatLong!.time()); + } + + // By default, date-fns-jalali is using fa-IR locale with am/pm enabled + return true; + }; + + public expandFormat = (format: string) => { + // @see https://github.com/date-fns/date-fns/blob/master/src/format/index.js#L31 + const longFormatRegexp = /P+p+|P+|p+|''|'(''|[^'])+('|$)|./g; + const locale = this.locale ?? defaultLocale; + return format + .match(longFormatRegexp)! + .map((token) => { + const firstCharacter = token[0]; + if (firstCharacter === 'p' || firstCharacter === 'P') { + const longFormatter = longFormatters[firstCharacter]; + return longFormatter(token, locale.formatLong, {}); + } + return token; + }) + .join(''); + }; + + public getFormatHelperText = (format: string) => { + return this.expandFormat(format) + .replace(/(aaa|aa|a)/g, '(a|p)m') + .toLocaleLowerCase(); + }; + + public isNull = (value: Date | null) => { + return value === null; + }; + + public isValid = (value: any) => { + return isValid(this.date(value)); + }; + + public format = (value: Date, formatKey: keyof AdapterFormats) => { + return this.formatByString(value, this.formats[formatKey]); + }; + + public formatByString = (value: Date, formatString: string) => { + return dateFnsFormat(value, formatString, { locale: this.locale }); + }; + + public formatNumber = (numberToFormat: string) => { + return numberToFormat + .replace(/\d/g, (match) => NUMBER_SYMBOL_MAP[match as keyof typeof NUMBER_SYMBOL_MAP]) + .replace(/,/g, '،'); + }; + + public getDiff = (value: Date, comparing: Date | string, unit?: AdapterUnits) => { + switch (unit) { + case 'years': + return differenceInYears(value, this.date(comparing)!); + case 'quarters': + return differenceInQuarters(value, this.date(comparing)!); + case 'months': + return differenceInMonths(value, this.date(comparing)!); + case 'weeks': + return differenceInWeeks(value, this.date(comparing)!); + case 'days': + return differenceInDays(value, this.date(comparing)!); + case 'hours': + return differenceInHours(value, this.date(comparing)!); + case 'minutes': + return differenceInMinutes(value, this.date(comparing)!); + case 'seconds': + return differenceInSeconds(value, this.date(comparing)!); + default: { + return differenceInMilliseconds(value, this.date(comparing)!); + } + } + }; + + public isEqual = (value: any, comparing: any) => { + if (value === null && comparing === null) { + return true; + } + + return isEqual(value, comparing); + }; + + public isSameYear = (value: Date, comparing: Date) => { + return isSameYear(value, comparing); + }; + + public isSameMonth = (value: Date, comparing: Date) => { + return isSameMonth(value, comparing); + }; + + public isSameDay = (value: Date, comparing: Date) => { + return isSameDay(value, comparing); + }; + + public isSameHour = (value: Date, comparing: Date) => { + return isSameHour(value, comparing); + }; + + public isAfter = (value: Date, comparing: Date) => { + return isAfter(value, comparing); + }; + + public isAfterYear = (value: Date, comparing: Date) => { + return isAfter(value, endOfYear(comparing)); + }; + + public isAfterDay = (value: Date, comparing: Date) => { + return isAfter(value, endOfDay(comparing)); + }; + + public isBefore = (value: Date, comparing: Date) => { + return isBefore(value, comparing); + }; + + public isBeforeYear = (value: Date, comparing: Date) => { + return isBefore(value, startOfYear(comparing)); + }; + + public isBeforeDay = (value: Date, comparing: Date) => { + return isBefore(value, startOfDay(comparing)); + }; + + public isWithinRange = (value: Date, [start, end]: [Date, Date]) => { + return isWithinInterval(value, { start, end }); + }; + + public startOfYear = (value: Date) => { + return startOfYear(value); + }; + + public startOfMonth = (value: Date) => { + return startOfMonth(value); + }; + + public startOfWeek = (value: Date) => { + return startOfWeek(value, { locale: this.locale }); + }; + + public startOfDay = (value: Date) => { + return startOfDay(value); + }; + + public endOfYear = (value: Date) => { + return endOfYear(value); + }; + + public endOfMonth = (value: Date) => { + return endOfMonth(value); + }; + + public endOfWeek = (value: Date) => { + return endOfWeek(value, { locale: this.locale }); + }; + + public endOfDay = (value: Date) => { + return endOfDay(value); + }; + + public addYears = (value: Date, amount: number) => { + return addYears(value, amount); + }; + + public addMonths = (value: Date, amount: number) => { + return addMonths(value, amount); + }; + + public addWeeks = (value: Date, amount: number) => { + return addWeeks(value, amount); + }; + + public addDays = (value: Date, amount: number) => { + return addDays(value, amount); + }; + + public addHours = (value: Date, amount: number) => { + return addHours(value, amount); + }; + + public addMinutes = (value: Date, amount: number) => { + return addMinutes(value, amount); + }; + + public addSeconds = (value: Date, amount: number) => { + return addSeconds(value, amount); + }; + + public getYear = (value: Date) => { + return getYear(value); + }; + + public getMonth = (value: Date) => { + return getMonth(value); + }; + + public getDate = (value: Date) => { + return getDate(value); + }; + + public getHours = (value: Date) => { + return getHours(value); + }; + + public getMinutes = (value: Date) => { + return getMinutes(value); + }; + + public getSeconds = (value: Date) => { + return getSeconds(value); + }; + + public setYear = (value: Date, year: number) => { + return setYear(value, year); + }; + + public setMonth = (value: Date, month: number) => { + return setMonth(value, month); + }; + + public setDate = (value: Date, date: number) => { + return setDate(value, date); + }; + + public setHours = (value: Date, hours: number) => { + return setHours(value, hours); + }; + + public setMinutes = (value: Date, minutes: number) => { + return setMinutes(value, minutes); + }; + + public setSeconds = (value: Date, seconds: number) => { + return setSeconds(value, seconds); + }; + + public getDaysInMonth = (value: Date) => { + return getDaysInMonth(value); + }; + + public getNextMonth = (value: Date) => { + return addMonths(value, 1); + }; + + public getPreviousMonth = (value: Date) => { + return addMonths(value, -1); + }; + + public getMonthArray = (value: Date) => { + const firstMonth = startOfYear(value); + const monthArray = [firstMonth]; + + while (monthArray.length < 12) { + const prevMonth = monthArray[monthArray.length - 1]; + monthArray.push(this.getNextMonth(prevMonth)); + } + + return monthArray; + }; + + public mergeDateAndTime = (dateParam: Date, timeParam: Date) => { + return this.setSeconds( + this.setMinutes( + this.setHours(dateParam, this.getHours(timeParam)), + this.getMinutes(timeParam), + ), + this.getSeconds(timeParam), + ); + }; + + public getWeekdays = () => { + const now = new Date(); + return eachDayOfInterval({ + start: startOfWeek(now, { locale: this.locale }), + end: endOfWeek(now, { locale: this.locale }), + }).map((day) => this.formatByString(day, 'EEEEEE')); + }; + + public getWeekArray = (value: Date) => { + const start = startOfWeek(startOfMonth(value), { locale: this.locale }); + const end = endOfWeek(endOfMonth(value), { locale: this.locale }); + + let count = 0; + let current = start; + const nestedWeeks: Date[][] = []; + let lastDay: number | null = null; + while (isBefore(current, end)) { + const weekNumber = Math.floor(count / 7); + nestedWeeks[weekNumber] = nestedWeeks[weekNumber] || []; + const day = getDay(current); + if (lastDay !== day) { + lastDay = day; + nestedWeeks[weekNumber].push(current); + count += 1; + } + current = addDays(current, 1); + } + return nestedWeeks; + }; + + public getWeekNumber = (date: Date) => { + return getWeek(date, { locale: this.locale }); + }; + + public getYearRange = (start: Date, end: Date) => { + const startDate = startOfYear(start); + const endDate = endOfYear(end); + const years: Date[] = []; + + let current = startDate; + while (isBefore(current, endDate)) { + years.push(current); + current = addYears(current, 1); + } + + return years; + }; + + public getMeridiemText = (ampm: 'am' | 'pm') => { + return ampm === 'am' ? 'ق.ظ' : 'ب.ظ'; + }; +} diff --git a/packages/x-date-pickers/src/AdapterDateFnsJalali/index.ts b/packages/x-date-pickers/src/AdapterDateFnsJalali/index.ts index 457979c7bf2b8..04079bdadd7a3 100644 --- a/packages/x-date-pickers/src/AdapterDateFnsJalali/index.ts +++ b/packages/x-date-pickers/src/AdapterDateFnsJalali/index.ts @@ -1,110 +1 @@ -import BaseAdapterDateFnsJalali from '@date-io/date-fns-jalali'; -import defaultLocale from 'date-fns-jalali/locale/fa-IR'; -import getWeek from 'date-fns-jalali/getWeek'; -// @ts-ignore -import longFormatters from 'date-fns-jalali/_lib/format/longFormatters'; -import { FieldFormatTokenMap, MuiPickersAdapter } from '../models'; - -const formatTokenMap: FieldFormatTokenMap = { - // Year - y: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, - yy: 'year', - yyy: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, - yyyy: 'year', - - // Month - M: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, - MM: 'month', - MMMM: { sectionType: 'month', contentType: 'letter' }, - MMM: { sectionType: 'month', contentType: 'letter' }, - L: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, - LL: 'month', - LLL: { sectionType: 'month', contentType: 'letter' }, - LLLL: { sectionType: 'month', contentType: 'letter' }, - - // Day of the month - d: { sectionType: 'day', contentType: 'digit', maxLength: 2 }, - dd: 'day', - do: { sectionType: 'day', contentType: 'digit-with-letter' }, - - // Day of the week - E: { sectionType: 'weekDay', contentType: 'letter' }, - EE: { sectionType: 'weekDay', contentType: 'letter' }, - EEE: { sectionType: 'weekDay', contentType: 'letter' }, - EEEE: { sectionType: 'weekDay', contentType: 'letter' }, - EEEEE: { sectionType: 'weekDay', contentType: 'letter' }, - i: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, - ii: 'weekDay', - iii: { sectionType: 'weekDay', contentType: 'letter' }, - iiii: { sectionType: 'weekDay', contentType: 'letter' }, - e: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, - ee: 'weekDay', - eee: { sectionType: 'weekDay', contentType: 'letter' }, - eeee: { sectionType: 'weekDay', contentType: 'letter' }, - eeeee: { sectionType: 'weekDay', contentType: 'letter' }, - eeeeee: { sectionType: 'weekDay', contentType: 'letter' }, - c: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 }, - cc: 'weekDay', - ccc: { sectionType: 'weekDay', contentType: 'letter' }, - cccc: { sectionType: 'weekDay', contentType: 'letter' }, - ccccc: { sectionType: 'weekDay', contentType: 'letter' }, - cccccc: { sectionType: 'weekDay', contentType: 'letter' }, - - // Meridiem - a: 'meridiem', - aa: 'meridiem', - aaa: 'meridiem', - - // Hours - H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - HH: 'hours', - h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - hh: 'hours', - - // Minutes - m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, - mm: 'minutes', - - // Seconds - s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 }, - ss: 'seconds', -}; - -export class AdapterDateFnsJalali - extends BaseAdapterDateFnsJalali - implements MuiPickersAdapter -{ - public isMUIAdapter = true; - - public formatTokenMap = formatTokenMap; - - public escapedCharacters = { start: "'", end: "'" }; - - public expandFormat = (format: string) => { - // @see https://github.com/date-fns/date-fns/blob/master/src/format/index.js#L31 - const longFormatRegexp = /P+p+|P+|p+|''|'(''|[^'])+('|$)|./g; - const locale = this.locale ?? defaultLocale; - return format - .match(longFormatRegexp)! - .map((token) => { - const firstCharacter = token[0]; - if (firstCharacter === 'p' || firstCharacter === 'P') { - const longFormatter = longFormatters[firstCharacter]; - return longFormatter(token, locale.formatLong, {}); - } - return token; - }) - .join(''); - }; - - // Redefined here just to show how it can be written using expandFormat - public getFormatHelperText = (format: string) => { - return this.expandFormat(format) - .replace(/(aaa|aa|a)/g, '(a|p)m') - .toLocaleLowerCase(); - }; - - public getWeekNumber = (date: Date) => { - return getWeek(date, { locale: this.locale }); - }; -} +export { AdapterDateFnsJalali } from './AdapterDateFnsJalali'; diff --git a/packages/x-date-pickers/src/AdapterDateFnsJalali/localization.test.tsx b/packages/x-date-pickers/src/AdapterDateFnsJalali/localization.test.tsx deleted file mode 100644 index 5c457e9e24e5e..0000000000000 --- a/packages/x-date-pickers/src/AdapterDateFnsJalali/localization.test.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import * as React from 'react'; -import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; -import { AdapterDateFnsJalali } from '@mui/x-date-pickers/AdapterDateFnsJalali'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; -import { expect } from 'chai'; -import { - createPickerRenderer, - expectInputPlaceholder, - expectInputValue, -} from 'test/utils/pickers-utils'; -import enUS from 'date-fns/locale/en-US'; -import faIR from 'date-fns-jalali/locale/fa-IR'; -import faJalaliIR from 'date-fns-jalali/locale/fa-jalali-IR'; - -const testDate = new Date(2018, 4, 15, 9, 35); -const localizedTexts = { - enUS: { - placeholder: 'MM/DD/YYYY hh:mm aa', - value: '02/25/1397 09:35 AM', - }, - faIR: { - placeholder: 'YYYY/MM/DD hh:mm aa', - value: '1397/02/25 09:35 ق.ظ.', - }, - faJalaliIR: { - // Not sure about what's the difference between this and fa-IR - placeholder: 'YYYY/MM/DD hh:mm aa', - value: '1397/02/25 09:35 ق.ظ.', - }, -}; - -describe('', () => { - Object.keys(localizedTexts).forEach((localeKey) => { - const localeName = localeKey === 'undefined' ? 'default' : `"${localeKey}"`; - const localeObject = - localeKey === 'undefined' - ? undefined - : { - faIR, - faJalaliIR, - enUS, - }[localeKey]; - - describe(`test with the ${localeName} locale`, () => { - const { render, adapter } = createPickerRenderer({ - clock: 'fake', - adapterName: 'date-fns-jalali', - locale: localeObject, - }); - - it('should have correct placeholder', () => { - render(); - - expectInputPlaceholder(screen.getByRole('textbox'), localizedTexts[localeKey].placeholder); - }); - - it('should have well formatted value', () => { - render(); - - expectInputValue(screen.getByRole('textbox'), localizedTexts[localeKey].value); - }); - }); - }); - - it('should return the correct week number', () => { - const adapter = new AdapterDateFnsJalali({ locale: faIR }); - - const dateToTest = adapter.date(new Date(2022, 10, 10)); - - expect(adapter.getWeekNumber!(dateToTest)).to.equal(34); - }); -}); diff --git a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx index b40436b89676d..53e8c3058ef2c 100644 --- a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx +++ b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx @@ -142,14 +142,6 @@ describe('', () => { }); }); }); - - it('should return the correct week number', () => { - const localizedAdapter = new AdapterDayjs({ locale: 'fr' }); - - const dateToTest = localizedAdapter.date(new Date(2022, 10, 10))!; - - expect(localizedAdapter.getWeekNumber(dateToTest)).to.equal(45); - }); }); describe('UTC plugin', () => { diff --git a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx index c884207ac834a..6f6a7e1ddf828 100644 --- a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx +++ b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx @@ -123,13 +123,5 @@ describe('', () => { }); }); }); - - it('should return the correct week number', () => { - const adapter = new AdapterLuxon({ locale: 'fr' }); - - const dateToTest = adapter.date(new Date(2022, 10, 10))!; - - expect(adapter.getWeekNumber!(dateToTest)).to.equal(45); - }); }); }); diff --git a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts index 7a797bffec514..f8bb1638e9c85 100644 --- a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts +++ b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts @@ -209,7 +209,6 @@ export class AdapterLuxon implements MuiPickersAdapter { ); }; - // Redefined here just to show how it can be written using expandFormat public getFormatHelperText = (format: string) => { return this.expandFormat(format).replace(/(a)/g, '(a|p)m').toLocaleLowerCase(); }; diff --git a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx index 85bf9398e8a59..b5bf61befdb9d 100644 --- a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx +++ b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx @@ -18,21 +18,6 @@ import { TEST_DATE_ISO, } from '@mui/x-date-pickers/tests/describeGregorianAdapter'; -const testDate = new Date(2018, 4, 15, 9, 35); -const localizedTexts = { - en: { - placeholder: 'MM/DD/YYYY hh:mm aa', - value: '05/15/2018 09:35 AM', - }, - fr: { - placeholder: 'DD/MM/YYYY hh:mm', - value: '15/05/2018 09:35', - }, - de: { - placeholder: 'DD.MM.YYYY hh:mm', - value: '15.05.2018 09:35', - }, -}; describe('', () => { describeGregorianAdapter(AdapterMoment, { formatDateTime: 'YYYY-MM-DD HH:mm:ss', locale: 'en' }); @@ -132,6 +117,23 @@ describe('', () => { }); describe('Picker localization', () => { + const testDate = new Date(2018, 4, 15, 9, 35); + + const localizedTexts = { + en: { + placeholder: 'MM/DD/YYYY hh:mm aa', + value: '05/15/2018 09:35 AM', + }, + fr: { + placeholder: 'DD/MM/YYYY hh:mm', + value: '15/05/2018 09:35', + }, + de: { + placeholder: 'DD.MM.YYYY hh:mm', + value: '15.05.2018 09:35', + }, + }; + Object.keys(localizedTexts).forEach((localeKey) => { const localeObject = { code: localeKey }; @@ -158,13 +160,5 @@ describe('', () => { }); }); }); - - it('should return the correct week number', () => { - const adapter = new AdapterMoment({ locale: 'fr' }); - - const dateToTest = adapter.date(new Date(2022, 10, 10))!; - - expect(adapter.getWeekNumber!(dateToTest)).to.equal(45); - }); }); }); diff --git a/packages/x-date-pickers/src/AdapterMomentHijri/localization.test.tsx b/packages/x-date-pickers/src/AdapterMomentHijri/localization.test.tsx index 6aa4975808bea..86fdebc37ed98 100644 --- a/packages/x-date-pickers/src/AdapterMomentHijri/localization.test.tsx +++ b/packages/x-date-pickers/src/AdapterMomentHijri/localization.test.tsx @@ -45,7 +45,6 @@ describe('', () => { it('should return the correct week number', () => { const adapter = new AdapterMomentHijri(); - const dateToTest = adapter.date(new Date(2022, 10, 10)); expect(adapter.getWeekNumber!(dateToTest)).to.equal(16); diff --git a/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx new file mode 100644 index 0000000000000..490500b3d0825 --- /dev/null +++ b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx @@ -0,0 +1,102 @@ +import * as React from 'react'; +import jMoment from 'moment-jalaali'; +import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; +import { AdapterMomentJalaali } from '@mui/x-date-pickers/AdapterMomentJalaali'; +import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { expect } from 'chai'; +import { + createPickerRenderer, + expectInputPlaceholder, + expectInputValue, +} from 'test/utils/pickers-utils'; +import 'moment/locale/fa'; +import moment from 'moment'; +import { AdapterFormats } from '@mui/x-date-pickers/models'; +import { describeJalaliAdapter } from '@mui/x-date-pickers/tests/describeJalaliAdapter'; + +describe('', () => { + describeJalaliAdapter(AdapterMomentJalaali, { + locale: 'fa', + before: () => { + jMoment.loadPersian({ dialect: 'persian-modern', usePersianDigits: true }); + }, + after: () => { + moment.locale('en'); + }, + }); + + describe('Adapter localization', () => { + before(() => { + jMoment.loadPersian({ dialect: 'persian-modern', usePersianDigits: true }); + }); + + after(() => { + moment.locale('en'); + }); + + it('Formatting', () => { + const adapter = new AdapterMomentJalaali(); + + const expectDate = (format: keyof AdapterFormats, expectedWithFaIR: string) => { + const date = adapter.date('2020-02-01T23:44:00.000Z')!; + + expect(adapter.format(date, format)).to.equal(expectedWithFaIR); + }; + + expectDate('fullDate', '۱۳۹۸، بهمن ۱م'); + expectDate('fullDateWithWeekday', 'شنبه ۱م بهمن ۱۳۹۸'); + expectDate('fullDateTime', '۱۳۹۸، بهمن ۱م، ۱۱:۴۴ ب.ظ'); + expectDate('fullDateTime12h', '۱۲ بهمن ۱۱:۴۴ ب.ظ'); + expectDate('fullDateTime24h', '۱۲ بهمن ۲۳:۴۴'); + expectDate('keyboardDate', '۱۳۹۸/۱۱/۱۲'); + expectDate('keyboardDateTime', '۱۳۹۸/۱۱/۱۲ ۲۳:۴۴'); + expectDate('keyboardDateTime12h', '۱۳۹۸/۱۱/۱۲ ۱۱:۴۴ ب.ظ'); + expectDate('keyboardDateTime24h', '۱۳۹۸/۱۱/۱۲ ۲۳:۴۴'); + }); + }); + + describe('Picker localization', () => { + before(() => { + jMoment.loadPersian(); + }); + + after(() => { + moment.locale('en'); + }); + + const testDate = new Date(2018, 4, 15, 9, 35); + const localizedTexts = { + fa: { + placeholder: 'YYYY/MM/DD hh:mm', + value: '1397/02/25 09:35', + }, + }; + + Object.keys(localizedTexts).forEach((localeKey) => { + const localeObject = { code: localeKey }; + + describe(`test with the locale "${localeKey}"`, () => { + const { render, adapter } = createPickerRenderer({ + clock: 'fake', + adapterName: 'moment-jalaali', + locale: localeObject, + }); + + it('should have correct placeholder', () => { + render(); + + expectInputPlaceholder( + screen.getByRole('textbox'), + localizedTexts[localeKey].placeholder, + ); + }); + + it('should have well formatted value', () => { + render(); + + expectInputValue(screen.getByRole('textbox'), localizedTexts[localeKey].value); + }); + }); + }); + }); +}); diff --git a/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts new file mode 100644 index 0000000000000..3c634f70da0a0 --- /dev/null +++ b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts @@ -0,0 +1,281 @@ +/* eslint-disable class-methods-use-this */ +import defaultJMoment, { Moment } from 'moment-jalaali'; +import { AdapterMoment } from '../AdapterMoment'; +import { AdapterFormats, FieldFormatTokenMap, MuiPickersAdapter } from '../models'; + +interface AdapterMomentJalaaliOptions { + instance?: typeof defaultJMoment; + formats?: Partial; +} + +// From https://momentjs.com/docs/#/displaying/format/ +const formatTokenMap: FieldFormatTokenMap = { + // Year + jYY: 'year', + jYYYY: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, + + // Month + jM: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, + jMM: 'month', + jMMM: { sectionType: 'month', contentType: 'letter' }, + jMMMM: { sectionType: 'month', contentType: 'letter' }, + + // Day of the month + jD: { sectionType: 'day', contentType: 'digit', maxLength: 2 }, + jDD: 'day', + + // Meridiem + A: 'meridiem', + a: 'meridiem', + + // Hours + H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, + HH: 'hours', + h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, + hh: 'hours', + + // Minutes + m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, + mm: 'minutes', + + // Seconds + s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 }, + ss: 'seconds', +}; + +const defaultFormats: AdapterFormats = { + dayOfMonth: 'jD', + fullDate: 'jYYYY, jMMMM Do', + fullDateWithWeekday: 'dddd Do jMMMM jYYYY', + fullDateTime: 'jYYYY, jMMMM Do, hh:mm A', + fullDateTime12h: 'jD jMMMM hh:mm A', + fullDateTime24h: 'jD jMMMM HH:mm', + fullTime: 'LT', + fullTime12h: 'hh:mm A', + fullTime24h: 'HH:mm', + hours12h: 'hh', + hours24h: 'HH', + keyboardDate: 'jYYYY/jMM/jDD', + keyboardDateTime: 'jYYYY/jMM/jDD LT', + keyboardDateTime12h: 'jYYYY/jMM/jDD hh:mm A', + keyboardDateTime24h: 'jYYYY/jMM/jDD HH:mm', + minutes: 'mm', + month: 'jMMMM', + monthAndDate: 'jD jMMMM', + monthAndYear: 'jMMMM jYYYY', + monthShort: 'jMMM', + weekday: 'dddd', + weekdayShort: 'ddd', + normalDate: 'dddd, jD jMMM', + normalDateWithWeekday: 'DD MMMM', + seconds: 'ss', + shortDate: 'jD jMMM', + year: 'jYYYY', +}; + +const NUMBER_SYMBOL_MAP = { + '1': '۱', + '2': '۲', + '3': '۳', + '4': '۴', + '5': '۵', + '6': '۶', + '7': '۷', + '8': '۸', + '9': '۹', + '0': '۰', +}; + +export class AdapterMomentJalaali extends AdapterMoment implements MuiPickersAdapter { + public isMUIAdapter = true; + + public lib = 'moment-jalaali'; + + public moment: typeof defaultJMoment; + + public locale?: string; + + public formats: AdapterFormats; + + public formatTokenMap = formatTokenMap; + + public escapedCharacters = { start: '[', end: ']' }; + + constructor({ formats, instance }: AdapterMomentJalaaliOptions = {}) { + super({ locale: 'fa', instance }); + + this.moment = instance || defaultJMoment; + this.locale = 'fa'; + this.formats = { ...defaultFormats, ...formats }; + } + + private toJMoment = (value?: Moment | undefined) => { + return this.moment(value ? value.clone() : undefined).locale('fa'); + }; + + public date = (value?: any) => { + if (value === null) { + return null; + } + + return this.moment(value).locale('fa'); + }; + + public parse = (value: string, format: string) => { + if (value === '') { + return null; + } + + return this.moment(value, format, true).locale('fa'); + }; + + public getFormatHelperText = (format: string) => { + return this.expandFormat(format) + .replace(/a/gi, '(a|p)m') + .replace('jY', 'Y') + .replace('jM', 'M') + .replace('jD', 'D') + .toLocaleLowerCase(); + }; + + public isValid = (value: any) => { + // We can't to `this.moment(value)` because moment-jalaali looses the invalidity information when creating a new moment object from an existing one + if (!this.moment.isMoment(value)) { + return false; + } + + return value.isValid(); + }; + + public formatNumber = (numberToFormat: string) => { + return numberToFormat + .replace(/\d/g, (match) => NUMBER_SYMBOL_MAP[match as keyof typeof NUMBER_SYMBOL_MAP]) + .replace(/,/g, '،'); + }; + + public isEqual = (value: any, comparing: any) => { + if (value === null && comparing === null) { + return true; + } + + return this.moment(value).isSame(comparing); + }; + + public isAfterYear = (value: Moment, comparing: Moment) => { + return value.jYear() > comparing.jYear(); + }; + + public isBeforeYear = (value: Moment, comparing: Moment) => { + return value.jYear() < comparing.jYear(); + }; + + public startOfYear = (value: Moment) => { + return value.clone().startOf('jYear'); + }; + + public startOfMonth = (value: Moment) => { + return value.clone().startOf('jMonth'); + }; + + public endOfYear = (value: Moment) => { + return value.clone().endOf('jYear'); + }; + + public endOfMonth = (value: Moment) => { + return value.clone().endOf('jMonth'); + }; + + public addYears = (value: Moment, amount: number) => { + return amount < 0 + ? value.clone().subtract(Math.abs(amount), 'jYear') + : value.clone().add(amount, 'jYear'); + }; + + public addMonths = (value: Moment, amount: number) => { + return amount < 0 + ? value.clone().subtract(Math.abs(amount), 'jMonth') + : value.clone().add(amount, 'jMonth'); + }; + + public getYear = (value: Moment) => { + return value.jYear(); + }; + + public getMonth = (value: Moment) => { + return value.jMonth(); + }; + + public getDate = (value: Moment) => { + return value.jDate(); + }; + + public setYear = (value: Moment, year: number) => { + return value.clone().jYear(year); + }; + + public setMonth = (value: Moment, month: number) => { + return value.clone().jMonth(month); + }; + + public setDate = (value: Moment, date: number) => { + return value.clone().jDate(date); + }; + + public getNextMonth = (value: Moment) => { + return value.clone().add(1, 'jMonth'); + }; + + public getPreviousMonth = (value: Moment) => { + return value.clone().subtract(1, 'jMonth'); + }; + + public getWeekdays = () => { + return [0, 1, 2, 3, 4, 5, 6].map((dayOfWeek) => { + return this.toJMoment().weekday(dayOfWeek).format('dd'); + }); + }; + + public getWeekArray = (value: Moment) => { + const start = value.clone().startOf('jMonth').startOf('week'); + const end = value.clone().endOf('jMonth').endOf('week'); + + let count = 0; + let current = start; + const nestedWeeks: Moment[][] = []; + + while (current.isBefore(end)) { + const weekNumber = Math.floor(count / 7); + nestedWeeks[weekNumber] = nestedWeeks[weekNumber] || []; + nestedWeeks[weekNumber].push(current); + + current = current.clone().add(1, 'day'); + count += 1; + } + + return nestedWeeks; + }; + + public getWeekNumber = (value: Moment) => { + return value.jWeek(); + }; + + public getYearRange = (start: Moment, end: Moment) => { + const startDate = this.moment(start).startOf('jYear'); + const endDate = this.moment(end).endOf('jYear'); + const years: Moment[] = []; + + let current = startDate; + while (current.isBefore(endDate)) { + years.push(current); + current = current.clone().add(1, 'jYear'); + } + + return years; + }; + + public getMeridiemText = (ampm: 'am' | 'pm') => { + return ampm === 'am' + ? this.toJMoment().hours(2).format('A') + : this.toJMoment().hours(14).format('A'); + }; +} diff --git a/packages/x-date-pickers/src/AdapterMomentJalaali/index.ts b/packages/x-date-pickers/src/AdapterMomentJalaali/index.ts index 18087e84c5d0c..2c0f92fe06800 100644 --- a/packages/x-date-pickers/src/AdapterMomentJalaali/index.ts +++ b/packages/x-date-pickers/src/AdapterMomentJalaali/index.ts @@ -1,115 +1 @@ -/* eslint-disable class-methods-use-this */ -import BaseAdapterMomentJalaali from '@date-io/jalaali'; -import defaultMoment, { LongDateFormatKey } from 'moment-jalaali'; -import { FieldFormatTokenMap, MuiPickersAdapter } from '../models'; - -type Moment = defaultMoment.Moment; - -// From https://momentjs.com/docs/#/displaying/format/ -const formatTokenMap: FieldFormatTokenMap = { - // Year - jYY: 'year', - jYYYY: 'year', - - // Month - jM: { sectionType: 'month', contentType: 'digit', maxLength: 2 }, - jMM: 'month', - jMMM: { sectionType: 'month', contentType: 'letter' }, - jMMMM: { sectionType: 'month', contentType: 'letter' }, - - // Day of the month - jD: { sectionType: 'day', contentType: 'digit', maxLength: 2 }, - jDD: 'day', - - // Meridiem - A: 'meridiem', - a: 'meridiem', - - // Hours - H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - HH: 'hours', - h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - hh: 'hours', - - // Minutes - m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, - mm: 'minutes', - - // Seconds - s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 }, - ss: 'seconds', -}; - -export class AdapterMomentJalaali - extends BaseAdapterMomentJalaali - implements MuiPickersAdapter -{ - public isMUIAdapter = true; - - public formatTokenMap = formatTokenMap; - - public escapedCharacters = { start: '[', end: ']' }; - - /** - * The current getFormatHelperText method uses an outdated format parsing logic. - * We should use this one in the future to support all localized formats. - */ - public expandFormat = (format: string) => { - // @see https://github.com/moment/moment/blob/develop/src/lib/format/format.js#L6 - const localFormattingTokens = /(\[[^[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})|./g; - - return format - .match(localFormattingTokens)! - .map((token) => { - const firstCharacter = token[0]; - if (firstCharacter === 'L' || firstCharacter === ';') { - return this.moment - .localeData(this.getCurrentLocaleCode()) - .longDateFormat(token as LongDateFormatKey); - } - - return token; - }) - .join('') - .replace('dd', 'jDD'); // Fix for https://github.com/dmtrKovalenko/date-io/pull/632; - }; - - // Redefined here just to show how it can be written using expandFormat - public getFormatHelperText = (format: string) => { - return this.expandFormat(format) - .replace(/a/gi, '(a|p)m') - .replace('jY', 'Y') - .replace('jM', 'M') - .replace('jD', 'D') - .toLocaleLowerCase(); - }; - - public getWeekNumber = (date: defaultMoment.Moment) => { - return date.jWeek(); - }; - - public addYears = (date: Moment, count: number) => { - return count < 0 - ? date.clone().subtract(Math.abs(count), 'jYear') - : date.clone().add(count, 'jYear'); - }; - - public addMonths = (date: Moment, count: number) => { - return count < 0 - ? date.clone().subtract(Math.abs(count), 'jMonth') - : date.clone().add(count, 'jMonth'); - }; - - public setMonth = (date: Moment, month: number) => { - return date.clone().jMonth(month); - }; - - public isValid = (value: any) => { - // We can't to `this.moment(value)` because moment-jalaali looses the invalidity information when creating a new moment object from an existing one - if (!this.moment.isMoment(value)) { - return false; - } - - return value.isValid(value); - }; -} +export { AdapterMomentJalaali } from './AdapterMomentJalaali'; diff --git a/packages/x-date-pickers/src/AdapterMomentJalaali/localization.test.tsx b/packages/x-date-pickers/src/AdapterMomentJalaali/localization.test.tsx deleted file mode 100644 index b014163b70b25..0000000000000 --- a/packages/x-date-pickers/src/AdapterMomentJalaali/localization.test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import * as React from 'react'; -import jMoment from 'moment-jalaali'; -import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; -import { AdapterMomentJalaali } from '@mui/x-date-pickers/AdapterMomentJalaali'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; -import { expect } from 'chai'; -import { - createPickerRenderer, - expectInputPlaceholder, - expectInputValue, -} from 'test/utils/pickers-utils'; -import 'moment/locale/fa'; -import moment from 'moment'; - -const testDate = new Date(2018, 4, 15, 9, 35); -const localizedTexts = { - fa: { - placeholder: 'YYYY/MM/DD hh:mm', - value: '1397/02/25 09:35', - }, -}; - -describe('', () => { - before(() => { - jMoment.loadPersian(); - }); - - after(() => { - moment.locale('en'); - }); - - Object.keys(localizedTexts).forEach((localeKey) => { - const localeObject = { code: localeKey }; - - describe(`test with the locale "${localeKey}"`, () => { - const { render, adapter } = createPickerRenderer({ - clock: 'fake', - adapterName: 'moment-jalaali', - locale: localeObject, - }); - - it('should have correct placeholder', () => { - render(); - - expectInputPlaceholder(screen.getByRole('textbox'), localizedTexts[localeKey].placeholder); - }); - - it('should have well formatted value', () => { - render(); - - expectInputValue(screen.getByRole('textbox'), localizedTexts[localeKey].value); - }); - }); - }); - - it('should return the correct week number', () => { - const adapter = new AdapterMomentJalaali(); - - const dateToTest = adapter.date(new Date(2022, 10, 10)); - - expect(adapter.getWeekNumber!(dateToTest)).to.equal(34); - }); -}); diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts b/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts index 282865c603a08..6b5c10348ba8b 100644 --- a/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts +++ b/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts @@ -277,6 +277,10 @@ export const testCalculations: DescribeGregorianAdapterTestSuite = ({ }); }); + it('Method: getWeekNumber', () => { + expect(adapter.getWeekNumber!(testDate)).to.equal(44); + }); + it('Method: getYearRange', () => { const yearRange = adapter.getYearRange(testDate, adapter.setYear(testDate, 2124)); diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.ts b/packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.ts new file mode 100644 index 0000000000000..2d05c6565a4f1 --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.ts @@ -0,0 +1,34 @@ +import createDescribe from '@mui/monorepo/test/utils/createDescribe'; +import { MuiPickersAdapter } from '@mui/x-date-pickers'; +import { testCalculations } from './testCalculations'; +import { testLocalization } from './testLocalization'; +import { DescribeJalaliAdapterParams } from './describeJalaliAdapter.types'; +import { TEST_DATE_ISO } from '../describeGregorianAdapter'; + +function innerJalaliDescribeAdapter( + Adapter: new (...args: any) => MuiPickersAdapter, + params: DescribeJalaliAdapterParams, +) { + const adapter = new Adapter({ locale: params.locale }); + + describe(adapter.lib, () => { + const testSuitParams = { + ...params, + adapter, + testDate: adapter.date(TEST_DATE_ISO), + }; + + if (params.before) { + before(params.before); + } + + if (params.after) { + after(params.after); + } + + testCalculations(testSuitParams); + testLocalization(testSuitParams); + }); +} + +export const describeJalaliAdapter = createDescribe('Adapter methods', innerJalaliDescribeAdapter); diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.types.ts b/packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.types.ts new file mode 100644 index 0000000000000..2853760e4193d --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.types.ts @@ -0,0 +1,13 @@ +import { MuiPickersAdapter } from '@mui/x-date-pickers/models'; + +export interface DescribeJalaliAdapterParams { + // TODO: Type once the adapter locale is correctly types + locale: any; + before?: () => void; + after?: () => void; +} + +export type DescribeJalaliAdapterTestSuite = (params: { + adapter: MuiPickersAdapter; + testDate: TDate; +}) => void; diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/index.ts b/packages/x-date-pickers/src/tests/describeJalaliAdapter/index.ts new file mode 100644 index 0000000000000..8186777ceb9ad --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeJalaliAdapter/index.ts @@ -0,0 +1 @@ +export { describeJalaliAdapter } from './describeJalaliAdapter'; diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts b/packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts new file mode 100644 index 0000000000000..6e69a7331dc52 --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts @@ -0,0 +1,109 @@ +import { expect } from 'chai'; +import { DescribeJalaliAdapterTestSuite } from './describeJalaliAdapter.types'; +import { TEST_DATE_ISO } from '../describeGregorianAdapter'; + +export const testCalculations: DescribeJalaliAdapterTestSuite = ({ adapter, testDate }) => { + it('Method: date', () => { + expect(adapter.date(null)).to.equal(null); + }); + + it('Method: parse', () => { + expect(adapter.parse('', adapter.formats.keyboardDate)).to.equal(null); + expect(adapter.parse('01/01/1395', adapter.formats.keyboardDate)).to.not.equal(null); + }); + + it('Method: isEqual', () => { + const anotherDate = adapter.date(TEST_DATE_ISO); + + expect(adapter.isEqual(testDate, anotherDate)).to.equal(true); + expect(adapter.isEqual(null, null)).to.equal(true); + }); + + it('Methods: isAfterYear', () => { + const afterYear = adapter.addYears(testDate, 2); + expect(adapter.isAfterYear(afterYear, testDate)).to.equal(true); + }); + + it('Methods: isBeforeYear', () => { + const afterYear = adapter.addYears(testDate, 2); + expect(adapter.isBeforeYear(testDate, afterYear)).to.equal(true); + }); + + it('Method: startOfYear', () => { + expect(adapter.startOfYear(testDate)).toEqualDateTime(new Date('2018-03-21T00:00:00.000Z')); + }); + + it('Method: startOfMonth', () => { + expect(adapter.startOfMonth(testDate)).toEqualDateTime(new Date('2018-10-23T00:00:00.000Z')); + }); + + it('Method: endOfYear', () => { + expect(adapter.endOfYear(testDate)).toEqualDateTime(new Date('2019-03-20T23:59:59.999Z')); + }); + + it('Method: endOfMonth', () => { + expect(adapter.endOfMonth(testDate)).toEqualDateTime(new Date('2018-11-21T23:59:59.999Z')); + }); + + it('Methods: getYear', () => { + expect(adapter.getYear(testDate)).to.equal(1397); + }); + + it('Method: getMonth', () => { + expect(adapter.getMonth(testDate)).to.equal(7); + }); + + it('Method: getDate', () => { + expect(adapter.getDate(testDate)).to.equal(8); + }); + + it('Method: setYear', () => { + expect(adapter.setYear(testDate, 1398)).toEqualDateTime(new Date('2019-10-30T11:44:00.000Z')); + }); + + it('Method: setDate', () => { + expect(adapter.setDate(testDate, 9)).toEqualDateTime(new Date('2018-10-31T11:44:00.000Z')); + }); + + it('Method: getNextMonth', () => { + expect(adapter.getNextMonth(testDate)).toEqualDateTime(new Date('2018-11-29T11:44:00.000Z')); + }); + + it('Method: getPreviousMonth', () => { + expect(adapter.getPreviousMonth(testDate)).toEqualDateTime( + new Date('2018-09-30T11:44:00.000Z'), + ); + }); + + it('Method: getWeekdays', () => { + // TODO: AdapterDateFnsJalali `getWeekDays` method seems broken + // Same behavior with the date-io adapter. + expect(adapter.getWeekdays()).to.deep.equal( + adapter.lib === 'date-fns-jalali' + ? ['ش', '1ش', '2ش', '3ش', '4ش', '5ش', 'ج'] + : ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'], + ); + }); + + it('Method: getWeekArray', () => { + const weekArray = adapter.getWeekArray(testDate); + const expectedDate = new Date('2018-10-20T00:00:00.000Z'); + + weekArray.forEach((week) => { + week.forEach((day) => { + expect(day).toEqualDateTime(expectedDate); + expectedDate.setDate(expectedDate.getDate() + 1); + }); + }); + }); + + it('Method: getWeekNumber', () => { + expect(adapter.getWeekNumber!(testDate)).to.equal(33); + }); + + it('Method: getYearRange', () => { + const anotherDate = adapter.setYear(testDate, 1400); + const yearRange = adapter.getYearRange(testDate, anotherDate); + expect(yearRange).to.have.length(4); + }); +}; diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/testLocalization.ts b/packages/x-date-pickers/src/tests/describeJalaliAdapter/testLocalization.ts new file mode 100644 index 0000000000000..f790c0eee533a --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeJalaliAdapter/testLocalization.ts @@ -0,0 +1,14 @@ +import { expect } from 'chai'; +import { DescribeJalaliAdapterTestSuite } from './describeJalaliAdapter.types'; + +export const testLocalization: DescribeJalaliAdapterTestSuite = ({ adapter }) => { + it('Method: formatNumber', () => { + expect(adapter.formatNumber('1')).to.equal('۱'); + expect(adapter.formatNumber('2')).to.equal('۲'); + }); + + it('Method: getMeridiemText', () => { + expect(adapter.getMeridiemText('am')).to.equal('ق.ظ'); + expect(adapter.getMeridiemText('pm')).to.equal('ب.ظ'); + }); +}; diff --git a/patches/@date-io+date-fns+2.16.0.patch b/patches/@date-io+date-fns+2.16.0.patch deleted file mode 100644 index 4513eb4a02f19..0000000000000 --- a/patches/@date-io+date-fns+2.16.0.patch +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/node_modules/@date-io/date-fns/type/index.d.ts b/node_modules/@date-io/date-fns/type/index.d.ts -index 983d68a..e21d4e7 100644 ---- a/node_modules/@date-io/date-fns/type/index.d.ts -+++ b/node_modules/@date-io/date-fns/type/index.d.ts -@@ -1,3 +1,3 @@ --declare module "@date-io/type" { -+declare module "@date-io/date-fns/type" { - export type DateType = Date; - } diff --git a/patches/@date-io+date-fns-jalali+2.16.0.patch b/patches/@date-io+date-fns-jalali+2.16.0.patch deleted file mode 100644 index 09da6cb52c6da..0000000000000 --- a/patches/@date-io+date-fns-jalali+2.16.0.patch +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/node_modules/@date-io/date-fns-jalali/type/index.d.ts b/node_modules/@date-io/date-fns-jalali/type/index.d.ts -index 983d68a..569aec4 100644 ---- a/node_modules/@date-io/date-fns-jalali/type/index.d.ts -+++ b/node_modules/@date-io/date-fns-jalali/type/index.d.ts -@@ -1,3 +1,3 @@ --declare module "@date-io/type" { -+declare module "@date-io/date-fns-jalali/type" { - export type DateType = Date; - } diff --git a/yarn.lock b/yarn.lock index f53dc3aac505f..68f259cf9ba8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1259,13 +1259,6 @@ dependencies: "@date-io/moment" "^2.16.1" -"@date-io/jalaali@^2.16.1": - version "2.16.1" - resolved "https://registry.yarnpkg.com/@date-io/jalaali/-/jalaali-2.16.1.tgz#c53323fc429f8fe6ab205d36c003d6de071f17e8" - integrity sha512-GLw87G/WJ1DNrQHW8p/LqkqAqTUSqBSRin0H1pRPwCccB5Fh7GT64sadjzEvjW56lPJ0aq2vp5yI2eIjZajfrw== - dependencies: - "@date-io/moment" "^2.16.1" - "@date-io/luxon@^2.16.1": version "2.16.1" resolved "https://registry.yarnpkg.com/@date-io/luxon/-/luxon-2.16.1.tgz#b08786614cb58831c729a15807753011e4acb966" @@ -3061,6 +3054,13 @@ dependencies: moment ">=2.14.0" +"@types/moment-jalaali@^0.7.6": + version "0.7.6" + resolved "https://registry.yarnpkg.com/@types/moment-jalaali/-/moment-jalaali-0.7.6.tgz#8debf51f7bc265a00ac98c4bbd77b260c71b46d8" + integrity sha512-TyqrJVGpuqadpQt49sPdBlWqLDYU2W3PzAoJ934CjE/u8iNf/wi/oIYv9x9vKB1XU6PhzFOXerDZn43LW1K8xA== + dependencies: + moment ">=2.14.0" + "@types/node@*", "@types/node@>= 8", "@types/node@>=10.0.0", "@types/node@^18.16.0": version "18.16.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.0.tgz#4668bc392bb6938637b47e98b1f2ed5426f33316" From 5a5c29328b346657835d6d1e3f4c6c9778024480 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 27 Apr 2023 16:27:28 +0300 Subject: [PATCH 23/80] [docs] Fix error in `minDateTime` `validation` page section (#8777) --- docs/data/date-pickers/validation/validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data/date-pickers/validation/validation.md b/docs/data/date-pickers/validation/validation.md index a69517df73145..c5a1abfa9a660 100644 --- a/docs/data/date-pickers/validation/validation.md +++ b/docs/data/date-pickers/validation/validation.md @@ -183,7 +183,7 @@ In the example below—the last quarter of each hour is not selectable. ### Minimum and maximum date time -The `minDateTime` prop prevents the selection of all values after `props.minDateTime`. +The `minDateTime` prop prevents the selection of all values before `props.minDateTime`. {{"demo": "DateTimeValidationMinDateTime.js", "defaultCodeOpen": false}} From b2f2a075cedffeee9579ca6791c426b1f5e339da Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Thu, 27 Apr 2023 16:07:02 +0200 Subject: [PATCH 24/80] [pickers] Migrate `MomentHijri` to our repository (#8776) --- codecov.yml | 5 +- packages/x-codemod/tsconfig.json | 2 - packages/x-date-pickers-pro/README.md | 9 - packages/x-date-pickers-pro/package.json | 2 - packages/x-date-pickers/README.md | 9 - packages/x-date-pickers/package.json | 5 - .../AdapterDateFnsJalali.ts | 25 ++ .../AdapterMomentHijri.test.tsx | 94 ++++++ .../AdapterMomentHijri/AdapterMomentHijri.ts | 292 ++++++++++++++++++ .../src/AdapterMomentHijri/index.ts | 91 +----- .../AdapterMomentHijri/localization.test.tsx | 52 ---- .../AdapterMomentJalaali.test.tsx | 2 +- .../AdapterMomentJalaali.ts | 25 ++ .../describeHijriAdapter.ts | 34 ++ .../describeHijriAdapter.types.ts | 11 + .../src/tests/describeHijriAdapter/index.ts | 1 + .../describeHijriAdapter/testCalculations.ts | 121 ++++++++ .../describeHijriAdapter/testLocalization.ts | 14 + .../describeJalaliAdapter/testCalculations.ts | 8 +- patches/@date-io+hijri+2.16.1.patch | 10 - patches/@date-io+moment+2.16.1.patch | 10 - renovate.json | 4 - yarn.lock | 40 --- 23 files changed, 627 insertions(+), 239 deletions(-) create mode 100644 packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.test.tsx create mode 100644 packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.ts delete mode 100644 packages/x-date-pickers/src/AdapterMomentHijri/localization.test.tsx create mode 100644 packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.ts create mode 100644 packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.types.ts create mode 100644 packages/x-date-pickers/src/tests/describeHijriAdapter/index.ts create mode 100644 packages/x-date-pickers/src/tests/describeHijriAdapter/testCalculations.ts create mode 100644 packages/x-date-pickers/src/tests/describeHijriAdapter/testLocalization.ts delete mode 100644 patches/@date-io+hijri+2.16.1.patch delete mode 100644 patches/@date-io+moment+2.16.1.patch diff --git a/codecov.yml b/codecov.yml index d0b9a4c709923..13e576e5e4dbd 100644 --- a/codecov.yml +++ b/codecov.yml @@ -7,10 +7,13 @@ coverage: adapters: target: 95% paths: + - 'packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts' + - 'packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts' - 'packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts' - 'packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts' - 'packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts' - - 'packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts' + - 'packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.ts' + - 'packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts' patch: off comment: false diff --git a/packages/x-codemod/tsconfig.json b/packages/x-codemod/tsconfig.json index 1a8c2d230e1b0..42152d1090d39 100644 --- a/packages/x-codemod/tsconfig.json +++ b/packages/x-codemod/tsconfig.json @@ -1,8 +1,6 @@ { "extends": "../../tsconfig", "compilerOptions": { - // @date-io libraries produce duplicate type `DateType` - "skipLibCheck": true, "types": ["react", "mocha", "node"] }, "include": [ diff --git a/packages/x-date-pickers-pro/README.md b/packages/x-date-pickers-pro/README.md index 625ed5bba0897..65f4a9e2bf943 100644 --- a/packages/x-date-pickers-pro/README.md +++ b/packages/x-date-pickers-pro/README.md @@ -42,15 +42,6 @@ This component has the following peer dependencies that you will need to install }, ``` -If you need to use `js-joda`, `date-fns-jalali`, `jalaali`, or `hijri` library, you should be able to find the corresponding date-library from [`@date-io`](https://github.com/dmtrKovalenko/date-io#projects). -In such a case, you will have to install both the date-library and the corresponding @date-io adapter. - -```jsx -// To use moment-jalaali -npm install moment-jalaali -npm install @date-io/jalaali -``` - After installation completed, you have to set the `dateAdapter` prop of the `LocalizationProvider` accordingly. The supported adapters are exported from `@mui/x-date-pickers-pro`. diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index ad3d00a2a9458..c962b8e700a57 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -42,8 +42,6 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@date-io/luxon": "^2.16.1", - "@date-io/moment": "^2.16.1", "@mui/utils": "^5.12.0", "@mui/x-date-pickers": "6.2.1", "@mui/x-license-pro": "6.0.4", diff --git a/packages/x-date-pickers/README.md b/packages/x-date-pickers/README.md index 76ebd80038420..d9ded1be1dfe6 100644 --- a/packages/x-date-pickers/README.md +++ b/packages/x-date-pickers/README.md @@ -42,15 +42,6 @@ This component has the following peer dependencies that you will need to install }, ``` -If you need to use `js-joda`, `date-fns-jalali`, `jalaali`, or `hijri` library, you should be able to find the corresponding date-library from [`@date-io`](https://github.com/dmtrKovalenko/date-io#projects). -In such a case, you will have to install both the date-library and the corresponding @date-io adapter. - -```jsx -// To use moment-jalaali -npm install moment-jalaali -npm install @date-io/jalaali -``` - After installation completed, you have to set the `dateAdapter` prop of the `LocalizationProvider` accordingly. The supported adapters are exported from `@mui/x-date-pickers`. diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index b8b43c0fafa06..c18f1df8bcf2f 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -45,11 +45,6 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@date-io/core": "^2.16.0", - "@date-io/date-fns": "^2.16.0", - "@date-io/date-fns-jalali": "^2.16.0", - "@date-io/hijri": "^2.16.1", - "@date-io/moment": "^2.16.1", "@mui/utils": "^5.12.0", "@types/react-transition-group": "^4.4.5", "clsx": "^1.2.1", diff --git a/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts index 7a84543db8759..534a74df70323 100644 --- a/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts +++ b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts @@ -172,6 +172,31 @@ const NUMBER_SYMBOL_MAP = { '0': '۰', }; +/** + * Based on `@date-io/date-fns-jalali` + * + * MIT License + * + * Copyright (c) 2017 Dmitriy Kovalenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ export class AdapterDateFnsJalali implements MuiPickersAdapter { public isMUIAdapter = true; diff --git a/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.test.tsx b/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.test.tsx new file mode 100644 index 0000000000000..ef6d9f99141c8 --- /dev/null +++ b/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.test.tsx @@ -0,0 +1,94 @@ +import * as React from 'react'; +import moment from 'moment'; +import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; +import { AdapterMomentHijri } from '@mui/x-date-pickers/AdapterMomentHijri'; +import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { + createPickerRenderer, + expectInputPlaceholder, + expectInputValue, +} from 'test/utils/pickers-utils'; +import { describeHijriAdapter } from '@mui/x-date-pickers/tests/describeHijriAdapter'; +import 'moment/locale/ar'; +import { AdapterFormats } from '@mui/x-date-pickers'; +import { expect } from 'chai'; + +describe('', () => { + describeHijriAdapter(AdapterMomentHijri, { + before: () => { + moment.locale('ar-SA'); + }, + after: () => { + moment.locale('en'); + }, + }); + + describe('Adapter localization', () => { + it('Formatting', () => { + const adapter = new AdapterMomentHijri(); + + const expectDate = (format: keyof AdapterFormats, expectedWithArSA: string) => { + const date = adapter.date('2020-01-01T23:44:00.000Z')!; + + expect(adapter.format(date, format)).to.equal(expectedWithArSA); + }; + + expectDate('keyboardDate', '١٤٤١/٠٥/٠٦'); + expectDate('fullDate', '١٤٤١، جمادى الأولى ١'); + expectDate('fullDateWithWeekday', '١٤٤١، جمادى الأولى ١، الأربعاء'); + expectDate('normalDate', 'الأربعاء، ٦ جمادى ١'); + expectDate('shortDate', '٦ جمادى ١'); + expectDate('year', '١٤٤١'); + expectDate('month', 'جمادى الأولى'); + expectDate('monthAndDate', '٦ جمادى الأولى'); + expectDate('weekday', 'الأربعاء'); + expectDate('weekdayShort', 'أربعاء'); + expectDate('dayOfMonth', '٦'); + expectDate('fullTime12h', '١١:٤٤ م'); + expectDate('fullTime24h', '٢٣:٤٤'); + expectDate('hours12h', '١١'); + expectDate('hours24h', '٢٣'); + expectDate('minutes', '٤٤'); + expectDate('seconds', '٠٠'); + expectDate('fullDateTime12h', '٦ جمادى الأولى ١١:٤٤ م'); + expectDate('fullDateTime24h', '٦ جمادى الأولى ٢٣:٤٤'); + }); + }); + + describe('Picker localization', () => { + const testDate = new Date(2018, 4, 15, 9, 35); + const localizedTexts = { + ar: { + placeholder: 'YYYY/MM/DD hh:mm', + value: '١٤٣٩/٠٨/٢٩ ٠٩:٣٥', + }, + }; + + Object.keys(localizedTexts).forEach((localeKey) => { + const localeObject = { code: localeKey }; + + describe(`test with the locale "${localeKey}"`, () => { + const { render, adapter } = createPickerRenderer({ + clock: 'fake', + adapterName: 'moment-hijri', + locale: localeObject, + }); + + it('should have correct placeholder', () => { + render(); + + expectInputPlaceholder( + screen.getByRole('textbox'), + localizedTexts[localeKey].placeholder, + ); + }); + + it('should have well formatted value', () => { + render(); + + expectInputValue(screen.getByRole('textbox'), localizedTexts[localeKey].value); + }); + }); + }); + }); +}); diff --git a/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.ts b/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.ts new file mode 100644 index 0000000000000..a10e3118f5792 --- /dev/null +++ b/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.ts @@ -0,0 +1,292 @@ +/* eslint-disable class-methods-use-this */ +import defaultHMoment, { Moment } from 'moment-hijri'; +import { AdapterMoment } from '../AdapterMoment'; +import { AdapterFormats, FieldFormatTokenMap, MuiPickersAdapter } from '../models'; + +interface AdapterMomentHijriOptions { + instance?: typeof defaultHMoment; + formats?: Partial; +} + +// From https://momentjs.com/docs/#/displaying/format/ +const formatTokenMap: FieldFormatTokenMap = { + // Year + iY: { sectionType: 'year', contentType: 'letter' }, + iYY: { sectionType: 'year', contentType: 'letter' }, + iYYYY: { sectionType: 'year', contentType: 'letter' }, + + // Month + iM: 'month', + iMM: 'month', + iMMM: { sectionType: 'month', contentType: 'letter' }, + iMMMM: { sectionType: 'month', contentType: 'letter' }, + + // Day of the month + iD: { sectionType: 'day', contentType: 'digit', maxLength: 2 }, + iDD: 'day', + + // Meridiem + A: 'meridiem', + a: 'meridiem', + + // Hours + H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, + HH: 'hours', + h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, + hh: 'hours', + + // Minutes + m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, + mm: 'minutes', + + // Seconds + s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 }, + ss: 'seconds', +}; + +const defaultFormats: AdapterFormats = { + dayOfMonth: 'iD', + fullDate: 'iYYYY, iMMMM Do', + fullDateWithWeekday: 'iYYYY, iMMMM Do, dddd', + fullDateTime: 'iYYYY, iMMMM Do, hh:mm A', + fullDateTime12h: 'iD iMMMM hh:mm A', + fullDateTime24h: 'iD iMMMM HH:mm', + fullTime: 'LT', + fullTime12h: 'hh:mm A', + fullTime24h: 'HH:mm', + hours12h: 'hh', + hours24h: 'HH', + keyboardDate: 'iYYYY/iMM/iDD', + keyboardDateTime: 'iYYYY/iMM/iDD LT', + keyboardDateTime12h: 'iYYYY/iMM/iDD hh:mm A', + keyboardDateTime24h: 'iYYYY/iMM/iDD HH:mm', + minutes: 'mm', + month: 'iMMMM', + monthAndDate: 'iD iMMMM', + monthAndYear: 'iMMMM iYYYY', + monthShort: 'iMMM', + weekday: 'dddd', + weekdayShort: 'ddd', + normalDate: 'dddd, iD iMMM', + normalDateWithWeekday: 'DD iMMMM', + seconds: 'ss', + shortDate: 'iD iMMM', + year: 'iYYYY', +}; + +const NUMBER_SYMBOL_MAP = { + '1': '١', + '2': '٢', + '3': '٣', + '4': '٤', + '5': '٥', + '6': '٦', + '7': '٧', + '8': '٨', + '9': '٩', + '0': '٠', +}; + +/** + * Based on `@date-io/hijri` + * + * MIT License + * + * Copyright (c) 2017 Dmitriy Kovalenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +export class AdapterMomentHijri extends AdapterMoment implements MuiPickersAdapter { + public isMUIAdapter = true; + + public formatTokenMap = formatTokenMap; + + public escapedCharacters = { start: '[', end: ']' }; + + constructor({ formats, instance }: AdapterMomentHijriOptions = {}) { + super({ locale: 'ar-SA', instance }); + + this.moment = instance || defaultHMoment; + this.locale = 'ar-SA'; + this.formats = { ...defaultFormats, ...formats }; + } + + private toIMoment = (value?: Moment | undefined) => { + return this.moment(value ? value.clone() : undefined).locale('ar-SA'); + }; + + public date = (value?: any) => { + if (value === null) { + return null; + } + + return this.moment(value).locale('ar-SA'); + }; + + public parse = (value: string, format: string) => { + if (value === '') { + return null; + } + + return this.moment(value, format, true).locale('ar-SA'); + }; + + public getFormatHelperText = (format: string) => { + return this.expandFormat(format) + .replace(/a/gi, '(a|p)m') + .replace('iY', 'Y') + .replace('iM', 'M') + .replace('iD', 'D') + .toLocaleLowerCase(); + }; + + public formatNumber = (numberToFormat: string) => { + return numberToFormat + .replace(/\d/g, (match) => NUMBER_SYMBOL_MAP[match as keyof typeof NUMBER_SYMBOL_MAP]) + .replace(/,/g, '،'); + }; + + public isEqual = (value: any, comparing: any) => { + if (value === null && comparing === null) { + return true; + } + + return this.moment(value).isSame(comparing); + }; + + public startOfYear = (value: Moment) => { + return value.clone().startOf('iYear'); + }; + + public startOfMonth = (value: Moment) => { + return value.clone().startOf('iMonth'); + }; + + public endOfYear = (value: Moment) => { + return value.clone().endOf('iYear'); + }; + + public endOfMonth = (value: Moment) => { + return value.clone().endOf('iMonth'); + }; + + public addYears = (value: Moment, amount: number) => { + return amount < 0 + ? value.clone().subtract(Math.abs(amount), 'iYear') + : value.clone().add(amount, 'iYear'); + }; + + public addMonths = (value: Moment, amount: number) => { + return amount < 0 + ? value.clone().subtract(Math.abs(amount), 'iMonth') + : value.clone().add(amount, 'iMonth'); + }; + + public getYear = (value: Moment) => { + return value.iYear(); + }; + + public getMonth = (value: Moment) => { + return value.iMonth(); + }; + + public getDate = (value: Moment) => { + return value.iDate(); + }; + + public setYear = (value: Moment, year: number) => { + return value.clone().iYear(year); + }; + + public setMonth = (value: Moment, month: number) => { + return value.clone().iMonth(month); + }; + + public setDate = (value: Moment, date: number) => { + return value.clone().iDate(date); + }; + + public getNextMonth = (value: Moment) => { + return value.clone().add(1, 'iMonth'); + }; + + public getPreviousMonth = (value: Moment) => { + return value.clone().subtract(1, 'iMonth'); + }; + + public getWeekdays = () => { + return [0, 1, 2, 3, 4, 5, 6].map((dayOfWeek) => { + return this.toIMoment().weekday(dayOfWeek).format('dd'); + }); + }; + + public getWeekArray = (value: Moment) => { + const start = value.clone().startOf('iMonth').startOf('week'); + + const end = value.clone().endOf('iMonth').endOf('week'); + + let count = 0; + let current = start; + const nestedWeeks: Moment[][] = []; + + while (current.isBefore(end)) { + const weekNumber = Math.floor(count / 7); + nestedWeeks[weekNumber] = nestedWeeks[weekNumber] || []; + nestedWeeks[weekNumber].push(current); + + current = current.clone().add(1, 'day'); + count += 1; + } + + return nestedWeeks; + }; + + public getWeekNumber = (value: Moment) => { + return value.iWeek(); + }; + + public getYearRange = (start: Moment, end: Moment) => { + // moment-hijri only supports dates between 1356-01-01 H and 1499-12-29 H + // We need to throw if outside min/max bounds, otherwise the while loop below will be infinite. + if (start.isBefore('1937-03-14')) { + throw new Error('min date must be on or after 1356-01-01 H (1937-03-14)'); + } + if (end.isAfter('2076-11-26')) { + throw new Error('max date must be on or before 1499-12-29 H (2076-11-26)'); + } + + const startDate = this.moment(start).startOf('iYear'); + const endDate = this.moment(end).endOf('iYear'); + const years: Moment[] = []; + + let current = startDate; + while (current.isBefore(endDate)) { + years.push(current); + current = current.clone().add(1, 'iYear'); + } + + return years; + }; + + public getMeridiemText = (ampm: 'am' | 'pm') => { + return ampm === 'am' + ? this.toIMoment().hours(2).format('A') + : this.toIMoment().hours(14).format('A'); + }; +} diff --git a/packages/x-date-pickers/src/AdapterMomentHijri/index.ts b/packages/x-date-pickers/src/AdapterMomentHijri/index.ts index 2a4817cae496e..4b6c2374f3e76 100644 --- a/packages/x-date-pickers/src/AdapterMomentHijri/index.ts +++ b/packages/x-date-pickers/src/AdapterMomentHijri/index.ts @@ -1,90 +1 @@ -/* eslint-disable class-methods-use-this */ -import BaseAdapterMomentHijri from '@date-io/hijri'; -// @ts-ignore -import defaultMoment, { LongDateFormatKey } from 'moment-hijri'; -import { FieldFormatTokenMap, MuiPickersAdapter } from '../models'; - -// From https://momentjs.com/docs/#/displaying/format/ -const formatTokenMap: FieldFormatTokenMap = { - // Year - iY: { sectionType: 'year', contentType: 'letter' }, - iYY: { sectionType: 'year', contentType: 'letter' }, - iYYYY: { sectionType: 'year', contentType: 'letter' }, - - // Month - iM: 'month', - iMM: 'month', - iMMM: { sectionType: 'month', contentType: 'letter' }, - iMMMM: { sectionType: 'month', contentType: 'letter' }, - - // Day of the month - iD: { sectionType: 'day', contentType: 'digit', maxLength: 2 }, - iDD: 'day', - - // Meridiem - A: 'meridiem', - a: 'meridiem', - - // Hours - H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - HH: 'hours', - h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 }, - hh: 'hours', - - // Minutes - m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 }, - mm: 'minutes', - - // Seconds - s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 }, - ss: 'seconds', -}; - -export class AdapterMomentHijri - extends BaseAdapterMomentHijri - implements MuiPickersAdapter -{ - public isMUIAdapter = true; - - public formatTokenMap = formatTokenMap; - - public escapedCharacters = { start: '[', end: ']' }; - - /** - * The current getFormatHelperText method uses an outdated format parsing logic. - * We should use this one in the future to support all localized formats. - */ - public expandFormat = (format: string) => { - // @see https://github.com/moment/moment/blob/develop/src/lib/format/format.js#L6 - const localFormattingTokens = /(\[[^[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})|./g; - - return format - .match(localFormattingTokens)! - .map((token) => { - const firstCharacter = token[0]; - if (firstCharacter === 'L' || firstCharacter === ';') { - return this.moment - .localeData(this.getCurrentLocaleCode()) - .longDateFormat(token as LongDateFormatKey); - } - - return token; - }) - .join('') - .replace('dd', 'iDD'); // Fix for https://github.com/dmtrKovalenko/date-io/pull/632 - }; - - // Redefined here just to show how it can be written using expandFormat - public getFormatHelperText = (format: string) => { - return this.expandFormat(format) - .replace(/a/gi, '(a|p)m') - .replace('iY', 'Y') - .replace('iM', 'M') - .replace('iD', 'D') - .toLocaleLowerCase(); - }; - - public getWeekNumber = (date: defaultMoment.Moment) => { - return date.iWeek(); - }; -} +export { AdapterMomentHijri } from './AdapterMomentHijri'; diff --git a/packages/x-date-pickers/src/AdapterMomentHijri/localization.test.tsx b/packages/x-date-pickers/src/AdapterMomentHijri/localization.test.tsx deleted file mode 100644 index 86fdebc37ed98..0000000000000 --- a/packages/x-date-pickers/src/AdapterMomentHijri/localization.test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from 'react'; -import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; -import { AdapterMomentHijri } from '@mui/x-date-pickers/AdapterMomentHijri'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; -import { expect } from 'chai'; -import { - createPickerRenderer, - expectInputPlaceholder, - expectInputValue, -} from 'test/utils/pickers-utils'; -import 'moment/locale/ar'; - -const testDate = new Date(2018, 4, 15, 9, 35); -const localizedTexts = { - ar: { - placeholder: 'YYYY/MM/DD hh:mm', - value: '١٤٣٩/٠٨/٢٩ ٠٩:٣٥', - }, -}; - -describe('', () => { - Object.keys(localizedTexts).forEach((localeKey) => { - const localeObject = { code: localeKey }; - - describe(`test with the locale "${localeKey}"`, () => { - const { render, adapter } = createPickerRenderer({ - clock: 'fake', - adapterName: 'moment-hijri', - locale: localeObject, - }); - - it('should have correct placeholder', () => { - render(); - - expectInputPlaceholder(screen.getByRole('textbox'), localizedTexts[localeKey].placeholder); - }); - - it('should have well formatted value', () => { - render(); - - expectInputValue(screen.getByRole('textbox'), localizedTexts[localeKey].value); - }); - }); - }); - - it('should return the correct week number', () => { - const adapter = new AdapterMomentHijri(); - const dateToTest = adapter.date(new Date(2022, 10, 10)); - - expect(adapter.getWeekNumber!(dateToTest)).to.equal(16); - }); -}); diff --git a/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx index 490500b3d0825..5e9c8cb3fbcf0 100644 --- a/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx +++ b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import moment from 'moment'; import jMoment from 'moment-jalaali'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { AdapterMomentJalaali } from '@mui/x-date-pickers/AdapterMomentJalaali'; @@ -10,7 +11,6 @@ import { expectInputValue, } from 'test/utils/pickers-utils'; import 'moment/locale/fa'; -import moment from 'moment'; import { AdapterFormats } from '@mui/x-date-pickers/models'; import { describeJalaliAdapter } from '@mui/x-date-pickers/tests/describeJalaliAdapter'; diff --git a/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts index 3c634f70da0a0..158e474359032 100644 --- a/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts +++ b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts @@ -86,6 +86,31 @@ const NUMBER_SYMBOL_MAP = { '0': '۰', }; +/** + * Based on `@date-io/jalaali` + * + * MIT License + * + * Copyright (c) 2017 Dmitriy Kovalenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ export class AdapterMomentJalaali extends AdapterMoment implements MuiPickersAdapter { public isMUIAdapter = true; diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.ts b/packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.ts new file mode 100644 index 0000000000000..898530d8668e8 --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.ts @@ -0,0 +1,34 @@ +import createDescribe from '@mui/monorepo/test/utils/createDescribe'; +import { MuiPickersAdapter } from '@mui/x-date-pickers'; +import { testCalculations } from './testCalculations'; +import { testLocalization } from './testLocalization'; +import { DescribeHijriAdapterParams } from './describeHijriAdapter.types'; +import { TEST_DATE_ISO } from '../describeGregorianAdapter'; + +function innerJalaliDescribeAdapter( + Adapter: new (...args: any) => MuiPickersAdapter, + params: DescribeHijriAdapterParams, +) { + const adapter = new Adapter(); + + describe(adapter.lib, () => { + const testSuitParams = { + ...params, + adapter, + testDate: adapter.date(TEST_DATE_ISO), + }; + + if (params.before) { + before(params.before); + } + + if (params.after) { + after(params.after); + } + + testCalculations(testSuitParams); + testLocalization(testSuitParams); + }); +} + +export const describeHijriAdapter = createDescribe('Adapter methods', innerJalaliDescribeAdapter); diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.types.ts b/packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.types.ts new file mode 100644 index 0000000000000..0d6590b7e2bd8 --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.types.ts @@ -0,0 +1,11 @@ +import { MuiPickersAdapter } from '@mui/x-date-pickers/models'; + +export interface DescribeHijriAdapterParams { + before?: () => void; + after?: () => void; +} + +export type DescribeHijriAdapterTestSuite = (params: { + adapter: MuiPickersAdapter; + testDate: TDate; +}) => void; diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/index.ts b/packages/x-date-pickers/src/tests/describeHijriAdapter/index.ts new file mode 100644 index 0000000000000..e2dfc1b9e621f --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeHijriAdapter/index.ts @@ -0,0 +1 @@ +export { describeHijriAdapter } from './describeHijriAdapter'; diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/testCalculations.ts b/packages/x-date-pickers/src/tests/describeHijriAdapter/testCalculations.ts new file mode 100644 index 0000000000000..222636a1cc00d --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeHijriAdapter/testCalculations.ts @@ -0,0 +1,121 @@ +import { expect } from 'chai'; +import { DescribeHijriAdapterTestSuite } from './describeHijriAdapter.types'; +import { TEST_DATE_ISO } from '../describeGregorianAdapter'; + +export const testCalculations: DescribeHijriAdapterTestSuite = ({ adapter, testDate }) => { + it('Method: date', () => { + expect(adapter.date(null)).to.equal(null); + }); + + it('Method: parse', () => { + expect(adapter.parse('', 'iYYYY/iM/iD')).to.equal(null); + expect(adapter.parse('01/01/1395', 'iYYYY/iM/iD')).not.to.equal(null); + }); + + it('Method: isEqual', () => { + const anotherDate = adapter.date(TEST_DATE_ISO); + + expect(adapter.isEqual(testDate, anotherDate)).to.equal(true); + expect(adapter.isEqual(null, null)).to.equal(true); + }); + + it('Method: isAfterYear', () => { + const afterYear = adapter.addYears(testDate, 2); + expect(adapter.isAfterYear(afterYear, testDate)).to.equal(true); + }); + + it('Method: isBeforeYear', () => { + const afterYear = adapter.addYears(testDate, 2); + expect(adapter.isBeforeYear(testDate, afterYear)).to.equal(true); + }); + + it('Method: startOfYear', () => { + expect(adapter.startOfYear(testDate)).toEqualDateTime(new Date('2018-09-11T00:00:00.000Z')); + }); + + it('Method: startOfMonth', () => { + expect(adapter.startOfMonth(testDate)).toEqualDateTime(new Date('2018-10-10T00:00:00.000Z')); + }); + + it('Method: endOfYear', () => { + expect(adapter.endOfYear(testDate)).toEqualDateTime(new Date('2019-08-30T23:59:59.999Z')); + }); + + it('Method: endOfMonth', () => { + expect(adapter.endOfMonth(testDate)).toEqualDateTime(new Date('2018-11-08T23:59:59.999Z')); + }); + + it('Method: getYear', () => { + expect(adapter.getYear(testDate)).to.equal(1440); + }); + + it('Method: getMonth', () => { + expect(adapter.getMonth(testDate)).to.equal(1); + }); + + it('Method: getDate', () => { + expect(adapter.getDate(testDate)).to.equal(21); + }); + + it('Method: setYear', () => { + expect(adapter.setYear(testDate, 1441)).toEqualDateTime(new Date('2019-10-20T11:44:00.000Z')); + }); + + it('Method: setDate', () => { + expect(adapter.setDate(testDate, 22)).toEqualDateTime(new Date('2018-10-31T11:44:00.000Z')); + }); + + it('Method: getNextMonth', () => { + expect(adapter.getNextMonth(testDate)).toEqualDateTime(new Date('2018-11-29T11:44:00.000Z')); + }); + + it('Method: getPreviousMonth', () => { + expect(adapter.getPreviousMonth(testDate)).toEqualDateTime( + new Date('2018-10-01T11:44:00.000Z'), + ); + }); + + it('Method: getWeekdays', () => { + expect(adapter.getWeekdays()).to.deep.equal(['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س']); + }); + + it('Method: getWeekArray', () => { + const weekArray = adapter.getWeekArray(testDate); + const expectedDate = new Date('2018-10-07T00:00:00.000Z'); + + weekArray.forEach((week) => { + week.forEach((day) => { + expect(day).toEqualDateTime(expectedDate); + expectedDate.setDate(expectedDate.getDate() + 1); + }); + }); + }); + + it('Method: getWeekNumber', () => { + expect(adapter.getWeekNumber!(testDate)).to.equal(8); + }); + + describe('Method: getYearRange', () => { + it('Minimum limit', () => { + const anotherYear = adapter.setYear(testDate, 1355); + + expect(() => adapter.getYearRange(anotherYear, testDate)).to.throw( + 'min date must be on or after 1356-01-01 H (1937-03-14)', + ); + }); + + it('Maximum limit', () => { + const anotherYear = adapter.setYear(testDate, 1500); + + expect(() => adapter.getYearRange(testDate, anotherYear)).to.throw( + 'max date must be on or before 1499-12-29 H (2076-11-26)', + ); + }); + }); + + it('Method: getYearRange', () => { + const anotherDate = adapter.setYear(testDate, 1445); + const yearRange = adapter.getYearRange(testDate, anotherDate); + expect(yearRange).to.have.length(6); + }); +}; diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/testLocalization.ts b/packages/x-date-pickers/src/tests/describeHijriAdapter/testLocalization.ts new file mode 100644 index 0000000000000..5eea8aad057dc --- /dev/null +++ b/packages/x-date-pickers/src/tests/describeHijriAdapter/testLocalization.ts @@ -0,0 +1,14 @@ +import { expect } from 'chai'; +import { DescribeHijriAdapterTestSuite } from './describeHijriAdapter.types'; + +export const testLocalization: DescribeHijriAdapterTestSuite = ({ adapter }) => { + it('Method: formatNumber', () => { + expect(adapter.formatNumber('1')).to.equal('١'); + expect(adapter.formatNumber('2')).to.equal('٢'); + }); + + it('Method: getMeridiemText', () => { + expect(adapter.getMeridiemText('am')).to.equal('ص'); + expect(adapter.getMeridiemText('pm')).to.equal('م'); + }); +}; diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts b/packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts index 6e69a7331dc52..14b48b3210156 100644 --- a/packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts +++ b/packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts @@ -9,7 +9,7 @@ export const testCalculations: DescribeJalaliAdapterTestSuite = ({ adapter, test it('Method: parse', () => { expect(adapter.parse('', adapter.formats.keyboardDate)).to.equal(null); - expect(adapter.parse('01/01/1395', adapter.formats.keyboardDate)).to.not.equal(null); + expect(adapter.parse('01/01/1395', adapter.formats.keyboardDate)).not.to.equal(null); }); it('Method: isEqual', () => { @@ -19,12 +19,12 @@ export const testCalculations: DescribeJalaliAdapterTestSuite = ({ adapter, test expect(adapter.isEqual(null, null)).to.equal(true); }); - it('Methods: isAfterYear', () => { + it('Method: isAfterYear', () => { const afterYear = adapter.addYears(testDate, 2); expect(adapter.isAfterYear(afterYear, testDate)).to.equal(true); }); - it('Methods: isBeforeYear', () => { + it('Method: isBeforeYear', () => { const afterYear = adapter.addYears(testDate, 2); expect(adapter.isBeforeYear(testDate, afterYear)).to.equal(true); }); @@ -45,7 +45,7 @@ export const testCalculations: DescribeJalaliAdapterTestSuite = ({ adapter, test expect(adapter.endOfMonth(testDate)).toEqualDateTime(new Date('2018-11-21T23:59:59.999Z')); }); - it('Methods: getYear', () => { + it('Method: getYear', () => { expect(adapter.getYear(testDate)).to.equal(1397); }); diff --git a/patches/@date-io+hijri+2.16.1.patch b/patches/@date-io+hijri+2.16.1.patch deleted file mode 100644 index 7b6a427fd212a..0000000000000 --- a/patches/@date-io+hijri+2.16.1.patch +++ /dev/null @@ -1,10 +0,0 @@ -diff --git a/node_modules/@date-io/hijri/type/index.d.ts b/node_modules/@date-io/hijri/type/index.d.ts -index 49d51ac..ffb0968 100644 ---- a/node_modules/@date-io/hijri/type/index.d.ts -+++ b/node_modules/@date-io/hijri/type/index.d.ts -@@ -1,4 +1,4 @@ --declare module "@date-io/type" { -+declare module "@date-io/moment-hijri/type" { - import { Moment } from "moment-hijri"; - - export type DateType = Moment; diff --git a/patches/@date-io+moment+2.16.1.patch b/patches/@date-io+moment+2.16.1.patch deleted file mode 100644 index 706f609488586..0000000000000 --- a/patches/@date-io+moment+2.16.1.patch +++ /dev/null @@ -1,10 +0,0 @@ -diff --git a/node_modules/@date-io/moment/type/index.d.ts b/node_modules/@date-io/moment/type/index.d.ts -index 93e75db..bda7019 100644 ---- a/node_modules/@date-io/moment/type/index.d.ts -+++ b/node_modules/@date-io/moment/type/index.d.ts -@@ -1,4 +1,4 @@ --declare module "@date-io/type" { -+declare module "@date-io/moment/type" { - import { Moment } from "moment"; - - export type DateType = Moment; diff --git a/renovate.json b/renovate.json index beecaadba6c75..9dcc03d1aef96 100644 --- a/renovate.json +++ b/renovate.json @@ -18,10 +18,6 @@ "groupName": "babel", "matchPackagePatterns": "@babel/*" }, - { - "groupName": "date-io", - "matchPackagePatterns": "@date-io/*" - }, { "groupName": "Emotion", "matchPackagePatterns": "@emotion/*" diff --git a/yarn.lock b/yarn.lock index 68f259cf9ba8f..2202c6800b48d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1233,46 +1233,6 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@date-io/core@^2.16.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@date-io/core/-/core-2.16.0.tgz#7871bfc1d9bca9aa35ad444a239505589d0f22f6" - integrity sha512-DYmSzkr+jToahwWrsiRA2/pzMEtz9Bq1euJwoOuYwuwIYXnZFtHajY2E6a1VNVDc9jP8YUXK1BvnZH9mmT19Zg== - -"@date-io/date-fns-jalali@^2.16.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@date-io/date-fns-jalali/-/date-fns-jalali-2.16.0.tgz#0e2916c287e00d708a6f9f6d6e6c44ec377994c5" - integrity sha512-MNVvGYwRiBydbvY7gvZM14W2kosIG29G1Ekw5qmYWOXkIIFngh6ZvV7/uVGDCW+gqlIeSz/XitZXA9n8RO0tJw== - dependencies: - "@date-io/core" "^2.16.0" - -"@date-io/date-fns@^2.16.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@date-io/date-fns/-/date-fns-2.16.0.tgz#bd5e09b6ecb47ee55e593fc3a87e7b2caaa3da40" - integrity sha512-bfm5FJjucqlrnQcXDVU5RD+nlGmL3iWgkHTq3uAZWVIuBu6dDmGa3m8a6zo2VQQpu8ambq9H22UyUpn7590joA== - dependencies: - "@date-io/core" "^2.16.0" - -"@date-io/hijri@^2.16.1": - version "2.16.1" - resolved "https://registry.yarnpkg.com/@date-io/hijri/-/hijri-2.16.1.tgz#a5e7e5b875e0ac8719c235eaf51d4188f21ea193" - integrity sha512-6BxY0mtnqj5cBiXluRs3uWN0mSJwGw0AB2ZxqtEHvBFoiSYEojW51AETnfPIWpdvDsBn+WAC7QrfBvQZnoyIkQ== - dependencies: - "@date-io/moment" "^2.16.1" - -"@date-io/luxon@^2.16.1": - version "2.16.1" - resolved "https://registry.yarnpkg.com/@date-io/luxon/-/luxon-2.16.1.tgz#b08786614cb58831c729a15807753011e4acb966" - integrity sha512-aeYp5K9PSHV28946pC+9UKUi/xMMYoaGelrpDibZSgHu2VWHXrr7zWLEr+pMPThSs5vt8Ei365PO+84pCm37WQ== - dependencies: - "@date-io/core" "^2.16.0" - -"@date-io/moment@^2.16.1": - version "2.16.1" - resolved "https://registry.yarnpkg.com/@date-io/moment/-/moment-2.16.1.tgz#ec6e0daa486871e0e6412036c6f806842a0eeed4" - integrity sha512-JkxldQxUqZBfZtsaCcCMkm/dmytdyq5pS1RxshCQ4fHhsvP5A7gSqPD22QbVXMcJydi3d3v1Y8BQdUKEuGACZQ== - dependencies: - "@date-io/core" "^2.16.0" - "@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" From 8b83338dd24ccd6e07175600eccb19b830bbccaa Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Thu, 27 Apr 2023 16:48:50 +0200 Subject: [PATCH 25/80] [core] Use `adapter.lib` instead of `adapterName` in `describeAdapters` (#8779) --- .../DateField/tests/editing.DateField.test.tsx | 18 +++++++++--------- .../tests/describeAdapters/describeAdapters.ts | 5 ++--- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx b/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx index 66e1797de70f2..5440338d67552 100644 --- a/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx +++ b/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx @@ -346,7 +346,7 @@ describe(' - Editing', () => { ); }); - describeAdapters('Digit editing', DateField, ({ adapter, adapterName, testFieldChange }) => { + describeAdapters('Digit editing', DateField, ({ adapter, testFieldChange }) => { it('should set the day to the digit pressed when no digit no value is provided', () => { testFieldChange({ format: adapter.formats.dayOfMonth, @@ -395,7 +395,7 @@ describe(' - Editing', () => { it('should support 2-digits year format', () => { testFieldChange({ // This format is not present in any of the adapter formats - format: adapterName.includes('moment') || adapterName.includes('dayjs') ? 'YY' : 'yy', + format: adapter.lib.includes('moment') || adapter.lib.includes('dayjs') ? 'YY' : 'yy', keyStrokes: [ // 1st year: 22 { value: '2', expected: '02' }, @@ -412,7 +412,7 @@ describe(' - Editing', () => { it('should support 2-digits year format when a value is provided', () => { testFieldChange({ // This format is not present in any of the adapter formats - format: adapterName.includes('moment') || adapterName.includes('dayjs') ? 'YY' : 'yy', + format: adapter.lib.includes('moment') || adapter.lib.includes('dayjs') ? 'YY' : 'yy', defaultValue: adapter.date(new Date(2022, 5, 4)), keyStrokes: [ { value: '2', expected: '02' }, @@ -469,12 +469,12 @@ describe(' - Editing', () => { it('should support day with letter suffix', function test() { // Luxon don't have any day format with a letter suffix - if (adapterName === 'luxon') { + if (adapter.lib === 'luxon') { this.skip(); } testFieldChange({ - format: adapterName === 'date-fns' ? 'do' : 'Do', + format: adapter.lib === 'date-fns' ? 'do' : 'Do', keyStrokes: [ { value: '1', expected: '1st' }, { value: '2', expected: '12th' }, @@ -485,7 +485,7 @@ describe(' - Editing', () => { it('should respect leading zeros when shouldRespectLeadingZeros = true', () => { testFieldChange({ - format: ['luxon', 'date-fns'].includes(adapterName) ? 'd' : 'D', + format: ['luxon', 'date-fns'].includes(adapter.lib) ? 'd' : 'D', shouldRespectLeadingZeros: true, keyStrokes: [ { value: '1', expected: '1' }, @@ -497,7 +497,7 @@ describe(' - Editing', () => { it('should not respect leading zeros when shouldRespectLeadingZeros = false', () => { testFieldChange({ - format: ['luxon', 'date-fns'].includes(adapterName) ? 'd' : 'D', + format: ['luxon', 'date-fns'].includes(adapter.lib) ? 'd' : 'D', shouldRespectLeadingZeros: false, keyStrokes: [ { value: '1', expected: '01' }, @@ -612,7 +612,7 @@ describe(' - Editing', () => { }, ); - describeAdapters('Full editing scenarios', DateField, ({ adapterName, render, clickOnInput }) => { + describeAdapters('Full editing scenarios', DateField, ({ render, adapter, clickOnInput }) => { it('should move to the last day of the month when the current day exceeds it', () => { const onChange = spy(); @@ -633,7 +633,7 @@ describe(' - Editing', () => { expectInputValue(input, '11/31/YYYY'); // TODO: Fix this behavior on day.js (`clampDaySection` generates an invalid date for the start of the month). - if (adapterName === 'dayjs') { + if (adapter.lib === 'dayjs') { return; } diff --git a/packages/x-date-pickers/src/tests/describeAdapters/describeAdapters.ts b/packages/x-date-pickers/src/tests/describeAdapters/describeAdapters.ts index ba4ee7abcbe2d..39954a7189c5b 100644 --- a/packages/x-date-pickers/src/tests/describeAdapters/describeAdapters.ts +++ b/packages/x-date-pickers/src/tests/describeAdapters/describeAdapters.ts @@ -9,8 +9,7 @@ import { } from 'test/utils/pickers-utils'; type AdapterTestRunner

= ( - params: ReturnType & - BuildFieldInteractionsResponse

& { adapterName: AdapterName }, + params: ReturnType & BuildFieldInteractionsResponse

, ) => void; const ADAPTERS: AdapterName[] = ['dayjs', 'date-fns', 'luxon', 'moment']; @@ -39,7 +38,7 @@ function innerDescribeAdapters

( Component: FieldComponent, }); - testRunner({ adapterName, ...pickerRendererResponse, ...fieldInteractions }); + testRunner({ ...pickerRendererResponse, ...fieldInteractions }); }); }); } From 79efe975567799fc31f676ae22db3a31d81a05d9 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Thu, 27 Apr 2023 17:43:03 +0200 Subject: [PATCH 26/80] [pickers] Add missing export for `caES` locale (#8782) --- packages/x-date-pickers/src/locales/index.ts | 1 + scripts/x-date-pickers-pro.exports.json | 1 + scripts/x-date-pickers.exports.json | 1 + 3 files changed, 3 insertions(+) diff --git a/packages/x-date-pickers/src/locales/index.ts b/packages/x-date-pickers/src/locales/index.ts index a26f1aca66a31..c5ff10acad258 100644 --- a/packages/x-date-pickers/src/locales/index.ts +++ b/packages/x-date-pickers/src/locales/index.ts @@ -1,3 +1,4 @@ +export * from './caES'; export * from './nlNL'; export * from './plPL'; export * from './ptBR'; diff --git a/scripts/x-date-pickers-pro.exports.json b/scripts/x-date-pickers-pro.exports.json index 2caddc7d93201..7c4ba1dbd6df9 100644 --- a/scripts/x-date-pickers-pro.exports.json +++ b/scripts/x-date-pickers-pro.exports.json @@ -4,6 +4,7 @@ { "name": "BaseMultiInputFieldProps", "kind": "Interface" }, { "name": "BaseSingleInputFieldProps", "kind": "Interface" }, { "name": "beBY", "kind": "Variable" }, + { "name": "caES", "kind": "Variable" }, { "name": "clockClasses", "kind": "Variable" }, { "name": "ClockClasses", "kind": "Interface" }, { "name": "ClockClassKey", "kind": "TypeAlias" }, diff --git a/scripts/x-date-pickers.exports.json b/scripts/x-date-pickers.exports.json index 03372da30354d..e5f0f03797527 100644 --- a/scripts/x-date-pickers.exports.json +++ b/scripts/x-date-pickers.exports.json @@ -3,6 +3,7 @@ { "name": "AdapterUnits", "kind": "TypeAlias" }, { "name": "BaseSingleInputFieldProps", "kind": "Interface" }, { "name": "beBY", "kind": "Variable" }, + { "name": "caES", "kind": "Variable" }, { "name": "clockClasses", "kind": "Variable" }, { "name": "ClockClasses", "kind": "Interface" }, { "name": "ClockClassKey", "kind": "TypeAlias" }, From 0cb20a005556b2e31ead8eddd9f4b8942384deab Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 27 Apr 2023 18:56:55 +0300 Subject: [PATCH 27/80] [pickers] Add new `DigitalClock` desktop time picking experience (#7958) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Co-authored-by: José Rodolfo Freitas Co-authored-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> --- .../digital-clock/DigitalClockAmPm.js | 67 ++ .../digital-clock/DigitalClockAmPm.tsx | 67 ++ .../digital-clock/DigitalClockBasic.js | 21 + .../digital-clock/DigitalClockBasic.tsx | 21 + .../DigitalClockBasic.tsx.preview | 6 + .../digital-clock/DigitalClockFormProps.js | 47 ++ .../digital-clock/DigitalClockFormProps.tsx | 47 ++ .../digital-clock/DigitalClockSkipDisabled.js | 42 ++ .../DigitalClockSkipDisabled.tsx | 43 ++ .../DigitalClockSkipDisabled.tsx.preview | 14 + .../digital-clock/DigitalClockTimeStep.js | 24 + .../digital-clock/DigitalClockTimeStep.tsx | 24 + .../DigitalClockTimeStep.tsx.preview | 9 + .../digital-clock/DigitalClockValue.js | 49 ++ .../digital-clock/DigitalClockValue.tsx | 49 ++ .../digital-clock/DigitalClockViews.js | 29 + .../digital-clock/DigitalClockViews.tsx | 29 + .../DigitalClockViews.tsx.preview | 9 + .../digital-clock/digital-clock.md | 83 +++ docs/data/date-pickers/localization/data.json | 52 +- .../date-pickers/time-clock/time-clock.md | 6 +- .../date-pickers/time-picker/time-picker.md | 9 +- .../date-pickers/validation/validation.md | 8 +- .../MobileKeyboardView.js | 1 + .../migration-pickers-v5.md | 8 +- docs/data/pages.ts | 10 + .../api/date-pickers/desktop-time-picker.json | 26 +- .../pages/x/api/date-pickers/digital-clock.js | 23 + .../x/api/date-pickers/digital-clock.json | 69 +++ docs/pages/x/api/date-pickers/index.md | 2 + .../multi-section-digital-clock.js | 23 + .../multi-section-digital-clock.json | 95 +++ docs/pages/x/api/date-pickers/time-clock.json | 13 +- .../api/date-pickers/time-picker-toolbar.json | 2 +- .../pages/x/api/date-pickers/time-picker.json | 26 +- .../x/react-date-pickers/digital-clock.js | 7 + .../date-pickers/desktop-time-picker.json | 5 + .../api-docs/date-pickers/digital-clock.json | 48 ++ .../multi-section-digital-clock.json | 38 ++ .../api-docs/date-pickers/time-clock.json | 18 +- .../api-docs/date-pickers/time-picker.json | 5 + .../dateRangeViewRenderers.tsx | 4 +- .../useDesktopRangePicker.tsx | 4 +- .../useDesktopRangePicker.types.ts | 18 +- .../hooks/useEnrichedRangePickerFieldProps.ts | 19 +- .../useMobileRangePicker.tsx | 4 +- .../useMobileRangePicker.types.ts | 14 +- .../useStaticRangePicker.tsx | 4 +- .../useStaticRangePicker.types.ts | 14 +- .../src/DatePicker/DatePickerToolbar.tsx | 4 +- .../src/DateTimePicker/shared.tsx | 9 +- .../DesktopTimePicker/DesktopTimePicker.tsx | 77 ++- .../DesktopTimePicker.types.ts | 29 +- .../tests/DesktopTimePicker.test.tsx | 160 +++++ .../describes.DesktopTimePicker.test.tsx | 30 +- .../src/DigitalClock/DigitalClock.tsx | 464 ++++++++++++++ .../src/DigitalClock/DigitalClock.types.ts | 57 ++ .../src/DigitalClock/digitalClockClasses.ts | 23 + .../x-date-pickers/src/DigitalClock/index.ts | 10 + .../tests/describes.DigitalClock.test.tsx | 75 +++ .../src/MobileTimePicker/MobileTimePicker.tsx | 9 +- .../MobileTimePicker.types.ts | 29 +- .../MultiSectionDigitalClock.tsx | 579 ++++++++++++++++++ .../MultiSectionDigitalClock.types.ts | 69 +++ .../MultiSectionDigitalClock.utils.ts | 98 +++ .../MultiSectionDigitalClockSection.tsx | 201 ++++++ .../src/MultiSectionDigitalClock/index.ts | 21 + .../multiSectionDigitalClockClasses.ts | 16 + .../multiSectionDigitalClockSectionClasses.ts | 18 + ...escribes.MultiSectionDigitalClock.test.tsx | 90 +++ .../src/PickersLayout/PickersLayout.tsx | 14 +- .../src/PickersLayout/PickersLayout.types.ts | 29 +- .../src/PickersLayout/usePickerLayout.tsx | 13 +- .../src/StaticTimePicker/StaticTimePicker.tsx | 9 +- .../StaticTimePicker.types.ts | 2 +- .../src/TimeClock/TimeClock.tsx | 42 +- .../src/TimeClock/TimeClock.types.ts | 75 +-- .../src/TimePicker/TimePicker.tsx | 30 +- .../src/TimePicker/TimePicker.types.ts | 7 +- .../src/TimePicker/TimePickerToolbar.tsx | 12 +- .../x-date-pickers/src/TimePicker/shared.tsx | 34 +- .../dateViewRenderers/dateViewRenderers.tsx | 5 +- packages/x-date-pickers/src/index.ts | 4 + .../internals/components/PickersToolbar.tsx | 8 +- .../src/internals/constants/dimensions.ts | 1 + .../src/internals/demo/DemoContainer.tsx | 7 +- .../internals/hooks/date-helpers-hooks.tsx | 6 +- .../useDesktopPicker/useDesktopPicker.tsx | 6 +- .../useDesktopPicker.types.ts | 26 +- .../src/internals/hooks/useIsLandscape.tsx | 4 +- .../hooks/useMobilePicker/useMobilePicker.tsx | 5 +- .../useMobilePicker/useMobilePicker.types.ts | 26 +- .../internals/hooks/usePicker/usePicker.ts | 5 +- .../hooks/usePicker/usePicker.types.ts | 11 +- .../hooks/usePicker/usePickerLayoutProps.ts | 15 +- .../hooks/usePicker/usePickerViews.ts | 22 +- .../hooks/useStaticPicker/useStaticPicker.tsx | 5 +- .../useStaticPicker/useStaticPicker.types.ts | 15 +- .../src/internals/hooks/useViews.tsx | 66 +- .../src/internals/models/common.ts | 6 + .../src/internals/models/index.ts | 1 + .../models/props/basePickerProps.tsx | 12 +- .../src/internals/models/props/clock.ts | 103 ++++ .../src/internals/models/props/tabs.ts | 4 +- .../src/internals/models/props/toolbar.ts | 6 +- .../src/internals/utils/time-utils.ts | 14 +- .../src/internals/utils/views.ts | 5 +- packages/x-date-pickers/src/locales/beBY.ts | 10 +- packages/x-date-pickers/src/locales/caES.ts | 7 +- packages/x-date-pickers/src/locales/csCZ.ts | 9 +- packages/x-date-pickers/src/locales/daDK.ts | 7 +- packages/x-date-pickers/src/locales/deDE.ts | 7 +- packages/x-date-pickers/src/locales/enUS.ts | 3 + packages/x-date-pickers/src/locales/esES.ts | 7 +- packages/x-date-pickers/src/locales/faIR.ts | 13 +- packages/x-date-pickers/src/locales/fiFI.ts | 7 +- packages/x-date-pickers/src/locales/frFR.ts | 7 +- packages/x-date-pickers/src/locales/heIL.ts | 7 +- packages/x-date-pickers/src/locales/huHU.ts | 7 +- packages/x-date-pickers/src/locales/isIS.ts | 13 +- packages/x-date-pickers/src/locales/itIT.ts | 7 +- packages/x-date-pickers/src/locales/jaJP.ts | 7 +- packages/x-date-pickers/src/locales/koKR.ts | 7 +- packages/x-date-pickers/src/locales/kzKZ.ts | 7 +- packages/x-date-pickers/src/locales/nbNO.ts | 13 +- packages/x-date-pickers/src/locales/nlNL.ts | 16 +- packages/x-date-pickers/src/locales/plPL.ts | 13 +- packages/x-date-pickers/src/locales/ptBR.ts | 13 +- packages/x-date-pickers/src/locales/ruRU.ts | 7 +- packages/x-date-pickers/src/locales/svSE.ts | 13 +- packages/x-date-pickers/src/locales/trTR.ts | 13 +- packages/x-date-pickers/src/locales/ukUA.ts | 13 +- packages/x-date-pickers/src/locales/urPK.ts | 13 +- .../src/locales/utils/pickersLocaleTextApi.ts | 4 + packages/x-date-pickers/src/locales/zhCN.ts | 9 +- packages/x-date-pickers/src/models/common.ts | 5 + packages/x-date-pickers/src/models/index.ts | 1 + .../src/tests/describe.types.ts | 9 +- .../testControlledUnControlled.tsx | 21 +- .../describeValue/testPickerActionBar.tsx | 7 +- .../testPickerOpenCloseLifeCycle.tsx | 5 - .../src/themeAugmentation/components.d.ts | 12 + .../src/themeAugmentation/overrides.d.ts | 8 + .../src/themeAugmentation/props.d.ts | 8 + .../themeAugmentation.spec.ts | 62 ++ .../src/timeViewRenderers/index.ts | 6 +- .../timeViewRenderers/timeViewRenderers.tsx | 168 ++++- scripts/x-date-pickers-pro.exports.json | 26 +- scripts/x-date-pickers.exports.json | 26 +- test/utils/pickers-utils.tsx | 12 + 150 files changed, 4153 insertions(+), 460 deletions(-) create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockAmPm.js create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockAmPm.tsx create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockBasic.js create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockBasic.tsx create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockBasic.tsx.preview create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockFormProps.js create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockFormProps.tsx create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.js create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.tsx create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.tsx.preview create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockTimeStep.js create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockTimeStep.tsx create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockTimeStep.tsx.preview create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockValue.js create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockValue.tsx create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockViews.js create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockViews.tsx create mode 100644 docs/data/date-pickers/digital-clock/DigitalClockViews.tsx.preview create mode 100644 docs/data/date-pickers/digital-clock/digital-clock.md create mode 100644 docs/pages/x/api/date-pickers/digital-clock.js create mode 100644 docs/pages/x/api/date-pickers/digital-clock.json create mode 100644 docs/pages/x/api/date-pickers/multi-section-digital-clock.js create mode 100644 docs/pages/x/api/date-pickers/multi-section-digital-clock.json create mode 100644 docs/pages/x/react-date-pickers/digital-clock.js create mode 100644 docs/translations/api-docs/date-pickers/digital-clock.json create mode 100644 docs/translations/api-docs/date-pickers/multi-section-digital-clock.json create mode 100644 packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx create mode 100644 packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx create mode 100644 packages/x-date-pickers/src/DigitalClock/DigitalClock.types.ts create mode 100644 packages/x-date-pickers/src/DigitalClock/digitalClockClasses.ts create mode 100644 packages/x-date-pickers/src/DigitalClock/index.ts create mode 100644 packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx create mode 100644 packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx create mode 100644 packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.types.ts create mode 100644 packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.utils.ts create mode 100644 packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx create mode 100644 packages/x-date-pickers/src/MultiSectionDigitalClock/index.ts create mode 100644 packages/x-date-pickers/src/MultiSectionDigitalClock/multiSectionDigitalClockClasses.ts create mode 100644 packages/x-date-pickers/src/MultiSectionDigitalClock/multiSectionDigitalClockSectionClasses.ts create mode 100644 packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx create mode 100644 packages/x-date-pickers/src/internals/models/props/clock.ts create mode 100644 packages/x-date-pickers/src/models/common.ts diff --git a/docs/data/date-pickers/digital-clock/DigitalClockAmPm.js b/docs/data/date-pickers/digital-clock/DigitalClockAmPm.js new file mode 100644 index 0000000000000..e3194a1d29049 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockAmPm.js @@ -0,0 +1,67 @@ +import * as React from 'react'; +import dayjs from 'dayjs'; +import Typography from '@mui/material/Typography'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockAmPm() { + return ( + + + + + Locale default behavior (enabled for enUS) + + + + + + + + + + + + AM PM enabled + + + + + + + + + + + AM PM disabled + + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockAmPm.tsx b/docs/data/date-pickers/digital-clock/DigitalClockAmPm.tsx new file mode 100644 index 0000000000000..e3194a1d29049 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockAmPm.tsx @@ -0,0 +1,67 @@ +import * as React from 'react'; +import dayjs from 'dayjs'; +import Typography from '@mui/material/Typography'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockAmPm() { + return ( + + + + + Locale default behavior (enabled for enUS) + + + + + + + + + + + + AM PM enabled + + + + + + + + + + + AM PM disabled + + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockBasic.js b/docs/data/date-pickers/digital-clock/DigitalClockBasic.js new file mode 100644 index 0000000000000..0280de136ee2c --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockBasic.js @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockBasic() { + return ( + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockBasic.tsx b/docs/data/date-pickers/digital-clock/DigitalClockBasic.tsx new file mode 100644 index 0000000000000..0280de136ee2c --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockBasic.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockBasic() { + return ( + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockBasic.tsx.preview b/docs/data/date-pickers/digital-clock/DigitalClockBasic.tsx.preview new file mode 100644 index 0000000000000..a7e2d0878a36c --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockBasic.tsx.preview @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/docs/data/date-pickers/digital-clock/DigitalClockFormProps.js b/docs/data/date-pickers/digital-clock/DigitalClockFormProps.js new file mode 100644 index 0000000000000..3ad71c6fe6c65 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockFormProps.js @@ -0,0 +1,47 @@ +import * as React from 'react'; +import dayjs from 'dayjs'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockFormProps() { + return ( + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockFormProps.tsx b/docs/data/date-pickers/digital-clock/DigitalClockFormProps.tsx new file mode 100644 index 0000000000000..3ad71c6fe6c65 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockFormProps.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import dayjs from 'dayjs'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockFormProps() { + return ( + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.js b/docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.js new file mode 100644 index 0000000000000..240a8f343ad86 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.js @@ -0,0 +1,42 @@ +import * as React from 'react'; +import dayjs from 'dayjs'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +const shouldDisableTime = (value, view) => { + const hour = value.hour(); + if (view === 'hours') { + return hour < 9 || hour > 13; + } + if (view === 'minutes') { + const minute = value.minute(); + return minute > 20 && hour === 13; + } + return false; +}; + +export default function DigitalClockSkipDisabled() { + return ( + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.tsx b/docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.tsx new file mode 100644 index 0000000000000..cd8c8127fc04b --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import dayjs, { Dayjs } from 'dayjs'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; +import { TimeView } from '@mui/x-date-pickers/models'; + +const shouldDisableTime = (value: Dayjs, view: TimeView) => { + const hour = value.hour(); + if (view === 'hours') { + return hour < 9 || hour > 13; + } + if (view === 'minutes') { + const minute = value.minute(); + return minute > 20 && hour === 13; + } + return false; +}; + +export default function DigitalClockSkipDisabled() { + return ( + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.tsx.preview b/docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.tsx.preview new file mode 100644 index 0000000000000..cae247f4015f6 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockSkipDisabled.tsx.preview @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/docs/data/date-pickers/digital-clock/DigitalClockTimeStep.js b/docs/data/date-pickers/digital-clock/DigitalClockTimeStep.js new file mode 100644 index 0000000000000..2889cf421b017 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockTimeStep.js @@ -0,0 +1,24 @@ +import * as React from 'react'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockTimeStep() { + return ( + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockTimeStep.tsx b/docs/data/date-pickers/digital-clock/DigitalClockTimeStep.tsx new file mode 100644 index 0000000000000..2889cf421b017 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockTimeStep.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockTimeStep() { + return ( + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockTimeStep.tsx.preview b/docs/data/date-pickers/digital-clock/DigitalClockTimeStep.tsx.preview new file mode 100644 index 0000000000000..4a15774bbd2ba --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockTimeStep.tsx.preview @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/docs/data/date-pickers/digital-clock/DigitalClockValue.js b/docs/data/date-pickers/digital-clock/DigitalClockValue.js new file mode 100644 index 0000000000000..8ab7c3bb66069 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockValue.js @@ -0,0 +1,49 @@ +import * as React from 'react'; +import dayjs from 'dayjs'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockValue() { + const [value, setValue] = React.useState(dayjs('2022-04-17T15:30')); + + return ( + + + + + + + + setValue(newValue)} + /> + + + + + + + + setValue(newValue)} + /> + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockValue.tsx b/docs/data/date-pickers/digital-clock/DigitalClockValue.tsx new file mode 100644 index 0000000000000..882d0bd52b0bf --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockValue.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import dayjs, { Dayjs } from 'dayjs'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockValue() { + const [value, setValue] = React.useState(dayjs('2022-04-17T15:30')); + + return ( + + + + + + + + setValue(newValue)} + /> + + + + + + + + setValue(newValue)} + /> + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockViews.js b/docs/data/date-pickers/digital-clock/DigitalClockViews.js new file mode 100644 index 0000000000000..ae39d36e2d1f2 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockViews.js @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockViews() { + return ( + + + + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockViews.tsx b/docs/data/date-pickers/digital-clock/DigitalClockViews.tsx new file mode 100644 index 0000000000000..ae39d36e2d1f2 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockViews.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; + +export default function DigitalClockViews() { + return ( + + + + + + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/digital-clock/DigitalClockViews.tsx.preview b/docs/data/date-pickers/digital-clock/DigitalClockViews.tsx.preview new file mode 100644 index 0000000000000..8afb427adfef1 --- /dev/null +++ b/docs/data/date-pickers/digital-clock/DigitalClockViews.tsx.preview @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/docs/data/date-pickers/digital-clock/digital-clock.md b/docs/data/date-pickers/digital-clock/digital-clock.md new file mode 100644 index 0000000000000..5a07a64eb7f4d --- /dev/null +++ b/docs/data/date-pickers/digital-clock/digital-clock.md @@ -0,0 +1,83 @@ +--- +product: date-pickers +title: React Digital Clock component +components: DigitalClock, MultiSectionDigitalClock +githubLabel: 'component: TimePicker' +packageName: '@mui/x-date-pickers' +--- + +# Digital Clock + +

The Digital Clock components let the user select a time without any input or popper / modal.

+ +## Description + +There are two component versions for different cases. The `DigitalClock` handles selection of a single time instance in one step, just like a `select` component. The `MultiSectionDigitalClock` allows selecting time using separate sections for separate views. + +The `DigitalClock` is more appropriate when there is a limited amount of time options needed, while the `MultiSectionDigitalClock` is suited for cases when a more granular time selection is needed. + +## Basic usage + +{{"demo": "DigitalClockBasic.js"}} + +## Uncontrolled vs. Controlled + +The components can be uncontrolled or controlled. + +{{"demo": "DigitalClockValue.js"}} + +## Form props + +The components can be disabled or read-only. + +{{"demo": "DigitalClockFormProps.js"}} + +## Views + +The `MultiSectionDigitalClock` component can contain three views: `hours`, `minutes`, and `seconds`. +By default, only the `hours` and `minutes` views are enabled. + +You can customize the enabled views using the `views` prop. +Views will appear in the order they're included in the `views` array. + +{{"demo": "DigitalClockViews.js"}} + +## 12h/24h format + +The components use the hour format of the locale's time setting, i.e. the 12-hour or 24-hour format. + +You can force a specific format using the `ampm` prop. + +You can find more information about 12h/24h format in the [Date localization page](/x/react-date-pickers/adapters-locale/#12h-24h-format). + +{{"demo": "DigitalClockAmPm.js"}} + +## Time steps + +By default, the components list the time options in the following way: + +- `DigitalClock` in `30` minutes intervals; +- `MultiSectionDigitalClock` component in `5` unit (`minutes` or `seconds`) intervals; + +You can set the desired interval using the `timeStep` and `timeSteps` props. +The prop accepts: + +- The `DigitalClock` component accepts a `number` value `timeStep` prop; +- The `MultiSectionDigitalClock` component accepts a `timeSteps` prop with `number` values for `hours`, `minutes`, or `seconds` units; + +{{"demo": "DigitalClockTimeStep.js"}} + +## Skip rendering disabled options + +With the `skipDisabled` prop, the components don't render options that are not available to the user (e.g. through `minTime`, `maxTime`, `shouldDisabledTime` etc.). + +The following example combines these properties to customize which options are rendered. + +- The first component does not show options before `9:00` (the value of `minTime`). +- The second one shows options between `09:00` and `13:20` thanks to `shouldDisableTime`. + +{{"demo": "DigitalClockSkipDisabled.js"}} + +## Validation + +You can find the documentation in the [Validation page](/x/react-date-pickers/validation/). diff --git a/docs/data/date-pickers/localization/data.json b/docs/data/date-pickers/localization/data.json index 178dcad0c53b8..b34a802d8b664 100644 --- a/docs/data/date-pickers/localization/data.json +++ b/docs/data/date-pickers/localization/data.json @@ -4,7 +4,7 @@ "importName": "beBY", "localeName": "Belarusian", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/beBY.ts" }, { @@ -12,7 +12,7 @@ "importName": "caES", "localeName": "Catalan", "missingKeysCount": 0, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/caES.ts" }, { @@ -20,7 +20,7 @@ "importName": "zhCN", "localeName": "Chinese (Simplified)", "missingKeysCount": 0, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/zhCN.ts" }, { @@ -28,7 +28,7 @@ "importName": "csCZ", "localeName": "Czech", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/csCZ.ts" }, { @@ -36,7 +36,7 @@ "importName": "daDK", "localeName": "Danish", "missingKeysCount": 0, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/daDK.ts" }, { @@ -44,7 +44,7 @@ "importName": "nlNL", "localeName": "Dutch", "missingKeysCount": 0, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/nlNL.ts" }, { @@ -52,7 +52,7 @@ "importName": "fiFI", "localeName": "Finnish", "missingKeysCount": 12, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/fiFI.ts" }, { @@ -60,7 +60,7 @@ "importName": "frFR", "localeName": "French", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/frFR.ts" }, { @@ -68,7 +68,7 @@ "importName": "deDE", "localeName": "German", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/deDE.ts" }, { @@ -76,7 +76,7 @@ "importName": "heIL", "localeName": "Hebrew", "missingKeysCount": 0, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/heIL.ts" }, { @@ -84,7 +84,7 @@ "importName": "huHU", "localeName": "Hungarian", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/huHU.ts" }, { @@ -92,7 +92,7 @@ "importName": "isIS", "localeName": "Icelandic", "missingKeysCount": 12, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/isIS.ts" }, { @@ -100,7 +100,7 @@ "importName": "itIT", "localeName": "Italian", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/itIT.ts" }, { @@ -108,7 +108,7 @@ "importName": "jaJP", "localeName": "Japanese", "missingKeysCount": 0, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/jaJP.ts" }, { @@ -116,7 +116,7 @@ "importName": "kzKZ", "localeName": "Kazakh", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/kzKZ.ts" }, { @@ -124,7 +124,7 @@ "importName": "koKR", "localeName": "Korean", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/koKR.ts" }, { @@ -132,7 +132,7 @@ "importName": "nbNO", "localeName": "Norwegian (bokmål)", "missingKeysCount": 8, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/nbNO.ts" }, { @@ -140,7 +140,7 @@ "importName": "faIR", "localeName": "Persian", "missingKeysCount": 0, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/faIR.ts" }, { @@ -148,7 +148,7 @@ "importName": "plPL", "localeName": "Polish", "missingKeysCount": 8, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/plPL.ts" }, { @@ -156,7 +156,7 @@ "importName": "ptBR", "localeName": "Portuguese (Brazil)", "missingKeysCount": 0, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/ptBR.ts" }, { @@ -164,7 +164,7 @@ "importName": "ruRU", "localeName": "Russian", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/ruRU.ts" }, { @@ -172,7 +172,7 @@ "importName": "esES", "localeName": "Spanish", "missingKeysCount": 0, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/esES.ts" }, { @@ -180,7 +180,7 @@ "importName": "svSE", "localeName": "Swedish", "missingKeysCount": 8, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/svSE.ts" }, { @@ -188,7 +188,7 @@ "importName": "trTR", "localeName": "Turkish", "missingKeysCount": 5, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/trTR.ts" }, { @@ -196,7 +196,7 @@ "importName": "ukUA", "localeName": "Ukrainian", "missingKeysCount": 1, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/ukUA.ts" }, { @@ -204,7 +204,7 @@ "importName": "urPK", "localeName": "Urdu (Pakistan)", "missingKeysCount": 8, - "totalKeysCount": 35, + "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/urPK.ts" } ] diff --git a/docs/data/date-pickers/time-clock/time-clock.md b/docs/data/date-pickers/time-clock/time-clock.md index 24e8433f38011..1a4b1169acb72 100644 --- a/docs/data/date-pickers/time-clock/time-clock.md +++ b/docs/data/date-pickers/time-clock/time-clock.md @@ -38,10 +38,14 @@ Views will appear in the order they're included in the `views` array. ## 12h/24h format -By default, the component uses the hour format of the locale's time setting, i.e. the 12-hour or 24-hour format. +The component uses the hour format of the locale's time setting, i.e. the 12-hour or 24-hour format. You can force a specific format using the `ampm` prop. You can find more information about 12h/24h format in the [Date localization page](/x/react-date-pickers/adapters-locale/#12h-24h-format). {{"demo": "TimeClockAmPm.js"}} + +## Validation + +You can find the documentation in the [Validation page](/x/react-date-pickers/validation/). diff --git a/docs/data/date-pickers/time-picker/time-picker.md b/docs/data/date-pickers/time-picker/time-picker.md index c765fdf79c62d..9a5f6051c0ca9 100644 --- a/docs/data/date-pickers/time-picker/time-picker.md +++ b/docs/data/date-pickers/time-picker/time-picker.md @@ -11,24 +11,19 @@ materialDesign: https://m2.material.io/components/time-pickers

The Time Picker component lets the user select a time.

-:::info -The component by default currently does not ship with **time** picker view experience on **desktop**. -It was a conscious decision and a first step towards having a more user friendly desktop experience [discussed in #4483](https://github.com/mui/mui-x/issues/4483). -If a desktop view experience is essential, you can revert to it by following the suggestion [in the migration guide](/x/migration/migration-pickers-v5/#stop-rendering-a-clock-on-desktop). -::: - ## Basic usage {{"demo": "BasicTimePicker.js"}} ## Component composition -The component is built using the `TimeField` for the keyboard editing and the `TimeClock` for the view editing. +The component is built using the `TimeField` for the keyboard editing, the `DigitalClock` for the desktop view editing, and the `TimeClock` for the mobile view editing. All the documented props of those two components can also be passed to the Time Picker component. Check-out their documentation page for more information: - [Time Field](/x/react-date-pickers/time-field/) +- [Digital Clock](/x/react-date-pickers/digital-clock/) - [Time Clock](/x/react-date-pickers/time-clock/) ## Uncontrolled vs. Controlled diff --git a/docs/data/date-pickers/validation/validation.md b/docs/data/date-pickers/validation/validation.md index c5a1abfa9a660..bd9bce42bf611 100644 --- a/docs/data/date-pickers/validation/validation.md +++ b/docs/data/date-pickers/validation/validation.md @@ -18,7 +18,7 @@ The validation props are showcased for each type of picker component using the r But the same props are available on: -- all the other variants of this picker +- all the other variants of this picker; For example—the validation props showcased with `DatePicker` are also available on: @@ -26,9 +26,13 @@ But the same props are available on: - `MobileDatePicker` - `StaticDatePicker` -- the field used by this picker +- the field used by this picker; For example—the validation props showcased with `DatePicker` are also available on `DateField`. + +- the view components; + + For example—the validation props showcased with `TimePicker` are also available on `TimeClock` and `DigitalClock`. ::: ## Invalid values feedback diff --git a/docs/data/migration/migration-pickers-v5/MobileKeyboardView.js b/docs/data/migration/migration-pickers-v5/MobileKeyboardView.js index a55ff54f4649c..b8b0683c4e9de 100644 --- a/docs/data/migration/migration-pickers-v5/MobileKeyboardView.js +++ b/docs/data/migration/migration-pickers-v5/MobileKeyboardView.js @@ -153,6 +153,7 @@ LayoutWithKeyboardView.propTypes = { view: PropTypes.oneOf([ 'day', 'hours', + 'meridiem', 'minutes', 'month', 'seconds', diff --git a/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md b/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md index beb6d4eeb51b3..7e792ec088924 100644 --- a/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md +++ b/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md @@ -112,13 +112,13 @@ import { DateTime } from 'luxon'; ### Stop rendering a clock on desktop -In desktop mode, the `DateTimePicker` and `TimePicker` components will not display the clock. -This is the first step towards moving to a [better implementation](https://github.com/mui/mui-x/issues/4483). +In desktop mode, the `DateTimePicker` and `TimePicker` components will no longer render the [`TimeClock`](/x/react-date-pickers/time-clock/) component. +The `DateTimePicker` component currently has no replacement, but on `TimePicker` a new [`DigitalClock`](/x/react-date-pickers/digital-clock/) component has been introduced instead. The behavior on mobile mode is still the same. If you were relying on Clock Picker in desktop mode for tests—make sure to check [testing caveats](/x/react-date-pickers/base-concepts/#testing-caveats) to choose the best replacement for it. -You can manually re-enable the clock using the new `viewRenderers` prop. -The code below enables the `Clock` UI on all the `DesktopTimePicker` and `DesktopDateTimePicker` in your application. +You can manually re-enable the previous clock component using the new `viewRenderers` prop. +The code below enables the `TimeClock` UI on all the `DesktopTimePicker` and `DesktopDateTimePicker` in your application. Take a look at the [default props via theme documentation](/material-ui/customization/theme-components/#theme-default-props) for more information. diff --git a/docs/data/pages.ts b/docs/data/pages.ts index 35c8373b80645..1ee6a4fe0d335 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -199,6 +199,11 @@ const pages: MuiPage[] = [ { pathname: '/x/react-date-pickers/time-picker', title: 'Time Picker' }, { pathname: '/x/react-date-pickers/time-field', title: 'Time Field', newFeature: true }, { pathname: '/x/react-date-pickers/time-clock', title: 'Time Clock' }, + { + pathname: '/x/react-date-pickers/digital-clock', + title: 'Digital Clock', + newFeature: true, + }, ], }, { @@ -338,6 +343,7 @@ const pages: MuiPage[] = [ title: 'DesktopDateTimePicker', }, { pathname: '/x/api/date-pickers/desktop-time-picker', title: 'DesktopTimePicker' }, + { pathname: '/x/api/date-pickers/digital-clock', title: 'DigitalClock' }, { pathname: '/x/api/date-pickers/localization-provider', title: 'LocalizationProvider' }, { pathname: '/x/api/date-pickers/mobile-date-picker', title: 'MobileDatePicker' }, { @@ -351,6 +357,10 @@ const pages: MuiPage[] = [ }, { pathname: '/x/api/date-pickers/mobile-time-picker', title: 'MobileTimePicker' }, { pathname: '/x/api/date-pickers/month-calendar', title: 'MonthCalendar' }, + { + pathname: '/x/api/date-pickers/multi-section-digital-clock', + title: 'MultiSectionDigitalClock', + }, { pathname: '/x/api/date-pickers/multi-input-date-range-field', title: 'MultiInputDateRangeField', diff --git a/docs/pages/x/api/date-pickers/desktop-time-picker.json b/docs/pages/x/api/date-pickers/desktop-time-picker.json index 1b13b6dfda587..088e97a37ff47 100644 --- a/docs/pages/x/api/date-pickers/desktop-time-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-time-picker.json @@ -50,7 +50,7 @@ "openTo": { "type": { "name": "enum", - "description": "'hours'
| 'minutes'
| 'seconds'" + "description": "'hours'
| 'meridiem'
| 'minutes'
| 'seconds'" } }, "orientation": { @@ -68,6 +68,7 @@ "deprecationInfo": "Consider using shouldDisableTime." }, "shouldDisableTime": { "type": { "name": "func" } }, + "skipDisabled": { "type": { "name": "bool" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" }, "sx": { @@ -76,15 +77,26 @@ "description": "Array<func
| object
| bool>
| func
| object" } }, + "thresholdToRenderTimeInASingleColumn": { "type": { "name": "number" }, "default": "24" }, + "timeSteps": { + "type": { + "name": "shape", + "description": "{ hours?: number, minutes?: number, seconds?: number }" + }, + "default": "{ hours: 1, minutes: 5, seconds: 5 }" + }, "value": { "type": { "name": "any" } }, "view": { "type": { "name": "enum", - "description": "'hours'
| 'minutes'
| 'seconds'" + "description": "'hours'
| 'meridiem'
| 'minutes'
| 'seconds'" } }, "viewRenderers": { - "type": { "name": "shape", "description": "{ hours?: func, minutes?: func, seconds?: func }" } + "type": { + "name": "shape", + "description": "{ hours?: func, meridiem?: func, minutes?: func, seconds?: func }" + } }, "views": { "type": { @@ -104,6 +116,14 @@ "default": "TrapFocus from @mui/material", "type": { "name": "elementType" } }, + "DigitalClockItem": { + "default": "MenuItem from '@mui/material'", + "type": { "name": "elementType" } + }, + "DigitalClockSectionItem": { + "default": "MenuItem from '@mui/material'", + "type": { "name": "elementType" } + }, "Field": { "type": { "name": "elementType" } }, "InputAdornment": { "default": "InputAdornment", "type": { "name": "elementType" } }, "Layout": { "type": { "name": "elementType" } }, diff --git a/docs/pages/x/api/date-pickers/digital-clock.js b/docs/pages/x/api/date-pickers/digital-clock.js new file mode 100644 index 0000000000000..1fbd938a90ee6 --- /dev/null +++ b/docs/pages/x/api/date-pickers/digital-clock.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docsx/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './digital-clock.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docsx/translations/api-docs/date-pickers', + false, + /\.\/digital-clock(-[a-z]{2})?\.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/x/api/date-pickers/digital-clock.json b/docs/pages/x/api/date-pickers/digital-clock.json new file mode 100644 index 0000000000000..35bd08fce8af9 --- /dev/null +++ b/docs/pages/x/api/date-pickers/digital-clock.json @@ -0,0 +1,69 @@ +{ + "props": { + "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, + "autoFocus": { "type": { "name": "bool" } }, + "classes": { "type": { "name": "object" } }, + "components": { + "type": { "name": "object" }, + "default": "{}", + "deprecated": true, + "deprecationInfo": "Please use slots." + }, + "componentsProps": { + "type": { "name": "object" }, + "default": "{}", + "deprecated": true, + "deprecationInfo": "Please use slotProps." + }, + "defaultValue": { "type": { "name": "any" } }, + "disabled": { "type": { "name": "bool" } }, + "disableFuture": { "type": { "name": "bool" } }, + "disableIgnoringDatePartForTimeValidation": { "type": { "name": "bool" } }, + "disablePast": { "type": { "name": "bool" } }, + "focusedView": { "type": { "name": "enum", "description": "'hours'" } }, + "maxTime": { "type": { "name": "any" } }, + "minTime": { "type": { "name": "any" } }, + "minutesStep": { "type": { "name": "number" }, "default": "1" }, + "onChange": { "type": { "name": "func" } }, + "onFocusedViewChange": { "type": { "name": "func" } }, + "onViewChange": { "type": { "name": "func" } }, + "openTo": { "type": { "name": "enum", "description": "'hours'" } }, + "readOnly": { "type": { "name": "bool" } }, + "shouldDisableClock": { + "type": { "name": "func" }, + "deprecated": true, + "deprecationInfo": "Consider using shouldDisableTime." + }, + "shouldDisableTime": { "type": { "name": "func" } }, + "skipDisabled": { "type": { "name": "bool" } }, + "slotProps": { "type": { "name": "object" }, "default": "{}" }, + "slots": { "type": { "name": "object" }, "default": "{}" }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + } + }, + "timeStep": { "type": { "name": "number" }, "default": "30" }, + "value": { "type": { "name": "any" } }, + "view": { "type": { "name": "enum", "description": "'hours'" } }, + "views": { "type": { "name": "arrayOf", "description": "Array<'hours'>" } } + }, + "slots": { + "DigitalClockItem": { + "default": "MenuItem from '@mui/material'", + "type": { "name": "elementType" } + } + }, + "name": "DigitalClock", + "styles": { "classes": ["root", "list", "item"], "globalClasses": {}, "name": "MuiDigitalClock" }, + "spread": false, + "forwardsRefTo": "HTMLDivElement", + "filename": "/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx", + "inheritance": null, + "demos": "", + "packages": [ + { "packageName": "@mui/x-date-pickers-pro", "componentName": "DigitalClock" }, + { "packageName": "@mui/x-date-pickers", "componentName": "DigitalClock" } + ] +} diff --git a/docs/pages/x/api/date-pickers/index.md b/docs/pages/x/api/date-pickers/index.md index 7ae8f4e657928..0ff78f7890bf4 100644 --- a/docs/pages/x/api/date-pickers/index.md +++ b/docs/pages/x/api/date-pickers/index.md @@ -54,6 +54,8 @@ ### Clocks - [TimeClock](/x/api/date-pickers/time-clock/) +- [DigitalClock](/x/api/date-pickers/digital-clock/) +- [MultiSectionDigitalClock](/x/api/date-pickers/multi-section-digital-clock/) ### Toolbars diff --git a/docs/pages/x/api/date-pickers/multi-section-digital-clock.js b/docs/pages/x/api/date-pickers/multi-section-digital-clock.js new file mode 100644 index 0000000000000..abd631204d6dd --- /dev/null +++ b/docs/pages/x/api/date-pickers/multi-section-digital-clock.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docsx/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './multi-section-digital-clock.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docsx/translations/api-docs/date-pickers', + false, + /\.\/multi-section-digital-clock(-[a-z]{2})?\.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/x/api/date-pickers/multi-section-digital-clock.json b/docs/pages/x/api/date-pickers/multi-section-digital-clock.json new file mode 100644 index 0000000000000..e45abddd6340f --- /dev/null +++ b/docs/pages/x/api/date-pickers/multi-section-digital-clock.json @@ -0,0 +1,95 @@ +{ + "props": { + "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, + "autoFocus": { "type": { "name": "bool" } }, + "classes": { "type": { "name": "object" } }, + "components": { + "type": { "name": "object" }, + "default": "{}", + "deprecated": true, + "deprecationInfo": "Please use slots." + }, + "componentsProps": { + "type": { "name": "object" }, + "default": "{}", + "deprecated": true, + "deprecationInfo": "Please use slotProps." + }, + "defaultValue": { "type": { "name": "any" } }, + "disabled": { "type": { "name": "bool" } }, + "disableFuture": { "type": { "name": "bool" } }, + "disableIgnoringDatePartForTimeValidation": { "type": { "name": "bool" } }, + "disablePast": { "type": { "name": "bool" } }, + "focusedView": { + "type": { + "name": "enum", + "description": "'hours'
| 'meridiem'
| 'minutes'
| 'seconds'" + } + }, + "maxTime": { "type": { "name": "any" } }, + "minTime": { "type": { "name": "any" } }, + "minutesStep": { "type": { "name": "number" }, "default": "1" }, + "onChange": { "type": { "name": "func" } }, + "onFocusedViewChange": { "type": { "name": "func" } }, + "onViewChange": { "type": { "name": "func" } }, + "openTo": { + "type": { + "name": "enum", + "description": "'hours'
| 'meridiem'
| 'minutes'
| 'seconds'" + } + }, + "readOnly": { "type": { "name": "bool" } }, + "shouldDisableClock": { + "type": { "name": "func" }, + "deprecated": true, + "deprecationInfo": "Consider using shouldDisableTime." + }, + "shouldDisableTime": { "type": { "name": "func" } }, + "skipDisabled": { "type": { "name": "bool" } }, + "slotProps": { "type": { "name": "object" }, "default": "{}" }, + "slots": { "type": { "name": "object" }, "default": "{}" }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + } + }, + "timeSteps": { + "type": { + "name": "shape", + "description": "{ hours?: number, minutes?: number, seconds?: number }" + }, + "default": "{ hours: 1, minutes: 5, seconds: 5 }" + }, + "value": { "type": { "name": "any" } }, + "view": { + "type": { + "name": "enum", + "description": "'hours'
| 'meridiem'
| 'minutes'
| 'seconds'" + } + }, + "views": { + "type": { + "name": "arrayOf", + "description": "Array<'hours'
| 'meridiem'
| 'minutes'
| 'seconds'>" + } + } + }, + "slots": { + "DigitalClockSectionItem": { + "default": "MenuItem from '@mui/material'", + "type": { "name": "elementType" } + } + }, + "name": "MultiSectionDigitalClock", + "styles": { "classes": ["root"], "globalClasses": {}, "name": "MuiMultiSectionDigitalClock" }, + "spread": false, + "forwardsRefTo": "HTMLDivElement", + "filename": "/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx", + "inheritance": null, + "demos": "", + "packages": [ + { "packageName": "@mui/x-date-pickers-pro", "componentName": "MultiSectionDigitalClock" }, + { "packageName": "@mui/x-date-pickers", "componentName": "MultiSectionDigitalClock" } + ] +} diff --git a/docs/pages/x/api/date-pickers/time-clock.json b/docs/pages/x/api/date-pickers/time-clock.json index 8afa651379151..4ae1f525df4c7 100644 --- a/docs/pages/x/api/date-pickers/time-clock.json +++ b/docs/pages/x/api/date-pickers/time-clock.json @@ -21,17 +21,23 @@ "disableFuture": { "type": { "name": "bool" } }, "disableIgnoringDatePartForTimeValidation": { "type": { "name": "bool" } }, "disablePast": { "type": { "name": "bool" } }, + "focusedView": { + "type": { + "name": "enum", + "description": "'hours'
| 'minutes'
| 'seconds'" + } + }, "maxTime": { "type": { "name": "any" } }, "minTime": { "type": { "name": "any" } }, "minutesStep": { "type": { "name": "number" }, "default": "1" }, "onChange": { "type": { "name": "func" } }, + "onFocusedViewChange": { "type": { "name": "func" } }, "onViewChange": { "type": { "name": "func" } }, "openTo": { "type": { "name": "enum", "description": "'hours'
| 'minutes'
| 'seconds'" - }, - "default": "'hours'" + } }, "readOnly": { "type": { "name": "bool" } }, "shouldDisableClock": { @@ -59,8 +65,7 @@ "type": { "name": "arrayOf", "description": "Array<'hours'
| 'minutes'
| 'seconds'>" - }, - "default": "['hours', 'minutes']" + } } }, "slots": { diff --git a/docs/pages/x/api/date-pickers/time-picker-toolbar.json b/docs/pages/x/api/date-pickers/time-picker-toolbar.json index 0caae8dae2b8a..c8d74c4d58e45 100644 --- a/docs/pages/x/api/date-pickers/time-picker-toolbar.json +++ b/docs/pages/x/api/date-pickers/time-picker-toolbar.json @@ -4,7 +4,7 @@ "view": { "type": { "name": "enum", - "description": "'hours'
| 'minutes'
| 'seconds'" + "description": "'hours'
| 'meridiem'
| 'minutes'
| 'seconds'" }, "required": true }, diff --git a/docs/pages/x/api/date-pickers/time-picker.json b/docs/pages/x/api/date-pickers/time-picker.json index 40dac8133db4e..e4eb4001f3a9e 100644 --- a/docs/pages/x/api/date-pickers/time-picker.json +++ b/docs/pages/x/api/date-pickers/time-picker.json @@ -54,7 +54,7 @@ "openTo": { "type": { "name": "enum", - "description": "'hours'
| 'minutes'
| 'seconds'" + "description": "'hours'
| 'meridiem'
| 'minutes'
| 'seconds'" } }, "orientation": { @@ -72,6 +72,7 @@ "deprecationInfo": "Consider using shouldDisableTime." }, "shouldDisableTime": { "type": { "name": "func" } }, + "skipDisabled": { "type": { "name": "bool" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" }, "sx": { @@ -80,15 +81,26 @@ "description": "Array<func
| object
| bool>
| func
| object" } }, + "thresholdToRenderTimeInASingleColumn": { "type": { "name": "number" }, "default": "24" }, + "timeSteps": { + "type": { + "name": "shape", + "description": "{ hours?: number, minutes?: number, seconds?: number }" + }, + "default": "{ hours: 1, minutes: 5, seconds: 5 }" + }, "value": { "type": { "name": "any" } }, "view": { "type": { "name": "enum", - "description": "'hours'
| 'minutes'
| 'seconds'" + "description": "'hours'
| 'meridiem'
| 'minutes'
| 'seconds'" } }, "viewRenderers": { - "type": { "name": "shape", "description": "{ hours?: func, minutes?: func, seconds?: func }" } + "type": { + "name": "shape", + "description": "{ hours?: func, meridiem?: func, minutes?: func, seconds?: func }" + } }, "views": { "type": { @@ -109,6 +121,14 @@ "type": { "name": "elementType" } }, "Dialog": { "default": "PickersModalDialogRoot", "type": { "name": "elementType" } }, + "DigitalClockItem": { + "default": "MenuItem from '@mui/material'", + "type": { "name": "elementType" } + }, + "DigitalClockSectionItem": { + "default": "MenuItem from '@mui/material'", + "type": { "name": "elementType" } + }, "Field": { "type": { "name": "elementType" } }, "InputAdornment": { "default": "InputAdornment", "type": { "name": "elementType" } }, "Layout": { "type": { "name": "elementType" } }, diff --git a/docs/pages/x/react-date-pickers/digital-clock.js b/docs/pages/x/react-date-pickers/digital-clock.js new file mode 100644 index 0000000000000..ac943c96310b2 --- /dev/null +++ b/docs/pages/x/react-date-pickers/digital-clock.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/date-pickers/digital-clock/digital-clock.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/translations/api-docs/date-pickers/desktop-time-picker.json b/docs/translations/api-docs/date-pickers/desktop-time-picker.json index 1695352224e77..06e38d42ca1cf 100644 --- a/docs/translations/api-docs/date-pickers/desktop-time-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-time-picker.json @@ -35,9 +35,12 @@ "selectedSections": "The currently selected sections. This prop accept four formats: 1. If a number is provided, the section at this index will be selected. 2. If an object with a startIndex and endIndex properties are provided, the sections between those two indexes will be selected. 3. If a string of type FieldSectionType is provided, the first section with that name will be selected. 4. If null is provided, no section will be selected If not provided, the selected sections will be handled internally.", "shouldDisableClock": "Disable specific clock time.

Signature:
function(clockValue: number, view: TimeView) => boolean
clockValue: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", "shouldDisableTime": "Disable specific time.

Signature:
function(value: TDate, view: TimeView) => boolean
value: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", + "skipDisabled": "If true, disabled digital clock items will not be rendered.", "slotProps": "The props used for each component slot.", "slots": "Overridable component slots.", "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", + "thresholdToRenderTimeInASingleColumn": "Amount of time options below or at which the single column time renderer is used.", + "timeSteps": "The time steps between two time unit options. For example, if timeStep.minutes = 8, then the available minute options will be [0, 8, 16, 24, 32, 40, 48, 56]. When single column time renderer is used, only timeStep.minutes will be used.", "value": "The selected value. Used when the component is controlled.", "view": "The visible view. Used when the component view is controlled. Must be a valid option from views list.", "viewRenderers": "Define custom view renderers for each section. If null, the section will only have field editing. If undefined, internally defined view will be the used.", @@ -49,6 +52,8 @@ "DesktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "DesktopTransition": "Custom component for the desktop popper Transition.", "DesktopTrapFocus": "Custom component for trapping the focus inside the views on desktop.", + "DigitalClockItem": "Component responsible for rendering a single digital clock item.", + "DigitalClockSectionItem": "Component responsible for rendering a single multi section digital clock section item.", "Field": "Component used to enter the date with the keyboard.", "InputAdornment": "Component displayed on the start or end input adornment used to open the picker on desktop.", "Layout": "Custom component for wrapping the layout.\nIt wraps the toolbar, views, action bar, and shortcuts.", diff --git a/docs/translations/api-docs/date-pickers/digital-clock.json b/docs/translations/api-docs/date-pickers/digital-clock.json new file mode 100644 index 0000000000000..61c5e8af6edf1 --- /dev/null +++ b/docs/translations/api-docs/date-pickers/digital-clock.json @@ -0,0 +1,48 @@ +{ + "componentDescription": "", + "propDescriptions": { + "ampm": "12h/24h view for hour selection clock.", + "autoFocus": "If true, the main element is focused during the first mount. This main element is: - the element chosen by the visible view if any (i.e: the selected day on the day view). - the input element if there is a field rendered.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "components": "Overrideable components.", + "componentsProps": "The props used for each component slot.", + "defaultValue": "The default selected value. Used when the component is not controlled.", + "disabled": "If true, the picker views and text field are disabled.", + "disableFuture": "If true, disable values after the current date for date components, time for time components and both for date time components.", + "disableIgnoringDatePartForTimeValidation": "Do not ignore date part when validating min/max time.", + "disablePast": "If true, disable values before the current date for date components, time for time components and both for date time components.", + "focusedView": "Controlled focused view.", + "maxTime": "Maximal selectable time. The date part of the object will be ignored unless props.disableIgnoringDatePartForTimeValidation === true.", + "minTime": "Minimal selectable time. The date part of the object will be ignored unless props.disableIgnoringDatePartForTimeValidation === true.", + "minutesStep": "Step over minutes.", + "onChange": "Callback fired when the value changes.

Signature:
function(value: TDate | null, selectionState: PickerSelectionState | undefined, selectedView: TView | undefined) => void
value: The new value.
selectionState: Indicates if the date selection is complete.
selectedView: Indicates the view in which the selection has been made.", + "onFocusedViewChange": "Callback fired on focused view change.

Signature:
function(view: TView, hasFocus: boolean) => void
view: The new view to focus or not.
hasFocus: true if the view should be focused.", + "onViewChange": "Callback fired on view change.

Signature:
function(view: TView) => void
view: The new view.", + "openTo": "The default visible view. Used when the component view is not controlled. Must be a valid option from views list.", + "readOnly": "If true, the picker views and text field are read-only.", + "shouldDisableClock": "Disable specific clock time.

Signature:
function(clockValue: number, view: TimeView) => boolean
clockValue: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", + "shouldDisableTime": "Disable specific time.

Signature:
function(value: TDate, view: TimeView) => boolean
value: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", + "skipDisabled": "If true, disabled digital clock items will not be rendered.", + "slotProps": "The props used for each component slot.", + "slots": "Overrideable component slots.", + "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", + "timeStep": "The time steps between two time options. For example, if timeStep = 45, then the available time options will be [00:00, 00:45, 01:30, 02:15, 03:00, etc.].", + "value": "The selected value. Used when the component is controlled.", + "view": "The visible view. Used when the component view is controlled. Must be a valid option from views list.", + "views": "Available views." + }, + "classDescriptions": { + "root": { "description": "Styles applied to the root element." }, + "list": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the list (by default: MenuList) element" + }, + "item": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the list item (by default: MenuItem) element" + } + }, + "slotDescriptions": { + "DigitalClockItem": "Component responsible for rendering a single digital clock item." + } +} diff --git a/docs/translations/api-docs/date-pickers/multi-section-digital-clock.json b/docs/translations/api-docs/date-pickers/multi-section-digital-clock.json new file mode 100644 index 0000000000000..2f2f489d0f551 --- /dev/null +++ b/docs/translations/api-docs/date-pickers/multi-section-digital-clock.json @@ -0,0 +1,38 @@ +{ + "componentDescription": "", + "propDescriptions": { + "ampm": "12h/24h view for hour selection clock.", + "autoFocus": "If true, the main element is focused during the first mount. This main element is: - the element chosen by the visible view if any (i.e: the selected day on the day view). - the input element if there is a field rendered.", + "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", + "components": "Overrideable components.", + "componentsProps": "The props used for each component slot.", + "defaultValue": "The default selected value. Used when the component is not controlled.", + "disabled": "If true, the picker views and text field are disabled.", + "disableFuture": "If true, disable values after the current date for date components, time for time components and both for date time components.", + "disableIgnoringDatePartForTimeValidation": "Do not ignore date part when validating min/max time.", + "disablePast": "If true, disable values before the current date for date components, time for time components and both for date time components.", + "focusedView": "Controlled focused view.", + "maxTime": "Maximal selectable time. The date part of the object will be ignored unless props.disableIgnoringDatePartForTimeValidation === true.", + "minTime": "Minimal selectable time. The date part of the object will be ignored unless props.disableIgnoringDatePartForTimeValidation === true.", + "minutesStep": "Step over minutes.", + "onChange": "Callback fired when the value changes.

Signature:
function(value: TDate | null, selectionState: PickerSelectionState | undefined, selectedView: TView | undefined) => void
value: The new value.
selectionState: Indicates if the date selection is complete.
selectedView: Indicates the view in which the selection has been made.", + "onFocusedViewChange": "Callback fired on focused view change.

Signature:
function(view: TView, hasFocus: boolean) => void
view: The new view to focus or not.
hasFocus: true if the view should be focused.", + "onViewChange": "Callback fired on view change.

Signature:
function(view: TView) => void
view: The new view.", + "openTo": "The default visible view. Used when the component view is not controlled. Must be a valid option from views list.", + "readOnly": "If true, the picker views and text field are read-only.", + "shouldDisableClock": "Disable specific clock time.

Signature:
function(clockValue: number, view: TimeView) => boolean
clockValue: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", + "shouldDisableTime": "Disable specific time.

Signature:
function(value: TDate, view: TimeView) => boolean
value: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", + "skipDisabled": "If true, disabled digital clock items will not be rendered.", + "slotProps": "The props used for each component slot.", + "slots": "Overrideable component slots.", + "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", + "timeSteps": "The time steps between two time unit options. For example, if timeStep.minutes = 8, then the available minute options will be [0, 8, 16, 24, 32, 40, 48, 56].", + "value": "The selected value. Used when the component is controlled.", + "view": "The visible view. Used when the component view is controlled. Must be a valid option from views list.", + "views": "Available views." + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, + "slotDescriptions": { + "DigitalClockSectionItem": "Component responsible for rendering a single multi section digital clock section item." + } +} diff --git a/docs/translations/api-docs/date-pickers/time-clock.json b/docs/translations/api-docs/date-pickers/time-clock.json index d71d77d5007f6..9b515d6633f18 100644 --- a/docs/translations/api-docs/date-pickers/time-clock.json +++ b/docs/translations/api-docs/date-pickers/time-clock.json @@ -3,30 +3,32 @@ "propDescriptions": { "ampm": "12h/24h view for hour selection clock.", "ampmInClock": "Display ampm controls under the clock (instead of in the toolbar).", - "autoFocus": "Set to true if focus should be moved to clock picker.", + "autoFocus": "If true, the main element is focused during the first mount. This main element is: - the element chosen by the visible view if any (i.e: the selected day on the day view). - the input element if there is a field rendered.", "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "components": "Overridable components.", "componentsProps": "The props used for each component slot.", "defaultValue": "The default selected value. Used when the component is not controlled.", - "disabled": "If true, the picker and text field are disabled.", + "disabled": "If true, the picker views and text field are disabled.", "disableFuture": "If true, disable values after the current date for date components, time for time components and both for date time components.", "disableIgnoringDatePartForTimeValidation": "Do not ignore date part when validating min/max time.", "disablePast": "If true, disable values before the current date for date components, time for time components and both for date time components.", + "focusedView": "Controlled focused view.", "maxTime": "Maximal selectable time. The date part of the object will be ignored unless props.disableIgnoringDatePartForTimeValidation === true.", "minTime": "Minimal selectable time. The date part of the object will be ignored unless props.disableIgnoringDatePartForTimeValidation === true.", "minutesStep": "Step over minutes.", - "onChange": "Callback fired when the value changes.

Signature:
function(value: TDate | null, selectionState: PickerSelectionState | undefined) => void
value: The new value.
selectionState: Indicates if the date selection is complete.", - "onViewChange": "Callback fired on view change.

Signature:
function(view: TimeView) => void
view: The new view.", - "openTo": "Initially open view.", - "readOnly": "Make picker read only.", + "onChange": "Callback fired when the value changes.

Signature:
function(value: TDate | null, selectionState: PickerSelectionState | undefined, selectedView: TView | undefined) => void
value: The new value.
selectionState: Indicates if the date selection is complete.
selectedView: Indicates the view in which the selection has been made.", + "onFocusedViewChange": "Callback fired on focused view change.

Signature:
function(view: TView, hasFocus: boolean) => void
view: The new view to focus or not.
hasFocus: true if the view should be focused.", + "onViewChange": "Callback fired on view change.

Signature:
function(view: TView) => void
view: The new view.", + "openTo": "The default visible view. Used when the component view is not controlled. Must be a valid option from views list.", + "readOnly": "If true, the picker views and text field are read-only.", "shouldDisableClock": "Disable specific clock time.

Signature:
function(clockValue: number, view: TimeView) => boolean
clockValue: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", "shouldDisableTime": "Disable specific time.

Signature:
function(value: TDate, view: TimeView) => boolean
value: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", "slotProps": "The props used for each component slot.", "slots": "Overridable component slots.", "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", "value": "The selected value. Used when the component is controlled.", - "view": "Controlled open view.", - "views": "Views for calendar picker." + "view": "The visible view. Used when the component view is controlled. Must be a valid option from views list.", + "views": "Available views." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." }, diff --git a/docs/translations/api-docs/date-pickers/time-picker.json b/docs/translations/api-docs/date-pickers/time-picker.json index 4c5301a182c8b..33cfe136b14da 100644 --- a/docs/translations/api-docs/date-pickers/time-picker.json +++ b/docs/translations/api-docs/date-pickers/time-picker.json @@ -36,9 +36,12 @@ "selectedSections": "The currently selected sections. This prop accept four formats: 1. If a number is provided, the section at this index will be selected. 2. If an object with a startIndex and endIndex properties are provided, the sections between those two indexes will be selected. 3. If a string of type FieldSectionType is provided, the first section with that name will be selected. 4. If null is provided, no section will be selected If not provided, the selected sections will be handled internally.", "shouldDisableClock": "Disable specific clock time.

Signature:
function(clockValue: number, view: TimeView) => boolean
clockValue: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", "shouldDisableTime": "Disable specific time.

Signature:
function(value: TDate, view: TimeView) => boolean
value: The value to check.
view: The clock type of the timeValue.
returns (boolean): If true the time will be disabled.", + "skipDisabled": "If true, disabled digital clock items will not be rendered.", "slotProps": "The props used for each component slot.", "slots": "Overridable component slots.", "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", + "thresholdToRenderTimeInASingleColumn": "Amount of time options below or at which the single column time renderer is used.", + "timeSteps": "The time steps between two time unit options. For example, if timeStep.minutes = 8, then the available minute options will be [0, 8, 16, 24, 32, 40, 48, 56]. When single column time renderer is used, only timeStep.minutes will be used.", "value": "The selected value. Used when the component is controlled.", "view": "The visible view. Used when the component view is controlled. Must be a valid option from views list.", "viewRenderers": "Define custom view renderers for each section. If null, the section will only have field editing. If undefined, internally defined view will be the used.", @@ -51,6 +54,8 @@ "DesktopTransition": "Custom component for the desktop popper Transition.", "DesktopTrapFocus": "Custom component for trapping the focus inside the views on desktop.", "Dialog": "Custom component for the dialog inside which the views are rendered on mobile.", + "DigitalClockItem": "Component responsible for rendering a single digital clock item.", + "DigitalClockSectionItem": "Component responsible for rendering a single multi section digital clock section item.", "Field": "Component used to enter the date with the keyboard.", "InputAdornment": "Component displayed on the start or end input adornment used to open the picker on desktop.", "Layout": "Custom component for wrapping the layout.\nIt wraps the toolbar, views, action bar, and shortcuts.", diff --git a/packages/x-date-pickers-pro/src/dateRangeViewRenderers/dateRangeViewRenderers.tsx b/packages/x-date-pickers-pro/src/dateRangeViewRenderers/dateRangeViewRenderers.tsx index ab6cb70021d18..d22eaf891978e 100644 --- a/packages/x-date-pickers-pro/src/dateRangeViewRenderers/dateRangeViewRenderers.tsx +++ b/packages/x-date-pickers-pro/src/dateRangeViewRenderers/dateRangeViewRenderers.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; -import { DateOrTimeView } from '@mui/x-date-pickers/models'; +import { DateOrTimeViewWithMeridiem } from '@mui/x-date-pickers/internals/models'; import { DateRangeCalendar, DateRangeCalendarProps } from '../DateRangeCalendar'; -export interface DateRangeViewRendererProps +export interface DateRangeViewRendererProps extends DateRangeCalendarProps { view: TView; onViewChange?: (view: TView) => void; diff --git a/packages/x-date-pickers-pro/src/internal/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx b/packages/x-date-pickers-pro/src/internal/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx index c7eb8d8c64026..3186163a2cbe1 100644 --- a/packages/x-date-pickers-pro/src/internal/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/internal/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx @@ -15,7 +15,7 @@ import { ExportedBaseToolbarProps, BaseFieldProps, } from '@mui/x-date-pickers/internals'; -import { DateOrTimeView } from '@mui/x-date-pickers/models'; +import { DateOrTimeViewWithMeridiem } from '@mui/x-date-pickers/internals/models'; import { DesktopRangePickerAdditionalViewProps, UseDesktopRangePickerParams, @@ -31,7 +31,7 @@ const releaseInfo = getReleaseInfo(); export const useDesktopRangePicker = < TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseDesktopRangePickerProps, >({ props, diff --git a/packages/x-date-pickers-pro/src/internal/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts b/packages/x-date-pickers-pro/src/internal/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts index 02c30eb94e817..1147492a4b67f 100644 --- a/packages/x-date-pickers-pro/src/internal/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/internal/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts @@ -10,7 +10,7 @@ import { UsePickerValueNonStaticProps, UsePickerViewsNonStaticProps, } from '@mui/x-date-pickers/internals'; -import { DateOrTimeView } from '@mui/x-date-pickers/models'; +import { DateOrTimeViewWithMeridiem } from '@mui/x-date-pickers/internals/models'; import { ExportedPickersLayoutSlotsComponent, ExportedPickersLayoutSlotsComponentsProps, @@ -22,13 +22,17 @@ import { RangePickerFieldSlotsComponentsProps, } from '../useEnrichedRangePickerFieldProps'; -export interface UseDesktopRangePickerSlotsComponent - extends PickersPopperSlotsComponent, +export interface UseDesktopRangePickerSlotsComponent< + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends PickersPopperSlotsComponent, ExportedPickersLayoutSlotsComponent, TDate, TView>, RangePickerFieldSlotsComponent {} -export interface UseDesktopRangePickerSlotsComponentsProps - extends PickersPopperSlotsComponentsProps, +export interface UseDesktopRangePickerSlotsComponentsProps< + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends PickersPopperSlotsComponentsProps, ExportedPickersLayoutSlotsComponentsProps, TDate, TView>, RangePickerFieldSlotsComponentsProps { toolbar?: ExportedBaseToolbarProps; @@ -48,7 +52,7 @@ export interface DesktopRangeOnlyPickerProps export interface UseDesktopRangePickerProps< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TError, TExternalProps extends UsePickerViewsProps, > extends DesktopRangeOnlyPickerProps, @@ -77,7 +81,7 @@ export interface DesktopRangePickerAdditionalViewProps export interface UseDesktopRangePickerParams< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseDesktopRangePickerProps, > extends Pick< UsePickerParams< diff --git a/packages/x-date-pickers-pro/src/internal/hooks/useEnrichedRangePickerFieldProps.ts b/packages/x-date-pickers-pro/src/internal/hooks/useEnrichedRangePickerFieldProps.ts index a0545f5dbd40f..08ee993b34f24 100644 --- a/packages/x-date-pickers-pro/src/internal/hooks/useEnrichedRangePickerFieldProps.ts +++ b/packages/x-date-pickers-pro/src/internal/hooks/useEnrichedRangePickerFieldProps.ts @@ -5,11 +5,8 @@ import TextField, { TextFieldProps } from '@mui/material/TextField'; import { resolveComponentProps, SlotComponentProps } from '@mui/base/utils'; import useEventCallback from '@mui/utils/useEventCallback'; import useForkRef from '@mui/utils/useForkRef'; -import { - BaseSingleInputFieldProps, - FieldSelectedSections, - DateOrTimeView, -} from '@mui/x-date-pickers/models'; +import { BaseSingleInputFieldProps, FieldSelectedSections } from '@mui/x-date-pickers/models'; +import { DateOrTimeViewWithMeridiem } from '@mui/x-date-pickers/internals/models'; import { PickersInputLocaleText } from '@mui/x-date-pickers/locales'; import { BaseFieldProps, @@ -71,7 +68,7 @@ export interface RangePickerFieldSlotsComponentsProps { export interface UseEnrichedRangePickerFieldPropsParams< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TError, FieldProps extends BaseFieldProps, RangeFieldSection, TError> = BaseFieldProps< DateRange, @@ -97,7 +94,7 @@ export interface UseEnrichedRangePickerFieldPropsParams< fieldProps: FieldProps; } -const useMultiInputFieldSlotProps = ({ +const useMultiInputFieldSlotProps = ({ wrapperVariant, open, actions, @@ -230,7 +227,7 @@ const useMultiInputFieldSlotProps = ({ +const useSingleInputFieldSlotProps = ({ wrapperVariant, open, actions, @@ -328,7 +325,11 @@ const useSingleInputFieldSlotProps = ( +export const useEnrichedRangePickerFieldProps = < + TDate, + TView extends DateOrTimeViewWithMeridiem, + TError, +>( params: UseEnrichedRangePickerFieldPropsParams, ) => { /* eslint-disable react-hooks/rules-of-hooks */ diff --git a/packages/x-date-pickers-pro/src/internal/hooks/useMobileRangePicker/useMobileRangePicker.tsx b/packages/x-date-pickers-pro/src/internal/hooks/useMobileRangePicker/useMobileRangePicker.tsx index 2b12784b7c9f0..33abbf9de8b61 100644 --- a/packages/x-date-pickers-pro/src/internal/hooks/useMobileRangePicker/useMobileRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/internal/hooks/useMobileRangePicker/useMobileRangePicker.tsx @@ -13,8 +13,8 @@ import { ExportedBaseToolbarProps, useLocaleText, } from '@mui/x-date-pickers/internals'; -import { DateOrTimeView } from '@mui/x-date-pickers/models'; import useId from '@mui/utils/useId'; +import { DateOrTimeViewWithMeridiem } from '@mui/x-date-pickers/internals/models'; import { MobileRangePickerAdditionalViewProps, UseMobileRangePickerParams, @@ -30,7 +30,7 @@ const releaseInfo = getReleaseInfo(); export const useMobileRangePicker = < TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseMobileRangePickerProps, >({ props, diff --git a/packages/x-date-pickers-pro/src/internal/hooks/useMobileRangePicker/useMobileRangePicker.types.ts b/packages/x-date-pickers-pro/src/internal/hooks/useMobileRangePicker/useMobileRangePicker.types.ts index c4542d54b0727..2ba30b71723c4 100644 --- a/packages/x-date-pickers-pro/src/internal/hooks/useMobileRangePicker/useMobileRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/internal/hooks/useMobileRangePicker/useMobileRangePicker.types.ts @@ -10,11 +10,11 @@ import { UsePickerValueNonStaticProps, UsePickerViewsNonStaticProps, } from '@mui/x-date-pickers/internals'; -import { DateOrTimeView } from '@mui/x-date-pickers/models'; import { ExportedPickersLayoutSlotsComponent, ExportedPickersLayoutSlotsComponentsProps, } from '@mui/x-date-pickers/PickersLayout'; +import { DateOrTimeViewWithMeridiem } from '@mui/x-date-pickers/internals/models'; import { DateRange, RangeFieldSection, BaseRangeNonStaticPickerProps } from '../../models'; import { UseRangePositionProps, UseRangePositionResponse } from '../useRangePosition'; import { @@ -22,13 +22,15 @@ import { RangePickerFieldSlotsComponentsProps, } from '../useEnrichedRangePickerFieldProps'; -export interface UseMobileRangePickerSlotsComponent +export interface UseMobileRangePickerSlotsComponent extends PickersModalDialogSlotsComponent, ExportedPickersLayoutSlotsComponent, TDate, TView>, RangePickerFieldSlotsComponent {} -export interface UseMobileRangePickerSlotsComponentsProps - extends PickersModalDialogSlotsComponentsProps, +export interface UseMobileRangePickerSlotsComponentsProps< + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends PickersModalDialogSlotsComponentsProps, ExportedPickersLayoutSlotsComponentsProps, TDate, TView>, RangePickerFieldSlotsComponentsProps { toolbar?: ExportedBaseToolbarProps; @@ -43,7 +45,7 @@ export interface MobileRangeOnlyPickerProps export interface UseMobileRangePickerProps< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TError, TExternalProps extends UsePickerViewsProps, > extends MobileRangeOnlyPickerProps, @@ -72,7 +74,7 @@ export interface MobileRangePickerAdditionalViewProps export interface UseMobileRangePickerParams< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseMobileRangePickerProps, > extends Pick< UsePickerParams< diff --git a/packages/x-date-pickers-pro/src/internal/hooks/useStaticRangePicker/useStaticRangePicker.tsx b/packages/x-date-pickers-pro/src/internal/hooks/useStaticRangePicker/useStaticRangePicker.tsx index 7dbfe7f00a8ab..a1a97ab8aa6fe 100644 --- a/packages/x-date-pickers-pro/src/internal/hooks/useStaticRangePicker/useStaticRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/internal/hooks/useStaticRangePicker/useStaticRangePicker.tsx @@ -7,7 +7,7 @@ import { PickersLayoutSlotsComponentsProps, } from '@mui/x-date-pickers/PickersLayout'; import { usePicker, DIALOG_WIDTH, ExportedBaseToolbarProps } from '@mui/x-date-pickers/internals'; -import { DateOrTimeView } from '@mui/x-date-pickers/models'; +import { DateOrTimeViewWithMeridiem } from '@mui/x-date-pickers/internals/models'; import { UseStaticRangePickerParams, UseStaticRangePickerProps, @@ -28,7 +28,7 @@ const PickerStaticLayout = styled(PickersLayout)(({ theme }) => ({ */ export const useStaticRangePicker = < TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseStaticRangePickerProps, >({ props, diff --git a/packages/x-date-pickers-pro/src/internal/hooks/useStaticRangePicker/useStaticRangePicker.types.ts b/packages/x-date-pickers-pro/src/internal/hooks/useStaticRangePicker/useStaticRangePicker.types.ts index 7b249c48218bb..c68398447ffda 100644 --- a/packages/x-date-pickers-pro/src/internal/hooks/useStaticRangePicker/useStaticRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/internal/hooks/useStaticRangePicker/useStaticRangePicker.types.ts @@ -10,16 +10,18 @@ import { ExportedPickersLayoutSlotsComponent, ExportedPickersLayoutSlotsComponentsProps, } from '@mui/x-date-pickers/PickersLayout'; -import { DateOrTimeView } from '@mui/x-date-pickers/models'; +import { DateOrTimeViewWithMeridiem } from '@mui/x-date-pickers/internals/models'; import { DateRange } from '../../models/range'; import { UseRangePositionProps } from '../useRangePosition'; import { RangeFieldSection } from '../../models/fields'; -export interface UseStaticRangePickerSlotsComponent +export interface UseStaticRangePickerSlotsComponent extends ExportedPickersLayoutSlotsComponent, TDate, TView> {} -export interface UseStaticRangePickerSlotsComponentsProps - extends ExportedPickersLayoutSlotsComponentsProps, TDate, TView> { +export interface UseStaticRangePickerSlotsComponentsProps< + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends ExportedPickersLayoutSlotsComponentsProps, TDate, TView> { toolbar?: ExportedBaseToolbarProps; } @@ -27,7 +29,7 @@ export interface StaticRangeOnlyPickerProps extends StaticOnlyPickerProps, UseRa export interface UseStaticRangePickerProps< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TError, TExternalProps extends UseStaticRangePickerProps, > extends BasePickerProps, TDate, TView, TError, TExternalProps, {}>, @@ -46,7 +48,7 @@ export interface UseStaticRangePickerProps< export interface UseStaticRangePickerParams< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseStaticRangePickerProps, > extends Pick< UsePickerParams, TDate, TView, RangeFieldSection, TExternalProps, {}>, diff --git a/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx b/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx index 5e8d48f375d38..60f46510cf690 100644 --- a/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx @@ -164,9 +164,7 @@ DatePickerToolbar.propTypes = { * Currently visible picker view. */ view: PropTypes.oneOf(['day', 'month', 'year']).isRequired, - views: PropTypes.arrayOf( - PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']).isRequired, - ).isRequired, + views: PropTypes.arrayOf(PropTypes.oneOf(['day', 'month', 'year']).isRequired).isRequired, } as any; export { DatePickerToolbar }; diff --git a/packages/x-date-pickers/src/DateTimePicker/shared.tsx b/packages/x-date-pickers/src/DateTimePicker/shared.tsx index 51c8065f3c2e5..12f8a4b7f2e35 100644 --- a/packages/x-date-pickers/src/DateTimePicker/shared.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/shared.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { useThemeProps } from '@mui/material/styles'; import { DefaultizedProps } from '../internals/models/helpers'; -import { DateOrTimeView, DateTimeValidationError } from '../models'; +import { DateOrTimeView, DateTimeValidationError, TimeView } from '../models'; import { useDefaultDates, useUtils } from '../internals/hooks/useUtils'; import { DateCalendarSlotsComponent, @@ -11,7 +11,6 @@ import { import { TimeClockSlotsComponent, TimeClockSlotsComponentsProps, - ExportedTimeClockProps, } from '../TimeClock/TimeClock.types'; import { BasePickerInputProps } from '../internals/models/props/basePickerProps'; import { applyDefaultDate } from '../internals/utils/date-utils'; @@ -32,6 +31,7 @@ import { DateViewRendererProps } from '../dateViewRenderers'; import { TimeViewRendererProps } from '../timeViewRenderers'; import { applyDefaultViewProps } from '../internals/utils/views'; import { uncapitalizeObjectKeys, UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { BaseClockProps, ExportedBaseClockProps } from '../internals/models/props/clock'; export interface BaseDateTimePickerSlotsComponent extends DateCalendarSlotsComponent, @@ -64,7 +64,7 @@ export interface BaseDateTimePickerSlotsComponentsProps export interface BaseDateTimePickerProps extends BasePickerInputProps, Omit, 'onViewChange'>, - ExportedTimeClockProps { + ExportedBaseClockProps { /** * Display ampm controls under the clock (instead of in the toolbar). * @default true on desktop, false on mobile @@ -109,7 +109,8 @@ export interface BaseDateTimePickerProps PickerViewRendererLookup< TDate | null, DateOrTimeView, - DateViewRendererProps & TimeViewRendererProps, + DateViewRendererProps & + TimeViewRendererProps>, {} > >; diff --git a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx index 3c39f14f8c2b1..13615e9f24281 100644 --- a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx +++ b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx @@ -6,11 +6,16 @@ import { TimeField } from '../TimeField'; import { DesktopTimePickerProps } from './DesktopTimePicker.types'; import { useTimePickerDefaultizedProps } from '../TimePicker/shared'; import { useLocaleText, validateTime } from '../internals'; -import { TimeView } from '../models'; import { Clock } from '../internals/components/icons'; import { useDesktopPicker } from '../internals/hooks/useDesktopPicker'; import { extractValidationProps } from '../internals/utils/validation/extractValidationProps'; import { PickerViewRendererLookup } from '../internals/hooks/usePicker/usePickerViews'; +import { + renderDigitalClockTimeView, + renderMultiSectionDigitalClockTimeView, +} from '../timeViewRenderers'; +import { PickersActionBarAction } from '../PickersActionBar'; +import { TimeViewWithMeridiem } from '../internals/models'; type DesktopTimePickerComponent = (( props: DesktopTimePickerProps & React.RefAttributes, @@ -23,25 +28,47 @@ const DesktopTimePicker = React.forwardRef(function DesktopTimePicker( const localeText = useLocaleText(); // Props with the default values common to all time pickers - const defaultizedProps = useTimePickerDefaultizedProps>( - inProps, - 'MuiDesktopTimePicker', - ); + const defaultizedProps = useTimePickerDefaultizedProps< + TDate, + TimeViewWithMeridiem, + DesktopTimePickerProps + >(inProps, 'MuiDesktopTimePicker'); - const viewRenderers: PickerViewRendererLookup = { - hours: null, - minutes: null, - seconds: null, + const thresholdToRenderTimeInASingleColumn = + defaultizedProps.thresholdToRenderTimeInASingleColumn ?? 24; + const timeSteps = { hours: 1, minutes: 5, seconds: 5, ...defaultizedProps.timeSteps }; + const shouldRenderTimeInASingleColumn = + (24 * 60) / (timeSteps.hours * timeSteps.minutes) <= thresholdToRenderTimeInASingleColumn; + + const renderTimeView = shouldRenderTimeInASingleColumn + ? renderDigitalClockTimeView + : renderMultiSectionDigitalClockTimeView; + + const viewRenderers: PickerViewRendererLookup = { + hours: renderTimeView, + minutes: renderTimeView, + seconds: renderTimeView, + meridiem: renderTimeView, ...defaultizedProps.viewRenderers, }; const ampmInClock = defaultizedProps.ampmInClock ?? true; + const actionBarActions: PickersActionBarAction[] = shouldRenderTimeInASingleColumn + ? [] + : ['accept']; + const views: readonly TimeViewWithMeridiem[] = defaultizedProps.ampm + ? [...defaultizedProps.views, 'meridiem'] + : defaultizedProps.views; // Props with the default values specific to the desktop variant const props = { ...defaultizedProps, ampmInClock, + timeSteps, viewRenderers, + // Setting only `hours` time view in case of single column time picker + // Allows for easy view lifecycle management + views: shouldRenderTimeInASingleColumn ? ['hours' as TimeViewWithMeridiem] : views, slots: { field: TimeField, openPickerIcon: Clock, @@ -60,10 +87,14 @@ const DesktopTimePicker = React.forwardRef(function DesktopTimePicker( ampmInClock, ...defaultizedProps.slotProps?.toolbar, }, + actionBar: { + actions: actionBarActions, + ...defaultizedProps.slotProps?.actionBar, + }, }, }; - const { renderPicker } = useDesktopPicker({ + const { renderPicker } = useDesktopPicker({ props, valueManager: singleItemValueManager, valueType: 'time', @@ -246,7 +277,7 @@ DesktopTimePicker.propTypes = { * Used when the component view is not controlled. * Must be a valid option from `views` list. */ - openTo: PropTypes.oneOf(['hours', 'minutes', 'seconds']), + openTo: PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']), /** * Force rendering in particular orientation. */ @@ -295,6 +326,11 @@ DesktopTimePicker.propTypes = { * @returns {boolean} If `true` the time will be disabled. */ shouldDisableTime: PropTypes.func, + /** + * If `true`, disabled digital clock items will not be rendered. + * @default false + */ + skipDisabled: PropTypes.bool, /** * The props used for each component slot. * @default {} @@ -313,6 +349,22 @@ DesktopTimePicker.propTypes = { PropTypes.func, PropTypes.object, ]), + /** + * Amount of time options below or at which the single column time renderer is used. + * @default 24 + */ + thresholdToRenderTimeInASingleColumn: PropTypes.number, + /** + * The time steps between two time unit options. + * For example, if `timeStep.minutes = 8`, then the available minute options will be `[0, 8, 16, 24, 32, 40, 48, 56]`. + * When single column time renderer is used, only `timeStep.minutes` will be used. + * @default{ hours: 1, minutes: 5, seconds: 5 } + */ + timeSteps: PropTypes.shape({ + hours: PropTypes.number, + minutes: PropTypes.number, + seconds: PropTypes.number, + }), /** * The selected value. * Used when the component is controlled. @@ -323,7 +375,7 @@ DesktopTimePicker.propTypes = { * Used when the component view is controlled. * Must be a valid option from `views` list. */ - view: PropTypes.oneOf(['hours', 'minutes', 'seconds']), + view: PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']), /** * Define custom view renderers for each section. * If `null`, the section will only have field editing. @@ -331,6 +383,7 @@ DesktopTimePicker.propTypes = { */ viewRenderers: PropTypes.shape({ hours: PropTypes.func, + meridiem: PropTypes.func, minutes: PropTypes.func, seconds: PropTypes.func, }), diff --git a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.types.ts b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.types.ts index 618510e3aa45f..38e9d03a7d404 100644 --- a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.types.ts +++ b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.types.ts @@ -9,20 +9,39 @@ import { BaseTimePickerSlotsComponentsProps, } from '../TimePicker/shared'; import { MakeOptional } from '../internals/models/helpers'; -import { TimeView } from '../models'; +import { TimeViewWithMeridiem } from '../internals/models'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { DesktopOnlyTimePickerProps } from '../internals/models/props/clock'; +import { DigitalClockSlotsComponent, DigitalClockSlotsComponentsProps } from '../DigitalClock'; +import { + MultiSectionDigitalClockSlotsComponent, + MultiSectionDigitalClockSlotsComponentsProps, +} from '../MultiSectionDigitalClock'; +import { TimeView } from '../models'; export interface DesktopTimePickerSlotsComponent extends BaseTimePickerSlotsComponent, - MakeOptional, 'Field' | 'OpenPickerIcon'> {} + MakeOptional< + UseDesktopPickerSlotsComponent, + 'Field' | 'OpenPickerIcon' + >, + DigitalClockSlotsComponent, + MultiSectionDigitalClockSlotsComponent {} export interface DesktopTimePickerSlotsComponentsProps extends BaseTimePickerSlotsComponentsProps, - ExportedUseDesktopPickerSlotsComponentsProps {} + ExportedUseDesktopPickerSlotsComponentsProps, + DigitalClockSlotsComponentsProps, + MultiSectionDigitalClockSlotsComponentsProps {} export interface DesktopTimePickerProps - extends BaseTimePickerProps, - DesktopOnlyPickerProps { + extends BaseTimePickerProps, + DesktopOnlyPickerProps, + DesktopOnlyTimePickerProps { + /** + * Available views. + */ + views?: readonly TimeView[]; /** * Overridable components. * @default {} diff --git a/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx new file mode 100644 index 0000000000000..4e7d81dcd1b74 --- /dev/null +++ b/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx @@ -0,0 +1,160 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { spy } from 'sinon'; +import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { DesktopTimePicker } from '@mui/x-date-pickers/DesktopTimePicker'; +import { createPickerRenderer, openPicker } from 'test/utils/pickers-utils'; + +describe('', () => { + const { render } = createPickerRenderer({ + clock: 'fake', + clockConfig: new Date('2018-01-01T10:05:05.000'), + }); + + describe('rendering behavior', () => { + it('should render "accept" action and 3 time sections by default', () => { + render(); + + expect(screen.getByRole('button', { name: 'OK' })).not.to.equal(null); + expect(screen.getByRole('listbox', { name: 'Select hours' })).not.to.equal(null); + expect(screen.getByRole('option', { name: '1 hours' })).not.to.equal(null); + expect(screen.getByRole('listbox', { name: 'Select minutes' })).not.to.equal(null); + expect(screen.getByRole('option', { name: '5 minutes' })).not.to.equal(null); + expect(screen.getByRole('listbox', { name: 'Select meridiem' })).not.to.equal(null); + expect(screen.getByRole('option', { name: 'AM' })).not.to.equal(null); + }); + + it('should render single column picker given big enough "thresholdToRenderTimeInASingleColumn" number', () => { + render(); + + expect(screen.getByRole('listbox', { name: 'Select time' })).not.to.equal(null); + expect(screen.getByRole('option', { name: '09:35 AM' })).not.to.equal(null); + }); + + it('should render single column picker given big enough "timeSteps.minutes" number', () => { + render(); + + expect(screen.getByRole('listbox', { name: 'Select time' })).not.to.equal(null); + expect(screen.getByRole('option', { name: '09:00 AM' })).not.to.equal(null); + }); + + it('should correctly use all "timeSteps"', () => { + render( + , + ); + + Array.from({ length: 12 / 3 }).forEach((_, i) => { + expect(screen.getByRole('option', { name: `${i * 3 || 12} hours` })).not.to.equal(null); + }); + Array.from({ length: 60 / 15 }).forEach((_, i) => { + expect(screen.getByRole('option', { name: `${i * 15} minutes` })).not.to.equal(null); + }); + Array.from({ length: 60 / 20 }).forEach((_, i) => { + expect(screen.getByRole('option', { name: `${i * 20} seconds` })).not.to.equal(null); + }); + }); + }); + + describe('selecting behavior', () => { + it('should call "onAccept", "onChange", and "onClose" when selecting a single option', () => { + const onChange = spy(); + const onAccept = spy(); + const onClose = spy(); + + render( + , + ); + + openPicker({ type: 'time', variant: 'desktop' }); + + userEvent.mousePress(screen.getByRole('option', { name: '09:00 AM' })); + expect(onChange.callCount).to.equal(1); + expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2018, 0, 1, 9, 0)); + expect(onAccept.callCount).to.equal(1); + expect(onAccept.lastCall.args[0]).toEqualDateTime(new Date(2018, 0, 1, 9, 0)); + expect(onClose.callCount).to.equal(1); + }); + + it('should call "onAccept", "onChange", and "onClose" when selecting all section', () => { + const onChange = spy(); + const onAccept = spy(); + const onClose = spy(); + + render(); + + openPicker({ type: 'time', variant: 'desktop' }); + + userEvent.mousePress(screen.getByRole('option', { name: '2 hours' })); + expect(onChange.callCount).to.equal(1); + expect(onAccept.callCount).to.equal(0); + expect(onClose.callCount).to.equal(0); + + userEvent.mousePress(screen.getByRole('option', { name: '15 minutes' })); + expect(onChange.callCount).to.equal(2); + expect(onAccept.callCount).to.equal(0); + expect(onClose.callCount).to.equal(0); + + userEvent.mousePress(screen.getByRole('option', { name: 'PM' })); + expect(onChange.callCount).to.equal(3); + expect(onAccept.callCount).to.equal(1); + expect(onAccept.lastCall.args[0]).toEqualDateTime(new Date(2018, 0, 1, 14, 15)); + expect(onClose.callCount).to.equal(1); + }); + + it('should allow out of order section selection', () => { + const onChange = spy(); + const onAccept = spy(); + const onClose = spy(); + + render(); + + openPicker({ type: 'time', variant: 'desktop' }); + + userEvent.mousePress(screen.getByRole('option', { name: '15 minutes' })); + expect(onChange.callCount).to.equal(1); + expect(onAccept.callCount).to.equal(0); + expect(onClose.callCount).to.equal(0); + + userEvent.mousePress(screen.getByRole('option', { name: '2 hours' })); + expect(onChange.callCount).to.equal(2); + expect(onAccept.callCount).to.equal(0); + expect(onClose.callCount).to.equal(0); + + userEvent.mousePress(screen.getByRole('option', { name: '25 minutes' })); + expect(onChange.callCount).to.equal(3); + expect(onAccept.callCount).to.equal(0); + expect(onClose.callCount).to.equal(0); + + userEvent.mousePress(screen.getByRole('option', { name: 'PM' })); + expect(onChange.callCount).to.equal(4); + expect(onAccept.callCount).to.equal(1); + expect(onAccept.lastCall.args[0]).toEqualDateTime(new Date(2018, 0, 1, 14, 25)); + expect(onClose.callCount).to.equal(1); + }); + + it('should finish selection when selecting only the last section', () => { + const onChange = spy(); + const onAccept = spy(); + const onClose = spy(); + + render(); + + openPicker({ type: 'time', variant: 'desktop' }); + + userEvent.mousePress(screen.getByRole('option', { name: 'PM' })); + expect(onChange.callCount).to.equal(1); + expect(onAccept.callCount).to.equal(1); + expect(onAccept.lastCall.args[0]).toEqualDateTime(new Date(2018, 0, 1, 12, 0)); + expect(onClose.callCount).to.equal(1); + }); + }); +}); diff --git a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx index 87edb3d164640..9421131ada3fe 100644 --- a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { userEvent, describeConformance } from '@mui/monorepo/test/utils'; +import { screen, userEvent, describeConformance } from '@mui/monorepo/test/utils'; import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; import { @@ -23,7 +23,6 @@ describe(' - Describes', () => { render, fieldType: 'single-input', variant: 'desktop', - hasNoView: true, }); describeValidation(DesktopTimePicker, () => ({ @@ -58,6 +57,9 @@ describe(' - Describes', () => { componentFamily: 'picker', type: 'time', variant: 'desktop', + defaultProps: { + views: ['hours'], + }, values: [ adapterToUse.date(new Date(2018, 0, 1, 15, 30)), adapterToUse.date(new Date(2018, 0, 1, 18, 30)), @@ -77,17 +79,27 @@ describe(' - Describes', () => { : '', ); }, - setNewValue: (value, { isOpened } = {}) => { - const newValue = adapterToUse.addHours(value, 1); + setNewValue: (value, { isOpened, applySameValue } = {}) => { + const newValue = applySameValue ? value : adapterToUse.addHours(value, 1); if (isOpened) { - throw new Error("Can't test UI views on DesktopTimePicker"); + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + const hours = adapterToUse.format(newValue, hasMeridiem ? 'hours12h' : 'hours24h'); + const hoursNumber = adapterToUse.getHours(newValue); + userEvent.mousePress(screen.getByRole('option', { name: `${parseInt(hours, 10)} hours` })); + if (hasMeridiem) { + // meridiem is an extra view on `DesktopTimePicker` + // we need to click it to finish selection + userEvent.mousePress( + screen.getByRole('option', { name: hoursNumber >= 12 ? 'PM' : 'AM' }), + ); + } + } else { + const input = getTextbox(); + clickOnInput(input, 1); // Update the hour + userEvent.keyPress(input, { key: 'ArrowUp' }); } - const input = getTextbox(); - clickOnInput(input, 1); // Update the hour - userEvent.keyPress(input, { key: 'ArrowUp' }); - return newValue; }, })); diff --git a/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx b/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx new file mode 100644 index 0000000000000..c5de1361cfc1f --- /dev/null +++ b/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx @@ -0,0 +1,464 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import PropTypes from 'prop-types'; +import { alpha, styled, useThemeProps } from '@mui/material/styles'; +import useEventCallback from '@mui/utils/useEventCallback'; +import composeClasses from '@mui/utils/composeClasses'; +import useControlled from '@mui/utils/useControlled'; +import MenuItem from '@mui/material/MenuItem'; +import MenuList from '@mui/material/MenuList'; +import useForkRef from '@mui/utils/useForkRef'; +import { useUtils, useNow, useLocaleText } from '../internals/hooks/useUtils'; +import { createIsAfterIgnoreDatePart } from '../internals/utils/time-utils'; +import { PickerViewRoot } from '../internals/components/PickerViewRoot'; +import { getDigitalClockUtilityClass } from './digitalClockClasses'; +import { DigitalClockProps } from './DigitalClock.types'; +import { useViews } from '../internals/hooks/useViews'; +import { TimeView } from '../models'; +import { DIGITAL_CLOCK_VIEW_HEIGHT } from '../internals/constants/dimensions'; + +const useUtilityClasses = (ownerState: DigitalClockProps) => { + const { classes } = ownerState; + const slots = { + root: ['root'], + list: ['list'], + item: ['item'], + }; + + return composeClasses(slots, getDigitalClockUtilityClass, classes); +}; + +const DigitalClockRoot = styled(PickerViewRoot, { + name: 'MuiDigitalClock', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})<{ ownerState: DigitalClockProps & { alreadyRendered: boolean } }>(({ ownerState }) => ({ + overflowY: 'auto', + width: '100%', + scrollBehavior: ownerState.alreadyRendered ? 'smooth' : 'auto', + maxHeight: DIGITAL_CLOCK_VIEW_HEIGHT, +})); + +const DigitalClockList = styled(MenuList, { + name: 'MuiDigitalClock', + slot: 'List', + overridesResolver: (props, styles) => styles.list, +})({ + padding: 0, +}); + +const DigitalClockItem = styled(MenuItem, { + name: 'MuiDigitalClock', + slot: 'Item', + overridesResolver: (props, styles) => styles.item, +})(({ theme }) => ({ + padding: '8px 16px', + margin: '2px 4px', + '&:first-of-type': { + marginTop: 4, + }, + '&:hover': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.hoverOpacity})` + : alpha(theme.palette.primary.main, theme.palette.action.hoverOpacity), + }, + '&.Mui-selected': { + backgroundColor: (theme.vars || theme).palette.primary.main, + color: (theme.vars || theme).palette.primary.contrastText, + '&:focus-visible, &:hover': { + backgroundColor: (theme.vars || theme).palette.primary.dark, + }, + }, + '&.Mui-focusVisible': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.focusOpacity})` + : alpha(theme.palette.primary.main, theme.palette.action.focusOpacity), + }, +})); + +type DigitalClockComponent = (( + props: DigitalClockProps & React.RefAttributes, +) => JSX.Element) & { propTypes?: any }; + +export const DigitalClock = React.forwardRef(function DigitalClock( + inProps: DigitalClockProps, + ref: React.Ref, +) { + const now = useNow(); + const utils = useUtils(); + const containerRef = React.useRef(null); + const handleRef = useForkRef(ref, containerRef); + const localeText = useLocaleText(); + + const props = useThemeProps({ + props: inProps, + name: 'MuiDigitalClock', + }); + + const { + ampm = utils.is12HourCycleInCurrentLocale(), + timeStep = 30, + autoFocus, + components, + componentsProps, + slots, + slotProps, + value: valueProp, + disableIgnoringDatePartForTimeValidation = false, + maxTime, + minTime, + disableFuture, + disablePast, + minutesStep = 1, + shouldDisableClock, + shouldDisableTime, + onChange, + defaultValue, + view: inView, + openTo, + onViewChange, + focusedView, + onFocusedViewChange, + className, + disabled, + readOnly, + views = ['hours'], + skipDisabled = false, + ...other + } = props; + + const ownerState = React.useMemo( + () => ({ ...props, alreadyRendered: !!containerRef.current }), + [props], + ); + + const classes = useUtilityClasses(ownerState); + + const ClockItem = slots?.digitalClockItem ?? components?.DigitalClockItem ?? DigitalClockItem; + const clockItemProps = slotProps?.digitalClockItem ?? componentsProps?.digitalClockItem; + + const [value, setValue] = useControlled({ + name: 'DigitalClock', + state: 'value', + controlled: valueProp, + default: defaultValue ?? null, + }); + + const handleValueChange = useEventCallback((newValue: TDate | null) => { + setValue(newValue); + onChange?.(newValue, 'finish'); + }); + + const { setValueAndGoToNextView } = useViews>({ + view: inView, + views, + openTo, + onViewChange, + onChange: handleValueChange, + focusedView, + onFocusedViewChange, + }); + + const handleItemSelect = useEventCallback((newValue: TDate | null) => { + setValueAndGoToNextView(newValue, 'finish'); + }); + + React.useEffect(() => { + if (containerRef.current === null) { + return; + } + const selectedItem = containerRef.current.querySelector( + '[role="listbox"] [role="option"][aria-selected="true"]', + ); + + if (!selectedItem) { + return; + } + const offsetTop = selectedItem.offsetTop; + + // Subtracting the 4px of extra margin intended for the first visible section item + containerRef.current.scrollTop = offsetTop - 4; + }); + + const selectedTimeOrMidnight = React.useMemo( + () => value || utils.setSeconds(utils.setMinutes(utils.setHours(now, 0), 0), 0), + [value, now, utils], + ); + + const isTimeDisabled = React.useCallback( + (valueToCheck: TDate) => { + const isAfter = createIsAfterIgnoreDatePart(disableIgnoringDatePartForTimeValidation, utils); + + const containsValidTime = () => { + if (minTime && isAfter(minTime, valueToCheck)) { + return false; + } + + if (maxTime && isAfter(valueToCheck, maxTime)) { + return false; + } + + if (disableFuture && isAfter(valueToCheck, now)) { + return false; + } + + if (disablePast && isAfter(now, valueToCheck)) { + return false; + } + + return true; + }; + + const isValidValue = () => { + if (utils.getMinutes(valueToCheck) % minutesStep !== 0) { + return false; + } + + if (shouldDisableClock?.(utils.toJsDate(valueToCheck).getTime(), 'hours')) { + return false; + } + + if (shouldDisableTime) { + return !shouldDisableTime(valueToCheck, 'hours'); + } + + return true; + }; + + return !containsValidTime() || !isValidValue(); + }, + [ + disableIgnoringDatePartForTimeValidation, + utils, + minTime, + maxTime, + disableFuture, + now, + disablePast, + minutesStep, + shouldDisableClock, + shouldDisableTime, + ], + ); + + const timeOptions = React.useMemo(() => { + const startOfDay = utils.startOfDay(selectedTimeOrMidnight); + return [ + startOfDay, + ...Array.from({ length: Math.ceil((24 * 60) / timeStep) - 1 }, (_, index) => + utils.addMinutes(startOfDay, timeStep * (index + 1)), + ), + utils.endOfDay(selectedTimeOrMidnight), + ]; + }, [selectedTimeOrMidnight, timeStep, utils]); + + return ( + + + {timeOptions.map((option) => { + if (skipDisabled && isTimeDisabled(option)) { + return null; + } + const isSelected = utils.isEqual(option, value); + return ( + !readOnly && handleItemSelect(option)} + selected={isSelected} + disabled={disabled || isTimeDisabled(option)} + disableRipple={readOnly} + role="option" + // aria-readonly is not supported here and does not have any effect + aria-disabled={readOnly} + aria-selected={isSelected} + {...clockItemProps} + > + {utils.format(option, ampm ? 'fullTime12h' : 'fullTime24h')} + + ); + })} + + + ); +}) as DigitalClockComponent; + +DigitalClock.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * 12h/24h view for hour selection clock. + * @default `utils.is12HourCycleInCurrentLocale()` + */ + ampm: PropTypes.bool, + /** + * If `true`, the main element is focused during the first mount. + * This main element is: + * - the element chosen by the visible view if any (i.e: the selected day on the `day` view). + * - the `input` element if there is a field rendered. + */ + autoFocus: PropTypes.bool, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + className: PropTypes.string, + /** + * Overrideable components. + * @default {} + * @deprecated Please use `slots`. + */ + components: PropTypes.object, + /** + * The props used for each component slot. + * @default {} + * @deprecated Please use `slotProps`. + */ + componentsProps: PropTypes.object, + /** + * The default selected value. + * Used when the component is not controlled. + */ + defaultValue: PropTypes.any, + /** + * If `true`, the picker views and text field are disabled. + * @default false + */ + disabled: PropTypes.bool, + /** + * If `true`, disable values after the current date for date components, time for time components and both for date time components. + * @default false + */ + disableFuture: PropTypes.bool, + /** + * Do not ignore date part when validating min/max time. + * @default false + */ + disableIgnoringDatePartForTimeValidation: PropTypes.bool, + /** + * If `true`, disable values before the current date for date components, time for time components and both for date time components. + * @default false + */ + disablePast: PropTypes.bool, + /** + * Controlled focused view. + */ + focusedView: PropTypes.oneOf(['hours']), + /** + * Maximal selectable time. + * The date part of the object will be ignored unless `props.disableIgnoringDatePartForTimeValidation === true`. + */ + maxTime: PropTypes.any, + /** + * Minimal selectable time. + * The date part of the object will be ignored unless `props.disableIgnoringDatePartForTimeValidation === true`. + */ + minTime: PropTypes.any, + /** + * Step over minutes. + * @default 1 + */ + minutesStep: PropTypes.number, + /** + * Callback fired when the value changes. + * @template TDate + * @param {TDate | null} value The new value. + * @param {PickerSelectionState | undefined} selectionState Indicates if the date selection is complete. + * @param {TView | undefined} selectedView Indicates the view in which the selection has been made. + */ + onChange: PropTypes.func, + /** + * Callback fired on focused view change. + * @template TView + * @param {TView} view The new view to focus or not. + * @param {boolean} hasFocus `true` if the view should be focused. + */ + onFocusedViewChange: PropTypes.func, + /** + * Callback fired on view change. + * @template TView + * @param {TView} view The new view. + */ + onViewChange: PropTypes.func, + /** + * The default visible view. + * Used when the component view is not controlled. + * Must be a valid option from `views` list. + */ + openTo: PropTypes.oneOf(['hours']), + /** + * If `true`, the picker views and text field are read-only. + * @default false + */ + readOnly: PropTypes.bool, + /** + * Disable specific clock time. + * @param {number} clockValue The value to check. + * @param {TimeView} view The clock type of the timeValue. + * @returns {boolean} If `true` the time will be disabled. + * @deprecated Consider using `shouldDisableTime`. + */ + shouldDisableClock: PropTypes.func, + /** + * Disable specific time. + * @template TDate + * @param {TDate} value The value to check. + * @param {TimeView} view The clock type of the timeValue. + * @returns {boolean} If `true` the time will be disabled. + */ + shouldDisableTime: PropTypes.func, + /** + * If `true`, disabled digital clock items will not be rendered. + * @default false + */ + skipDisabled: PropTypes.bool, + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overrideable component slots. + * @default {} + */ + slots: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), + /** + * The time steps between two time options. + * For example, if `timeStep = 45`, then the available time options will be `[00:00, 00:45, 01:30, 02:15, 03:00, etc.]`. + * @default 30 + */ + timeStep: PropTypes.number, + /** + * The selected value. + * Used when the component is controlled. + */ + value: PropTypes.any, + /** + * The visible view. + * Used when the component view is controlled. + * Must be a valid option from `views` list. + */ + view: PropTypes.oneOf(['hours']), + /** + * Available views. + */ + views: PropTypes.arrayOf(PropTypes.oneOf(['hours'])), +} as any; diff --git a/packages/x-date-pickers/src/DigitalClock/DigitalClock.types.ts b/packages/x-date-pickers/src/DigitalClock/DigitalClock.types.ts new file mode 100644 index 0000000000000..3d17c19a5cab3 --- /dev/null +++ b/packages/x-date-pickers/src/DigitalClock/DigitalClock.types.ts @@ -0,0 +1,57 @@ +import { SlotComponentProps } from '@mui/base/utils'; +import MenuItem from '@mui/material/MenuItem'; +import { DigitalClockClasses } from './digitalClockClasses'; +import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { + BaseClockProps, + DigitalClockOnlyProps, + ExportedBaseClockProps, +} from '../internals/models/props/clock'; +import { TimeView } from '../models'; + +export interface ExportedDigitalClockProps + extends ExportedBaseClockProps, + DigitalClockOnlyProps {} + +export interface DigitalClockSlotsComponent { + /** + * Component responsible for rendering a single digital clock item. + * @default MenuItem from '@mui/material' + */ + DigitalClockItem?: React.ElementType; +} + +export interface DigitalClockSlotsComponentsProps { + digitalClockItem?: SlotComponentProps>; +} + +export interface DigitalClockProps + extends ExportedDigitalClockProps, + BaseClockProps> { + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial; + /** + * Overrideable components. + * @default {} + * @deprecated Please use `slots`. + */ + components?: DigitalClockSlotsComponent; + /** + * The props used for each component slot. + * @default {} + * @deprecated Please use `slotProps`. + */ + componentsProps?: DigitalClockSlotsComponentsProps; + /** + * Overrideable component slots. + * @default {} + */ + slots?: UncapitalizeObjectKeys; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: DigitalClockSlotsComponentsProps; +} diff --git a/packages/x-date-pickers/src/DigitalClock/digitalClockClasses.ts b/packages/x-date-pickers/src/DigitalClock/digitalClockClasses.ts new file mode 100644 index 0000000000000..a2bd879ae5c2b --- /dev/null +++ b/packages/x-date-pickers/src/DigitalClock/digitalClockClasses.ts @@ -0,0 +1,23 @@ +import generateUtilityClass from '@mui/utils/generateUtilityClass'; +import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; + +export interface DigitalClockClasses { + /** Styles applied to the root element. */ + root: string; + /** Styles applied to the list (by default: MenuList) element. */ + list: string; + /** Styles applied to the list item (by default: MenuItem) element. */ + item: string; +} + +export type DigitalClockClassKey = keyof DigitalClockClasses; + +export function getDigitalClockUtilityClass(slot: string) { + return generateUtilityClass('MuiDigitalClock', slot); +} + +export const digitalClockClasses: DigitalClockClasses = generateUtilityClasses('MuiDigitalClock', [ + 'root', + 'list', + 'item', +]); diff --git a/packages/x-date-pickers/src/DigitalClock/index.ts b/packages/x-date-pickers/src/DigitalClock/index.ts new file mode 100644 index 0000000000000..ccbe0d812c4c2 --- /dev/null +++ b/packages/x-date-pickers/src/DigitalClock/index.ts @@ -0,0 +1,10 @@ +export { DigitalClock } from './DigitalClock'; +export type { + DigitalClockProps, + DigitalClockSlotsComponent, + DigitalClockSlotsComponentsProps, + ExportedDigitalClockProps, +} from './DigitalClock.types'; + +export { digitalClockClasses, getDigitalClockUtilityClass } from './digitalClockClasses'; +export type { DigitalClockClasses, DigitalClockClassKey } from './digitalClockClasses'; diff --git a/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx b/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx new file mode 100644 index 0000000000000..b3c358217efbc --- /dev/null +++ b/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import { screen, userEvent, describeConformance } from '@mui/monorepo/test/utils'; +import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; +import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { createPickerRenderer, adapterToUse, wrapPickerMount } from 'test/utils/pickers-utils'; +import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; +import { expect } from 'chai'; + +describe(' - Describes', () => { + const { render, clock } = createPickerRenderer({ clock: 'fake' }); + + describeValidation(DigitalClock, () => ({ + render, + clock, + views: ['hours'], + componentFamily: 'digital-clock', + variant: 'desktop', + })); + + describeConformance(, () => ({ + classes: {} as any, + render, + muiName: 'MuiDigitalClock', + wrapMount: wrapPickerMount, + refInstanceof: window.HTMLDivElement, + skip: [ + 'componentProp', + 'componentsProp', + 'themeDefaultProps', + 'themeStyleOverrides', + 'themeVariants', + 'mergeClassName', + 'propsSpread', + 'rootClass', + 'reactTestRenderer', + ], + })); + + describeValue(DigitalClock, () => ({ + render, + componentFamily: 'digital-clock', + type: 'time', + variant: 'desktop', + defaultProps: { + views: ['hours'], + }, + values: [ + adapterToUse.date(new Date(2018, 0, 1, 15, 30)), + adapterToUse.date(new Date(2018, 0, 1, 17, 0)), + ], + emptyValue: null, + clock, + assertRenderedValue: (expectedValue: any) => { + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + const selectedItem = screen.queryByRole('option', { selected: true }); + if (!expectedValue) { + expect(selectedItem).to.equal(null); + } else { + expect(selectedItem).to.have.text( + adapterToUse.format(expectedValue, hasMeridiem ? 'fullTime12h' : 'fullTime24h'), + ); + } + }, + setNewValue: (value) => { + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + const formattedLabel = adapterToUse.format( + value, + hasMeridiem ? 'fullTime12h' : 'fullTime24h', + ); + userEvent.mousePress(screen.getByRole('option', { name: formattedLabel })); + + return value; + }, + })); +}); diff --git a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx index 802f55a6acc9a..5635d34323ae6 100644 --- a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx +++ b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx @@ -22,10 +22,11 @@ const MobileTimePicker = React.forwardRef(function MobileTimePicker( const localeText = useLocaleText(); // Props with the default values common to all time pickers - const defaultizedProps = useTimePickerDefaultizedProps>( - inProps, - 'MuiMobileTimePicker', - ); + const defaultizedProps = useTimePickerDefaultizedProps< + TDate, + TimeView, + MobileTimePickerProps + >(inProps, 'MuiMobileTimePicker'); const viewRenderers: PickerViewRendererLookup = { hours: renderTimeViewClock, diff --git a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.types.ts b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.types.ts index a9802531c8dc3..98412691d2f4f 100644 --- a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.types.ts +++ b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.types.ts @@ -10,39 +10,44 @@ import { } from '../TimePicker/shared'; import { MakeOptional } from '../internals/models/helpers'; import { TimeView } from '../models'; +import { TimeViewWithMeridiem } from '../internals/models'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; -export interface MobileTimePickerSlotsComponent - extends BaseTimePickerSlotsComponent, - MakeOptional, 'Field'> {} +export interface MobileTimePickerSlotsComponent< + TDate, + TView extends TimeViewWithMeridiem = TimeView, +> extends BaseTimePickerSlotsComponent, + MakeOptional, 'Field'> {} -export interface MobileTimePickerSlotsComponentsProps - extends BaseTimePickerSlotsComponentsProps, - ExportedUseMobilePickerSlotsComponentsProps {} +export interface MobileTimePickerSlotsComponentsProps< + TDate, + TView extends TimeViewWithMeridiem = TimeView, +> extends BaseTimePickerSlotsComponentsProps, + ExportedUseMobilePickerSlotsComponentsProps {} -export interface MobileTimePickerProps - extends BaseTimePickerProps, +export interface MobileTimePickerProps + extends BaseTimePickerProps, MobileOnlyPickerProps { /** * Overridable components. * @default {} * @deprecated Please use `slots`. */ - components?: MobileTimePickerSlotsComponent; + components?: MobileTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} * @deprecated Please use `slotProps`. */ - componentsProps?: MobileTimePickerSlotsComponentsProps; + componentsProps?: MobileTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: UncapitalizeObjectKeys>; /** * The props used for each component slot. * @default {} */ - slotProps?: MobileTimePickerSlotsComponentsProps; + slotProps?: MobileTimePickerSlotsComponentsProps; } diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx new file mode 100644 index 0000000000000..6095ab3f19470 --- /dev/null +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx @@ -0,0 +1,579 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import PropTypes from 'prop-types'; +import { styled, useThemeProps } from '@mui/material/styles'; +import useEventCallback from '@mui/utils/useEventCallback'; +import composeClasses from '@mui/utils/composeClasses'; +import useControlled from '@mui/utils/useControlled'; +import { useUtils, useNow, useLocaleText } from '../internals/hooks/useUtils'; +import { convertValueToMeridiem, createIsAfterIgnoreDatePart } from '../internals/utils/time-utils'; +import { useViews } from '../internals/hooks/useViews'; +import type { PickerSelectionState } from '../internals/hooks/usePicker'; +import { useMeridiemMode } from '../internals/hooks/date-helpers-hooks'; +import { PickerViewRoot } from '../internals/components/PickerViewRoot'; +import { getMultiSectionDigitalClockUtilityClass } from './multiSectionDigitalClockClasses'; +import { MultiSectionDigitalClockSection } from './MultiSectionDigitalClockSection'; +import { + MultiSectionDigitalClockProps, + MultiSectionDigitalClockViewProps, +} from './MultiSectionDigitalClock.types'; +import { getHourSectionOptions, getTimeSectionOptions } from './MultiSectionDigitalClock.utils'; +import { TimeStepOptions, TimeView } from '../models'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const useUtilityClasses = (ownerState: MultiSectionDigitalClockProps) => { + const { classes } = ownerState; + const slots = { + root: ['root'], + }; + + return composeClasses(slots, getMultiSectionDigitalClockUtilityClass, classes); +}; + +const MultiSectionDigitalClockRoot = styled(PickerViewRoot, { + name: 'MuiMultiSectionDigitalClock', + slot: 'Root', + overridesResolver: (_, styles) => styles.root, +})<{ ownerState: MultiSectionDigitalClockProps }>(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + width: '100%', + borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, +})); + +type MultiSectionDigitalClockComponent = (( + props: MultiSectionDigitalClockProps & React.RefAttributes, +) => JSX.Element) & { propTypes?: any }; + +export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDigitalClock< + TDate extends unknown, +>(inProps: MultiSectionDigitalClockProps, ref: React.Ref) { + const now = useNow(); + const utils = useUtils(); + const localeText = useLocaleText(); + + const props = useThemeProps({ + props: inProps, + name: 'MuiMultiSectionDigitalClock', + }); + + const { + ampm = utils.is12HourCycleInCurrentLocale(), + timeSteps: inTimeSteps, + autoFocus, + components, + componentsProps, + slots, + slotProps, + value: valueProp, + disableIgnoringDatePartForTimeValidation = false, + maxTime, + minTime, + disableFuture, + disablePast, + minutesStep = 1, + shouldDisableClock, + shouldDisableTime, + onChange, + defaultValue, + view: inView, + views: inViews = ['hours', 'minutes'], + openTo, + onViewChange, + focusedView: inFocusedView, + onFocusedViewChange, + className, + disabled, + readOnly, + skipDisabled = false, + ...other + } = props; + const timeSteps = React.useMemo>( + () => ({ + hours: 1, + minutes: 5, + seconds: 5, + ...inTimeSteps, + }), + [inTimeSteps], + ); + + const [value, setValue] = useControlled({ + name: 'MultiSectionDigitalClock', + state: 'value', + controlled: valueProp, + default: defaultValue ?? null, + }); + + const handleValueChange = useEventCallback( + ( + newValue: TDate | null, + selectionState?: PickerSelectionState, + selectedView?: TimeViewWithMeridiem, + ) => { + setValue(newValue); + onChange?.(newValue, selectionState, selectedView); + }, + ); + + const views = React.useMemo(() => { + if (!ampm || !inViews.includes('hours')) { + return inViews; + } + return inViews.includes('meridiem') ? inViews : [...inViews, 'meridiem']; + }, [ampm, inViews]); + + const { view, setValueAndGoToView, focusedView } = useViews({ + view: inView, + views, + openTo, + onViewChange, + onChange: handleValueChange, + focusedView: inFocusedView, + onFocusedViewChange, + }); + + const selectedTimeOrMidnight = React.useMemo( + () => value || utils.setSeconds(utils.setMinutes(utils.setHours(now, 0), 0), 0), + [value, now, utils], + ); + + const handleMeridiemValueChange = useEventCallback((newValue: TDate | null) => { + setValueAndGoToView(newValue, null, 'meridiem'); + }); + + const { meridiemMode, handleMeridiemChange } = useMeridiemMode( + selectedTimeOrMidnight, + ampm, + handleMeridiemValueChange, + 'finish', + ); + + const isTimeDisabled = React.useCallback( + (rawValue: number, viewType: TimeView) => { + const isAfter = createIsAfterIgnoreDatePart(disableIgnoringDatePartForTimeValidation, utils); + const shouldCheckPastEnd = + viewType === 'hours' || (viewType === 'minutes' && views.includes('seconds')); + + const containsValidTime = ({ start, end }: { start: TDate; end: TDate }) => { + if (minTime && isAfter(minTime, end)) { + return false; + } + + if (maxTime && isAfter(start, maxTime)) { + return false; + } + + if (disableFuture && isAfter(start, now)) { + return false; + } + + if (disablePast && isAfter(now, shouldCheckPastEnd ? end : start)) { + return false; + } + + return true; + }; + + const isValidValue = (timeValue: number, step = 1) => { + if (timeValue % step !== 0) { + return false; + } + + if (shouldDisableClock?.(timeValue, viewType)) { + return false; + } + + if (shouldDisableTime) { + switch (viewType) { + case 'hours': + return !shouldDisableTime(utils.setHours(selectedTimeOrMidnight, timeValue), 'hours'); + case 'minutes': + return !shouldDisableTime( + utils.setMinutes(selectedTimeOrMidnight, timeValue), + 'minutes', + ); + + case 'seconds': + return !shouldDisableTime( + utils.setSeconds(selectedTimeOrMidnight, timeValue), + 'seconds', + ); + + default: + return false; + } + } + + return true; + }; + + switch (viewType) { + case 'hours': { + const valueWithMeridiem = convertValueToMeridiem(rawValue, meridiemMode, ampm); + const dateWithNewHours = utils.setHours(selectedTimeOrMidnight, valueWithMeridiem); + const start = utils.setSeconds(utils.setMinutes(dateWithNewHours, 0), 0); + const end = utils.setSeconds(utils.setMinutes(dateWithNewHours, 59), 59); + + return !containsValidTime({ start, end }) || !isValidValue(valueWithMeridiem); + } + + case 'minutes': { + const dateWithNewMinutes = utils.setMinutes(selectedTimeOrMidnight, rawValue); + const start = utils.setSeconds(dateWithNewMinutes, 0); + const end = utils.setSeconds(dateWithNewMinutes, 59); + + return !containsValidTime({ start, end }) || !isValidValue(rawValue, minutesStep); + } + + case 'seconds': { + const dateWithNewSeconds = utils.setSeconds(selectedTimeOrMidnight, rawValue); + const start = dateWithNewSeconds; + const end = dateWithNewSeconds; + + return !containsValidTime({ start, end }) || !isValidValue(rawValue); + } + + default: + throw new Error('not supported'); + } + }, + [ + ampm, + selectedTimeOrMidnight, + disableIgnoringDatePartForTimeValidation, + maxTime, + meridiemMode, + minTime, + minutesStep, + shouldDisableClock, + shouldDisableTime, + utils, + disableFuture, + disablePast, + now, + views, + ], + ); + + const handleSectionChange = useEventCallback( + (sectionView: TimeViewWithMeridiem, newValue: TDate | null) => { + const viewIndex = views.indexOf(sectionView); + const nextView = views[viewIndex + 1]; + setValueAndGoToView(newValue, nextView, sectionView); + }, + ); + + const buildViewProps = React.useCallback( + (viewToBuild: TimeViewWithMeridiem): MultiSectionDigitalClockViewProps => { + switch (viewToBuild) { + case 'hours': { + return { + onChange: (hours) => { + const valueWithMeridiem = convertValueToMeridiem(hours, meridiemMode, ampm); + handleSectionChange( + 'hours', + utils.setHours(selectedTimeOrMidnight, valueWithMeridiem), + ); + }, + items: getHourSectionOptions({ + now, + value, + ampm, + utils, + isDisabled: (hours) => disabled || isTimeDisabled(hours, 'hours'), + timeStep: timeSteps.hours, + resolveAriaLabel: localeText.hoursClockNumberText, + }), + }; + } + + case 'minutes': { + return { + onChange: (minutes) => { + handleSectionChange('minutes', utils.setMinutes(selectedTimeOrMidnight, minutes)); + }, + items: getTimeSectionOptions({ + value: utils.getMinutes(selectedTimeOrMidnight), + isDisabled: (minutes) => disabled || isTimeDisabled(minutes, 'minutes'), + resolveLabel: (minutes) => utils.format(utils.setMinutes(now, minutes), 'minutes'), + timeStep: timeSteps.minutes, + hasValue: !!value, + resolveAriaLabel: localeText.minutesClockNumberText, + }), + }; + } + + case 'seconds': { + return { + onChange: (seconds) => { + handleSectionChange('seconds', utils.setSeconds(selectedTimeOrMidnight, seconds)); + }, + items: getTimeSectionOptions({ + value: utils.getSeconds(selectedTimeOrMidnight), + isDisabled: (seconds) => disabled || isTimeDisabled(seconds, 'seconds'), + resolveLabel: (seconds) => utils.format(utils.setSeconds(now, seconds), 'seconds'), + timeStep: timeSteps.seconds, + hasValue: !!value, + resolveAriaLabel: localeText.secondsClockNumberText, + }), + }; + } + + case 'meridiem': { + const amLabel = utils.getMeridiemText('am'); + const pmLabel = utils.getMeridiemText('pm'); + return { + onChange: handleMeridiemChange, + items: [ + { + value: 'am', + label: amLabel, + isSelected: () => !!value && meridiemMode === 'am', + ariaLabel: amLabel, + }, + { + value: 'pm', + label: pmLabel, + isSelected: () => !!value && meridiemMode === 'pm', + ariaLabel: pmLabel, + }, + ], + }; + } + + default: + throw new Error(`Unknown view: ${viewToBuild} found.`); + } + }, + [ + now, + value, + ampm, + utils, + timeSteps.hours, + timeSteps.minutes, + timeSteps.seconds, + localeText.hoursClockNumberText, + localeText.minutesClockNumberText, + localeText.secondsClockNumberText, + meridiemMode, + handleSectionChange, + selectedTimeOrMidnight, + disabled, + isTimeDisabled, + handleMeridiemChange, + ], + ); + + const viewTimeOptions = React.useMemo(() => { + return views.reduce((result, currentView) => { + return { ...result, [currentView]: buildViewProps(currentView) }; + }, {} as Record>); + }, [views, buildViewProps]); + + const ownerState = props; + const classes = useUtilityClasses(ownerState); + + return ( + + {Object.entries(viewTimeOptions).map(([timeView, viewOptions]) => ( + + ))} + + ); +}) as MultiSectionDigitalClockComponent; + +MultiSectionDigitalClock.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * 12h/24h view for hour selection clock. + * @default `utils.is12HourCycleInCurrentLocale()` + */ + ampm: PropTypes.bool, + /** + * If `true`, the main element is focused during the first mount. + * This main element is: + * - the element chosen by the visible view if any (i.e: the selected day on the `day` view). + * - the `input` element if there is a field rendered. + */ + autoFocus: PropTypes.bool, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + className: PropTypes.string, + /** + * Overrideable components. + * @default {} + * @deprecated Please use `slots`. + */ + components: PropTypes.object, + /** + * The props used for each component slot. + * @default {} + * @deprecated Please use `slotProps`. + */ + componentsProps: PropTypes.object, + /** + * The default selected value. + * Used when the component is not controlled. + */ + defaultValue: PropTypes.any, + /** + * If `true`, the picker views and text field are disabled. + * @default false + */ + disabled: PropTypes.bool, + /** + * If `true`, disable values after the current date for date components, time for time components and both for date time components. + * @default false + */ + disableFuture: PropTypes.bool, + /** + * Do not ignore date part when validating min/max time. + * @default false + */ + disableIgnoringDatePartForTimeValidation: PropTypes.bool, + /** + * If `true`, disable values before the current date for date components, time for time components and both for date time components. + * @default false + */ + disablePast: PropTypes.bool, + /** + * Controlled focused view. + */ + focusedView: PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']), + /** + * Maximal selectable time. + * The date part of the object will be ignored unless `props.disableIgnoringDatePartForTimeValidation === true`. + */ + maxTime: PropTypes.any, + /** + * Minimal selectable time. + * The date part of the object will be ignored unless `props.disableIgnoringDatePartForTimeValidation === true`. + */ + minTime: PropTypes.any, + /** + * Step over minutes. + * @default 1 + */ + minutesStep: PropTypes.number, + /** + * Callback fired when the value changes. + * @template TDate + * @param {TDate | null} value The new value. + * @param {PickerSelectionState | undefined} selectionState Indicates if the date selection is complete. + * @param {TView | undefined} selectedView Indicates the view in which the selection has been made. + */ + onChange: PropTypes.func, + /** + * Callback fired on focused view change. + * @template TView + * @param {TView} view The new view to focus or not. + * @param {boolean} hasFocus `true` if the view should be focused. + */ + onFocusedViewChange: PropTypes.func, + /** + * Callback fired on view change. + * @template TView + * @param {TView} view The new view. + */ + onViewChange: PropTypes.func, + /** + * The default visible view. + * Used when the component view is not controlled. + * Must be a valid option from `views` list. + */ + openTo: PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']), + /** + * If `true`, the picker views and text field are read-only. + * @default false + */ + readOnly: PropTypes.bool, + /** + * Disable specific clock time. + * @param {number} clockValue The value to check. + * @param {TimeView} view The clock type of the timeValue. + * @returns {boolean} If `true` the time will be disabled. + * @deprecated Consider using `shouldDisableTime`. + */ + shouldDisableClock: PropTypes.func, + /** + * Disable specific time. + * @template TDate + * @param {TDate} value The value to check. + * @param {TimeView} view The clock type of the timeValue. + * @returns {boolean} If `true` the time will be disabled. + */ + shouldDisableTime: PropTypes.func, + /** + * If `true`, disabled digital clock items will not be rendered. + * @default false + */ + skipDisabled: PropTypes.bool, + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overrideable component slots. + * @default {} + */ + slots: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), + /** + * The time steps between two time unit options. + * For example, if `timeStep.minutes = 8`, then the available minute options will be `[0, 8, 16, 24, 32, 40, 48, 56]`. + * @default{ hours: 1, minutes: 5, seconds: 5 } + */ + timeSteps: PropTypes.shape({ + hours: PropTypes.number, + minutes: PropTypes.number, + seconds: PropTypes.number, + }), + /** + * The selected value. + * Used when the component is controlled. + */ + value: PropTypes.any, + /** + * The visible view. + * Used when the component view is controlled. + * Must be a valid option from `views` list. + */ + view: PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']), + /** + * Available views. + */ + views: PropTypes.arrayOf(PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']).isRequired), +} as any; diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.types.ts b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.types.ts new file mode 100644 index 0000000000000..bc51ef50d49f7 --- /dev/null +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.types.ts @@ -0,0 +1,69 @@ +import { SlotComponentProps } from '@mui/base/utils'; +import MenuItem from '@mui/material/MenuItem'; +import { MultiSectionDigitalClockClasses } from './multiSectionDigitalClockClasses'; +import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { + BaseClockProps, + ExportedBaseClockProps, + MultiSectionDigitalClockOnlyProps, +} from '../internals/models/props/clock'; +import { MultiSectionDigitalClockSectionProps } from './MultiSectionDigitalClockSection'; +import { TimeViewWithMeridiem } from '../internals/models'; + +export interface MultiSectionDigitalClockOption { + isDisabled?: (value: TValue) => boolean; + isSelected: (value: TValue) => boolean; + label: string; + value: TValue; + ariaLabel: string; +} + +export interface ExportedMultiSectionDigitalClockProps + extends ExportedBaseClockProps, + MultiSectionDigitalClockOnlyProps {} + +export interface MultiSectionDigitalClockViewProps + extends Pick, 'onChange' | 'items'> {} + +export interface MultiSectionDigitalClockSlotsComponent { + /** + * Component responsible for rendering a single multi section digital clock section item. + * @default MenuItem from '@mui/material' + */ + DigitalClockSectionItem?: React.ElementType; +} + +export interface MultiSectionDigitalClockSlotsComponentsProps { + digitalClockSectionItem?: SlotComponentProps>; +} + +export interface MultiSectionDigitalClockProps + extends ExportedMultiSectionDigitalClockProps, + BaseClockProps { + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial; + /** + * Overrideable components. + * @default {} + * @deprecated Please use `slots`. + */ + components?: MultiSectionDigitalClockSlotsComponent; + /** + * The props used for each component slot. + * @default {} + * @deprecated Please use `slotProps`. + */ + componentsProps?: MultiSectionDigitalClockSlotsComponentsProps; + /** + * Overrideable component slots. + * @default {} + */ + slots?: UncapitalizeObjectKeys; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: MultiSectionDigitalClockSlotsComponentsProps; +} diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.utils.ts b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.utils.ts new file mode 100644 index 0000000000000..da44fac419543 --- /dev/null +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.utils.ts @@ -0,0 +1,98 @@ +import { MuiPickersAdapter } from '../models'; +import { MultiSectionDigitalClockOption } from './MultiSectionDigitalClock.types'; + +interface IGetHoursSectionOptions { + now: TDate; + value: TDate | null; + utils: MuiPickersAdapter; + ampm: boolean; + isDisabled: (value: number) => boolean; + timeStep: number; + resolveAriaLabel: (value: string) => string; +} + +export const getHourSectionOptions = ({ + now, + value, + utils, + ampm, + isDisabled, + resolveAriaLabel, + timeStep, +}: IGetHoursSectionOptions): MultiSectionDigitalClockOption[] => { + const currentHours = value ? utils.getHours(value) : null; + + const result: MultiSectionDigitalClockOption[] = []; + + const isSelected = (hour: number) => { + if (currentHours === null) { + return false; + } + + if (ampm) { + if (hour === 12) { + return currentHours === 12 || currentHours === 0; + } + + return currentHours === hour || currentHours - 12 === hour; + } + + return currentHours === hour; + }; + + const endHour = ampm ? 11 : 23; + for (let hour = 0; hour <= endHour; hour += timeStep) { + let label = utils.format(utils.setHours(now, hour), ampm ? 'hours12h' : 'hours24h'); + const ariaLabel = resolveAriaLabel(parseInt(label, 10).toString()); + + label = utils.formatNumber(label); + + result.push({ + value: hour, + label, + isSelected, + isDisabled, + ariaLabel, + }); + } + return result; +}; + +interface IGetTimeSectionOptions { + value: number | null; + isDisabled: (value: number) => boolean; + timeStep: number; + resolveLabel: (value: number) => string; + hasValue?: boolean; + resolveAriaLabel: (value: string) => string; +} + +export const getTimeSectionOptions = ({ + value, + isDisabled, + timeStep, + resolveLabel, + resolveAriaLabel, + hasValue = true, +}: IGetTimeSectionOptions): MultiSectionDigitalClockOption[] => { + const isSelected = (timeValue: number) => { + if (value === null) { + return false; + } + + return hasValue && value === timeValue; + }; + + return [ + ...Array.from({ length: Math.ceil(60 / timeStep) }, (_, index) => { + const timeValue = timeStep * index; + return { + value: timeValue, + label: resolveLabel(timeValue), + isDisabled, + isSelected, + ariaLabel: resolveAriaLabel(timeValue.toString()), + }; + }), + ]; +}; diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx new file mode 100644 index 0000000000000..f6b032929f39c --- /dev/null +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx @@ -0,0 +1,201 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import { alpha, styled, useThemeProps } from '@mui/material/styles'; +import composeClasses from '@mui/utils/composeClasses'; +import MenuList from '@mui/material/MenuList'; +import MenuItem from '@mui/material/MenuItem'; +import useForkRef from '@mui/utils/useForkRef'; +import { + MultiSectionDigitalClockSectionClasses, + getMultiSectionDigitalClockSectionUtilityClass, +} from './multiSectionDigitalClockSectionClasses'; +import type { + MultiSectionDigitalClockOption, + MultiSectionDigitalClockSlotsComponent, + MultiSectionDigitalClockSlotsComponentsProps, +} from './MultiSectionDigitalClock.types'; +import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { DIGITAL_CLOCK_VIEW_HEIGHT } from '../internals/constants/dimensions'; + +export interface ExportedMultiSectionDigitalClockSectionProps { + className?: string; + classes?: Partial; + slots?: UncapitalizeObjectKeys; + slotProps?: MultiSectionDigitalClockSlotsComponentsProps; +} + +export interface MultiSectionDigitalClockSectionProps + extends ExportedMultiSectionDigitalClockSectionProps { + autoFocus?: boolean; + disabled?: boolean; + readOnly?: boolean; + items: MultiSectionDigitalClockOption[]; + onChange: (value: TValue) => void; + active?: boolean; + skipDisabled?: boolean; + role?: string; +} + +const useUtilityClasses = (ownerState: MultiSectionDigitalClockSectionProps) => { + const { classes } = ownerState; + const slots = { + root: ['root'], + item: ['item'], + }; + + return composeClasses(slots, getMultiSectionDigitalClockSectionUtilityClass, classes); +}; + +const MultiSectionDigitalClockSectionRoot = styled(MenuList, { + name: 'MuiMultiSectionDigitalClockSection', + slot: 'Root', + overridesResolver: (_, styles) => styles.root, +})<{ ownerState: MultiSectionDigitalClockSectionProps & { alreadyRendered: boolean } }>( + ({ theme, ownerState }) => ({ + maxHeight: DIGITAL_CLOCK_VIEW_HEIGHT, + width: 56, + padding: 0, + overflow: 'hidden', + scrollBehavior: ownerState.alreadyRendered ? 'smooth' : 'auto', + '&:hover': { + overflowY: 'auto', + }, + '&:not(:first-of-type)': { + borderLeft: `1px solid ${(theme.vars || theme).palette.divider}`, + }, + '&:after': { + display: 'block', + content: '""', + height: 188, + }, + }), +); + +const MultiSectionDigitalClockSectionItem = styled(MenuItem, { + name: 'MuiMultiSectionDigitalClockSection', + slot: 'Item', + overridesResolver: (_, styles) => styles.item, +})(({ theme }) => ({ + padding: 8, + margin: '2px 4px', + width: 48, + justifyContent: 'center', + '&:first-of-type': { + marginTop: 4, + }, + '&:hover': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.hoverOpacity})` + : alpha(theme.palette.primary.main, theme.palette.action.hoverOpacity), + }, + '&.Mui-selected': { + backgroundColor: (theme.vars || theme).palette.primary.main, + color: (theme.vars || theme).palette.primary.contrastText, + '&:focus-visible, &:hover': { + backgroundColor: (theme.vars || theme).palette.primary.dark, + }, + }, + '&.Mui-focusVisible': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.focusOpacity})` + : alpha(theme.palette.primary.main, theme.palette.action.focusOpacity), + }, +})); + +type MultiSectionDigitalClockSectionComponent = ( + props: MultiSectionDigitalClockSectionProps & React.RefAttributes, +) => JSX.Element & { propTypes?: any }; + +/** + * @ignore - internal component. + */ +export const MultiSectionDigitalClockSection = React.forwardRef( + function MultiSectionDigitalClockSection( + inProps: MultiSectionDigitalClockSectionProps, + ref: React.Ref, + ) { + const containerRef = React.useRef(null); + const handleRef = useForkRef(ref, containerRef); + + const props = useThemeProps({ + props: inProps, + name: 'MuiMultiSectionDigitalClockSection', + }); + + const { + autoFocus, + onChange, + className, + disabled, + readOnly, + items, + active, + slots, + slotProps, + skipDisabled, + ...other + } = props; + + const ownerState = React.useMemo( + () => ({ ...props, alreadyRendered: !!containerRef.current }), + [props], + ); + const classes = useUtilityClasses(ownerState); + const DigitalClockSectionItem = + slots?.digitalClockSectionItem ?? MultiSectionDigitalClockSectionItem; + + React.useEffect(() => { + if (containerRef.current === null) { + return; + } + const selectedItem = containerRef.current.querySelector( + '[role="option"][aria-selected="true"]', + ); + if (!selectedItem) { + return; + } + if (active && autoFocus) { + selectedItem.focus(); + } + const offsetTop = selectedItem.offsetTop; + + // Subtracting the 4px of extra margin intended for the first visible section item + containerRef.current.scrollTop = offsetTop - 4; + }); + + return ( + + {items.map((option) => { + if (skipDisabled && option.isDisabled?.(option.value)) { + return null; + } + const isSelected = option.isSelected(option.value); + return ( + !readOnly && onChange(option.value)} + selected={isSelected} + disabled={disabled ?? option.isDisabled?.(option.value)} + disableRipple={readOnly} + role="option" + // aria-readonly is not supported here and does not have any effect + aria-disabled={readOnly} + aria-label={option.ariaLabel} + aria-selected={isSelected} + {...slotProps?.digitalClockSectionItem} + > + {option.label} + + ); + })} + + ); + }, +) as MultiSectionDigitalClockSectionComponent; diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/index.ts b/packages/x-date-pickers/src/MultiSectionDigitalClock/index.ts new file mode 100644 index 0000000000000..1e3d5b0974993 --- /dev/null +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/index.ts @@ -0,0 +1,21 @@ +export { MultiSectionDigitalClock } from './MultiSectionDigitalClock'; +export type { + MultiSectionDigitalClockProps, + MultiSectionDigitalClockSlotsComponent, + MultiSectionDigitalClockSlotsComponentsProps, +} from './MultiSectionDigitalClock.types'; + +export { multiSectionDigitalClockSectionClasses } from './multiSectionDigitalClockSectionClasses'; +export type { + MultiSectionDigitalClockSectionClasses, + MultiSectionDigitalClockSectionClassKey, +} from './multiSectionDigitalClockSectionClasses'; +export type { ExportedMultiSectionDigitalClockSectionProps } from './MultiSectionDigitalClockSection'; +export { + multiSectionDigitalClockClasses, + getMultiSectionDigitalClockUtilityClass, +} from './multiSectionDigitalClockClasses'; +export type { + MultiSectionDigitalClockClasses, + MultiSectionDigitalClockClassKey, +} from './multiSectionDigitalClockClasses'; diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/multiSectionDigitalClockClasses.ts b/packages/x-date-pickers/src/MultiSectionDigitalClock/multiSectionDigitalClockClasses.ts new file mode 100644 index 0000000000000..84a4c2e3b5649 --- /dev/null +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/multiSectionDigitalClockClasses.ts @@ -0,0 +1,16 @@ +import generateUtilityClass from '@mui/utils/generateUtilityClass'; +import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; + +export interface MultiSectionDigitalClockClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type MultiSectionDigitalClockClassKey = keyof MultiSectionDigitalClockClasses; + +export function getMultiSectionDigitalClockUtilityClass(slot: string) { + return generateUtilityClass('MuiMultiSectionDigitalClock', slot); +} + +export const multiSectionDigitalClockClasses: MultiSectionDigitalClockClasses = + generateUtilityClasses('MuiMultiSectionDigitalClock', ['root']); diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/multiSectionDigitalClockSectionClasses.ts b/packages/x-date-pickers/src/MultiSectionDigitalClock/multiSectionDigitalClockSectionClasses.ts new file mode 100644 index 0000000000000..97721ba6ef3ad --- /dev/null +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/multiSectionDigitalClockSectionClasses.ts @@ -0,0 +1,18 @@ +import generateUtilityClass from '@mui/utils/generateUtilityClass'; +import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; + +export interface MultiSectionDigitalClockSectionClasses { + /** Styles applied to the root (list) element. */ + root: string; + /** Styles applied to the list item (by default: MenuItem) element. */ + item: string; +} + +export type MultiSectionDigitalClockSectionClassKey = keyof MultiSectionDigitalClockSectionClasses; + +export function getMultiSectionDigitalClockSectionUtilityClass(slot: string) { + return generateUtilityClass('MuiMultiSectionDigitalClock', slot); +} + +export const multiSectionDigitalClockSectionClasses: MultiSectionDigitalClockSectionClasses = + generateUtilityClasses('MuiMultiSectionDigitalClock', ['root', 'item']); diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx new file mode 100644 index 0000000000000..02cb357fb8cb7 --- /dev/null +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx @@ -0,0 +1,90 @@ +import * as React from 'react'; +import { screen, userEvent, describeConformance } from '@mui/monorepo/test/utils'; +import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; +import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { createPickerRenderer, adapterToUse, wrapPickerMount } from 'test/utils/pickers-utils'; +import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; +import { expect } from 'chai'; + +describe(' - Describes', () => { + const { render, clock } = createPickerRenderer({ clock: 'fake' }); + + describeValidation(MultiSectionDigitalClock, () => ({ + render, + clock, + views: ['hours', 'minutes'], + componentFamily: 'multi-section-digital-clock', + variant: 'desktop', + })); + + describeConformance(, () => ({ + classes: {} as any, + render, + muiName: 'MuiMultiSectionDigitalClock', + wrapMount: wrapPickerMount, + refInstanceof: window.HTMLDivElement, + skip: [ + 'componentProp', + 'componentsProp', + 'themeDefaultProps', + 'themeStyleOverrides', + 'themeVariants', + 'mergeClassName', + 'propsSpread', + 'rootClass', + 'reactTestRenderer', + ], + })); + + describeValue(MultiSectionDigitalClock, () => ({ + render, + componentFamily: 'multi-section-digital-clock', + type: 'time', + variant: 'desktop', + values: [ + adapterToUse.date(new Date(2018, 0, 1, 15, 30)), + adapterToUse.date(new Date(2018, 0, 1, 16, 15)), + ], + emptyValue: null, + clock, + assertRenderedValue: (expectedValue: any) => { + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + const selectedItems = screen.queryAllByRole('option', { selected: true }); + if (!expectedValue) { + expect(selectedItems).to.have.length(0); + } else { + const hoursLabel = adapterToUse.format( + expectedValue, + hasMeridiem ? 'hours12h' : 'hours24h', + ); + const minutesLabel = adapterToUse.getMinutes(expectedValue).toString(); + expect(selectedItems[0]).to.have.text(hoursLabel); + expect(selectedItems[1]).to.have.text(minutesLabel); + if (hasMeridiem) { + expect(selectedItems[2]).to.have.text( + adapterToUse.getMeridiemText(adapterToUse.getHours(expectedValue) > 12 ? 'pm' : 'am'), + ); + } + } + }, + setNewValue: (value) => { + const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); + const hoursLabel = parseInt( + adapterToUse.format(value, hasMeridiem ? 'hours12h' : 'hours24h'), + 10, + ); + const minutesLabel = adapterToUse.getMinutes(value).toString(); + userEvent.mousePress(screen.getByRole('option', { name: `${hoursLabel} hours` })); + userEvent.mousePress(screen.getByRole('option', { name: `${minutesLabel} minutes` })); + if (hasMeridiem) { + userEvent.mousePress( + screen.getByRole('option', { + name: adapterToUse.getMeridiemText(adapterToUse.getHours(value) > 12 ? 'pm' : 'am'), + }), + ); + } + + return value; + }, + })); +}); diff --git a/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx b/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx index 4bbf908fbccbd..5a619a87f4a8b 100644 --- a/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx +++ b/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx @@ -6,7 +6,7 @@ import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { PickersLayoutProps } from './PickersLayout.types'; import { pickersLayoutClasses, getPickersLayoutUtilityClass } from './pickersLayoutClasses'; import usePickerLayout from './usePickerLayout'; -import { DateOrTimeView } from '../models'; +import { DateOrTimeViewWithMeridiem } from '../internals/models'; const useUtilityClasses = (ownerState: PickersLayoutProps) => { const { isLandscape, classes } = ownerState; @@ -70,9 +70,11 @@ export const PickersLayoutContentWrapper = styled('div', { flexDirection: 'column', }); -const PickersLayout = function PickersLayout( - inProps: PickersLayoutProps, -) { +const PickersLayout = function PickersLayout< + TValue, + TDate, + TView extends DateOrTimeViewWithMeridiem, +>(inProps: PickersLayoutProps) { const props = useThemeProps({ props: inProps, name: 'MuiPickersLayout' }); const { toolbar, content, tabs, actionBar, shortcuts } = usePickerLayout(props); @@ -161,9 +163,9 @@ PickersLayout.propTypes = { PropTypes.object, ]), value: PropTypes.any, - view: PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']), + view: PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']), views: PropTypes.arrayOf( - PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']).isRequired, + PropTypes.oneOf(['day', 'hours', 'meridiem', 'minutes', 'month', 'seconds', 'year']).isRequired, ).isRequired, wrapperVariant: PropTypes.oneOf(['desktop', 'mobile']), } as any; diff --git a/packages/x-date-pickers/src/PickersLayout/PickersLayout.types.ts b/packages/x-date-pickers/src/PickersLayout/PickersLayout.types.ts index fca18cf465fa1..1b9a223a5c54a 100644 --- a/packages/x-date-pickers/src/PickersLayout/PickersLayout.types.ts +++ b/packages/x-date-pickers/src/PickersLayout/PickersLayout.types.ts @@ -2,16 +2,19 @@ import * as React from 'react'; import { SxProps, Theme } from '@mui/material/styles'; import { SlotComponentProps } from '@mui/base/utils'; import { PickersActionBarProps } from '../PickersActionBar'; -import { DateOrTimeView } from '../models'; import { BaseToolbarProps, ExportedBaseToolbarProps } from '../internals/models/props/toolbar'; import { BaseTabsProps, ExportedBaseTabsProps } from '../internals/models/props/tabs'; import { UsePickerLayoutPropsResponseLayoutProps } from '../internals/hooks/usePicker/usePickerLayoutProps'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { PickersLayoutClasses } from './pickersLayoutClasses'; -import { WrapperVariant } from '../internals/models/common'; +import { DateOrTimeViewWithMeridiem, WrapperVariant } from '../internals/models/common'; import { PickersShortcutsProps } from '../PickersShortcuts'; -export interface ExportedPickersLayoutSlotsComponent { +export interface ExportedPickersLayoutSlotsComponent< + TValue, + TDate, + TView extends DateOrTimeViewWithMeridiem, +> { /** * Custom component for the action bar, it is placed below the picker views. * @default PickersActionBar @@ -31,7 +34,7 @@ export interface ExportedPickersLayoutSlotsComponent; } -interface PickersLayoutActionBarOwnerState +interface PickersLayoutActionBarOwnerState extends PickersLayoutProps { wrapperVariant: WrapperVariant; } @@ -43,7 +46,7 @@ interface PickersShortcutsOwnerState extends PickersShortcutsProps { /** * Props passed down to the action bar component. @@ -69,8 +72,11 @@ export interface ExportedPickersLayoutSlotsComponentsProps< layout?: Partial>; } -export interface PickersLayoutSlotsComponent - extends ExportedPickersLayoutSlotsComponent { +export interface PickersLayoutSlotsComponent< + TValue, + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends ExportedPickersLayoutSlotsComponent { /** * Tabs enabling toggling between views. */ @@ -82,8 +88,11 @@ export interface PickersLayoutSlotsComponent>; } -export interface PickersLayoutSlotsComponentsProps - extends ExportedPickersLayoutSlotsComponentsProps { +export interface PickersLayoutSlotsComponentsProps< + TValue, + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends ExportedPickersLayoutSlotsComponentsProps { /** * Props passed down to the tabs component. */ @@ -94,7 +103,7 @@ export interface PickersLayoutSlotsComponentsProps +export interface PickersLayoutProps extends Omit, 'value'> { value?: TValue; className?: string; diff --git a/packages/x-date-pickers/src/PickersLayout/usePickerLayout.tsx b/packages/x-date-pickers/src/PickersLayout/usePickerLayout.tsx index 608c112371724..57969b59b7160 100644 --- a/packages/x-date-pickers/src/PickersLayout/usePickerLayout.tsx +++ b/packages/x-date-pickers/src/PickersLayout/usePickerLayout.tsx @@ -4,12 +4,12 @@ import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { PickersActionBar, PickersActionBarAction } from '../PickersActionBar'; import { PickersLayoutProps, SubComponents } from './PickersLayout.types'; import { getPickersLayoutUtilityClass } from './pickersLayoutClasses'; -import { DateOrTimeView } from '../models'; import { PickersShortcuts } from '../PickersShortcuts'; import { BaseToolbarProps } from '../internals/models/props/toolbar'; import { uncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { DateOrTimeViewWithMeridiem } from '../internals/models'; -function toolbarHasView( +function toolbarHasView( toolbarProps: BaseToolbarProps | any, ): toolbarProps is BaseToolbarProps { return toolbarProps.view !== null; @@ -30,13 +30,16 @@ const useUtilityClasses = (ownerState: PickersLayoutProps) => { return composeClasses(slots, getPickersLayoutUtilityClass, classes); }; -interface PickersLayoutPropsWithValueRequired - extends PickersLayoutProps { +interface PickersLayoutPropsWithValueRequired< + TValue, + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends PickersLayoutProps { value: TValue; } interface UsePickerLayoutResponse extends SubComponents {} -const usePickerLayout = ( +const usePickerLayout = ( props: PickersLayoutProps, ): UsePickerLayoutResponse => { const { diff --git a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx index 2a0006f2bcf53..dd6e8b27c4b80 100644 --- a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx +++ b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx @@ -17,10 +17,11 @@ const StaticTimePicker = React.forwardRef(function StaticTimePicker( inProps: StaticTimePickerProps, ref: React.Ref, ) { - const defaultizedProps = useTimePickerDefaultizedProps>( - inProps, - 'MuiStaticTimePicker', - ); + const defaultizedProps = useTimePickerDefaultizedProps< + TDate, + TimeView, + StaticTimePickerProps + >(inProps, 'MuiStaticTimePicker'); const displayStaticWrapperAs = defaultizedProps.displayStaticWrapperAs ?? 'mobile'; const ampmInClock = defaultizedProps.ampmInClock ?? displayStaticWrapperAs === 'desktop'; diff --git a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.types.ts b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.types.ts index 13b91db763923..28c08d303b017 100644 --- a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.types.ts +++ b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.types.ts @@ -20,7 +20,7 @@ export interface StaticTimePickerSlotsComponentsProps UseStaticPickerSlotsComponentsProps {} export interface StaticTimePickerProps - extends BaseTimePickerProps, + extends BaseTimePickerProps, MakeOptional { /** * Overridable components. diff --git a/packages/x-date-pickers/src/TimeClock/TimeClock.tsx b/packages/x-date-pickers/src/TimeClock/TimeClock.tsx index 7b95d7de5d13a..e074df8b0a510 100644 --- a/packages/x-date-pickers/src/TimeClock/TimeClock.tsx +++ b/packages/x-date-pickers/src/TimeClock/TimeClock.tsx @@ -99,6 +99,8 @@ export const TimeClock = React.forwardRef(function TimeClock - autoFocus={autoFocus} + autoFocus={autoFocus ?? !!focusedView} ampmInClock={ampmInClock && views.includes('hours')} value={value} type={view} @@ -396,7 +400,10 @@ TimeClock.propTypes = { */ ampmInClock: PropTypes.bool, /** - * Set to `true` if focus should be moved to clock picker. + * If `true`, the main element is focused during the first mount. + * This main element is: + * - the element chosen by the visible view if any (i.e: the selected day on the `day` view). + * - the `input` element if there is a field rendered. */ autoFocus: PropTypes.bool, /** @@ -422,7 +429,7 @@ TimeClock.propTypes = { */ defaultValue: PropTypes.any, /** - * If `true`, the picker and text field are disabled. + * If `true`, the picker views and text field are disabled. * @default false */ disabled: PropTypes.bool, @@ -441,6 +448,10 @@ TimeClock.propTypes = { * @default false */ disablePast: PropTypes.bool, + /** + * Controlled focused view. + */ + focusedView: PropTypes.oneOf(['hours', 'minutes', 'seconds']), /** * Maximal selectable time. * The date part of the object will be ignored unless `props.disableIgnoringDatePartForTimeValidation === true`. @@ -461,20 +472,30 @@ TimeClock.propTypes = { * @template TDate * @param {TDate | null} value The new value. * @param {PickerSelectionState | undefined} selectionState Indicates if the date selection is complete. + * @param {TView | undefined} selectedView Indicates the view in which the selection has been made. */ onChange: PropTypes.func, + /** + * Callback fired on focused view change. + * @template TView + * @param {TView} view The new view to focus or not. + * @param {boolean} hasFocus `true` if the view should be focused. + */ + onFocusedViewChange: PropTypes.func, /** * Callback fired on view change. - * @param {TimeView} view The new view. + * @template TView + * @param {TView} view The new view. */ onViewChange: PropTypes.func, /** - * Initially open view. - * @default 'hours' + * The default visible view. + * Used when the component view is not controlled. + * Must be a valid option from `views` list. */ openTo: PropTypes.oneOf(['hours', 'minutes', 'seconds']), /** - * Make picker read only. + * If `true`, the picker views and text field are read-only. * @default false */ readOnly: PropTypes.bool, @@ -519,12 +540,13 @@ TimeClock.propTypes = { */ value: PropTypes.any, /** - * Controlled open view. + * The visible view. + * Used when the component view is controlled. + * Must be a valid option from `views` list. */ view: PropTypes.oneOf(['hours', 'minutes', 'seconds']), /** - * Views for calendar picker. - * @default ['hours', 'minutes'] + * Available views. */ views: PropTypes.arrayOf(PropTypes.oneOf(['hours', 'minutes', 'seconds']).isRequired), } as any; diff --git a/packages/x-date-pickers/src/TimeClock/TimeClock.types.ts b/packages/x-date-pickers/src/TimeClock/TimeClock.types.ts index 7769ce0626b39..8f02aa2d09a1b 100644 --- a/packages/x-date-pickers/src/TimeClock/TimeClock.types.ts +++ b/packages/x-date-pickers/src/TimeClock/TimeClock.types.ts @@ -1,23 +1,13 @@ -import { SxProps } from '@mui/system'; -import { Theme } from '@mui/material/styles'; import { TimeClockClasses } from './timeClockClasses'; -import { TimeValidationProps, BaseTimeValidationProps } from '../internals/models/validation'; import { PickersArrowSwitcherSlotsComponent, PickersArrowSwitcherSlotsComponentsProps, } from '../internals/components/PickersArrowSwitcher'; -import { TimeView } from '../models'; -import { PickerSelectionState } from '../internals/hooks/usePicker/usePickerValue.types'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { BaseClockProps, ExportedBaseClockProps } from '../internals/models/props/clock'; +import { TimeView } from '../models'; -export interface ExportedTimeClockProps - extends TimeValidationProps, - BaseTimeValidationProps { - /** - * 12h/24h view for hour selection clock. - * @default `utils.is12HourCycleInCurrentLocale()` - */ - ampm?: boolean; +export interface ExportedTimeClockProps extends ExportedBaseClockProps { /** * Display ampm controls under the clock (instead of in the toolbar). * @default false @@ -29,16 +19,9 @@ export interface TimeClockSlotsComponent extends PickersArrowSwitcherSlotsCompon export interface TimeClockSlotsComponentsProps extends PickersArrowSwitcherSlotsComponentsProps {} -export interface TimeClockProps extends ExportedTimeClockProps { - className?: string; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * Set to `true` if focus should be moved to clock picker. - */ - autoFocus?: boolean; +export interface TimeClockProps + extends ExportedTimeClockProps, + BaseClockProps { /** * Override or extend the styles applied to the component. */ @@ -65,51 +48,5 @@ export interface TimeClockProps extends ExportedTimeClockProps { * @default {} */ slotProps?: TimeClockSlotsComponentsProps; - /** - * The selected value. - * Used when the component is controlled. - */ - value?: TDate | null; - /** - * The default selected value. - * Used when the component is not controlled. - */ - defaultValue?: TDate | null; - /** - * Callback fired when the value changes. - * @template TDate - * @param {TDate | null} value The new value. - * @param {PickerSelectionState | undefined} selectionState Indicates if the date selection is complete. - */ - onChange?: (value: TDate | null, selectionState?: PickerSelectionState) => void; showViewSwitcher?: boolean; - /** - * Controlled open view. - */ - view?: TimeView; - /** - * Views for calendar picker. - * @default ['hours', 'minutes'] - */ - views?: readonly TimeView[]; - /** - * Callback fired on view change. - * @param {TimeView} view The new view. - */ - onViewChange?: (view: TimeView) => void; - /** - * Initially open view. - * @default 'hours' - */ - openTo?: TimeView; - /** - * If `true`, the picker and text field are disabled. - * @default false - */ - disabled?: boolean; - /** - * Make picker read only. - * @default false - */ - readOnly?: boolean; } diff --git a/packages/x-date-pickers/src/TimePicker/TimePicker.tsx b/packages/x-date-pickers/src/TimePicker/TimePicker.tsx index 492e0a99a013d..e3fc8cdb32510 100644 --- a/packages/x-date-pickers/src/TimePicker/TimePicker.tsx +++ b/packages/x-date-pickers/src/TimePicker/TimePicker.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import useMediaQuery from '@mui/material/useMediaQuery'; import { useThemeProps } from '@mui/material/styles'; import { DesktopTimePicker } from '../DesktopTimePicker'; -import { MobileTimePicker } from '../MobileTimePicker'; +import { MobileTimePicker, MobileTimePickerProps } from '../MobileTimePicker'; import { TimePickerProps } from './TimePicker.types'; import { DEFAULT_DESKTOP_MODE_MEDIA_QUERY } from '../internals/utils/utils'; @@ -26,7 +26,7 @@ const TimePicker = React.forwardRef(function TimePicker( return ; } - return ; + return )} />; }) as TimePickerComponent; TimePicker.propTypes = { @@ -207,7 +207,7 @@ TimePicker.propTypes = { * Used when the component view is not controlled. * Must be a valid option from `views` list. */ - openTo: PropTypes.oneOf(['hours', 'minutes', 'seconds']), + openTo: PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']), /** * Force rendering in particular orientation. */ @@ -256,6 +256,11 @@ TimePicker.propTypes = { * @returns {boolean} If `true` the time will be disabled. */ shouldDisableTime: PropTypes.func, + /** + * If `true`, disabled digital clock items will not be rendered. + * @default false + */ + skipDisabled: PropTypes.bool, /** * The props used for each component slot. * @default {} @@ -274,6 +279,22 @@ TimePicker.propTypes = { PropTypes.func, PropTypes.object, ]), + /** + * Amount of time options below or at which the single column time renderer is used. + * @default 24 + */ + thresholdToRenderTimeInASingleColumn: PropTypes.number, + /** + * The time steps between two time unit options. + * For example, if `timeStep.minutes = 8`, then the available minute options will be `[0, 8, 16, 24, 32, 40, 48, 56]`. + * When single column time renderer is used, only `timeStep.minutes` will be used. + * @default{ hours: 1, minutes: 5, seconds: 5 } + */ + timeSteps: PropTypes.shape({ + hours: PropTypes.number, + minutes: PropTypes.number, + seconds: PropTypes.number, + }), /** * The selected value. * Used when the component is controlled. @@ -284,7 +305,7 @@ TimePicker.propTypes = { * Used when the component view is controlled. * Must be a valid option from `views` list. */ - view: PropTypes.oneOf(['hours', 'minutes', 'seconds']), + view: PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']), /** * Define custom view renderers for each section. * If `null`, the section will only have field editing. @@ -292,6 +313,7 @@ TimePicker.propTypes = { */ viewRenderers: PropTypes.shape({ hours: PropTypes.func, + meridiem: PropTypes.func, minutes: PropTypes.func, seconds: PropTypes.func, }), diff --git a/packages/x-date-pickers/src/TimePicker/TimePicker.types.ts b/packages/x-date-pickers/src/TimePicker/TimePicker.types.ts index 02a316ed2044c..6116ab13f0b1a 100644 --- a/packages/x-date-pickers/src/TimePicker/TimePicker.types.ts +++ b/packages/x-date-pickers/src/TimePicker/TimePicker.types.ts @@ -3,6 +3,7 @@ import { DesktopTimePickerSlotsComponent, DesktopTimePickerSlotsComponentsProps, } from '../DesktopTimePicker'; +import { TimeViewWithMeridiem } from '../internals/models'; import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { MobileTimePickerProps, @@ -12,15 +13,15 @@ import { export interface TimePickerSlotsComponents extends DesktopTimePickerSlotsComponent, - MobileTimePickerSlotsComponent {} + MobileTimePickerSlotsComponent {} export interface TimePickerSlotsComponentsProps extends DesktopTimePickerSlotsComponentsProps, - MobileTimePickerSlotsComponentsProps {} + MobileTimePickerSlotsComponentsProps {} export interface TimePickerProps extends DesktopTimePickerProps, - MobileTimePickerProps { + Omit, 'views'> { /** * CSS media query when `Mobile` mode will be changed to `Desktop`. * @default '@media (pointer: fine)' diff --git a/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx b/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx index 0072af2aec8c8..0c1bc44bbfa11 100644 --- a/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx @@ -15,9 +15,10 @@ import { timePickerToolbarClasses, TimePickerToolbarClasses, } from './timePickerToolbarClasses'; -import { TimeView } from '../models'; +import { TimeViewWithMeridiem } from '../internals/models'; -export interface TimePickerToolbarProps extends BaseToolbarProps { +export interface TimePickerToolbarProps + extends BaseToolbarProps { ampm?: boolean; ampmInClock?: boolean; classes?: Partial; @@ -297,10 +298,9 @@ TimePickerToolbar.propTypes = { /** * Currently visible picker view. */ - view: PropTypes.oneOf(['hours', 'minutes', 'seconds']).isRequired, - views: PropTypes.arrayOf( - PropTypes.oneOf(['day', 'hours', 'minutes', 'month', 'seconds', 'year']).isRequired, - ).isRequired, + view: PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']).isRequired, + views: PropTypes.arrayOf(PropTypes.oneOf(['hours', 'meridiem', 'minutes', 'seconds']).isRequired) + .isRequired, } as any; export { TimePickerToolbar }; diff --git a/packages/x-date-pickers/src/TimePicker/shared.tsx b/packages/x-date-pickers/src/TimePicker/shared.tsx index df677b8dad329..e71fd37957859 100644 --- a/packages/x-date-pickers/src/TimePicker/shared.tsx +++ b/packages/x-date-pickers/src/TimePicker/shared.tsx @@ -5,7 +5,6 @@ import { useUtils } from '../internals/hooks/useUtils'; import { TimeClockSlotsComponent, TimeClockSlotsComponentsProps, - ExportedTimeClockProps, } from '../TimeClock/TimeClock.types'; import { BasePickerInputProps } from '../internals/models/props/basePickerProps'; import { BaseTimeValidationProps } from '../internals/models/validation'; @@ -15,11 +14,13 @@ import { ExportedTimePickerToolbarProps, TimePickerToolbar, } from './TimePickerToolbar'; -import { TimeValidationError, TimeView } from '../models'; +import { TimeValidationError } from '../models'; import { PickerViewRendererLookup } from '../internals/hooks/usePicker/usePickerViews'; import { TimeViewRendererProps } from '../timeViewRenderers'; import { applyDefaultViewProps } from '../internals/utils/views'; import { uncapitalizeObjectKeys, UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { BaseClockProps, ExportedBaseClockProps } from '../internals/models/props/clock'; +import { TimeViewWithMeridiem } from '../internals/models'; export interface BaseTimePickerSlotsComponent extends TimeClockSlotsComponent { /** @@ -33,9 +34,9 @@ export interface BaseTimePickerSlotsComponentsProps extends TimeClockSlotsCompon toolbar?: ExportedTimePickerToolbarProps; } -export interface BaseTimePickerProps - extends BasePickerInputProps, - ExportedTimeClockProps { +export interface BaseTimePickerProps + extends BasePickerInputProps, + ExportedBaseClockProps { /** * Display ampm controls under the clock (instead of in the toolbar). * @default true on desktop, false on mobile @@ -69,13 +70,19 @@ export interface BaseTimePickerProps * If `undefined`, internally defined view will be the used. */ viewRenderers?: Partial< - PickerViewRendererLookup, {}> + PickerViewRendererLookup< + TDate | null, + TView, + TimeViewRendererProps>, + {} + > >; } type UseTimePickerDefaultizedProps< TDate, - Props extends BaseTimePickerProps, + TView extends TimeViewWithMeridiem, + Props extends BaseTimePickerProps, > = LocalizedComponent< TDate, Omit< @@ -84,10 +91,11 @@ type UseTimePickerDefaultizedProps< > >; -export function useTimePickerDefaultizedProps>( - props: Props, - name: string, -): UseTimePickerDefaultizedProps { +export function useTimePickerDefaultizedProps< + TDate, + TView extends TimeViewWithMeridiem, + Props extends BaseTimePickerProps, +>(props: Props, name: string): UseTimePickerDefaultizedProps { const utils = useUtils(); const themeProps = useThemeProps({ props, @@ -116,8 +124,8 @@ export function useTimePickerDefaultizedProps view === 'year' || view === 'month' || view === 'day'; -export interface DateViewRendererProps +export interface DateViewRendererProps extends Omit< DateCalendarProps, 'views' | 'openTo' | 'view' | 'onViewChange' | 'focusedView' diff --git a/packages/x-date-pickers/src/index.ts b/packages/x-date-pickers/src/index.ts index 0ed92c2d51403..bc8c4ff7974a6 100644 --- a/packages/x-date-pickers/src/index.ts +++ b/packages/x-date-pickers/src/index.ts @@ -1,4 +1,8 @@ +// Clocks export * from './TimeClock'; +export * from './DigitalClock'; +export * from './MultiSectionDigitalClock'; + export * from './LocalizationProvider'; export * from './PickersDay'; export * from './locales'; diff --git a/packages/x-date-pickers/src/internals/components/PickersToolbar.tsx b/packages/x-date-pickers/src/internals/components/PickersToolbar.tsx index cd4b26513c6a7..6047fd0839aa6 100644 --- a/packages/x-date-pickers/src/internals/components/PickersToolbar.tsx +++ b/packages/x-date-pickers/src/internals/components/PickersToolbar.tsx @@ -6,9 +6,9 @@ import { styled, useThemeProps } from '@mui/material/styles'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { BaseToolbarProps } from '../models/props/toolbar'; import { getPickersToolbarUtilityClass, PickersToolbarClasses } from './pickersToolbarClasses'; -import { DateOrTimeView } from '../../models'; +import { DateOrTimeViewWithMeridiem } from '../models'; -export interface PickersToolbarProps +export interface PickersToolbarProps extends Pick, 'isLandscape' | 'hidden' | 'titleId'> { className?: string; landscapeDirection?: 'row' | 'column'; @@ -61,14 +61,14 @@ const PickersToolbarContent = styled(Grid, { }), })); -type PickersToolbarComponent = (( +type PickersToolbarComponent = (( props: React.PropsWithChildren> & React.RefAttributes, ) => JSX.Element) & { propTypes?: any }; export const PickersToolbar = React.forwardRef(function PickersToolbar< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, >( inProps: React.PropsWithChildren>, ref: React.Ref, diff --git a/packages/x-date-pickers/src/internals/constants/dimensions.ts b/packages/x-date-pickers/src/internals/constants/dimensions.ts index 62c797505549a..1542c38c35eb9 100644 --- a/packages/x-date-pickers/src/internals/constants/dimensions.ts +++ b/packages/x-date-pickers/src/internals/constants/dimensions.ts @@ -2,3 +2,4 @@ export const DAY_SIZE = 36; export const DAY_MARGIN = 2; export const DIALOG_WIDTH = 320; export const VIEW_HEIGHT = 358; +export const DIGITAL_CLOCK_VIEW_HEIGHT = 232; diff --git a/packages/x-date-pickers/src/internals/demo/DemoContainer.tsx b/packages/x-date-pickers/src/internals/demo/DemoContainer.tsx index 73ddce0722bc8..798ef3520c63e 100644 --- a/packages/x-date-pickers/src/internals/demo/DemoContainer.tsx +++ b/packages/x-date-pickers/src/internals/demo/DemoContainer.tsx @@ -15,6 +15,7 @@ type PickersGridChildComponentType = | 'multi-input-range-field' | 'single-input-range-field' | 'UI-view' + | 'Tall-UI-view' | 'multi-panel-UI-view'; type PickersSupportedSections = 'date' | 'time' | 'date-time'; @@ -24,6 +25,10 @@ const getChildTypeFromChildName = (childName: string): PickersGridChildComponent return 'multi-panel-UI-view'; } + if (childName.match(/^([A-Za-z]*)(DigitalClock)$/)) { + return 'Tall-UI-view'; + } + if (childName.match(/^Static([A-Za-z]+)/) || childName.match(/^([A-Za-z]+)(Calendar|Clock)$/)) { return 'UI-view'; } @@ -98,7 +103,7 @@ export function DemoContainer(props: DemoGridProps) { const getSpacing = (direction: 'column' | 'row') => { if (direction === 'row') { - return childrenTypes.has('UI-view') ? 3 : 2; + return childrenTypes.has('UI-view') || childrenTypes.has('Tall-UI-view') ? 3 : 2; } return childrenTypes.has('UI-view') ? 4 : 3; diff --git a/packages/x-date-pickers/src/internals/hooks/date-helpers-hooks.tsx b/packages/x-date-pickers/src/internals/hooks/date-helpers-hooks.tsx index b73739a40d7c2..a53023ff122a9 100644 --- a/packages/x-date-pickers/src/internals/hooks/date-helpers-hooks.tsx +++ b/packages/x-date-pickers/src/internals/hooks/date-helpers-hooks.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { useUtils } from './useUtils'; import { PickerOnChangeFn } from './useViews'; import { getMeridiem, convertToMeridiem } from '../utils/time-utils'; +import { PickerSelectionState } from './usePicker'; interface MonthValidationOptions { disablePast?: boolean; @@ -43,6 +44,7 @@ export function useMeridiemMode( date: TDate | null, ampm: boolean | undefined, onChange: PickerOnChangeFn, + selectionState?: PickerSelectionState, ) { const utils = useUtils(); const meridiemMode = getMeridiem(date, utils); @@ -51,9 +53,9 @@ export function useMeridiemMode( (mode: 'am' | 'pm') => { const timeWithMeridiem = date == null ? null : convertToMeridiem(date, mode, Boolean(ampm), utils); - onChange(timeWithMeridiem, 'partial'); + onChange(timeWithMeridiem, selectionState ?? 'partial'); }, - [ampm, date, onChange, utils], + [ampm, date, onChange, selectionState, utils], ); return { meridiemMode, handleMeridiemChange }; diff --git a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx index 217da4e09a1c8..970cc59a9080d 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx @@ -11,7 +11,8 @@ import { usePicker } from '../usePicker'; import { LocalizationProvider } from '../../../LocalizationProvider'; import { PickersLayout } from '../../../PickersLayout'; import { InferError } from '../useValidation'; -import { FieldSection, BaseSingleInputFieldProps, DateOrTimeView } from '../../../models'; +import { FieldSection, BaseSingleInputFieldProps } from '../../../models'; +import { DateOrTimeViewWithMeridiem } from '../../models'; /** * Hook managing all the single-date desktop pickers: @@ -21,7 +22,7 @@ import { FieldSection, BaseSingleInputFieldProps, DateOrTimeView } from '../../. */ export const useDesktopPicker = < TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseDesktopPickerProps, >({ props, @@ -110,6 +111,7 @@ export const useDesktopPicker = < formatDensity, label, autoFocus: autoFocus && !props.open, + focused: open ? true : undefined, }, ownerState: props, }); diff --git a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts index 27819832b625b..f19c210f4c19b 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts @@ -13,12 +13,7 @@ import { PickersPopperSlotsComponentsProps, } from '../../components/PickersPopper'; import { UsePickerParams, UsePickerProps } from '../usePicker'; -import { - BaseSingleInputFieldProps, - FieldSection, - DateOrTimeView, - MuiPickersAdapter, -} from '../../../models'; +import { BaseSingleInputFieldProps, FieldSection, MuiPickersAdapter } from '../../../models'; import { ExportedPickersLayoutSlotsComponent, ExportedPickersLayoutSlotsComponentsProps, @@ -27,8 +22,9 @@ import { import { UsePickerValueNonStaticProps } from '../usePicker/usePickerValue.types'; import { UsePickerViewsNonStaticProps, UsePickerViewsProps } from '../usePicker/usePickerViews'; import { UncapitalizeObjectKeys } from '../../utils/slots-migration'; +import { DateOrTimeViewWithMeridiem } from '../../models'; -export interface UseDesktopPickerSlotsComponent +export interface UseDesktopPickerSlotsComponent extends Pick< PickersPopperSlotsComponent, 'DesktopPaper' | 'DesktopTransition' | 'DesktopTrapFocus' | 'Popper' @@ -60,12 +56,16 @@ export interface UseDesktopPickerSlotsComponent - extends ExportedUseDesktopPickerSlotsComponentsProps, +export interface UseDesktopPickerSlotsComponentsProps< + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends ExportedUseDesktopPickerSlotsComponentsProps, Pick, 'toolbar'> {} -export interface ExportedUseDesktopPickerSlotsComponentsProps - extends PickersPopperSlotsComponentsProps, +export interface ExportedUseDesktopPickerSlotsComponentsProps< + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends PickersPopperSlotsComponentsProps, ExportedPickersLayoutSlotsComponentsProps { field?: SlotComponentProps< React.ElementType>, @@ -95,7 +95,7 @@ export interface DesktopOnlyPickerProps export interface UseDesktopPickerProps< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TError, TExternalProps extends UsePickerViewsProps, > extends BasePickerProps, @@ -114,7 +114,7 @@ export interface UseDesktopPickerProps< export interface UseDesktopPickerParams< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseDesktopPickerProps, > extends Pick< UsePickerParams, diff --git a/packages/x-date-pickers/src/internals/hooks/useIsLandscape.tsx b/packages/x-date-pickers/src/internals/hooks/useIsLandscape.tsx index 7958aa95155ec..200bb74b7cd23 100644 --- a/packages/x-date-pickers/src/internals/hooks/useIsLandscape.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useIsLandscape.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/utils'; import { arrayIncludes } from '../utils/utils'; -import { DateOrTimeView } from '../../models'; +import { DateOrTimeViewWithMeridiem } from '../models'; type Orientation = 'portrait' | 'landscape'; @@ -23,7 +23,7 @@ function getOrientation(): Orientation { } export const useIsLandscape = ( - views: readonly DateOrTimeView[], + views: readonly DateOrTimeViewWithMeridiem[], customOrientation: Orientation | undefined, ): boolean => { const [orientation, setOrientation] = React.useState(getOrientation); diff --git a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx index 373eca171b043..5f0187380290d 100644 --- a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.tsx @@ -10,7 +10,8 @@ import { useUtils } from '../useUtils'; import { LocalizationProvider } from '../../../LocalizationProvider'; import { PickersLayout } from '../../../PickersLayout'; import { InferError } from '../useValidation'; -import { FieldSection, BaseSingleInputFieldProps, DateOrTimeView } from '../../../models'; +import { FieldSection, BaseSingleInputFieldProps } from '../../../models'; +import { DateOrTimeViewWithMeridiem } from '../../models'; /** * Hook managing all the single-date mobile pickers: @@ -20,7 +21,7 @@ import { FieldSection, BaseSingleInputFieldProps, DateOrTimeView } from '../../. */ export const useMobilePicker = < TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseMobilePickerProps, >({ props, diff --git a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts index 9c3756974b9b3..769717b6523cf 100644 --- a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts @@ -11,12 +11,7 @@ import { PickersModalDialogSlotsComponentsProps, } from '../../components/PickersModalDialog'; import { UsePickerParams, UsePickerProps } from '../usePicker'; -import { - BaseSingleInputFieldProps, - FieldSection, - DateOrTimeView, - MuiPickersAdapter, -} from '../../../models'; +import { BaseSingleInputFieldProps, FieldSection, MuiPickersAdapter } from '../../../models'; import { ExportedPickersLayoutSlotsComponent, ExportedPickersLayoutSlotsComponentsProps, @@ -25,8 +20,9 @@ import { import { UsePickerValueNonStaticProps } from '../usePicker/usePickerValue.types'; import { UsePickerViewsNonStaticProps, UsePickerViewsProps } from '../usePicker/usePickerViews'; import { UncapitalizeObjectKeys } from '../../utils/slots-migration'; +import { DateOrTimeViewWithMeridiem } from '../../models'; -export interface UseMobilePickerSlotsComponent +export interface UseMobilePickerSlotsComponent extends PickersModalDialogSlotsComponent, ExportedPickersLayoutSlotsComponent { /** @@ -41,8 +37,10 @@ export interface UseMobilePickerSlotsComponent; } -export interface ExportedUseMobilePickerSlotsComponentsProps - extends PickersModalDialogSlotsComponentsProps, +export interface ExportedUseMobilePickerSlotsComponentsProps< + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends PickersModalDialogSlotsComponentsProps, ExportedPickersLayoutSlotsComponentsProps { field?: SlotComponentProps< React.ElementType>, @@ -52,8 +50,10 @@ export interface ExportedUseMobilePickerSlotsComponentsProps>; } -export interface UseMobilePickerSlotsComponentsProps - extends ExportedUseMobilePickerSlotsComponentsProps, +export interface UseMobilePickerSlotsComponentsProps< + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends ExportedUseMobilePickerSlotsComponentsProps, Pick, 'toolbar'> {} export interface MobileOnlyPickerProps @@ -64,7 +64,7 @@ export interface MobileOnlyPickerProps export interface UseMobilePickerProps< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TError, TExternalProps extends UsePickerViewsProps, > extends BasePickerProps, @@ -83,7 +83,7 @@ export interface UseMobilePickerProps< export interface UseMobilePickerParams< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseMobilePickerProps, > extends Pick< UsePickerParams, diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts index eb09fd7a47987..3e53d8b8fc1cd 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts @@ -4,7 +4,8 @@ import { usePickerViews } from './usePickerViews'; import { usePickerLayoutProps } from './usePickerLayoutProps'; import { InferError } from '../useValidation'; import { buildWarning } from '../../utils/warning'; -import { FieldSection, DateOrTimeView } from '../../../models'; +import { FieldSection } from '../../../models'; +import { DateOrTimeViewWithMeridiem } from '../../models'; const warnRenderInputIsDefined = buildWarning([ 'The `renderInput` prop has been removed in version 6.0 of the Date and Time Pickers.', @@ -15,7 +16,7 @@ const warnRenderInputIsDefined = buildWarning([ export const usePicker = < TValue, TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TSection extends FieldSection, TExternalProps extends UsePickerProps, TAdditionalProps extends {}, diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts index 059d2ef5ab217..0c00c3c7b8a29 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts @@ -11,14 +11,15 @@ import { UsePickerViewsBaseProps, } from './usePickerViews'; import { UsePickerLayoutProps, UsePickerLayoutPropsResponse } from './usePickerLayoutProps'; -import { FieldSection, DateOrTimeView } from '../../../models'; +import { FieldSection } from '../../../models'; +import { DateOrTimeViewWithMeridiem } from '../../models'; /** * Props common to all picker headless implementations. */ export interface UsePickerBaseProps< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TError, TExternalProps extends UsePickerViewsProps, TAdditionalProps extends {}, @@ -28,7 +29,7 @@ export interface UsePickerBaseProps< export interface UsePickerProps< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TSection extends FieldSection, TError, TExternalProps extends UsePickerViewsProps, @@ -40,7 +41,7 @@ export interface UsePickerProps< export interface UsePickerParams< TValue, TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TSection extends FieldSection, TExternalProps extends UsePickerProps, TAdditionalProps extends {}, @@ -57,7 +58,7 @@ export interface UsePickerParams< export interface UsePickerResponse< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TSection extends FieldSection, TError, > extends Omit, 'viewProps' | 'layoutProps'>, diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerLayoutProps.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerLayoutProps.ts index 0518469f86b3c..db3a329d9e8f5 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerLayoutProps.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerLayoutProps.ts @@ -1,8 +1,7 @@ import { useIsLandscape } from '../useIsLandscape'; import { UsePickerValueLayoutResponse } from './usePickerValue.types'; import { UsePickerViewsLayoutResponse } from './usePickerViews'; -import { DateOrTimeView } from '../../../models'; -import { WrapperVariant } from '../../models/common'; +import { DateOrTimeViewWithMeridiem, WrapperVariant } from '../../models/common'; /** * Props used to create the layout of the views. @@ -17,8 +16,10 @@ export interface UsePickerLayoutProps { orientation?: 'portrait' | 'landscape'; } -export interface UsePickerLayoutPropsResponseLayoutProps - extends UsePickerValueLayoutResponse, +export interface UsePickerLayoutPropsResponseLayoutProps< + TValue, + TView extends DateOrTimeViewWithMeridiem, +> extends UsePickerValueLayoutResponse, UsePickerViewsLayoutResponse, UsePickerLayoutProps { isLandscape: boolean; @@ -26,11 +27,11 @@ export interface UsePickerLayoutPropsResponseLayoutProps boolean; } -export interface UsePickerLayoutPropsResponse { +export interface UsePickerLayoutPropsResponse { layoutProps: UsePickerLayoutPropsResponseLayoutProps; } -export interface UsePickerLayoutPropsParams { +export interface UsePickerLayoutPropsParams { props: UsePickerLayoutProps; propsFromPickerValue: UsePickerValueLayoutResponse; propsFromPickerViews: UsePickerViewsLayoutResponse; @@ -40,7 +41,7 @@ export interface UsePickerLayoutPropsParams({ +export const usePickerLayoutProps = ({ props, propsFromPickerValue, propsFromPickerViews, diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts index 73d0fd220015f..ac9a36cbd5b4f 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts @@ -3,17 +3,17 @@ import { SxProps } from '@mui/system'; import { Theme } from '@mui/material/styles'; import useEnhancedEffect from '@mui/utils/useEnhancedEffect'; import useEventCallback from '@mui/utils/useEventCallback'; -import { DateOrTimeView } from '../../../models'; import { useViews, UseViewsOptions } from '../useViews'; import type { UsePickerValueViewsResponse } from './usePickerValue.types'; import { isTimeView } from '../../utils/time-utils'; +import { DateOrTimeViewWithMeridiem } from '../../models'; -interface PickerViewsRendererBaseExternalProps +interface PickerViewsRendererBaseExternalProps extends Omit, 'openTo' | 'viewRenderers'> {} export type PickerViewsRendererProps< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends PickerViewsRendererBaseExternalProps, TAdditionalProps extends {}, > = TExternalProps & @@ -27,7 +27,7 @@ export type PickerViewsRendererProps< type PickerViewRenderer< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends PickerViewsRendererBaseExternalProps, TAdditionalProps extends {}, > = ( @@ -36,7 +36,7 @@ type PickerViewRenderer< export type PickerViewRendererLookup< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends PickerViewsRendererBaseExternalProps, TAdditionalProps extends {}, > = { @@ -48,7 +48,7 @@ export type PickerViewRendererLookup< */ export interface UsePickerViewsBaseProps< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UsePickerViewsProps, TAdditionalProps extends {}, > extends Omit, 'onChange' | 'onFocusedViewChange' | 'focusedView'> { @@ -80,7 +80,7 @@ export interface UsePickerViewsNonStaticProps { */ export interface UsePickerViewsProps< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UsePickerViewsProps, TAdditionalProps extends {}, > extends UsePickerViewsBaseProps, @@ -91,7 +91,7 @@ export interface UsePickerViewsProps< export interface UsePickerViewParams< TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UsePickerViewsProps, TAdditionalProps extends {}, > { @@ -102,7 +102,7 @@ export interface UsePickerViewParams< autoFocusView: boolean; } -export interface UsePickerViewsResponse { +export interface UsePickerViewsResponse { /** * Does the picker have at least one view that should be rendered in UI mode ? * If not, we can hide the icon to open the picker. @@ -113,7 +113,7 @@ export interface UsePickerViewsResponse { layoutProps: UsePickerViewsLayoutResponse; } -export interface UsePickerViewsLayoutResponse { +export interface UsePickerViewsLayoutResponse { view: TView | null; onViewChange: (view: TView) => void; views: readonly TView[]; @@ -127,7 +127,7 @@ export interface UsePickerViewsLayoutResponse { */ export const usePickerViews = < TValue, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UsePickerViewsProps, TAdditionalProps extends {}, >({ diff --git a/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.tsx b/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.tsx index 11d4ca2d00a0a..7bd800892bc20 100644 --- a/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.tsx @@ -6,7 +6,8 @@ import { usePicker } from '../usePicker'; import { LocalizationProvider } from '../../../LocalizationProvider'; import { PickersLayout } from '../../../PickersLayout'; import { DIALOG_WIDTH } from '../../constants/dimensions'; -import { FieldSection, DateOrTimeView } from '../../../models'; +import { FieldSection } from '../../../models'; +import { DateOrTimeViewWithMeridiem } from '../../models'; const PickerStaticLayout = styled(PickersLayout)(({ theme }) => ({ overflow: 'hidden', @@ -22,7 +23,7 @@ const PickerStaticLayout = styled(PickersLayout)(({ theme }) => ({ */ export const useStaticPicker = < TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseStaticPickerProps, >({ props, diff --git a/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.types.ts b/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.types.ts index c1525c5e41217..0abec5acb2703 100644 --- a/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.types.ts @@ -7,13 +7,16 @@ import { BasePickerProps } from '../../models/props/basePickerProps'; import { UncapitalizeObjectKeys } from '../../utils/slots-migration'; import { UsePickerParams } from '../usePicker'; import { UsePickerViewsProps } from '../usePicker/usePickerViews'; -import { FieldSection, DateOrTimeView } from '../../../models'; +import { FieldSection } from '../../../models'; +import { DateOrTimeViewWithMeridiem } from '../../models'; -export interface UseStaticPickerSlotsComponent +export interface UseStaticPickerSlotsComponent extends ExportedPickersLayoutSlotsComponent {} -export interface UseStaticPickerSlotsComponentsProps - extends ExportedPickersLayoutSlotsComponentsProps {} +export interface UseStaticPickerSlotsComponentsProps< + TDate, + TView extends DateOrTimeViewWithMeridiem, +> extends ExportedPickersLayoutSlotsComponentsProps {} export interface StaticOnlyPickerProps { /** @@ -35,7 +38,7 @@ export interface StaticOnlyPickerProps { export interface UseStaticPickerProps< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TError, TExternalProps extends UsePickerViewsProps, > extends BasePickerProps, @@ -66,7 +69,7 @@ export interface UseStaticPickerProps< export interface UseStaticPickerParams< TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TExternalProps extends UseStaticPickerProps, > extends Pick< UsePickerParams, diff --git a/packages/x-date-pickers/src/internals/hooks/useViews.tsx b/packages/x-date-pickers/src/internals/hooks/useViews.tsx index 3e84cf2d5474a..fc8733b69e9ad 100644 --- a/packages/x-date-pickers/src/internals/hooks/useViews.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useViews.tsx @@ -2,22 +2,23 @@ import * as React from 'react'; import useEventCallback from '@mui/utils/useEventCallback'; import { unstable_useControlled as useControlled } from '@mui/utils'; import type { PickerSelectionState } from './usePicker'; -import { DateOrTimeView } from '../../models'; import { MakeOptional } from '../models/helpers'; +import { DateOrTimeViewWithMeridiem } from '../models'; export type PickerOnChangeFn = ( date: TDate | null, selectionState?: PickerSelectionState, ) => void; -export interface UseViewsOptions { +export interface UseViewsOptions { /** * Callback fired when the value changes. * @template TValue * @param {TValue} value The new value. * @param {PickerSelectionState | undefined} selectionState Indicates if the date selection is complete. + * @param {TView | undefined} selectedView Indicates the view in which the selection has been made. */ - onChange: (value: TValue, selectionState?: PickerSelectionState) => void; + onChange: (value: TValue, selectionState?: PickerSelectionState, selectedView?: TView) => void; /** * Callback fired on view change. * @template TView @@ -60,12 +61,12 @@ export interface UseViewsOptions { onFocusedViewChange?: (view: TView, hasFocus: boolean) => void; } -export interface ExportedUseViewsOptions +export interface ExportedUseViewsOptions extends MakeOptional, 'onChange'>, 'openTo' | 'views'> {} let warnedOnceNotValidView = false; -interface UseViewsResponse { +interface UseViewsResponse { view: TView; setView: (view: TView) => void; focusedView: TView | null; @@ -78,9 +79,10 @@ interface UseViewsResponse { value: TValue, currentViewSelectionState?: PickerSelectionState, ) => void; + setValueAndGoToView: (value: TValue, newView: TView | null, selectedView: TView) => void; } -export function useViews({ +export function useViews({ onChange, onViewChange, openTo, @@ -144,6 +146,9 @@ export function useViews({ const nextView: TView | null = views[viewIndex + 1] ?? null; const handleChangeView = useEventCallback((newView: TView) => { + if (newView === view) { + return; + } setView(newView); if (onViewChange) { @@ -151,19 +156,37 @@ export function useViews({ } }); + const handleFocusedViewChange = useEventCallback((viewToFocus: TView, hasFocus: boolean) => { + if (hasFocus) { + // Focus event + setFocusedView(viewToFocus); + } else { + // Blur event + setFocusedView( + (prevFocusedView) => (viewToFocus === prevFocusedView ? null : prevFocusedView), // If false the blur is due to view switching + ); + } + + onFocusedViewChange?.(viewToFocus, hasFocus); + }); + const goToNextView = useEventCallback(() => { if (nextView) { handleChangeView(nextView); } + handleFocusedViewChange(nextView, true); }); const setValueAndGoToNextView = useEventCallback( - (value: TValue, currentViewSelectionState?: PickerSelectionState) => { + (value: TValue, currentViewSelectionState?: PickerSelectionState, selectedView?: TView) => { const isSelectionFinishedOnCurrentView = currentViewSelectionState === 'finish'; + const hasMoreViews = selectedView + ? // handles case like `DateTimePicker`, where a view might return a `finish` selection state + // but we it's not the final view given all `views` -> overall selection state should be `partial`. + views.indexOf(selectedView) < views.length - 1 + : Boolean(nextView); const globalSelectionState = - isSelectionFinishedOnCurrentView && Boolean(nextView) - ? 'partial' - : currentViewSelectionState; + isSelectionFinishedOnCurrentView && hasMoreViews ? 'partial' : currentViewSelectionState; onChange(value, globalSelectionState); if (isSelectionFinishedOnCurrentView) { @@ -172,19 +195,15 @@ export function useViews({ }, ); - const handleFocusedViewChange = useEventCallback((viewToFocus: TView, hasFocus: boolean) => { - if (hasFocus) { - // Focus event - setFocusedView(viewToFocus); - } else { - // Blur event - setFocusedView( - (prevFocusedView) => (viewToFocus === prevFocusedView ? null : prevFocusedView), // If false the blur is due to view switching - ); - } - - onFocusedViewChange?.(viewToFocus, hasFocus); - }); + const setValueAndGoToView = useEventCallback( + (value: TValue, newView: TView | null, selectedView: TView) => { + onChange(value, newView ? 'partial' : 'finish', selectedView); + if (newView) { + handleChangeView(newView); + handleFocusedViewChange(newView, true); + } + }, + ); return { view, @@ -196,5 +215,6 @@ export function useViews({ defaultView: defaultView.current, goToNextView, setValueAndGoToNextView, + setValueAndGoToView, }; } diff --git a/packages/x-date-pickers/src/internals/models/common.ts b/packages/x-date-pickers/src/internals/models/common.ts index f95b803d7019e..fed0d479c57e8 100644 --- a/packages/x-date-pickers/src/internals/models/common.ts +++ b/packages/x-date-pickers/src/internals/models/common.ts @@ -1 +1,7 @@ +import { DateView, TimeView } from '@mui/x-date-pickers/models/views'; + export type WrapperVariant = 'mobile' | 'desktop' | null; + +export type TimeViewWithMeridiem = TimeView | 'meridiem'; + +export type DateOrTimeViewWithMeridiem = DateView | TimeViewWithMeridiem; diff --git a/packages/x-date-pickers/src/internals/models/index.ts b/packages/x-date-pickers/src/internals/models/index.ts index cbc6c8c464c5f..7bb9a9cf63ac4 100644 --- a/packages/x-date-pickers/src/internals/models/index.ts +++ b/packages/x-date-pickers/src/internals/models/index.ts @@ -1 +1,2 @@ export * from './fields'; +export * from './common'; diff --git a/packages/x-date-pickers/src/internals/models/props/basePickerProps.tsx b/packages/x-date-pickers/src/internals/models/props/basePickerProps.tsx index 4ca0934d2645f..1fc745944af22 100644 --- a/packages/x-date-pickers/src/internals/models/props/basePickerProps.tsx +++ b/packages/x-date-pickers/src/internals/models/props/basePickerProps.tsx @@ -2,10 +2,10 @@ import * as React from 'react'; import { Theme } from '@mui/material/styles'; import { SxProps } from '@mui/system'; import { UsePickerBaseProps } from '../../hooks/usePicker'; -import { DateOrTimeView } from '../../../models'; import { PickersInputComponentLocaleText } from '../../../locales/utils/pickersLocaleTextApi'; import type { UsePickerViewsProps } from '../../hooks/usePicker/usePickerViews'; import { MakeOptional } from '../helpers'; +import { DateOrTimeViewWithMeridiem } from '../common'; /** * Props common to all pickers after applying the default props on each picker. @@ -13,7 +13,7 @@ import { MakeOptional } from '../helpers'; export interface BasePickerProps< TValue, TDate, - TView extends DateOrTimeView, + TView extends DateOrTimeViewWithMeridiem, TError, TExternalProps extends UsePickerViewsProps, TAdditionalProps extends {}, @@ -36,8 +36,12 @@ export interface BasePickerProps< /** * Props common to all pickers before applying the default props on each picker. */ -export interface BasePickerInputProps - extends Omit< +export interface BasePickerInputProps< + TValue, + TDate, + TView extends DateOrTimeViewWithMeridiem, + TError, +> extends Omit< MakeOptional, 'openTo' | 'views'>, 'viewRenderers' > {} diff --git a/packages/x-date-pickers/src/internals/models/props/clock.ts b/packages/x-date-pickers/src/internals/models/props/clock.ts new file mode 100644 index 0000000000000..04a50a75da303 --- /dev/null +++ b/packages/x-date-pickers/src/internals/models/props/clock.ts @@ -0,0 +1,103 @@ +import { SxProps, Theme } from '@mui/material/styles'; +import { BaseTimeValidationProps, TimeValidationProps } from '../validation'; +import { PickerSelectionState } from '../../hooks/usePicker/usePickerValue.types'; +import { TimeStepOptions } from '../../../models'; +import type { ExportedDigitalClockProps } from '../../../DigitalClock/DigitalClock.types'; +import type { ExportedMultiSectionDigitalClockProps } from '../../../MultiSectionDigitalClock/MultiSectionDigitalClock.types'; +import type { ExportedUseViewsOptions } from '../../hooks/useViews'; +import { TimeViewWithMeridiem } from '../common'; + +export interface ExportedBaseClockProps + extends TimeValidationProps, + BaseTimeValidationProps { + /** + * 12h/24h view for hour selection clock. + * @default `utils.is12HourCycleInCurrentLocale()` + */ + ampm?: boolean; +} + +export interface BaseClockProps + extends ExportedUseViewsOptions, + ExportedBaseClockProps { + className?: string; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; + /** + * The selected value. + * Used when the component is controlled. + */ + value?: TDate | null; + /** + * The default selected value. + * Used when the component is not controlled. + */ + defaultValue?: TDate | null; + /** + * Callback fired when the value changes. + * @template TDate + * @param {TDate | null} value The new value. + * @param {PickerSelectionState | undefined} selectionState Indicates if the date selection is complete. + * @param {TView | undefined} selectedView Indicates the view in which the selection has been made. + */ + onChange?: ( + value: TDate | null, + selectionState?: PickerSelectionState, + selectedView?: TView, + ) => void; + /** + * If `true`, the picker views and text field are disabled. + * @default false + */ + disabled?: boolean; + /** + * If `true`, the picker views and text field are read-only. + * @default false + */ + readOnly?: boolean; +} + +export interface DesktopOnlyTimePickerProps + extends Omit, 'timeStep'>, + Omit, 'timeStep'> { + /** + * Amount of time options below or at which the single column time renderer is used. + * @default 24 + */ + thresholdToRenderTimeInASingleColumn?: number; + /** + * The time steps between two time unit options. + * For example, if `timeStep.minutes = 8`, then the available minute options will be `[0, 8, 16, 24, 32, 40, 48, 56]`. + * When single column time renderer is used, only `timeStep.minutes` will be used. + * @default{ hours: 1, minutes: 5, seconds: 5 } + */ + timeSteps?: TimeStepOptions; +} + +interface DigitalClockOnlyBaseProps { + /** + * If `true`, disabled digital clock items will not be rendered. + * @default false + */ + skipDisabled?: boolean; +} + +export interface DigitalClockOnlyProps extends DigitalClockOnlyBaseProps { + /** + * The time steps between two time options. + * For example, if `timeStep = 45`, then the available time options will be `[00:00, 00:45, 01:30, 02:15, 03:00, etc.]`. + * @default 30 + */ + timeStep?: number; +} + +export interface MultiSectionDigitalClockOnlyProps extends DigitalClockOnlyBaseProps { + /** + * The time steps between two time unit options. + * For example, if `timeStep.minutes = 8`, then the available minute options will be `[0, 8, 16, 24, 32, 40, 48, 56]`. + * @default{ hours: 1, minutes: 5, seconds: 5 } + */ + timeSteps?: TimeStepOptions; +} diff --git a/packages/x-date-pickers/src/internals/models/props/tabs.ts b/packages/x-date-pickers/src/internals/models/props/tabs.ts index 972b321c83c54..688dd69d4deff 100644 --- a/packages/x-date-pickers/src/internals/models/props/tabs.ts +++ b/packages/x-date-pickers/src/internals/models/props/tabs.ts @@ -1,6 +1,6 @@ -import { DateOrTimeView } from '../../../models'; +import { DateOrTimeViewWithMeridiem } from '../common'; -export interface BaseTabsProps { +export interface BaseTabsProps { /** * Currently visible picker view. */ diff --git a/packages/x-date-pickers/src/internals/models/props/toolbar.ts b/packages/x-date-pickers/src/internals/models/props/toolbar.ts index 0c469c5e0de79..36275b5103a4a 100644 --- a/packages/x-date-pickers/src/internals/models/props/toolbar.ts +++ b/packages/x-date-pickers/src/internals/models/props/toolbar.ts @@ -1,7 +1,7 @@ import * as React from 'react'; -import { DateOrTimeView } from '../../../models'; +import { DateOrTimeViewWithMeridiem } from '../common'; -export interface BaseToolbarProps +export interface BaseToolbarProps extends ExportedBaseToolbarProps { isLandscape: boolean; onChange: (newValue: TValue) => void; @@ -16,7 +16,7 @@ export interface BaseToolbarProps * @param {TView} view The view to open */ onViewChange: (view: TView) => void; - views: readonly DateOrTimeView[]; + views: readonly TView[]; disabled?: boolean; readOnly?: boolean; titleId?: string; diff --git a/packages/x-date-pickers/src/internals/utils/time-utils.ts b/packages/x-date-pickers/src/internals/utils/time-utils.ts index b58980322c7d7..7710220e78aba 100644 --- a/packages/x-date-pickers/src/internals/utils/time-utils.ts +++ b/packages/x-date-pickers/src/internals/utils/time-utils.ts @@ -1,13 +1,15 @@ -import { DateOrTimeView, MuiPickersAdapter } from '../../models'; +import { MuiPickersAdapter } from '../../models'; +import { DateOrTimeViewWithMeridiem } from '../models'; -export const isTimeView = (view: DateOrTimeView) => ['hours', 'minutes', 'seconds'].includes(view); +const timeViews = ['hours', 'minutes', 'seconds']; +export const isTimeView = (view: DateOrTimeViewWithMeridiem) => timeViews.includes(view); -type Meridiem = 'am' | 'pm' | null; +export type Meridiem = 'am' | 'pm'; export const getMeridiem = ( date: TDate | null, utils: MuiPickersAdapter, -): Meridiem => { +): Meridiem | null => { if (!date) { return null; } @@ -15,7 +17,7 @@ export const getMeridiem = ( return utils.getHours(date) >= 12 ? 'pm' : 'am'; }; -export const convertValueToMeridiem = (value: number, meridiem: Meridiem, ampm: boolean) => { +export const convertValueToMeridiem = (value: number, meridiem: Meridiem | null, ampm: boolean) => { if (ampm) { const currentMeridiem = value >= 12 ? 'pm' : 'am'; if (currentMeridiem !== meridiem) { @@ -28,7 +30,7 @@ export const convertValueToMeridiem = (value: number, meridiem: Meridiem, ampm: export const convertToMeridiem = ( time: TDate, - meridiem: 'am' | 'pm', + meridiem: Meridiem, ampm: boolean, utils: MuiPickersAdapter, ) => { diff --git a/packages/x-date-pickers/src/internals/utils/views.ts b/packages/x-date-pickers/src/internals/utils/views.ts index 74d6d5b8b10d4..4393c555fcc79 100644 --- a/packages/x-date-pickers/src/internals/utils/views.ts +++ b/packages/x-date-pickers/src/internals/utils/views.ts @@ -1,4 +1,5 @@ -import { DateOrTimeView, DateView } from '../../models'; +import { DateView } from '../../models'; +import { DateOrTimeViewWithMeridiem } from '../models'; export const isYearOnlyView = (views: readonly DateView[]): views is ReadonlyArray<'year'> => views.length === 1 && views[0] === 'year'; @@ -8,7 +9,7 @@ export const isYearAndMonthViews = ( ): views is ReadonlyArray<'month' | 'year'> => views.length === 2 && views.indexOf('month') !== -1 && views.indexOf('year') !== -1; -export const applyDefaultViewProps = ({ +export const applyDefaultViewProps = ({ openTo, defaultOpenTo, views, diff --git a/packages/x-date-pickers/src/locales/beBY.ts b/packages/x-date-pickers/src/locales/beBY.ts index a00641df6ed68..76004e920f7ad 100644 --- a/packages/x-date-pickers/src/locales/beBY.ts +++ b/packages/x-date-pickers/src/locales/beBY.ts @@ -1,14 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; -const views = { +const views: Record = { // maps TimeView to its translation hours: 'гадзіны', minutes: 'хвіліны', seconds: 'секунды', - // maps PickersToolbar["viewType"] to its translation - date: 'календара', - time: 'часу', + meridiem: 'мерыдыем', }; const beBYPickers: Partial> = { @@ -49,6 +48,9 @@ const beBYPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} хвілін`, secondsClockNumberText: (seconds) => `${seconds} секунд`, + // Digital clock labels + selectViewText: (view) => `Абярыце ${views[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Нумар тыдня', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/caES.ts b/packages/x-date-pickers/src/locales/caES.ts index 921767ba3f8d4..4253843313b34 100644 --- a/packages/x-date-pickers/src/locales/caES.ts +++ b/packages/x-date-pickers/src/locales/caES.ts @@ -1,10 +1,12 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; -const views = { +const views: Record = { hours: 'les hores', minutes: 'els minuts', seconds: 'els segons', + meridiem: 'meridiem', }; const caESPickers: Partial> = { @@ -47,6 +49,9 @@ const caESPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} minuts`, secondsClockNumberText: (seconds) => `${seconds} segons`, + // Digital clock labels + selectViewText: (view) => `Seleccionar ${views[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Número de setmana', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/csCZ.ts b/packages/x-date-pickers/src/locales/csCZ.ts index 8c6f7788b557b..35b44e4c8b025 100644 --- a/packages/x-date-pickers/src/locales/csCZ.ts +++ b/packages/x-date-pickers/src/locales/csCZ.ts @@ -1,13 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; - -// This object is not Partial because it is the default values +import { TimeViewWithMeridiem } from '../internals/models'; // maps TimeView to its translation -const timeViews = { +const timeViews: Record = { hours: 'Hodiny', minutes: 'Minuty', seconds: 'Sekundy', + meridiem: 'Odpoledne', }; const csCZPickers: Partial> = { @@ -48,6 +48,9 @@ const csCZPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} minut`, secondsClockNumberText: (seconds) => `${seconds} sekund`, + // Digital clock labels + selectViewText: (view) => `Vyberte ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Týden v roce', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/daDK.ts b/packages/x-date-pickers/src/locales/daDK.ts index 1499c288f5221..f46f0b28a7972 100644 --- a/packages/x-date-pickers/src/locales/daDK.ts +++ b/packages/x-date-pickers/src/locales/daDK.ts @@ -1,11 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; // maps TimeView to its translation -const timeViews = { +const timeViews: Record = { hours: 'Timer', minutes: 'Minutter', seconds: 'Sekunder', + meridiem: 'Meridiem', }; const daDKPickers: Partial> = { @@ -48,6 +50,9 @@ const daDKPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} minutter`, secondsClockNumberText: (seconds) => `${seconds} sekunder`, + // Digital clock labels + selectViewText: (view) => `Vælg ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Ugenummer', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/deDE.ts b/packages/x-date-pickers/src/locales/deDE.ts index 258782bba123b..479b02455bd2e 100644 --- a/packages/x-date-pickers/src/locales/deDE.ts +++ b/packages/x-date-pickers/src/locales/deDE.ts @@ -1,11 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; // maps TimeView to its translation -const timeViews = { +const timeViews: Record = { hours: 'Stunden', minutes: 'Minuten', seconds: 'Sekunden', + meridiem: 'Meridiem', }; const deDEPickers: Partial> = { @@ -48,6 +50,9 @@ const deDEPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} ${timeViews.minutes}`, secondsClockNumberText: (seconds) => `${seconds} ${timeViews.seconds}`, + // Digital clock labels + selectViewText: (view) => `Auswählen ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Kalenderwoche', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/enUS.ts b/packages/x-date-pickers/src/locales/enUS.ts index 34e94f533567e..53fce05b2081b 100644 --- a/packages/x-date-pickers/src/locales/enUS.ts +++ b/packages/x-date-pickers/src/locales/enUS.ts @@ -41,6 +41,9 @@ const enUSPickers: PickersLocaleText = { minutesClockNumberText: (minutes) => `${minutes} minutes`, secondsClockNumberText: (seconds) => `${seconds} seconds`, + // Digital clock labels + selectViewText: (view) => `Select ${view}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Week number', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/esES.ts b/packages/x-date-pickers/src/locales/esES.ts index fd28744db22bc..b5fec0ff00d6e 100644 --- a/packages/x-date-pickers/src/locales/esES.ts +++ b/packages/x-date-pickers/src/locales/esES.ts @@ -1,10 +1,12 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; -const views = { +const views: Record = { hours: 'las horas', minutes: 'los minutos', seconds: 'los segundos', + meridiem: 'meridiano', }; const esESPickers: Partial> = { @@ -47,6 +49,9 @@ const esESPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} minutos`, secondsClockNumberText: (seconds) => `${seconds} segundos`, + // Digital clock labels + selectViewText: (view) => `Seleccionar ${views[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Número de semana', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/faIR.ts b/packages/x-date-pickers/src/locales/faIR.ts index 29c6a753711e0..5a748c92810dc 100644 --- a/packages/x-date-pickers/src/locales/faIR.ts +++ b/packages/x-date-pickers/src/locales/faIR.ts @@ -1,5 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'ساعت ها', + minutes: 'دقیقه ها', + seconds: 'ثانیه ها', + meridiem: 'بعد از ظهر', +}; const faIRPickers: Partial> = { // Calendar navigation @@ -32,7 +40,7 @@ const faIRPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `Select ${view}. ${ + ` را انتخاب کنید ${timeViews[view]}. ${ time === null ? 'هیچ ساعتی انتخاب نشده است' : `ساعت انتخاب ${adapter.format(time, 'fullTime')} می باشد` @@ -41,6 +49,9 @@ const faIRPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} دقیقه ها`, secondsClockNumberText: (seconds) => `${seconds} ثانیه ها`, + // Digital clock labels + selectViewText: (view) => ` را انتخاب کنید ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'عدد هفته', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/fiFI.ts b/packages/x-date-pickers/src/locales/fiFI.ts index 832d9b8a83c66..b4577defc3153 100644 --- a/packages/x-date-pickers/src/locales/fiFI.ts +++ b/packages/x-date-pickers/src/locales/fiFI.ts @@ -1,10 +1,12 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; -const views = { +const views: Record = { hours: 'tunnit', minutes: 'minuutit', seconds: 'sekuntit', + meridiem: 'iltapäivä', }; const fiFIPickers: Partial> = { @@ -45,6 +47,9 @@ const fiFIPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} minuuttia`, secondsClockNumberText: (seconds) => `${seconds} sekunttia`, + // Digital clock labels + selectViewText: (view) => `Valitse ${views[view]}`, + // Calendar labels // calendarWeekNumberHeaderLabel: 'Week number', // calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/frFR.ts b/packages/x-date-pickers/src/locales/frFR.ts index 7bb1d96efbeaa..14ac7e7d7dbc4 100644 --- a/packages/x-date-pickers/src/locales/frFR.ts +++ b/packages/x-date-pickers/src/locales/frFR.ts @@ -1,10 +1,12 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; -const views = { +const views: Record = { hours: 'heures', minutes: 'minutes', seconds: 'secondes', + meridiem: 'méridien', }; const frFRPickers: Partial> = { @@ -47,6 +49,9 @@ const frFRPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} minutes`, secondsClockNumberText: (seconds) => `${seconds} secondes`, + // Digital clock labels + selectViewText: (view) => `Choisir ${views[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Semaine', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/heIL.ts b/packages/x-date-pickers/src/locales/heIL.ts index 15ef5407e2ba6..dceedbbff2b61 100644 --- a/packages/x-date-pickers/src/locales/heIL.ts +++ b/packages/x-date-pickers/src/locales/heIL.ts @@ -1,10 +1,12 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; -const views = { +const views: Record = { hours: 'שעות', minutes: 'דקות', seconds: 'שניות', + meridiem: 'מרידיאם', }; const heILPickers: Partial> = { @@ -45,6 +47,9 @@ const heILPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} דקות`, secondsClockNumberText: (seconds) => `${seconds} שניות`, + // Digital clock labels + selectViewText: (view) => `בחירת ${views[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'שבוע מספר', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/huHU.ts b/packages/x-date-pickers/src/locales/huHU.ts index 5c122d550ad63..86fc0a23469c8 100644 --- a/packages/x-date-pickers/src/locales/huHU.ts +++ b/packages/x-date-pickers/src/locales/huHU.ts @@ -1,11 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; // maps TimeView to its translation -const timeViews = { +const timeViews: Record = { hours: 'Óra', minutes: 'Perc', seconds: 'Másodperc', + meridiem: 'Délután', }; const huHUPickers: Partial> = { @@ -48,6 +50,9 @@ const huHUPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} ${timeViews.minutes.toLowerCase()}`, secondsClockNumberText: (seconds) => `${seconds} ${timeViews.seconds.toLowerCase()}`, + // Digital clock labels + selectViewText: (view) => `${timeViews[view]} kiválasztása`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Hét', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/isIS.ts b/packages/x-date-pickers/src/locales/isIS.ts index 087449dd16a0a..161570338a8e3 100644 --- a/packages/x-date-pickers/src/locales/isIS.ts +++ b/packages/x-date-pickers/src/locales/isIS.ts @@ -1,5 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'klukkustundir', + minutes: 'mínútur', + seconds: 'sekúndur', + meridiem: 'eftirmiðdagur', +}; const isISPickers: Partial> = { // Calendar navigation @@ -32,13 +40,16 @@ const isISPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `Select ${view}. ${ + `Velja ${timeViews[view]}. ${ time === null ? 'Enginn tími valinn' : `Valinn tími er ${adapter.format(time, 'fullTime')}` }`, hoursClockNumberText: (hours) => `${hours} klukkustundir`, minutesClockNumberText: (minutes) => `${minutes} mínútur`, secondsClockNumberText: (seconds) => `${seconds} sekúndur`, + // Digital clock labels + selectViewText: (view) => `Velja ${timeViews[view]}`, + // Calendar labels // calendarWeekNumberHeaderLabel: 'Week number', // calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/itIT.ts b/packages/x-date-pickers/src/locales/itIT.ts index 110e89c959d6e..feb675cc5fcce 100644 --- a/packages/x-date-pickers/src/locales/itIT.ts +++ b/packages/x-date-pickers/src/locales/itIT.ts @@ -1,10 +1,12 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; -const views = { +const views: Record = { hours: 'le ore', minutes: 'i minuti', seconds: 'i secondi', + meridiem: 'il meridiano', }; const itITPickers: Partial> = { @@ -47,6 +49,9 @@ const itITPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} minuti`, secondsClockNumberText: (seconds) => `${seconds} secondi`, + // Digital clock labels + selectViewText: (view) => `Seleziona ${views[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Numero settimana', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/jaJP.ts b/packages/x-date-pickers/src/locales/jaJP.ts index 1c27c1e73127e..74aa23834109a 100644 --- a/packages/x-date-pickers/src/locales/jaJP.ts +++ b/packages/x-date-pickers/src/locales/jaJP.ts @@ -1,11 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; // maps TimeView to its translation -const timeViews = { +const timeViews: Record = { hours: '時間', minutes: '分', seconds: '秒', + meridiem: 'メリディム', }; const jaJPPickers: Partial> = { @@ -48,6 +50,9 @@ const jaJPPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} ${timeViews.minutes}`, secondsClockNumberText: (seconds) => `${seconds} ${timeViews.seconds}`, + // Digital clock labels + selectViewText: (view) => `を選択 ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: '週番号', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/koKR.ts b/packages/x-date-pickers/src/locales/koKR.ts index 16987610632ce..a44d3c2182918 100644 --- a/packages/x-date-pickers/src/locales/koKR.ts +++ b/packages/x-date-pickers/src/locales/koKR.ts @@ -1,10 +1,12 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; -const views = { +const views: Record = { hours: '시간을', minutes: '분을', seconds: '초를', + meridiem: '메리디엠', }; const koKRPickers: Partial> = { @@ -47,6 +49,9 @@ const koKRPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes}분`, secondsClockNumberText: (seconds) => `${seconds}초`, + // Digital clock labels + selectViewText: (view) => `${views[view]} 선택하기`, + // Calendar labels calendarWeekNumberHeaderLabel: '주 번호', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/kzKZ.ts b/packages/x-date-pickers/src/locales/kzKZ.ts index 50589f09fd348..bb43a39e2ae7d 100644 --- a/packages/x-date-pickers/src/locales/kzKZ.ts +++ b/packages/x-date-pickers/src/locales/kzKZ.ts @@ -1,11 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; // Translation map for Clock Label -const timeViews = { +const timeViews: Record = { hours: 'Сағатты', minutes: 'Минутты', seconds: 'Секундты', + meridiem: 'Меридием', }; const kzKZPickers: Partial> = { @@ -46,6 +48,9 @@ const kzKZPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} минут`, secondsClockNumberText: (seconds) => `${seconds} секунд`, + // Digital clock labels + selectViewText: (view) => `${timeViews[view]} таңдау`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Апта нөмірі', calendarWeekNumberHeaderText: '№', diff --git a/packages/x-date-pickers/src/locales/nbNO.ts b/packages/x-date-pickers/src/locales/nbNO.ts index 178a34da0c960..d808a2d3fd55c 100644 --- a/packages/x-date-pickers/src/locales/nbNO.ts +++ b/packages/x-date-pickers/src/locales/nbNO.ts @@ -1,5 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'timer', + minutes: 'minutter', + seconds: 'sekunder', + meridiem: 'meridiem', +}; const nbNOPickers: Partial> = { // Calendar navigation @@ -32,13 +40,16 @@ const nbNOPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `Velg ${view}. ${ + `Velg ${timeViews[view]}. ${ time === null ? 'Ingen tid valgt' : `Valgt tid er ${adapter.format(time, 'fullTime')}` }`, hoursClockNumberText: (hours) => `${hours} timer`, minutesClockNumberText: (minutes) => `${minutes} minutter`, secondsClockNumberText: (seconds) => `${seconds} sekunder`, + // Digital clock labels + selectViewText: (view) => `Velg ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Ukenummer', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/nlNL.ts b/packages/x-date-pickers/src/locales/nlNL.ts index 53f0a2c8f10a9..3d73af4c05952 100644 --- a/packages/x-date-pickers/src/locales/nlNL.ts +++ b/packages/x-date-pickers/src/locales/nlNL.ts @@ -1,6 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; -import { DateView } from '../models'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'uren', + minutes: 'minuten', + seconds: 'seconden', + meridiem: 'meridium', +}; const nlNLPickers: Partial> = { // Calendar navigation @@ -10,7 +17,7 @@ const nlNLPickers: Partial> = { // View navigation openPreviousView: 'open vorige view', openNextView: 'open volgende view', - calendarViewSwitchingButtonAriaLabel: (view: DateView) => + calendarViewSwitchingButtonAriaLabel: (view) => view === 'year' ? 'jaarweergave is geopend, schakel over naar kalenderweergave' : 'kalenderweergave is geopend, switch naar jaarweergave', @@ -33,7 +40,7 @@ const nlNLPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `Selecteer ${view}. ${ + `Selecteer ${timeViews[view]}. ${ time === null ? 'Geen tijd geselecteerd' : `Geselecteerde tijd is ${adapter.format(time, 'fullTime')}` @@ -42,6 +49,9 @@ const nlNLPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} minuten`, secondsClockNumberText: (seconds) => `${seconds} seconden`, + // Digital clock labels + selectViewText: (view) => `Selecteer ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Weeknummer', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/plPL.ts b/packages/x-date-pickers/src/locales/plPL.ts index e5b7cb8a1d982..6b7720e969c32 100644 --- a/packages/x-date-pickers/src/locales/plPL.ts +++ b/packages/x-date-pickers/src/locales/plPL.ts @@ -1,5 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'godzin', + minutes: 'minut', + seconds: 'sekund', + meridiem: 'popołudnie', +}; const plPLPickers: Partial> = { // Calendar navigation @@ -32,13 +40,16 @@ const plPLPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `Select ${view}. ${ + `Wybierz ${timeViews[view]}. ${ time === null ? 'Nie wybrano czasu' : `Wybrany czas to ${adapter.format(time, 'fullTime')}` }`, hoursClockNumberText: (hours) => `${hours} godzin`, minutesClockNumberText: (minutes) => `${minutes} minut`, secondsClockNumberText: (seconds) => `${seconds} sekund`, + // Digital clock labels + selectViewText: (view) => `Wybierz ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Numer tygodnia', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/ptBR.ts b/packages/x-date-pickers/src/locales/ptBR.ts index 3f0da4834f452..032c8ccfccf0d 100644 --- a/packages/x-date-pickers/src/locales/ptBR.ts +++ b/packages/x-date-pickers/src/locales/ptBR.ts @@ -1,5 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'horas', + minutes: 'minutos', + seconds: 'segundos', + meridiem: 'meridiano', +}; const ptBRPickers: Partial> = { // Calendar navigation @@ -32,7 +40,7 @@ const ptBRPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `Selecione ${view}. ${ + `Selecione ${timeViews[view]}. ${ time === null ? 'Hora não selecionada' : `Selecionado a hora ${adapter.format(time, 'fullTime')}` @@ -41,6 +49,9 @@ const ptBRPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} minutos`, secondsClockNumberText: (seconds) => `${seconds} segundos`, + // Digital clock labels + selectViewText: (view) => `Selecione ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Número da semana', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/ruRU.ts b/packages/x-date-pickers/src/locales/ruRU.ts index d412bd7efa31c..d947bcd5b2538 100644 --- a/packages/x-date-pickers/src/locales/ruRU.ts +++ b/packages/x-date-pickers/src/locales/ruRU.ts @@ -1,11 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; // Translation map for Clock Label -const timeViews = { +const timeViews: Record = { hours: 'часы', minutes: 'минуты', seconds: 'секунды', + meridiem: 'меридием', }; const ruRUPickers: Partial> = { @@ -46,6 +48,9 @@ const ruRUPickers: Partial> = { minutesClockNumberText: (minutes) => `${minutes} минут`, secondsClockNumberText: (seconds) => `${seconds} секунд`, + // Digital clock labels + selectViewText: (view) => `Выбрать ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Номер недели', calendarWeekNumberHeaderText: '№', diff --git a/packages/x-date-pickers/src/locales/svSE.ts b/packages/x-date-pickers/src/locales/svSE.ts index ebcb693c8aa49..b8296507ce945 100644 --- a/packages/x-date-pickers/src/locales/svSE.ts +++ b/packages/x-date-pickers/src/locales/svSE.ts @@ -1,5 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'timmar', + minutes: 'minuter', + seconds: 'sekunder', + meridiem: 'meridiem', +}; const svSEPickers: Partial> = { // Calendar navigation @@ -32,13 +40,16 @@ const svSEPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `Select ${view}. ${ + `Välj ${timeViews[view]}. ${ time === null ? 'Ingen tid vald' : `Vald tid är ${adapter.format(time, 'fullTime')}` }`, hoursClockNumberText: (hours) => `${hours} timmar`, minutesClockNumberText: (minutes) => `${minutes} minuter`, secondsClockNumberText: (seconds) => `${seconds} sekunder`, + // Digital clock labels + selectViewText: (view) => `Välj ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Vecka nummer', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/trTR.ts b/packages/x-date-pickers/src/locales/trTR.ts index d7a396ec0fc9e..f7cdfe13dfd8f 100644 --- a/packages/x-date-pickers/src/locales/trTR.ts +++ b/packages/x-date-pickers/src/locales/trTR.ts @@ -1,5 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'saat', + minutes: 'dakika', + seconds: 'saniye', + meridiem: 'öğleden sonra', +}; const trTRPickers: Partial> = { // Calendar navigation @@ -32,13 +40,16 @@ const trTRPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `${view} seç. ${ + `${timeViews[view]} seç. ${ time === null ? 'Zaman seçilmedi' : `Seçilen zaman: ${adapter.format(time, 'fullTime')}` }`, hoursClockNumberText: (hours) => `${hours} saat`, minutesClockNumberText: (minutes) => `${minutes} dakika`, secondsClockNumberText: (seconds) => `${seconds} saniye`, + // Digital clock labels + selectViewText: (view) => `Seç ${timeViews[view]}`, + // Calendar labels // calendarWeekNumberHeaderLabel: 'Week number', // calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/ukUA.ts b/packages/x-date-pickers/src/locales/ukUA.ts index de743fcec89b3..dbfc9d42f9662 100644 --- a/packages/x-date-pickers/src/locales/ukUA.ts +++ b/packages/x-date-pickers/src/locales/ukUA.ts @@ -1,5 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'годин', + minutes: 'хвилин', + seconds: 'секунд', + meridiem: 'Південь', +}; const ukUAPickers: Partial> = { // Calendar navigation @@ -32,13 +40,16 @@ const ukUAPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `Select ${view}. ${ + `Вибрати ${timeViews[view]}. ${ time === null ? 'Час не вибраний' : `Вибрано час ${adapter.format(time, 'fullTime')}` }`, hoursClockNumberText: (hours) => `${hours} годин`, minutesClockNumberText: (minutes) => `${minutes} хвилин`, secondsClockNumberText: (seconds) => `${seconds} секунд`, + // Digital clock labels + selectViewText: (view) => `Вибрати ${timeViews[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: 'Номер тижня', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/locales/urPK.ts b/packages/x-date-pickers/src/locales/urPK.ts index ac2d77ef15e82..1c73d41e999b2 100644 --- a/packages/x-date-pickers/src/locales/urPK.ts +++ b/packages/x-date-pickers/src/locales/urPK.ts @@ -1,5 +1,13 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const timeViews: Record = { + hours: 'گھنٹے', + minutes: 'منٹ', + seconds: 'سیکنڈ', + meridiem: 'میریڈیم', +}; const urPKPickers: Partial> = { // Calendar navigation @@ -32,13 +40,16 @@ const urPKPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `${view} منتخب کریں ${ + `${timeViews[view]} منتخب کریں ${ time === null ? 'کوئی وقت منتخب نہیں' : `منتخب وقت ہے ${adapter.format(time, 'fullTime')}` }`, hoursClockNumberText: (hours) => `${hours} گھنٹے`, minutesClockNumberText: (minutes) => `${minutes} منٹ`, secondsClockNumberText: (seconds) => `${seconds} سیکنڈ`, + // Digital clock labels + selectViewText: (view) => `${timeViews[view]} منتخب کریں`, + // Calendar labels calendarWeekNumberHeaderLabel: 'ہفتہ نمبر', calendarWeekNumberHeaderText: 'نمبر', diff --git a/packages/x-date-pickers/src/locales/utils/pickersLocaleTextApi.ts b/packages/x-date-pickers/src/locales/utils/pickersLocaleTextApi.ts index 686f167a8eb0b..a2acb4fa27bf7 100644 --- a/packages/x-date-pickers/src/locales/utils/pickersLocaleTextApi.ts +++ b/packages/x-date-pickers/src/locales/utils/pickersLocaleTextApi.ts @@ -1,3 +1,4 @@ +import { TimeViewWithMeridiem } from '../../internals/models'; import { DateView, TimeView, MuiPickersAdapter, FieldSectionContentType } from '../../models'; export interface PickersComponentSpecificLocaleText { @@ -55,6 +56,9 @@ export interface PickersComponentAgnosticLocaleText { minutesClockNumberText: (minutes: string) => string; secondsClockNumberText: (seconds: string) => string; + // Digital clock labels + selectViewText: (view: TimeViewWithMeridiem) => string; + // Open picker labels openDatePickerDialogue: (date: TDate | null, utils: MuiPickersAdapter) => string; openTimePickerDialogue: (date: TDate | null, utils: MuiPickersAdapter) => string; diff --git a/packages/x-date-pickers/src/locales/zhCN.ts b/packages/x-date-pickers/src/locales/zhCN.ts index 8a7ac7da2d480..544b6d23c1bd0 100644 --- a/packages/x-date-pickers/src/locales/zhCN.ts +++ b/packages/x-date-pickers/src/locales/zhCN.ts @@ -1,10 +1,12 @@ import { PickersLocaleText } from './utils/pickersLocaleTextApi'; import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; -const views = { +const views: Record = { hours: '小时', minutes: '分钟', seconds: '秒', + meridiem: '子午线', }; const zhCNPickers: Partial> = { @@ -36,13 +38,16 @@ const zhCNPickers: Partial> = { // Clock labels clockLabelText: (view, time, adapter) => - `Select ${views[view]}. ${ + `选择 ${views[view]}. ${ time === null ? '未选择时间' : `已选择${adapter.format(time, 'fullTime')}` }`, hoursClockNumberText: (hours) => `${hours}小时`, minutesClockNumberText: (minutes) => `${minutes}分钟`, secondsClockNumberText: (seconds) => `${seconds}秒`, + // Digital clock labels + selectViewText: (view) => `选择 ${views[view]}`, + // Calendar labels calendarWeekNumberHeaderLabel: '周数', calendarWeekNumberHeaderText: '#', diff --git a/packages/x-date-pickers/src/models/common.ts b/packages/x-date-pickers/src/models/common.ts new file mode 100644 index 0000000000000..902616712b716 --- /dev/null +++ b/packages/x-date-pickers/src/models/common.ts @@ -0,0 +1,5 @@ +export interface TimeStepOptions { + hours?: number; + minutes?: number; + seconds?: number; +} diff --git a/packages/x-date-pickers/src/models/index.ts b/packages/x-date-pickers/src/models/index.ts index 4a55f5ec3aeab..340d121ff3c77 100644 --- a/packages/x-date-pickers/src/models/index.ts +++ b/packages/x-date-pickers/src/models/index.ts @@ -2,3 +2,4 @@ export * from './fields'; export * from './validation'; export * from './views'; export * from './adapters'; +export * from './common'; diff --git a/packages/x-date-pickers/src/tests/describe.types.ts b/packages/x-date-pickers/src/tests/describe.types.ts index c16c9c983482e..3166ba6b727c6 100644 --- a/packages/x-date-pickers/src/tests/describe.types.ts +++ b/packages/x-date-pickers/src/tests/describe.types.ts @@ -1 +1,8 @@ -export type PickerComponentFamily = 'picker' | 'field' | 'calendar' | 'clock' | 'static-picker'; +export type PickerComponentFamily = + | 'picker' + | 'field' + | 'calendar' + | 'clock' + | 'digital-clock' + | 'multi-section-digital-clock' + | 'static-picker'; diff --git a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx b/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx index 2b1f7078b317e..195f09ca9fc6a 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx @@ -3,6 +3,7 @@ import { expect } from 'chai'; import { spy } from 'sinon'; import { screen, act, userEvent } from '@mui/monorepo/test/utils'; import { inputBaseClasses } from '@mui/material/InputBase'; +import { getExpectedOnChangeCount } from 'test/utils/pickers-utils'; import { DescribeValueOptions, DescribeValueTestSuite } from './describeValue.types'; export const testControlledUnControlled: DescribeValueTestSuite = ( @@ -20,6 +21,8 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( ...pickerParams } = getOptions(); + const params = pickerParams as DescribeValueOptions<'picker', any>; + describe('Controlled / uncontrolled value', () => { it('should render `props.defaultValue` if no `props.value` is passed', () => { render(); @@ -49,7 +52,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( assertRenderedValue(newValue); // TODO: Clean this exception or change the clock behavior - expect(onChange.callCount).to.equal(componentFamily === 'clock' ? 2 : 1); + expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily)); if (Array.isArray(newValue)) { newValue.forEach((value, index) => { expect(onChange.lastCall.args[0][index]).toEqualDateTime(value); @@ -65,7 +68,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( render(); const newValue = setNewValue(values[0]); - expect(onChange.callCount).to.equal(componentFamily === 'clock' ? 2 : 1); + expect(onChange.callCount).to.equal(getExpectedOnChangeCount(componentFamily)); if (Array.isArray(newValue)) { newValue.forEach((value, index) => { expect(onChange.lastCall.args[0][index]).toEqualDateTime(value); @@ -97,10 +100,7 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( }); it('should not allow editing with keyboard in mobile pickers', () => { - if ( - componentFamily !== 'picker' || - (pickerParams as DescribeValueOptions<'picker', any>).variant !== 'mobile' - ) { + if (componentFamily !== 'picker' || params.variant !== 'mobile') { return; } const handleChange = spy(); @@ -117,10 +117,9 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( }); it('should have correct labelledby relationship when toolbar is shown', () => { - const params = pickerParams as DescribeValueOptions<'picker', any>; if ( componentFamily !== 'picker' || - (params.variant === 'desktop' && (params.type === 'time' || params.type === 'date-range')) + (params.variant === 'desktop' && params.type === 'date-range') ) { return; } @@ -136,10 +135,9 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( }); it('should have correct labelledby relationship with provided label when toolbar is hidden', () => { - const params = pickerParams as DescribeValueOptions<'picker', any>; if ( componentFamily !== 'picker' || - (params.variant === 'desktop' && (params.type === 'time' || params.type === 'date-range')) + (params.variant === 'desktop' && params.type === 'date-range') ) { return; } @@ -165,10 +163,9 @@ export const testControlledUnControlled: DescribeValueTestSuite = ( }); it('should have correct labelledby relationship without label and hidden toolbar but external props', () => { - const params = pickerParams as DescribeValueOptions<'picker', any>; if ( componentFamily !== 'picker' || - (params.variant === 'desktop' && (params.type === 'time' || params.type === 'date-range')) + (params.variant === 'desktop' && params.type === 'date-range') ) { return; } diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx b/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx index 11ac3cbd7644f..e62a725995eb3 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx @@ -9,17 +9,12 @@ export const testPickerActionBar: DescribeValueTestSuite = ( ElementToTest, getOptions, ) => { - const { componentFamily, render, values, emptyValue, setNewValue, variant, type } = getOptions(); + const { componentFamily, render, values, emptyValue, setNewValue, type } = getOptions(); if (componentFamily !== 'picker') { return; } - // No view to test - if (variant === 'desktop' && type === 'time') { - return; - } - describe('Picker action bar', () => { describe('clear action', () => { it('should call onClose, onChange with empty value and onAccept with empty value', () => { diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx b/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx index 48f0d6ee5146a..63efe7b358499 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx +++ b/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx @@ -15,11 +15,6 @@ export const testPickerOpenCloseLifeCycle: DescribeValueTestSuite return; } - // No view to test - if (pickerParams.variant === 'desktop' && pickerParams.type === 'time') { - return; - } - const viewWrapperRole = pickerParams.type === 'date-range' && pickerParams.variant === 'desktop' ? 'tooltip' : 'dialog'; diff --git a/packages/x-date-pickers/src/themeAugmentation/components.d.ts b/packages/x-date-pickers/src/themeAugmentation/components.d.ts index c567aacf537ed..a394afa83a253 100644 --- a/packages/x-date-pickers/src/themeAugmentation/components.d.ts +++ b/packages/x-date-pickers/src/themeAugmentation/components.d.ts @@ -41,6 +41,10 @@ export interface PickerComponents { defaultProps?: ComponentsProps['MuiDayCalendarSkeleton']; styleOverrides?: ComponentsOverrides['MuiDayCalendarSkeleton']; }; + MuiDigitalClock?: { + defaultProps?: ComponentsProps['MuiDigitalClock']; + styleOverrides?: ComponentsOverrides['MuiDigitalClock']; + }; MuiLocalizationProvider?: { defaultProps?: ComponentsProps['MuiLocalizationProvider']; styleOverrides?: ComponentsOverrides['MuiLocalizationProvider']; @@ -49,6 +53,14 @@ export interface PickerComponents { defaultProps?: ComponentsProps['MuiMonthCalendar']; styleOverrides?: ComponentsOverrides['MuiMonthCalendar']; }; + MuiMultiSectionDigitalClock?: { + defaultProps?: ComponentsProps['MuiMultiSectionDigitalClock']; + styleOverrides?: ComponentsOverrides['MuiMultiSectionDigitalClock']; + }; + MuiMultiSectionDigitalClockSection?: { + defaultProps?: ComponentsProps['MuiMultiSectionDigitalClockSection']; + styleOverrides?: ComponentsOverrides['MuiMultiSectionDigitalClockSection']; + }; MuiPickersArrowSwitcher?: { defaultProps?: ComponentsProps['MuiPickersArrowSwitcher']; styleOverrides?: ComponentsOverrides['MuiPickersArrowSwitcher']; diff --git a/packages/x-date-pickers/src/themeAugmentation/overrides.d.ts b/packages/x-date-pickers/src/themeAugmentation/overrides.d.ts index 11601591d4f24..d250c8731c831 100644 --- a/packages/x-date-pickers/src/themeAugmentation/overrides.d.ts +++ b/packages/x-date-pickers/src/themeAugmentation/overrides.d.ts @@ -26,6 +26,11 @@ import { PickersToolbarClassKey, PickersToolbarTextClassKey, } from '../internals'; +import { DigitalClockClassKey } from '../DigitalClock'; +import { + MultiSectionDigitalClockClassKey, + MultiSectionDigitalClockSectionClassKey, +} from '../MultiSectionDigitalClock'; // prettier-ignore export interface PickersComponentNameToClassKey { @@ -39,8 +44,11 @@ export interface PickersComponentNameToClassKey { MuiDateTimePickerToolbar: DateTimePickerToolbarClassKey; MuiDayCalendar: DayCalendarClassKey; MuiDayCalendarSkeleton: DayCalendarSkeletonClassKey; + MuiDigitalClock: DigitalClockClassKey; MuiLocalizationProvider: never; MuiMonthCalendar: MonthCalendarClassKey; + MuiMultiSectionDigitalClock: MultiSectionDigitalClockClassKey; + MuiMultiSectionDigitalClockSection: MultiSectionDigitalClockSectionClassKey; MuiPickersArrowSwitcher: PickersArrowSwitcherClassKey; MuiPickersCalendarHeader: PickersCalendarHeaderClassKey; MuiPickersDay: PickersDayClassKey; diff --git a/packages/x-date-pickers/src/themeAugmentation/props.d.ts b/packages/x-date-pickers/src/themeAugmentation/props.d.ts index d15e448bb5153..a75544b6a5f21 100644 --- a/packages/x-date-pickers/src/themeAugmentation/props.d.ts +++ b/packages/x-date-pickers/src/themeAugmentation/props.d.ts @@ -40,6 +40,11 @@ import { TimePickerProps, TimePickerToolbarProps } from '../TimePicker'; import { DesktopTimePickerProps } from '../DesktopTimePicker'; import { MobileTimePickerProps } from '../MobileTimePicker'; import { StaticTimePickerProps } from '../StaticTimePicker'; +import { ExportedDigitalClockProps } from '../DigitalClock'; +import { + ExportedMultiSectionDigitalClockSectionProps, + MultiSectionDigitalClockProps, +} from '../MultiSectionDigitalClock'; export interface PickersComponentsPropsList { MuiClock: ClockProps; @@ -52,8 +57,11 @@ export interface PickersComponentsPropsList { MuiDateTimePickerToolbar: DateTimePickerToolbarProps; MuiDayCalendar: DayCalendarProps; MuiDayCalendarSkeleton: DayCalendarSkeletonProps; + MuiDigitalClock: ExportedDigitalClockProps; MuiLocalizationProvider: LocalizationProviderProps; MuiMonthCalendar: MonthCalendarProps; + MuiMultiSectionDigitalClock: MultiSectionDigitalClockProps; + MuiMultiSectionDigitalClockSection: ExportedMultiSectionDigitalClockSectionProps; MuiPickersArrowSwitcher: ExportedPickersArrowSwitcherProps; MuiPickersCalendarHeader: ExportedCalendarHeaderProps; MuiPickersDay: PickersDayProps; diff --git a/packages/x-date-pickers/src/themeAugmentation/themeAugmentation.spec.ts b/packages/x-date-pickers/src/themeAugmentation/themeAugmentation.spec.ts index a70e0dc1eba76..6f32a6967c7f9 100644 --- a/packages/x-date-pickers/src/themeAugmentation/themeAugmentation.spec.ts +++ b/packages/x-date-pickers/src/themeAugmentation/themeAugmentation.spec.ts @@ -22,6 +22,11 @@ import { import { pickersDayClasses } from '../PickersDay'; import { timePickerToolbarClasses } from '../TimePicker'; import { pickersMonthClasses } from '../MonthCalendar'; +import { digitalClockClasses } from '../DigitalClock'; +import { + multiSectionDigitalClockClasses, + multiSectionDigitalClockSectionClasses, +} from '../MultiSectionDigitalClock'; createTheme({ components: { @@ -63,6 +68,63 @@ createTheme({ }, }, }, + MuiDigitalClock: { + defaultProps: { + timeStep: 42, + // @ts-expect-error invalid MuiDigitalClock prop + someRandomProp: true, + }, + styleOverrides: { + root: { + backgroundColor: 'red', + [`.${digitalClockClasses.item}`]: { + backgroundColor: 'green', + }, + }, + // @ts-expect-error invalid MuiDigitalClock class key + content: { + backgroundColor: 'blue', + }, + }, + }, + MuiMultiSectionDigitalClock: { + defaultProps: { + timeSteps: { minutes: 42 }, + // @ts-expect-error invalid MuiMultiSectionDigitalClock prop + someRandomProp: true, + }, + styleOverrides: { + root: { + backgroundColor: 'red', + [`&.${multiSectionDigitalClockClasses.root}`]: { + backgroundColor: 'green', + }, + }, + // @ts-expect-error invalid MuiMultiSectionDigitalClock class key + content: { + backgroundColor: 'blue', + }, + }, + }, + MuiMultiSectionDigitalClockSection: { + defaultProps: { + className: 'class', + // @ts-expect-error invalid MuiMultiSectionDigitalClockSection prop + someRandomProp: true, + }, + styleOverrides: { + root: { + backgroundColor: 'red', + [`.${multiSectionDigitalClockSectionClasses.item}`]: { + backgroundColor: 'green', + }, + }, + // @ts-expect-error invalid MuiMultiSectionDigitalClockSection class key + content: { + backgroundColor: 'blue', + }, + }, + }, MuiClock: { defaultProps: { ampmInClock: true, diff --git a/packages/x-date-pickers/src/timeViewRenderers/index.ts b/packages/x-date-pickers/src/timeViewRenderers/index.ts index 6189103806540..70b1b35e2c44a 100644 --- a/packages/x-date-pickers/src/timeViewRenderers/index.ts +++ b/packages/x-date-pickers/src/timeViewRenderers/index.ts @@ -1,2 +1,6 @@ -export { renderTimeViewClock } from './timeViewRenderers'; +export { + renderTimeViewClock, + renderDigitalClockTimeView, + renderMultiSectionDigitalClockTimeView, +} from './timeViewRenderers'; export type { TimeViewRendererProps } from './timeViewRenderers'; diff --git a/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx b/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx index 0d903d64a3c78..5a1bc9963246e 100644 --- a/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx +++ b/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx @@ -1,20 +1,30 @@ import * as React from 'react'; import { TimeClock, TimeClockProps } from '../TimeClock'; -import { DateOrTimeView, TimeView } from '../models'; +import { TimeView } from '../models'; +import { DigitalClock, DigitalClockProps } from '../DigitalClock'; +import { BaseClockProps } from '../internals/models/props/clock'; +import { + MultiSectionDigitalClock, + MultiSectionDigitalClockProps, +} from '../MultiSectionDigitalClock'; +import { isTimeView } from '../internals/utils/time-utils'; +import { TimeViewWithMeridiem } from '../internals/models'; +import type { TimePickerProps } from '../TimePicker/TimePicker.types'; -const isTimePickerView = (view: unknown): view is TimeView => - view === 'hours' || view === 'minutes' || view === 'seconds'; - -export interface TimeViewRendererProps - extends Omit, 'views' | 'openTo' | 'view' | 'onViewChange'> { +export type TimeViewRendererProps< + TView extends TimeViewWithMeridiem, + TComponentProps extends BaseClockProps, +> = Omit & { view: TView; onViewChange?: (view: TView) => void; views: readonly TView[]; -} +}; export const renderTimeViewClock = ({ view, onViewChange, + focusedView, + onFocusedViewChange, views, value, defaultValue, @@ -40,11 +50,13 @@ export const renderTimeViewClock = ({ autoFocus, showViewSwitcher, disableIgnoringDatePartForTimeValidation, -}: TimeViewRendererProps) => ( +}: TimeViewRendererProps>) => ( - view={view as TimeView} + view={view} onViewChange={onViewChange} - views={views.filter(isTimePickerView)} + focusedView={focusedView} + onFocusedViewChange={onFocusedViewChange} + views={views.filter(isTimeView)} value={value} defaultValue={defaultValue} onChange={onChange} @@ -71,3 +83,139 @@ export const renderTimeViewClock = ({ disableIgnoringDatePartForTimeValidation={disableIgnoringDatePartForTimeValidation} /> ); + +export const renderDigitalClockTimeView = ({ + view, + onViewChange, + focusedView, + onFocusedViewChange, + views, + value, + defaultValue, + onChange, + className, + classes, + disableFuture, + disablePast, + minTime, + maxTime, + shouldDisableTime, + shouldDisableClock, + minutesStep, + ampm, + components, + componentsProps, + slots, + slotProps, + readOnly, + disabled, + sx, + autoFocus, + disableIgnoringDatePartForTimeValidation, + timeSteps, + skipDisabled, +}: TimeViewRendererProps< + Extract, + Omit, 'timeStep'> & Pick, 'timeSteps'> +>) => ( + + view={view} + onViewChange={onViewChange} + focusedView={focusedView} + onFocusedViewChange={onFocusedViewChange} + views={views.filter(isTimeView)} + value={value} + defaultValue={defaultValue} + onChange={onChange} + className={className} + classes={classes} + disableFuture={disableFuture} + disablePast={disablePast} + minTime={minTime} + maxTime={maxTime} + shouldDisableTime={shouldDisableTime} + shouldDisableClock={shouldDisableClock} + minutesStep={minutesStep} + ampm={ampm} + components={components} + componentsProps={componentsProps} + slots={slots} + slotProps={slotProps} + readOnly={readOnly} + disabled={disabled} + sx={sx} + autoFocus={autoFocus} + disableIgnoringDatePartForTimeValidation={disableIgnoringDatePartForTimeValidation} + timeStep={timeSteps?.minutes} + skipDisabled={skipDisabled} + /> +); + +export const renderMultiSectionDigitalClockTimeView = ({ + view, + onViewChange, + focusedView, + onFocusedViewChange, + views, + value, + defaultValue, + onChange, + className, + classes, + disableFuture, + disablePast, + minTime, + maxTime, + shouldDisableTime, + shouldDisableClock, + minutesStep, + ampm, + components, + componentsProps, + slots, + slotProps, + readOnly, + disabled, + sx, + autoFocus, + disableIgnoringDatePartForTimeValidation, + timeSteps, + skipDisabled, +}: TimeViewRendererProps< + TimeViewWithMeridiem, + Omit, 'timeSteps'> & + Omit, 'timeStep'> & + Pick, 'timeSteps'> +>) => ( + + view={view} + onViewChange={onViewChange} + focusedView={focusedView} + onFocusedViewChange={onFocusedViewChange} + views={views.filter(isTimeView)} + value={value} + defaultValue={defaultValue} + onChange={onChange} + className={className} + classes={classes} + disableFuture={disableFuture} + disablePast={disablePast} + minTime={minTime} + maxTime={maxTime} + shouldDisableTime={shouldDisableTime} + shouldDisableClock={shouldDisableClock} + minutesStep={minutesStep} + ampm={ampm} + components={components} + componentsProps={componentsProps} + slots={slots} + slotProps={slotProps} + readOnly={readOnly} + disabled={disabled} + sx={sx} + autoFocus={autoFocus} + disableIgnoringDatePartForTimeValidation={disableIgnoringDatePartForTimeValidation} + timeSteps={timeSteps} + skipDisabled={skipDisabled} + /> +); diff --git a/scripts/x-date-pickers-pro.exports.json b/scripts/x-date-pickers-pro.exports.json index 7c4ba1dbd6df9..51d12ba955e83 100644 --- a/scripts/x-date-pickers-pro.exports.json +++ b/scripts/x-date-pickers-pro.exports.json @@ -109,9 +109,18 @@ { "name": "DesktopTimePickerProps", "kind": "Interface" }, { "name": "DesktopTimePickerSlotsComponent", "kind": "Interface" }, { "name": "DesktopTimePickerSlotsComponentsProps", "kind": "Interface" }, + { "name": "DigitalClock", "kind": "Variable" }, + { "name": "digitalClockClasses", "kind": "Variable" }, + { "name": "DigitalClockClasses", "kind": "Interface" }, + { "name": "DigitalClockClassKey", "kind": "TypeAlias" }, + { "name": "DigitalClockProps", "kind": "Interface" }, + { "name": "DigitalClockSlotsComponent", "kind": "Interface" }, + { "name": "DigitalClockSlotsComponentsProps", "kind": "Interface" }, { "name": "enUS", "kind": "Variable" }, { "name": "esES", "kind": "Variable" }, { "name": "ExportedDateRangeCalendarProps", "kind": "Interface" }, + { "name": "ExportedDigitalClockProps", "kind": "Interface" }, + { "name": "ExportedMultiSectionDigitalClockSectionProps", "kind": "Interface" }, { "name": "ExportedPickersLayoutSlotsComponent", "kind": "Interface" }, { "name": "ExportedPickersLayoutSlotsComponentsProps", "kind": "Interface" }, { "name": "ExportedPickersMonthProps", "kind": "Interface" }, @@ -132,7 +141,9 @@ { "name": "getDateRangePickerDayUtilityClass", "kind": "Function" }, { "name": "getDateRangePickerToolbarUtilityClass", "kind": "Function" }, { "name": "getDayCalendarSkeletonUtilityClass", "kind": "Variable" }, + { "name": "getDigitalClockUtilityClass", "kind": "Function" }, { "name": "getMonthCalendarUtilityClass", "kind": "Function" }, + { "name": "getMultiSectionDigitalClockUtilityClass", "kind": "Function" }, { "name": "getPickersDayUtilityClass", "kind": "Function" }, { "name": "getTimeClockUtilityClass", "kind": "Function" }, { "name": "getYearCalendarUtilityClass", "kind": "Function" }, @@ -177,6 +188,16 @@ { "name": "MultiInputFieldSlotTextFieldProps", "kind": "Interface" }, { "name": "MultiInputTimeRangeField", "kind": "Variable" }, { "name": "MultiInputTimeRangeFieldProps", "kind": "Interface" }, + { "name": "MultiSectionDigitalClock", "kind": "Variable" }, + { "name": "multiSectionDigitalClockClasses", "kind": "Variable" }, + { "name": "MultiSectionDigitalClockClasses", "kind": "Interface" }, + { "name": "MultiSectionDigitalClockClassKey", "kind": "TypeAlias" }, + { "name": "MultiSectionDigitalClockProps", "kind": "Interface" }, + { "name": "multiSectionDigitalClockSectionClasses", "kind": "Variable" }, + { "name": "MultiSectionDigitalClockSectionClasses", "kind": "Interface" }, + { "name": "MultiSectionDigitalClockSectionClassKey", "kind": "TypeAlias" }, + { "name": "MultiSectionDigitalClockSlotsComponent", "kind": "Interface" }, + { "name": "MultiSectionDigitalClockSlotsComponentsProps", "kind": "Interface" }, { "name": "nbNO", "kind": "Variable" }, { "name": "nlNL", "kind": "Variable" }, { "name": "PickersActionBar", "kind": "Function" }, @@ -226,6 +247,8 @@ { "name": "RangePosition", "kind": "TypeAlias" }, { "name": "renderDateRangeViewCalendar", "kind": "Variable" }, { "name": "renderDateViewCalendar", "kind": "Variable" }, + { "name": "renderDigitalClockTimeView", "kind": "Variable" }, + { "name": "renderMultiSectionDigitalClockTimeView", "kind": "Variable" }, { "name": "renderTimeViewClock", "kind": "Variable" }, { "name": "ruRU", "kind": "Variable" }, { "name": "SingleInputDateRangeField", "kind": "Variable" }, @@ -270,9 +293,10 @@ { "name": "TimePickerToolbarClassKey", "kind": "TypeAlias" }, { "name": "TimePickerToolbarProps", "kind": "Interface" }, { "name": "TimeRangeValidationError", "kind": "TypeAlias" }, + { "name": "TimeStepOptions", "kind": "Interface" }, { "name": "TimeValidationError", "kind": "TypeAlias" }, { "name": "TimeView", "kind": "TypeAlias" }, - { "name": "TimeViewRendererProps", "kind": "Interface" }, + { "name": "TimeViewRendererProps", "kind": "TypeAlias" }, { "name": "trTR", "kind": "Variable" }, { "name": "ukUA", "kind": "Variable" }, { "name": "unstable_useDateField", "kind": "Variable" }, diff --git a/scripts/x-date-pickers.exports.json b/scripts/x-date-pickers.exports.json index e5f0f03797527..c21243e7c87f4 100644 --- a/scripts/x-date-pickers.exports.json +++ b/scripts/x-date-pickers.exports.json @@ -79,8 +79,17 @@ { "name": "DesktopTimePickerProps", "kind": "Interface" }, { "name": "DesktopTimePickerSlotsComponent", "kind": "Interface" }, { "name": "DesktopTimePickerSlotsComponentsProps", "kind": "Interface" }, + { "name": "DigitalClock", "kind": "Variable" }, + { "name": "digitalClockClasses", "kind": "Variable" }, + { "name": "DigitalClockClasses", "kind": "Interface" }, + { "name": "DigitalClockClassKey", "kind": "TypeAlias" }, + { "name": "DigitalClockProps", "kind": "Interface" }, + { "name": "DigitalClockSlotsComponent", "kind": "Interface" }, + { "name": "DigitalClockSlotsComponentsProps", "kind": "Interface" }, { "name": "enUS", "kind": "Variable" }, { "name": "esES", "kind": "Variable" }, + { "name": "ExportedDigitalClockProps", "kind": "Interface" }, + { "name": "ExportedMultiSectionDigitalClockSectionProps", "kind": "Interface" }, { "name": "ExportedPickersLayoutSlotsComponent", "kind": "Interface" }, { "name": "ExportedPickersLayoutSlotsComponentsProps", "kind": "Interface" }, { "name": "ExportedPickersMonthProps", "kind": "Interface" }, @@ -98,7 +107,9 @@ { "name": "frFR", "kind": "Variable" }, { "name": "getDateCalendarUtilityClass", "kind": "Variable" }, { "name": "getDayCalendarSkeletonUtilityClass", "kind": "Variable" }, + { "name": "getDigitalClockUtilityClass", "kind": "Function" }, { "name": "getMonthCalendarUtilityClass", "kind": "Function" }, + { "name": "getMultiSectionDigitalClockUtilityClass", "kind": "Function" }, { "name": "getPickersDayUtilityClass", "kind": "Function" }, { "name": "getTimeClockUtilityClass", "kind": "Function" }, { "name": "getYearCalendarUtilityClass", "kind": "Function" }, @@ -131,6 +142,16 @@ { "name": "MonthCalendarProps", "kind": "Interface" }, { "name": "MuiPickersAdapter", "kind": "Interface" }, { "name": "MuiPickersAdapterContext", "kind": "Variable" }, + { "name": "MultiSectionDigitalClock", "kind": "Variable" }, + { "name": "multiSectionDigitalClockClasses", "kind": "Variable" }, + { "name": "MultiSectionDigitalClockClasses", "kind": "Interface" }, + { "name": "MultiSectionDigitalClockClassKey", "kind": "TypeAlias" }, + { "name": "MultiSectionDigitalClockProps", "kind": "Interface" }, + { "name": "multiSectionDigitalClockSectionClasses", "kind": "Variable" }, + { "name": "MultiSectionDigitalClockSectionClasses", "kind": "Interface" }, + { "name": "MultiSectionDigitalClockSectionClassKey", "kind": "TypeAlias" }, + { "name": "MultiSectionDigitalClockSlotsComponent", "kind": "Interface" }, + { "name": "MultiSectionDigitalClockSlotsComponentsProps", "kind": "Interface" }, { "name": "nbNO", "kind": "Variable" }, { "name": "nlNL", "kind": "Variable" }, { "name": "PickersActionBar", "kind": "Function" }, @@ -177,6 +198,8 @@ { "name": "plPL", "kind": "Variable" }, { "name": "ptBR", "kind": "Variable" }, { "name": "renderDateViewCalendar", "kind": "Variable" }, + { "name": "renderDigitalClockTimeView", "kind": "Variable" }, + { "name": "renderMultiSectionDigitalClockTimeView", "kind": "Variable" }, { "name": "renderTimeViewClock", "kind": "Variable" }, { "name": "ruRU", "kind": "Variable" }, { "name": "StaticDatePicker", "kind": "Variable" }, @@ -210,9 +233,10 @@ { "name": "TimePickerToolbarClasses", "kind": "Interface" }, { "name": "TimePickerToolbarClassKey", "kind": "TypeAlias" }, { "name": "TimePickerToolbarProps", "kind": "Interface" }, + { "name": "TimeStepOptions", "kind": "Interface" }, { "name": "TimeValidationError", "kind": "TypeAlias" }, { "name": "TimeView", "kind": "TypeAlias" }, - { "name": "TimeViewRendererProps", "kind": "Interface" }, + { "name": "TimeViewRendererProps", "kind": "TypeAlias" }, { "name": "trTR", "kind": "Variable" }, { "name": "ukUA", "kind": "Variable" }, { "name": "unstable_useDateField", "kind": "Variable" }, diff --git a/test/utils/pickers-utils.tsx b/test/utils/pickers-utils.tsx index dc2877f37ee11..9a7ed49af244f 100644 --- a/test/utils/pickers-utils.tsx +++ b/test/utils/pickers-utils.tsx @@ -25,6 +25,7 @@ import { AdapterDateFnsJalali } from '@mui/x-date-pickers/AdapterDateFnsJalali'; import { MuiPickersAdapter } from '@mui/x-date-pickers/models'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { CLOCK_WIDTH } from '@mui/x-date-pickers/TimeClock/shared'; +import { PickerComponentFamily } from '@mui/x-date-pickers/tests/describe.types'; export type AdapterName = | 'date-fns' @@ -546,3 +547,14 @@ export class MockedDataTransfer implements DataTransfer { this.yOffset = yOffset; } } + +export const getExpectedOnChangeCount = (componentFamily: PickerComponentFamily) => { + switch (componentFamily) { + case 'clock': + return 2; + case 'multi-section-digital-clock': + return 3; + default: + return 1; + } +}; From 487af9ea3e176c49060cb0518cad65564295a4ac Mon Sep 17 00:00:00 2001 From: Denis <7009699+someden@users.noreply.github.com> Date: Thu, 27 Apr 2023 23:36:11 +0300 Subject: [PATCH 28/80] [DataGrid] Avoid passing `api` prop to div (#8679) --- .../src/components/cell/GridActionsCell.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/grid/x-data-grid/src/components/cell/GridActionsCell.tsx b/packages/grid/x-data-grid/src/components/cell/GridActionsCell.tsx index 13b62d4780359..5eb94358b0d78 100644 --- a/packages/grid/x-data-grid/src/components/cell/GridActionsCell.tsx +++ b/packages/grid/x-data-grid/src/components/cell/GridActionsCell.tsx @@ -17,15 +17,14 @@ interface TouchRippleActions { stop: (event: any, callback?: () => void) => void; } -interface GridActionsCellProps - extends Omit { - value?: GridRenderCellParams['value']; - formattedValue?: GridRenderCellParams['formattedValue']; +interface GridActionsCellProps extends Omit { + api?: GridRenderCellParams['api']; position?: GridMenuProps['position']; } function GridActionsCell(props: GridActionsCellProps) { const { + api, colDef, id, hasFocus, @@ -243,6 +242,7 @@ GridActionsCell.propTypes = { // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- + api: PropTypes.object, /** * The mode of the cell. */ @@ -268,6 +268,9 @@ GridActionsCell.propTypes = { }), }), ]), + /** + * The cell value formatted with the column valueFormatter. + */ formattedValue: PropTypes.any, /** * If true, the cell is the active element. @@ -307,6 +310,10 @@ GridActionsCell.propTypes = { * the tabIndex value. */ tabIndex: PropTypes.oneOf([-1, 0]).isRequired, + /** + * The cell value. + * If the column has `valueGetter`, use `params.row` to directly access the fields. + */ value: PropTypes.any, } as any; From 9b6234121d3df5107cae593bd21e9274f4b121aa Mon Sep 17 00:00:00 2001 From: Can Kurt <41245214+cccaaannn@users.noreply.github.com> Date: Fri, 28 Apr 2023 08:47:18 +0300 Subject: [PATCH 29/80] [l10n] Improve Turkish (tr-TR) locale (#8783) --- docs/data/data-grid/localization/data.json | 2 +- docs/data/date-pickers/localization/data.json | 2 +- packages/grid/x-data-grid/src/locales/trTR.ts | 4 ++-- packages/x-date-pickers/src/locales/trTR.ts | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/data/data-grid/localization/data.json b/docs/data/data-grid/localization/data.json index 2647662315785..7b9450c270052 100644 --- a/docs/data/data-grid/localization/data.json +++ b/docs/data/data-grid/localization/data.json @@ -211,7 +211,7 @@ "languageTag": "tr-TR", "importName": "trTR", "localeName": "Turkish", - "missingKeysCount": 2, + "missingKeysCount": 0, "totalKeysCount": 94, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/grid/x-data-grid/src/locales/trTR.ts" }, diff --git a/docs/data/date-pickers/localization/data.json b/docs/data/date-pickers/localization/data.json index b34a802d8b664..6133e9f06bc6a 100644 --- a/docs/data/date-pickers/localization/data.json +++ b/docs/data/date-pickers/localization/data.json @@ -187,7 +187,7 @@ "languageTag": "tr-TR", "importName": "trTR", "localeName": "Turkish", - "missingKeysCount": 5, + "missingKeysCount": 0, "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/trTR.ts" }, diff --git a/packages/grid/x-data-grid/src/locales/trTR.ts b/packages/grid/x-data-grid/src/locales/trTR.ts index 0b41e7e2e8bc2..76fa74c5418b7 100644 --- a/packages/grid/x-data-grid/src/locales/trTR.ts +++ b/packages/grid/x-data-grid/src/locales/trTR.ts @@ -46,7 +46,7 @@ const trTRGrid: Partial = { // Filter panel text filterPanelAddFilter: 'Filtre Ekle', - // filterPanelRemoveAll: 'Remove all', + filterPanelRemoveAll: 'Hepsini kaldır', filterPanelDeleteIconLabel: 'Kaldır', filterPanelLogicOperator: 'Mantıksal operatörler', filterPanelOperator: 'Operatör', @@ -79,7 +79,7 @@ const trTRGrid: Partial = { // Column menu text columnMenuLabel: 'Menü', columnMenuShowColumns: 'Sütunları göster', - // columnMenuManageColumns: 'Manage columns', + columnMenuManageColumns: 'Sütunları yönet', columnMenuFilter: 'Filtre uygula', columnMenuHideColumn: 'Gizle', columnMenuUnsort: 'Sıralama', diff --git a/packages/x-date-pickers/src/locales/trTR.ts b/packages/x-date-pickers/src/locales/trTR.ts index f7cdfe13dfd8f..a640746cadf61 100644 --- a/packages/x-date-pickers/src/locales/trTR.ts +++ b/packages/x-date-pickers/src/locales/trTR.ts @@ -51,10 +51,10 @@ const trTRPickers: Partial> = { selectViewText: (view) => `Seç ${timeViews[view]}`, // Calendar labels - // calendarWeekNumberHeaderLabel: 'Week number', - // calendarWeekNumberHeaderText: '#', - // calendarWeekNumberAriaLabelText: weekNumber => `Week ${weekNumber}`, - // calendarWeekNumberText: weekNumber => `${weekNumber}`, + calendarWeekNumberHeaderLabel: 'Hafta numarası', + calendarWeekNumberHeaderText: '#', + calendarWeekNumberAriaLabelText: (weekNumber) => `Hafta ${weekNumber}`, + calendarWeekNumberText: (weekNumber) => `${weekNumber}`, // Open picker labels openDatePickerDialogue: (value, utils) => @@ -74,7 +74,7 @@ const trTRPickers: Partial> = { fieldYearPlaceholder: (params) => 'Y'.repeat(params.digitAmount), fieldMonthPlaceholder: (params) => (params.contentType === 'letter' ? 'AAA' : 'AA'), fieldDayPlaceholder: () => 'GG', - // fieldWeekDayPlaceholder: params => params.contentType === 'letter' ? 'EEEE' : 'EE', + fieldWeekDayPlaceholder: (params) => (params.contentType === 'letter' ? 'HHH' : 'HH'), fieldHoursPlaceholder: () => 'ss', fieldMinutesPlaceholder: () => 'dd', fieldSecondsPlaceholder: () => 'ss', From 0e1e4d879ed4f1e9c11e8d67d40cd024effcde2c Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 28 Apr 2023 10:18:37 +0300 Subject: [PATCH 30/80] v6.3.0 (#8784) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Co-authored-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Co-authored-by: José Rodolfo Freitas Co-authored-by: Flavien DELANGLE --- CHANGELOG.md | 69 +++++++++++++++++++ docs/package.json | 2 +- package.json | 2 +- .../eslint-plugin-material-ui/package.json | 2 +- .../grid/x-data-grid-generator/package.json | 4 +- .../grid/x-data-grid-premium/package.json | 6 +- packages/grid/x-data-grid-pro/package.json | 4 +- packages/grid/x-data-grid/package.json | 2 +- packages/x-date-pickers-pro/package.json | 4 +- packages/x-date-pickers/package.json | 2 +- 10 files changed, 83 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 445138b02374f..61da091068b6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,75 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 6.3.0 + +_Apr 28, 2023_ + +We'd like to offer a big thanks to the 15 contributors who made this release possible. Here are some highlights ✨: + +- 🚀 New [time-picking UI](https://mui.com/x/react-date-pickers/digital-clock/) designed for desktops (#7958) @LukasTy + + + +- ✨ Picker fields [now always include a leading zero](https://mui.com/x/react-date-pickers/adapters-locale/#respect-leading-zeros-in-fields) on digit sections (#8527) @flaviendelangle +- 🌍 Improve Chinese (zh-CN), French (fr-FR), and Turkish (tr-TR) locales +- 🐞 Bugfixes +- 📚 Documentation improvements + +### `@mui/x-data-grid@v6.3.0` / `@mui/x-data-grid-pro@v6.3.0` / `@mui/x-data-grid-premium@v6.3.0` + +#### Changes + +- [DataGrid] Add overlay classes to `gridClasses` (#8686) @lindapaiste +- [DataGrid] Avoid passing `api` prop to div (#8679) @someden +- [DataGrid] Fix 'ResizeObserver loop limit exceeded' error (#8744) @m4theushw +- [DataGrid] Add Joy UI slots (button and switch) (#8699) @siriwatknp +- [DataGrid] Fix aggregation label alignment (#8694) @joserodolfofreitas +- [DataGridPremium] Fix infinite loop when updating grouped rows (#8693) @cherniavskii +- [DataGridPro] Fix error after updating `columns` and `columnGroupingModel` at once (#8730) @cherniavskii +- [l10n] Improve Chinese (zh-CN) locale (#8753) @SakumyZ +- [l10n] Improve French (fr-FR) locale (#8704) @Jul13nT +- [l10n] Improve Turkish (tr-TR) locale (#8783) @cccaaannn + +### `@mui/x-date-pickers@v6.3.0` / `@mui/x-date-pickers-pro@v6.3.0` + +#### Changes + +- [fields] Always add leading zeroes on digit sections (#8527) @flaviendelangle +- [fields] Pass the `readOnly` prop to `InputProps` instead of `inputProps` (#8659) @flaviendelangle +- [pickers] Add missing export for `caES` locale (#8782) @flaviendelangle +- [pickers] Add new `DigitalClock` desktop time picking experience (#7958) @LukasTy +- [pickers] Do not use `instanceOf DateTime` in `AdapterLuxon` (#8734) @flaviendelangle +- [pickers] Fix date calendar `selected` & `disabled` day style (#8773) @LukasTy +- [pickers] Migrate `AdapterDateFns` to our repository (#8736) @flaviendelangle +- [pickers] Migrate `AdapterLuxon` to our repository (#8600) @flaviendelangle +- [pickers] Migrate `AdapterMomentHijri` to our repository (#8776) @flaviendelangle +- [pickers] Migrate `AdapterMomentJalaali` and `AdapterDateFnsJalali` to our repository (#8741) @flaviendelangle +- [pickers] Migrate `AdapterMoment` to our repository (#8700) @flaviendelangle +- [pickers] Refactor the validation files (#8622) @flaviendelangle +- [pickers] Use `en dash` instead of `em dash` in multi input range fields (#8738) @flaviendelangle +- [l10n] Improve Chinese (zh-CN) locale (#8753) @SakumyZ +- [l10n] Improve Turkish (tr-TR) locale (#8783) @cccaaannn + +### Docs + +- [docs] Add icons for charts menu (#8752) @alexfauquette +- [docs] Document the supported formats (#8746) @flaviendelangle +- [docs] Fix Hijri demo (#8698) @alexfauquette +- [docs] Fix `x-codemod` package version in changelog (#8690) @MBilalShafi +- [docs] Fix columns special properties code example (#8414) @mikkelhl +- [docs] Fix error in `minDateTime` `validation` page section (#8777) @LukasTy +- [docs] Update custom field pickers using theme scoping (#8609) @siriwatknp +- [docs] Use community version of data grid for column grouping demo (#7346) @ASchwad +- [docs] Use new `slots` / `slotProps` props in the pickers migration guide (#8341) @flaviendelangle + +### Core + +- [core] Cleanup picker tests (#8652) @flaviendelangle +- [core] Use `adapter.lib` instead of `adapterName` in `describeAdapters` (#8779) @flaviendelangle +- [charts] Adapt line and scatter plot to the "band" scale type (#8701) @alexfauquette +- [charts] Link the Gantt Charts issue in the docs (#8739) @flaviendelangle + ## 6.2.1 _Apr 20, 2023_ diff --git a/docs/package.json b/docs/package.json index 578c367eb1a7c..a1b9e0c5834ca 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "6.2.1", + "version": "6.3.0", "private": true, "author": "MUI Team", "license": "MIT", diff --git a/package.json b/package.json index ce4c83b636e55..6e7e6227d800d 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "6.2.1", + "version": "6.3.0", "private": true, "scripts": { "start": "yarn && yarn docs:dev", diff --git a/packages/eslint-plugin-material-ui/package.json b/packages/eslint-plugin-material-ui/package.json index 93d1d425beb2f..dabf3e588c41a 100644 --- a/packages/eslint-plugin-material-ui/package.json +++ b/packages/eslint-plugin-material-ui/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-material-ui", - "version": "6.0.5", + "version": "6.3.0", "private": true, "description": "Custom eslint rules for MUI X.", "main": "src/index.js", diff --git a/packages/grid/x-data-grid-generator/package.json b/packages/grid/x-data-grid-generator/package.json index b437541340943..4cfc78d7b4252 100644 --- a/packages/grid/x-data-grid-generator/package.json +++ b/packages/grid/x-data-grid-generator/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-generator", - "version": "6.2.1", + "version": "6.3.0", "description": "Generate fake data for demo purposes only.", "author": "MUI Team", "main": "src/index.ts", @@ -32,7 +32,7 @@ "dependencies": { "@babel/runtime": "^7.21.0", "@mui/base": "^5.0.0-alpha.127", - "@mui/x-data-grid-premium": "6.2.1", + "@mui/x-data-grid-premium": "6.3.0", "chance": "^1.1.11", "clsx": "^1.2.1", "lru-cache": "^7.18.3" diff --git a/packages/grid/x-data-grid-premium/package.json b/packages/grid/x-data-grid-premium/package.json index ee323efb74c24..fc3d4bce897f2 100644 --- a/packages/grid/x-data-grid-premium/package.json +++ b/packages/grid/x-data-grid-premium/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-premium", - "version": "6.2.1", + "version": "6.3.0", "description": "The Premium plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -44,8 +44,8 @@ "dependencies": { "@babel/runtime": "^7.21.0", "@mui/utils": "^5.12.0", - "@mui/x-data-grid": "6.2.1", - "@mui/x-data-grid-pro": "6.2.1", + "@mui/x-data-grid": "6.3.0", + "@mui/x-data-grid-pro": "6.3.0", "@mui/x-license-pro": "6.0.4", "@types/format-util": "^1.0.2", "clsx": "^1.2.1", diff --git a/packages/grid/x-data-grid-pro/package.json b/packages/grid/x-data-grid-pro/package.json index 05738da8208c3..294cc171e8bf1 100644 --- a/packages/grid/x-data-grid-pro/package.json +++ b/packages/grid/x-data-grid-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-pro", - "version": "6.2.1", + "version": "6.3.0", "description": "The Pro plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -44,7 +44,7 @@ "dependencies": { "@babel/runtime": "^7.21.0", "@mui/utils": "^5.12.0", - "@mui/x-data-grid": "6.2.1", + "@mui/x-data-grid": "6.3.0", "@mui/x-license-pro": "6.0.4", "@types/format-util": "^1.0.2", "clsx": "^1.2.1", diff --git a/packages/grid/x-data-grid/package.json b/packages/grid/x-data-grid/package.json index 347e113c143bd..201ff4d0e95c7 100644 --- a/packages/grid/x-data-grid/package.json +++ b/packages/grid/x-data-grid/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid", - "version": "6.2.1", + "version": "6.3.0", "description": "The community edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index c962b8e700a57..31e739385f2be 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers-pro", - "version": "6.2.1", + "version": "6.3.0", "description": "The commercial edition of the date picker components (MUI X).", "author": "MUI Team", "main": "./src/index.js", @@ -43,7 +43,7 @@ "dependencies": { "@babel/runtime": "^7.21.0", "@mui/utils": "^5.12.0", - "@mui/x-date-pickers": "6.2.1", + "@mui/x-date-pickers": "6.3.0", "@mui/x-license-pro": "6.0.4", "clsx": "^1.2.1", "prop-types": "^15.8.1", diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index c18f1df8bcf2f..78fa5f8ca6a82 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers", - "version": "6.2.1", + "version": "6.3.0", "description": "The community edition of the date picker components (MUI X).", "author": "MUI Team", "main": "./src/index.js", From 94609d82565faacddd646506574ac9fd7ec8c8e7 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Fri, 28 Apr 2023 10:43:21 +0200 Subject: [PATCH 31/80] [docs] clarify what is (#8764) --- .../migration-data-grid-v5/migration-data-grid-v5.md | 2 ++ .../migration/migration-pickers-lab/migration-pickers-lab.md | 4 +++- .../migration/migration-pickers-v5/migration-pickers-v5.md | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/data/migration/migration-data-grid-v5/migration-data-grid-v5.md b/docs/data/migration/migration-data-grid-v5/migration-data-grid-v5.md index ec1978351166b..a8a1da196bfac 100644 --- a/docs/data/migration/migration-data-grid-v5/migration-data-grid-v5.md +++ b/docs/data/migration/migration-data-grid-v5/migration-data-grid-v5.md @@ -25,6 +25,8 @@ Described below are the steps needed to migrate from v5 to v6. The `preset-safe` codemod will automatically adjust the bulk of your code to account for breaking changes in v6. You can run `v6.0.0/data-grid/preset-safe` targeting only Data Grid or `v6.0.0/preset-safe` to target Date and Time pickers as well. +You can either run it on a specific file, folder, or your entire codebase when choosing the `` argument. + ```sh // Data Grid specific npx @mui/x-codemod v6.0.0/data-grid/preset-safe diff --git a/docs/data/migration/migration-pickers-lab/migration-pickers-lab.md b/docs/data/migration/migration-pickers-lab/migration-pickers-lab.md index 189cbfde22a17..15efcb440fd86 100644 --- a/docs/data/migration/migration-pickers-lab/migration-pickers-lab.md +++ b/docs/data/migration/migration-pickers-lab/migration-pickers-lab.md @@ -58,7 +58,9 @@ Learn more on [the Licensing page](/x/introduction/licensing/#license-key-instal ### 2. Run the code mod -We have prepared a codemod to help you migrate your codebase +We have prepared a codemod to help you migrate your codebase. + +You can either run it on a specific file, folder, or your entire codebase when choosing the `` argument. ```sh npx @mui/codemod v5.0.0/date-pickers-moved-to-x diff --git a/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md b/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md index 7e792ec088924..50a33d4af7b9b 100644 --- a/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md +++ b/docs/data/migration/migration-pickers-v5/migration-pickers-v5.md @@ -24,7 +24,8 @@ Described below are the steps needed to migrate from v5 to v6. ## Run codemods The `preset-safe` codemod will automatically adjust the bulk of your code to account for breaking changes in v6. You can run `v6.0.0/pickers/preset-safe` targeting only Date and Time Pickers or `v6.0.0/preset-safe` to target Data Grid as well. -It should be only applied **once per folder.** + +You can either run it on a specific file, folder, or your entire codebase when choosing the `` argument. ```sh // Date and Time Pickers specific From cf56ebd262b0b2a0bc607cf76aec89b171d5af9a Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Fri, 28 Apr 2023 13:13:39 +0200 Subject: [PATCH 32/80] [chart] Clean some styling (#8778) --- docs/data/charts/styling/ColorTemplate.js | 180 ++++++++++++++++++ docs/data/charts/styling/ColorTemplate.tsx | 180 ++++++++++++++++++ docs/data/charts/styling/styling.md | 34 ++++ docs/data/pages.ts | 1 + docs/pages/x/react-charts/styling.js | 7 + packages/x-charts/src/BarChart/BarPlot.tsx | 9 +- packages/x-charts/src/BarChart/extremums.ts | 14 +- .../x-charts/src/ChartContainer/index.tsx | 17 +- .../x-charts/src/LineChart/LineElement.tsx | 2 +- .../x-charts/src/LineChart/MarkElement.tsx | 4 +- .../src/ResponsiveChartContainer/index.tsx | 4 +- 11 files changed, 436 insertions(+), 16 deletions(-) create mode 100644 docs/data/charts/styling/ColorTemplate.js create mode 100644 docs/data/charts/styling/ColorTemplate.tsx create mode 100644 docs/data/charts/styling/styling.md create mode 100644 docs/pages/x/react-charts/styling.js diff --git a/docs/data/charts/styling/ColorTemplate.js b/docs/data/charts/styling/ColorTemplate.js new file mode 100644 index 0000000000000..7334b60b6a7d3 --- /dev/null +++ b/docs/data/charts/styling/ColorTemplate.js @@ -0,0 +1,180 @@ +import * as React from 'react'; +import { ScatterChart } from '@mui/x-charts/ScatterChart'; + +import Stack from '@mui/material/Stack'; +import TextField from '@mui/material/TextField'; +import MenuItem from '@mui/material/MenuItem'; +import Typography from '@mui/material/Typography'; + +const Dt = 100; +const series = [...Array(20)].map((_, seriesIndex) => { + return { + id: `series_${seriesIndex}`, + type: 'scatter', + data: [...Array(Dt)].map((x, i) => { + const t = seriesIndex * Dt + i; + return { + x: t / Dt, + y: Math.cos(t / 30) / Dt, + id: i, + }; + }), + }; +}); +const categories = { + Category10: [ + '#1f77b4', + '#ff7f0e', + '#2ca02c', + '#d62728', + '#9467bd', + '#8c564b', + '#e377c2', + '#7f7f7f', + '#bcbd22', + '#17becf', + ], + Accent: [ + '#7fc97f', + '#beaed4', + '#fdc086', + '#ffff99', + '#386cb0', + '#f0027f', + '#bf5b17', + '#666666', + ], + Dark2: [ + '#1b9e77', + '#d95f02', + '#7570b3', + '#e7298a', + '#66a61e', + '#e6ab02', + '#a6761d', + '#666666', + ], + Paired: [ + '#a6cee3', + '#1f78b4', + '#b2df8a', + '#33a02c', + '#fb9a99', + '#e31a1c', + '#fdbf6f', + '#ff7f00', + '#cab2d6', + '#6a3d9a', + '#ffff99', + '#b15928', + ], + Pastel1: [ + '#fbb4ae', + '#b3cde3', + '#ccebc5', + '#decbe4', + '#fed9a6', + '#ffffcc', + '#e5d8bd', + '#fddaec', + '#f2f2f2', + ], + Pastel2: [ + '#b3e2cd', + '#fdcdac', + '#cbd5e8', + '#f4cae4', + '#e6f5c9', + '#fff2ae', + '#f1e2cc', + '#cccccc', + ], + Set1: [ + '#e41a1c', + '#377eb8', + '#4daf4a', + '#984ea3', + '#ff7f00', + '#ffff33', + '#a65628', + '#f781bf', + '#999999', + ], + Set2: [ + '#66c2a5', + '#fc8d62', + '#8da0cb', + '#e78ac3', + '#a6d854', + '#ffd92f', + '#e5c494', + '#b3b3b3', + ], + Set3: [ + '#8dd3c7', + '#ffffb3', + '#bebada', + '#fb8072', + '#80b1d3', + '#fdb462', + '#b3de69', + '#fccde5', + '#d9d9d9', + '#bc80bd', + '#ccebc5', + '#ffed6f', + ], + Tableau10: [ + '#4e79a7', + '#f28e2c', + '#e15759', + '#76b7b2', + '#59a14f', + '#edc949', + '#af7aa1', + '#ff9da7', + '#9c755f', + '#bab0ab', + ], +}; + +export default function ColorTemplate() { + const [colorScheme, setColorScheme] = React.useState('Category10'); + + return ( + + + setColorScheme(event.target.value)} + > + {Object.entries(categories).map(([name, colors]) => ( + + + {name} +
+ {colors.map((c) => ( +
+ ))} +
+ + + ))} + + + ); +} diff --git a/docs/data/charts/styling/ColorTemplate.tsx b/docs/data/charts/styling/ColorTemplate.tsx new file mode 100644 index 0000000000000..6dfbb0da76216 --- /dev/null +++ b/docs/data/charts/styling/ColorTemplate.tsx @@ -0,0 +1,180 @@ +import * as React from 'react'; +import { ScatterChart } from '@mui/x-charts/ScatterChart'; +import { ScatterSeriesType } from '@mui/x-charts/models'; +import Stack from '@mui/material/Stack'; +import TextField from '@mui/material/TextField'; +import MenuItem from '@mui/material/MenuItem'; +import Typography from '@mui/material/Typography'; + +const Dt = 100; +const series = [...Array(20)].map((_, seriesIndex) => { + return { + id: `series_${seriesIndex}`, + type: 'scatter', + data: [...Array(Dt)].map((x, i) => { + const t = seriesIndex * Dt + i; + return { + x: t / Dt, + y: Math.cos(t / 30) / Dt, + id: i, + }; + }), + } as ScatterSeriesType; +}); +const categories: { [key: string]: string[] } = { + Category10: [ + '#1f77b4', + '#ff7f0e', + '#2ca02c', + '#d62728', + '#9467bd', + '#8c564b', + '#e377c2', + '#7f7f7f', + '#bcbd22', + '#17becf', + ], + Accent: [ + '#7fc97f', + '#beaed4', + '#fdc086', + '#ffff99', + '#386cb0', + '#f0027f', + '#bf5b17', + '#666666', + ], + Dark2: [ + '#1b9e77', + '#d95f02', + '#7570b3', + '#e7298a', + '#66a61e', + '#e6ab02', + '#a6761d', + '#666666', + ], + Paired: [ + '#a6cee3', + '#1f78b4', + '#b2df8a', + '#33a02c', + '#fb9a99', + '#e31a1c', + '#fdbf6f', + '#ff7f00', + '#cab2d6', + '#6a3d9a', + '#ffff99', + '#b15928', + ], + Pastel1: [ + '#fbb4ae', + '#b3cde3', + '#ccebc5', + '#decbe4', + '#fed9a6', + '#ffffcc', + '#e5d8bd', + '#fddaec', + '#f2f2f2', + ], + Pastel2: [ + '#b3e2cd', + '#fdcdac', + '#cbd5e8', + '#f4cae4', + '#e6f5c9', + '#fff2ae', + '#f1e2cc', + '#cccccc', + ], + Set1: [ + '#e41a1c', + '#377eb8', + '#4daf4a', + '#984ea3', + '#ff7f00', + '#ffff33', + '#a65628', + '#f781bf', + '#999999', + ], + Set2: [ + '#66c2a5', + '#fc8d62', + '#8da0cb', + '#e78ac3', + '#a6d854', + '#ffd92f', + '#e5c494', + '#b3b3b3', + ], + Set3: [ + '#8dd3c7', + '#ffffb3', + '#bebada', + '#fb8072', + '#80b1d3', + '#fdb462', + '#b3de69', + '#fccde5', + '#d9d9d9', + '#bc80bd', + '#ccebc5', + '#ffed6f', + ], + Tableau10: [ + '#4e79a7', + '#f28e2c', + '#e15759', + '#76b7b2', + '#59a14f', + '#edc949', + '#af7aa1', + '#ff9da7', + '#9c755f', + '#bab0ab', + ], +}; + +export default function ColorTemplate() { + const [colorScheme, setColorScheme] = React.useState('Category10'); + + return ( + + + setColorScheme(event.target.value)} + > + {Object.entries(categories).map(([name, colors]) => ( + + + {name} +
+ {colors.map((c) => ( +
+ ))} +
+ + + ))} + + + ); +} diff --git a/docs/data/charts/styling/styling.md b/docs/data/charts/styling/styling.md new file mode 100644 index 0000000000000..475b321c89086 --- /dev/null +++ b/docs/data/charts/styling/styling.md @@ -0,0 +1,34 @@ +--- +product: charts +title: Charts - Styling +--- + +# Charts - Styling + +

This page groups topics about charts customistion.

+ +## Colors + +Series accept a property `color` which is the based color used to render its components. + +```jsx + +``` + +To avoid having to color series one by one, charts have a default color cycle. +If a series does not have a color, a default color will be applied according to the index of the series. + +You can override this color cycle by using prop `colors` to charts components (or `` if you are using composition). +This prop takes an array of colors. +Such array of colors can be generated by using [d3-scale-chromatic](https://observablehq.com/@d3/color-schemes). + +Here is the example of the d3 Categorical color palette. + +{{"demo": "ColorTemplate.js", "bg": "inline"}} diff --git a/docs/data/pages.ts b/docs/data/pages.ts index 1ee6a4fe0d335..7d33f83ebb461 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -455,6 +455,7 @@ const pages: MuiPage[] = [ icon: ChartIcon, children: [ { pathname: '/x/react-charts', title: '🚧 Overview' }, + { pathname: '/x/react-charts/styling', title: 'Styling' }, { pathname: '/x/react-charts/bars', title: '🚧 Bars' }, { pathname: '/x/react-charts/lines', title: '🚧 Lines' }, { pathname: '/x/react-charts/areas', title: '🚧 Areas' }, diff --git a/docs/pages/x/react-charts/styling.js b/docs/pages/x/react-charts/styling.js new file mode 100644 index 0000000000000..2f47cd803d90d --- /dev/null +++ b/docs/pages/x/react-charts/styling.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/charts/styling/styling.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/packages/x-charts/src/BarChart/BarPlot.tsx b/packages/x-charts/src/BarChart/BarPlot.tsx index d2a6adc42b687..a9adb40baafb0 100644 --- a/packages/x-charts/src/BarChart/BarPlot.tsx +++ b/packages/x-charts/src/BarChart/BarPlot.tsx @@ -59,17 +59,18 @@ export function BarPlot() { // @ts-ignore TODO: fix when adding a correct API for customisation const { stackedData, color } = series[seriesId]; - return stackedData.map(([baseline, value], dataIndex: number) => { + return stackedData.map((values, dataIndex: number) => { + const baseline = Math.min(...values); + const value = Math.max(...values); return ( ); diff --git a/packages/x-charts/src/BarChart/extremums.ts b/packages/x-charts/src/BarChart/extremums.ts index a4b8260c02267..0091f7fe67847 100644 --- a/packages/x-charts/src/BarChart/extremums.ts +++ b/packages/x-charts/src/BarChart/extremums.ts @@ -16,13 +16,17 @@ export const getExtremumY: ExtremumGetter<'bar'> = (params) => { .reduce( (acc: ExtremumGetterResult, seriesId) => { const [seriesMin, seriesMax] = series[seriesId].stackedData.reduce( - (seriesAcc, [min, max]) => [Math.min(min, seriesAcc[0]), Math.max(max, seriesAcc[1])], + (seriesAcc, values) => [ + Math.min(...values, ...(seriesAcc[0] === null ? [] : [seriesAcc[0]])), + Math.max(...values, ...(seriesAcc[1] === null ? [] : [seriesAcc[1]])), + ], series[seriesId].stackedData[0], ); - if (acc[0] === null || acc[1] === null) { - return [seriesMin, seriesMax]; - } - return [Math.min(seriesMin, acc[0]), Math.max(seriesMax, acc[1])]; + + return [ + acc[0] === null ? seriesMin : Math.min(seriesMin, acc[0]), + acc[1] === null ? seriesMax : Math.max(seriesMax, acc[1]), + ]; }, [null, null], ); diff --git a/packages/x-charts/src/ChartContainer/index.tsx b/packages/x-charts/src/ChartContainer/index.tsx index b902d96960440..d0cf8c9d29e3b 100644 --- a/packages/x-charts/src/ChartContainer/index.tsx +++ b/packages/x-charts/src/ChartContainer/index.tsx @@ -21,12 +21,25 @@ export type ChartContainerProps = Omit< > & { children?: React.ReactNode; tooltip?: TooltipProps }; export function ChartContainer(props: ChartContainerProps) { - const { width, height, series, margin, xAxis, yAxis, sx, title, desc, tooltip, children } = props; + const { + width, + height, + series, + margin, + xAxis, + yAxis, + colors, + sx, + title, + desc, + tooltip, + children, + } = props; const ref = React.useRef(null); return ( - + diff --git a/packages/x-charts/src/LineChart/LineElement.tsx b/packages/x-charts/src/LineChart/LineElement.tsx index b595ac05bbaf7..793087b23c4ab 100644 --- a/packages/x-charts/src/LineChart/LineElement.tsx +++ b/packages/x-charts/src/LineChart/LineElement.tsx @@ -41,7 +41,7 @@ const LineElementPath = styled('path', { overridesResolver: (_, styles) => styles.root, })<{ ownerState: LineElementOwnerState }>(({ ownerState }) => ({ stroke: ownerState.color, - strokeWidth: 5, + strokeWidth: 2, strokeLinejoin: 'round', fill: 'none', opacity: ownerState.isNotHighlighted ? 0.3 : 1, diff --git a/packages/x-charts/src/LineChart/MarkElement.tsx b/packages/x-charts/src/LineChart/MarkElement.tsx index 2ea7b5550f015..87f3c8a97c81a 100644 --- a/packages/x-charts/src/LineChart/MarkElement.tsx +++ b/packages/x-charts/src/LineChart/MarkElement.tsx @@ -45,11 +45,11 @@ const MarkElementPath = styled('path', { overridesResolver: (_, styles) => styles.root, })<{ ownerState: MarkElementOwnerState }>(({ ownerState }) => ({ transform: `translate(${ownerState.x}px, ${ownerState.y}px) ${ - ownerState.isHighlighted ? 'scale(2)' : '' + ownerState.isHighlighted ? 'scale(1.5)' : '' }`, fill: d3Color(ownerState.color)!.brighter(1).formatHex(), stroke: ownerState.color, - strokeWidth: ownerState.isHighlighted ? 2 : 1, + strokeWidth: 2, pointerEvents: 'none', })); diff --git a/packages/x-charts/src/ResponsiveChartContainer/index.tsx b/packages/x-charts/src/ResponsiveChartContainer/index.tsx index 00c5ecb2d91b2..117b94fe8eb6f 100644 --- a/packages/x-charts/src/ResponsiveChartContainer/index.tsx +++ b/packages/x-charts/src/ResponsiveChartContainer/index.tsx @@ -43,7 +43,7 @@ type ChartContainerProps = LayoutConfig & CartesianContextProviderProps; export function ResponsiveChartContainer(props: ChartContainerProps) { - const { series, margin, xAxis, yAxis, children } = props; + const { series, margin, xAxis, colors, yAxis, children } = props; const ref = React.useRef(null); const [containerRef, width, height] = useChartDimensions(); @@ -51,7 +51,7 @@ export function ResponsiveChartContainer(props: ChartContainerProps) { return (
- + From caf3268acef6a22b5ef28e4993c5d9d1709ccf7c Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Fri, 28 Apr 2023 13:52:16 +0200 Subject: [PATCH 33/80] [charts] Improve tooltip (#8792) --- docs/data/charts/lines/CSSCustomization.js | 9 +- docs/data/charts/lines/CSSCustomization.tsx | 9 +- docs/data/charts/lines/StackedAreas.js | 12 +- docs/data/charts/lines/StackedAreas.tsx | 12 +- docs/tsconfig.json | 2 +- packages/x-charts/src/BarChart/formatter.ts | 9 +- packages/x-charts/src/LineChart/LinePlot.tsx | 2 - .../x-charts/src/LineChart/MarkElement.tsx | 1 - packages/x-charts/src/LineChart/formatter.ts | 6 +- .../x-charts/src/ScatterChart/formatter.ts | 6 +- .../src/Tooltip/AxisTooltipContent.tsx | 111 ++++++ .../src/Tooltip/ItemTooltipContent.tsx | 60 ++++ packages/x-charts/src/Tooltip/Tooltip.tsx | 320 ++---------------- .../x-charts/src/Tooltip/TooltipTable.tsx | 31 ++ packages/x-charts/src/Tooltip/utils.tsx | 207 +++++++++++ .../src/context/InteractionProvider.tsx | 18 +- .../src/hooks/useInteractionItemProps.ts | 4 +- .../src/internals/defaultizeValueFormatter.ts | 25 ++ packages/x-charts/src/models/axis.ts | 10 +- .../x-charts/src/models/seriesType/bar.ts | 3 +- .../x-charts/src/models/seriesType/common.ts | 8 +- .../x-charts/src/models/seriesType/config.ts | 17 +- .../x-charts/src/models/seriesType/line.ts | 3 +- .../x-charts/src/models/seriesType/scatter.ts | 7 +- 24 files changed, 560 insertions(+), 332 deletions(-) create mode 100644 packages/x-charts/src/Tooltip/AxisTooltipContent.tsx create mode 100644 packages/x-charts/src/Tooltip/ItemTooltipContent.tsx create mode 100644 packages/x-charts/src/Tooltip/TooltipTable.tsx create mode 100644 packages/x-charts/src/Tooltip/utils.tsx create mode 100644 packages/x-charts/src/internals/defaultizeValueFormatter.ts diff --git a/docs/data/charts/lines/CSSCustomization.js b/docs/data/charts/lines/CSSCustomization.js index 65cd361db367f..8454cb2adad67 100644 --- a/docs/data/charts/lines/CSSCustomization.js +++ b/docs/data/charts/lines/CSSCustomization.js @@ -65,7 +65,14 @@ export default function CSSCustomization() { fill: "url('#myGradient')", }, }} - xAxis={[{ id: 'Years', data: years, scaleName: 'time' }]} + xAxis={[ + { + id: 'Years', + data: years, + scaleName: 'time', + valueFormatter: (date) => date.getFullYear(), + }, + ]} series={[ { type: 'line', diff --git a/docs/data/charts/lines/CSSCustomization.tsx b/docs/data/charts/lines/CSSCustomization.tsx index 65cd361db367f..8454cb2adad67 100644 --- a/docs/data/charts/lines/CSSCustomization.tsx +++ b/docs/data/charts/lines/CSSCustomization.tsx @@ -65,7 +65,14 @@ export default function CSSCustomization() { fill: "url('#myGradient')", }, }} - xAxis={[{ id: 'Years', data: years, scaleName: 'time' }]} + xAxis={[ + { + id: 'Years', + data: years, + scaleName: 'time', + valueFormatter: (date) => date.getFullYear(), + }, + ]} series={[ { type: 'line', diff --git a/docs/data/charts/lines/StackedAreas.js b/docs/data/charts/lines/StackedAreas.js index 9c4353f95eca6..cef4fd3c8daf0 100644 --- a/docs/data/charts/lines/StackedAreas.js +++ b/docs/data/charts/lines/StackedAreas.js @@ -57,11 +57,19 @@ const GermanyGDPperCapita = [ export default function StackedAreas() { return ( date.getFullYear(), + }, + ]} series={[ { type: 'line', id: 'France', + label: 'French GDP per capite', xAxisKey: 'Years', data: FranceGDPperCapita, stack: 'total', @@ -70,6 +78,7 @@ export default function StackedAreas() { { type: 'line', id: 'Germany', + label: 'German GDP per capite', xAxisKey: 'Years', data: GermanyGDPperCapita, stack: 'total', @@ -78,6 +87,7 @@ export default function StackedAreas() { { type: 'line', id: 'United Kingdom', + label: 'UK GDP per capite', xAxisKey: 'Years', data: UKGDPperCapita, stack: 'total', diff --git a/docs/data/charts/lines/StackedAreas.tsx b/docs/data/charts/lines/StackedAreas.tsx index 9c4353f95eca6..cef4fd3c8daf0 100644 --- a/docs/data/charts/lines/StackedAreas.tsx +++ b/docs/data/charts/lines/StackedAreas.tsx @@ -57,11 +57,19 @@ const GermanyGDPperCapita = [ export default function StackedAreas() { return ( date.getFullYear(), + }, + ]} series={[ { type: 'line', id: 'France', + label: 'French GDP per capite', xAxisKey: 'Years', data: FranceGDPperCapita, stack: 'total', @@ -70,6 +78,7 @@ export default function StackedAreas() { { type: 'line', id: 'Germany', + label: 'German GDP per capite', xAxisKey: 'Years', data: GermanyGDPperCapita, stack: 'total', @@ -78,6 +87,7 @@ export default function StackedAreas() { { type: 'line', id: 'United Kingdom', + label: 'UK GDP per capite', xAxisKey: 'Years', data: UKGDPperCapita, stack: 'total', diff --git a/docs/tsconfig.json b/docs/tsconfig.json index 2f1b353aad963..4969813d44a68 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -17,5 +17,5 @@ "src/modules/components/**/*", "../node_modules/@mui/material/themeCssVarsAugmentation" ], - "exclude": ["docs/.next", "docs/export"] + "exclude": ["docs/.next", "docs/export", "pages/playground"] } diff --git a/packages/x-charts/src/BarChart/formatter.ts b/packages/x-charts/src/BarChart/formatter.ts index 5d30a9d09f565..a6d633e9faf51 100644 --- a/packages/x-charts/src/BarChart/formatter.ts +++ b/packages/x-charts/src/BarChart/formatter.ts @@ -6,6 +6,7 @@ import { import defaultizeCartesianSeries from '../internals/defaultizeCartesianSeries'; import { getStackingGroups } from '../internals/stackSeries'; import { ChartSeries, Formatter } from '../models/seriesType/config'; +import defaultizeValueFormatter from '../internals/defaultizeValueFormatter'; const formatter: Formatter<'bar'> = (params) => { const { seriesOrder, series } = params; @@ -40,7 +41,13 @@ const formatter: Formatter<'bar'> = (params) => { }); }); - return { seriesOrder, stackingGroups, series: defaultizeCartesianSeries(completedSeries) }; + return { + seriesOrder, + stackingGroups, + series: defaultizeValueFormatter(defaultizeCartesianSeries(completedSeries), (v) => + v.toLocaleString(), + ), + }; }; export default formatter; diff --git a/packages/x-charts/src/LineChart/LinePlot.tsx b/packages/x-charts/src/LineChart/LinePlot.tsx index f90ad7208c12b..2a97bc9cc84ce 100644 --- a/packages/x-charts/src/LineChart/LinePlot.tsx +++ b/packages/x-charts/src/LineChart/LinePlot.tsx @@ -73,7 +73,6 @@ export function LinePlot() { id={seriesId} d={areaPath.curve(curve)(d3Data) || undefined} color={series[seriesId].area.color ?? series[seriesId].color} - {...getInteractionItemProps({ type: 'line', seriesId })} /> ) ); @@ -114,7 +113,6 @@ export function LinePlot() { id={seriesId} d={linePath.curve(curve)(d3Data) || undefined} color={series[seriesId].color} - {...getInteractionItemProps({ type: 'line', seriesId })} /> ); }); diff --git a/packages/x-charts/src/LineChart/MarkElement.tsx b/packages/x-charts/src/LineChart/MarkElement.tsx index 87f3c8a97c81a..afd729aa07844 100644 --- a/packages/x-charts/src/LineChart/MarkElement.tsx +++ b/packages/x-charts/src/LineChart/MarkElement.tsx @@ -50,7 +50,6 @@ const MarkElementPath = styled('path', { fill: d3Color(ownerState.color)!.brighter(1).formatHex(), stroke: ownerState.color, strokeWidth: 2, - pointerEvents: 'none', })); export type MarkElementProps = Omit & diff --git a/packages/x-charts/src/LineChart/formatter.ts b/packages/x-charts/src/LineChart/formatter.ts index 97b100e68cab0..79a871600e112 100644 --- a/packages/x-charts/src/LineChart/formatter.ts +++ b/packages/x-charts/src/LineChart/formatter.ts @@ -6,6 +6,7 @@ import { import defaultizeCartesianSeries from '../internals/defaultizeCartesianSeries'; import { getStackingGroups } from '../internals/stackSeries'; import { ChartSeries, Formatter } from '../models/seriesType/config'; +import defaultizeValueFormatter from '../internals/defaultizeValueFormatter'; // For now it's a copy past of bar charts formatter, but maybe will diverge later const formatter: Formatter<'line'> = (params) => { @@ -44,7 +45,10 @@ const formatter: Formatter<'line'> = (params) => { return { seriesOrder, stackingGroups, - series: defaultizeCartesianSeries>(completedSeries), + series: defaultizeValueFormatter( + defaultizeCartesianSeries>(completedSeries), + (v) => v.toLocaleString(), + ), }; }; diff --git a/packages/x-charts/src/ScatterChart/formatter.ts b/packages/x-charts/src/ScatterChart/formatter.ts index 444e82ff7790a..7aad4fa359c52 100644 --- a/packages/x-charts/src/ScatterChart/formatter.ts +++ b/packages/x-charts/src/ScatterChart/formatter.ts @@ -1,8 +1,12 @@ import defaultizeCartesianSeries from '../internals/defaultizeCartesianSeries'; +import defaultizeValueFormatter from '../internals/defaultizeValueFormatter'; import { Formatter } from '../models/seriesType/config'; const formatter: Formatter<'scatter'> = ({ series, seriesOrder }) => { - return { series: defaultizeCartesianSeries(series), seriesOrder }; + return { + series: defaultizeValueFormatter(defaultizeCartesianSeries(series), (v) => `(${v.x}, ${v.y})`), + seriesOrder, + }; }; export default formatter; diff --git a/packages/x-charts/src/Tooltip/AxisTooltipContent.tsx b/packages/x-charts/src/Tooltip/AxisTooltipContent.tsx new file mode 100644 index 0000000000000..82a3028ddcae2 --- /dev/null +++ b/packages/x-charts/src/Tooltip/AxisTooltipContent.tsx @@ -0,0 +1,111 @@ +import * as React from 'react'; +import Typography from '@mui/material/Typography'; +import { AxisInteractionData } from '../context/InteractionProvider'; +import { FormattedSeries, SeriesContext } from '../context/SeriesContextProvider'; +import { CartesianContext } from '../context/CartesianContextProvider'; +import { ChartSeriesDefaultized, ChartSeriesType } from '../models/seriesType/config'; +import { AxisDefaultized } from '../models/axis'; +import { TooltipCell, TooltipPaper, TooltipTable, TooltipMark } from './TooltipTable'; + +export type AxisContentProps = { + /** + * Data identifying the triggered axis. + */ + // eslint-disable-next-line react/no-unused-prop-types + axisData: AxisInteractionData; + /** + * The series linked to the triggered axis. + */ + series: ChartSeriesDefaultized[]; + /** + * The properties of the triggered axis. + */ + axis: AxisDefaultized; + /** + * The index of the data item triggered. + */ + dataIndex?: null | number; + /** + * The value associated to the current mouse position. + */ + axisValue: any; +}; +export function DefaultAxisContent(props: AxisContentProps) { + const { series, axis, dataIndex, axisValue } = props; + + if (dataIndex == null) { + return null; + } + const axisFormatter = axis.valueFormatter ?? ((v) => v.toLocaleString()); + return ( + + + {axisValue != null && ( + + + {axisFormatter(axisValue)} + + + )} + + {series.map(({ color, id, label, valueFormatter, data }: ChartSeriesDefaultized) => ( + + + + + + + {label ?? id} + + + + {valueFormatter(data[dataIndex])} + + + ))} + + + + ); +} + +export function AxisTooltipContent(props: { + axisData: AxisInteractionData; + content?: React.ElementType; +}) { + const { content, axisData } = props; + const dataIndex = axisData.x && axisData.x.index; + const axisValue = axisData.x && axisData.x.value; + + const { xAxisIds, xAxis } = React.useContext(CartesianContext); + const series = React.useContext(SeriesContext); + + const USED_X_AXIS_ID = xAxisIds[0]; + + const relevantSeries = React.useMemo(() => { + const rep: any[] = []; + (Object.keys(series) as (keyof FormattedSeries)[]).forEach((seriesType) => { + series[seriesType]!.seriesOrder.forEach((seriesId) => { + if (series[seriesType]!.series[seriesId].xAxisKey === USED_X_AXIS_ID) { + rep.push(series[seriesType]!.series[seriesId]); + } + }); + }); + return rep; + }, [USED_X_AXIS_ID, series]); + + const relevantAxis = React.useMemo(() => { + return xAxis[USED_X_AXIS_ID]; + }, [USED_X_AXIS_ID, xAxis]); + + const Content = content ?? DefaultAxisContent; + return ( + + ); +} diff --git a/packages/x-charts/src/Tooltip/ItemTooltipContent.tsx b/packages/x-charts/src/Tooltip/ItemTooltipContent.tsx new file mode 100644 index 0000000000000..d8303cc2c2f6f --- /dev/null +++ b/packages/x-charts/src/Tooltip/ItemTooltipContent.tsx @@ -0,0 +1,60 @@ +import * as React from 'react'; +import { ItemInteractionData } from '../context/InteractionProvider'; +import { SeriesContext } from '../context/SeriesContextProvider'; +import { ChartSeriesDefaultized, ChartSeriesType } from '../models/seriesType/config'; +import { TooltipTable, TooltipCell, TooltipMark, TooltipPaper } from './TooltipTable'; + +export type ItemContentProps = { + /** + * The data used to identify the triggered item. + */ + itemData: ItemInteractionData; + /** + * The series linked to the triggered axis. + */ + series: ChartSeriesDefaultized; +}; + +export function DefaultItemContent(props: ItemContentProps) { + const { series, itemData } = props; + + if (itemData.dataIndex === undefined) { + return null; + } + + const displayedLabel = series.label ?? series.id; + const color = series.color; + // TODO: Manage to let TS understand series.data and series.valueFormatter are coherent + // @ts-ignore + const formattedValue = series.valueFormatter(series.data[itemData.dataIndex]); + return ( + + + + + + + + {displayedLabel} + + {formattedValue} + + + + ); +} + +export function ItemTooltipContent(props: { + itemData: ItemInteractionData; + content?: React.ElementType>; +}) { + const { content, itemData } = props; + + const series = React.useContext(SeriesContext)[itemData.type]!.series[ + itemData.seriesId + ] as ChartSeriesDefaultized; + + const Content = content ?? DefaultItemContent; + + return ; +} diff --git a/packages/x-charts/src/Tooltip/Tooltip.tsx b/packages/x-charts/src/Tooltip/Tooltip.tsx index 225b82ceda6dd..3aa7908423947 100644 --- a/packages/x-charts/src/Tooltip/Tooltip.tsx +++ b/packages/x-charts/src/Tooltip/Tooltip.tsx @@ -1,284 +1,16 @@ import * as React from 'react'; import Popper from '@mui/material/Popper'; -import Paper from '@mui/material/Paper'; -import Typography from '@mui/material/Typography'; -import Divider from '@mui/material/Divider'; import NoSsr from '@mui/material/NoSsr'; -import { symbol as d3Symbol, symbolsFill as d3SymbolsFill } from 'd3-shape'; import { AxisInteractionData, InteractionContext, ItemInteractionData, } from '../context/InteractionProvider'; -import { FormattedSeries, SeriesContext } from '../context/SeriesContextProvider'; -import { CartesianContext } from '../context/CartesianContextProvider'; import { Highlight, HighlightProps } from '../Highlight'; -import { isBandScale } from '../hooks/useScale'; -import { SVGContext, DrawingContext } from '../context/DrawingProvider'; -import { getSymbol } from '../internals/utils'; - -function generateVirtualElement(mousePosition: { x: number; y: number } | null) { - if (mousePosition === null) { - return { - getBoundingClientRect: () => ({ - width: 0, - height: 0, - x: 0, - y: 0, - top: 0, - right: 0, - bottom: 0, - left: 0, - toJSON: () => '', - }), - }; - } - const { x, y } = mousePosition; - return { - getBoundingClientRect: () => ({ - width: 0, - height: 0, - x, - y, - top: y, - right: x, - bottom: y, - left: x, - toJSON: () => - JSON.stringify({ width: 0, height: 0, x, y, top: y, right: x, bottom: y, left: x }), - }), - }; -} - -const useAxisEvents = (trigger: TooltipProps['trigger']) => { - const svgRef = React.useContext(SVGContext); - const { width, height, top, left } = React.useContext(DrawingContext); - const { xAxis, yAxis, xAxisIds, yAxisIds } = React.useContext(CartesianContext); - const { dispatch } = React.useContext(InteractionContext); - - const usedXAxis = xAxisIds[0]; - const usedYAxis = yAxisIds[0]; - - // Use a ref to avoid rerendering on every mousemove event. - const mousePosition = React.useRef({ - x: -1, - y: -1, - }); - - React.useEffect(() => { - const element = svgRef.current; - if (element === null || trigger !== 'axis') { - return () => {}; - } - - const getUpdateY = (y: number) => { - if (usedYAxis === null) { - return null; - } - const { scale: yScale, data: yAxisData } = yAxis[usedYAxis]; - if (!isBandScale(yScale)) { - return { value: yScale.invert(y) }; - } - const dataIndex = Math.floor((y - yScale.range()[0]) / yScale.step()); - if (dataIndex < 0 || dataIndex >= yAxisData!.length) { - return null; - } - return { - index: dataIndex, - value: yAxisData![dataIndex], - }; - }; - - const getUpdateX = (x: number) => { - if (usedXAxis === null) { - return null; - } - const { scale: xScale, data: xAxisData } = xAxis[usedXAxis]; - if (!isBandScale(xScale)) { - const value = xScale.invert(x); - - const closestIndex = xAxisData?.findIndex((v: typeof value, index) => { - if (v > value) { - // @ts-ignore - if (index === 0 || Math.abs(value - v) < Math.abs(value - xAxisData[index - 1])) { - return true; - } - } - if (v <= value) { - if ( - index === xAxisData.length - 1 || - // @ts-ignore - Math.abs(value - v) < Math.abs(value - xAxisData[index + 1]) - ) { - return true; - } - } - return false; - }); - - return { - value: closestIndex !== undefined && closestIndex >= 0 ? xAxisData![closestIndex] : value, - index: closestIndex, - }; - } - const dataIndex = Math.floor((x - xScale.range()[0]) / xScale.step()); - if (dataIndex < 0 || dataIndex >= xAxisData!.length) { - return null; - } - return { - index: dataIndex, - value: xAxisData![dataIndex], - }; - }; - - const handleMouseOut = () => { - mousePosition.current = { - x: -1, - y: -1, - }; - dispatch({ type: 'updateAxis', data: { x: null, y: null } }); - }; - - const handleMouseMove = (event: MouseEvent) => { - mousePosition.current = { - x: event.offsetX, - y: event.offsetY, - }; - const outsideX = event.offsetX < left || event.offsetX > left + width; - const outsideY = event.offsetY < top || event.offsetY > top + height; - if (outsideX || outsideY) { - dispatch({ type: 'updateAxis', data: { x: null, y: null } }); - return; - } - const newStateX = getUpdateX(event.offsetX); - const newStateY = getUpdateY(event.offsetY); - - dispatch({ type: 'updateAxis', data: { x: newStateX, y: newStateY } }); - }; - - element.addEventListener('mouseout', handleMouseOut); - element.addEventListener('mousemove', handleMouseMove); - return () => { - element.removeEventListener('mouseout', handleMouseOut); - element.removeEventListener('mousemove', handleMouseMove); - }; - }, [svgRef, trigger, dispatch, left, width, top, height, usedYAxis, yAxis, usedXAxis, xAxis]); -}; - -const format = (data: any) => (typeof data === 'object' ? `(${data.x}, ${data.y})` : data); - -function ItemTooltipContent(props: ItemInteractionData) { - const { seriesId, type, dataIndex } = props; - - const series = React.useContext(SeriesContext)[type]!.series[seriesId]; - - if (dataIndex === undefined) { - return null; - } - - const data = series.data[dataIndex]; - return ( -

- {seriesId}: {format(data)} -

- ); -} -function AxisTooltipContent(props: AxisInteractionData) { - const dataIndex = props.x && props.x.index; - const axisValue = props.x && props.x.value; - - const { xAxisIds, xAxis } = React.useContext(CartesianContext); - const series = React.useContext(SeriesContext); - - const USED_X_AXIS_ID = xAxisIds[0]; - const xAxisName = xAxis[USED_X_AXIS_ID].id; - - const relevantSeries = React.useMemo(() => { - const rep: { type: string; id: string; color: string }[] = []; - (Object.keys(series) as (keyof FormattedSeries)[]).forEach((seriesType) => { - series[seriesType]!.seriesOrder.forEach((seriesId) => { - if (series[seriesType]!.series[seriesId].xAxisKey === USED_X_AXIS_ID) { - rep.push({ - type: seriesType, - id: seriesId, - color: series[seriesType]!.series[seriesId].color, - }); - } - }); - }); - return rep; - }, [USED_X_AXIS_ID, series]); - - if (dataIndex == null) { - return null; - } - - const markerSize = 30; // TODO: allows customization - const shape = 'square'; - return ( - - {axisValue != null && ( - - - {xAxisName}: {axisValue.toLocaleString()} - - - - )} - {relevantSeries.map(({ type, color, id }) => ( - - - - - {/* @ts-ignore */} - {id}: {format(series[type].series[id].data[dataIndex])} - - ))} - - ); -} - -const useMouseTracker = () => { - const svgRef = React.useContext(SVGContext); - - // Use a ref to avoid rerendering on every mousemove event. - const [mousePosition, setMousePosition] = React.useState(null); - - React.useEffect(() => { - const element = svgRef.current; - if (element === null) { - return () => {}; - } - - const handleMouseOut = () => { - setMousePosition(null); - }; - - const handleMouseMove = (event: MouseEvent) => { - setMousePosition({ - x: event.clientX, - y: event.clientY, - }); - }; - - element.addEventListener('mouseout', handleMouseOut); - element.addEventListener('mousemove', handleMouseMove); - return () => { - element.removeEventListener('mouseout', handleMouseOut); - element.removeEventListener('mousemove', handleMouseMove); - }; - }, [svgRef]); - - return mousePosition; -}; +import { generateVirtualElement, useAxisEvents, useMouseTracker, getTootipHasData } from './utils'; +import { ChartSeriesType } from '../models/seriesType/config'; +import { ItemContentProps, ItemTooltipContent } from './ItemTooltipContent'; +import { AxisContentProps, AxisTooltipContent } from './AxisTooltipContent'; export type TooltipProps = { /** @@ -293,24 +25,18 @@ export type TooltipProps = { * Props propagate to the highlight */ highlightProps?: Partial; + /** + * Component to override the tooltip content when triger is set to 'item'. + */ + itemContent?: React.ElementType>; + /** + * Component to override the tooltip content when triger is set to 'axis'. + */ + axisContent?: React.ElementType; }; -function getTootipHasData( - trigger: TooltipProps['trigger'], - displayedData: null | AxisInteractionData | ItemInteractionData, -): boolean { - if (trigger === 'item') { - return displayedData !== null; - } - - const hasAxisXData = (displayedData as AxisInteractionData).x !== null; - const hasAxisYData = (displayedData as AxisInteractionData).y !== null; - - return hasAxisXData || hasAxisYData; -} - export function Tooltip(props: TooltipProps) { - const { trigger = 'axis', highlightProps } = props; + const { trigger = 'axis', highlightProps, itemContent, axisContent } = props; useAxisEvents(trigger); const mousePosition = useMouseTracker(); @@ -334,15 +60,19 @@ export function Tooltip(props: TooltipProps) { open={popperOpen} placement="right-start" anchorEl={generateVirtualElement(mousePosition)} - style={{ padding: '16px', pointerEvents: 'none' }} + style={{ pointerEvents: 'none' }} > - - {trigger === 'item' ? ( - - ) : ( - - )} - + {trigger === 'item' ? ( + } + content={itemContent} + /> + ) : ( + + )} )} diff --git a/packages/x-charts/src/Tooltip/TooltipTable.tsx b/packages/x-charts/src/Tooltip/TooltipTable.tsx new file mode 100644 index 0000000000000..b769f79a04ce2 --- /dev/null +++ b/packages/x-charts/src/Tooltip/TooltipTable.tsx @@ -0,0 +1,31 @@ +import Paper from '@mui/material/Paper'; +import { styled } from '@mui/material/styles'; + +export const TooltipPaper = styled(Paper, { + name: 'MuiChartsTooltip', + slot: 'Table', +})(({ theme }) => ({ + padding: `${theme.spacing(2)} ${theme.spacing(3)}`, +})); + +export const TooltipTable = styled('table', { + name: 'MuiChartsTooltip', + slot: 'Table', +})(() => ({})); + +export const TooltipCell = styled('td', { + name: 'MuiChartsTooltip', + slot: 'Cell', +})(({ theme }) => ({ + padding: `${theme.spacing(0.5)} ${theme.spacing(1)}`, +})); + +export const TooltipMark = styled('div', { + name: 'MuiChartsTooltip', + slot: 'Mark', +})<{ ownerState: { color: string } }>(({ theme, ownerState }) => ({ + width: theme.spacing(1), + height: theme.spacing(1), + borderRadius: '50%', + backgroundColor: ownerState.color, +})); diff --git a/packages/x-charts/src/Tooltip/utils.tsx b/packages/x-charts/src/Tooltip/utils.tsx new file mode 100644 index 0000000000000..0a9cf407fc898 --- /dev/null +++ b/packages/x-charts/src/Tooltip/utils.tsx @@ -0,0 +1,207 @@ +import * as React from 'react'; +import { + AxisInteractionData, + InteractionContext, + ItemInteractionData, +} from '../context/InteractionProvider'; +import { CartesianContext } from '../context/CartesianContextProvider'; +import { isBandScale } from '../hooks/useScale'; +import { SVGContext, DrawingContext } from '../context/DrawingProvider'; +import type { TooltipProps } from './Tooltip'; +import { ChartSeriesType } from '../models/seriesType/config'; + +export function generateVirtualElement(mousePosition: { x: number; y: number } | null) { + if (mousePosition === null) { + return { + getBoundingClientRect: () => ({ + width: 0, + height: 0, + x: 0, + y: 0, + top: 0, + right: 0, + bottom: 0, + left: 0, + toJSON: () => '', + }), + }; + } + const { x, y } = mousePosition; + return { + getBoundingClientRect: () => ({ + width: 0, + height: 0, + x, + y, + top: y, + right: x, + bottom: y, + left: x, + toJSON: () => + JSON.stringify({ width: 0, height: 0, x, y, top: y, right: x, bottom: y, left: x }), + }), + }; +} + +export const useAxisEvents = (trigger: TooltipProps['trigger']) => { + const svgRef = React.useContext(SVGContext); + const { width, height, top, left } = React.useContext(DrawingContext); + const { xAxis, yAxis, xAxisIds, yAxisIds } = React.useContext(CartesianContext); + const { dispatch } = React.useContext(InteractionContext); + + const usedXAxis = xAxisIds[0]; + const usedYAxis = yAxisIds[0]; + + // Use a ref to avoid rerendering on every mousemove event. + const mousePosition = React.useRef({ + x: -1, + y: -1, + }); + + React.useEffect(() => { + const element = svgRef.current; + if (element === null || trigger !== 'axis') { + return () => {}; + } + + const getUpdateY = (y: number) => { + if (usedYAxis === null) { + return null; + } + const { scale: yScale, data: yAxisData } = yAxis[usedYAxis]; + if (!isBandScale(yScale)) { + return { value: yScale.invert(y) }; + } + const dataIndex = Math.floor((y - yScale.range()[0]) / yScale.step()); + if (dataIndex < 0 || dataIndex >= yAxisData!.length) { + return null; + } + return { + index: dataIndex, + value: yAxisData![dataIndex], + }; + }; + + const getUpdateX = (x: number) => { + if (usedXAxis === null) { + return null; + } + const { scale: xScale, data: xAxisData } = xAxis[usedXAxis]; + if (!isBandScale(xScale)) { + const value = xScale.invert(x); + + const closestIndex = xAxisData?.findIndex((v: typeof value, index) => { + if (v > value) { + // @ts-ignore + if (index === 0 || Math.abs(value - v) < Math.abs(value - xAxisData[index - 1])) { + return true; + } + } + if (v <= value) { + if ( + index === xAxisData.length - 1 || + // @ts-ignore + Math.abs(value - v) < Math.abs(value - xAxisData[index + 1]) + ) { + return true; + } + } + return false; + }); + + return { + value: closestIndex !== undefined && closestIndex >= 0 ? xAxisData![closestIndex] : value, + index: closestIndex, + }; + } + const dataIndex = Math.floor((x - xScale.range()[0]) / xScale.step()); + if (dataIndex < 0 || dataIndex >= xAxisData!.length) { + return null; + } + return { + index: dataIndex, + value: xAxisData![dataIndex], + }; + }; + + const handleMouseOut = () => { + mousePosition.current = { + x: -1, + y: -1, + }; + dispatch({ type: 'updateAxis', data: { x: null, y: null } }); + }; + + const handleMouseMove = (event: MouseEvent) => { + mousePosition.current = { + x: event.offsetX, + y: event.offsetY, + }; + const outsideX = event.offsetX < left || event.offsetX > left + width; + const outsideY = event.offsetY < top || event.offsetY > top + height; + if (outsideX || outsideY) { + dispatch({ type: 'updateAxis', data: { x: null, y: null } }); + return; + } + const newStateX = getUpdateX(event.offsetX); + const newStateY = getUpdateY(event.offsetY); + + dispatch({ type: 'updateAxis', data: { x: newStateX, y: newStateY } }); + }; + + element.addEventListener('mouseout', handleMouseOut); + element.addEventListener('mousemove', handleMouseMove); + return () => { + element.removeEventListener('mouseout', handleMouseOut); + element.removeEventListener('mousemove', handleMouseMove); + }; + }, [svgRef, trigger, dispatch, left, width, top, height, usedYAxis, yAxis, usedXAxis, xAxis]); +}; + +export function useMouseTracker() { + const svgRef = React.useContext(SVGContext); + + // Use a ref to avoid rerendering on every mousemove event. + const [mousePosition, setMousePosition] = React.useState(null); + + React.useEffect(() => { + const element = svgRef.current; + if (element === null) { + return () => {}; + } + + const handleMouseOut = () => { + setMousePosition(null); + }; + + const handleMouseMove = (event: MouseEvent) => { + setMousePosition({ + x: event.clientX, + y: event.clientY, + }); + }; + + element.addEventListener('mouseout', handleMouseOut); + element.addEventListener('mousemove', handleMouseMove); + return () => { + element.removeEventListener('mouseout', handleMouseOut); + element.removeEventListener('mousemove', handleMouseMove); + }; + }, [svgRef]); + + return mousePosition; +} + +export function getTootipHasData( + trigger: TooltipProps['trigger'], + displayedData: null | AxisInteractionData | ItemInteractionData, +): boolean { + if (trigger === 'item') { + return displayedData !== null; + } + + const hasAxisXData = (displayedData as AxisInteractionData).x !== null; + const hasAxisYData = (displayedData as AxisInteractionData).y !== null; + + return hasAxisXData || hasAxisYData; +} diff --git a/packages/x-charts/src/context/InteractionProvider.tsx b/packages/x-charts/src/context/InteractionProvider.tsx index 6c751bf47a466..ca638fb0695f0 100644 --- a/packages/x-charts/src/context/InteractionProvider.tsx +++ b/packages/x-charts/src/context/InteractionProvider.tsx @@ -1,15 +1,11 @@ import * as React from 'react'; -import { BarItemIdentifier } from '../models/seriesType/bar'; -import { LineItemIdentifier } from '../models/seriesType/line'; -import { ScatterItemIdentifier } from '../models/seriesType/scatter'; +import { ChartItemIdentifier, ChartSeriesType } from '../models/seriesType/config'; export interface InteractionProviderProps { children: React.ReactNode; } -export type ItemInteractionData = { - target?: SVGElement; -} & (BarItemIdentifier | LineItemIdentifier | ScatterItemIdentifier); +export type ItemInteractionData = ChartItemIdentifier; export type AxisInteractionData = { x: null | { @@ -22,14 +18,14 @@ export type AxisInteractionData = { }; }; -type InteractionActions = +type InteractionActions = | { type: 'enterItem'; - data: ItemInteractionData; + data: ItemInteractionData; } | { type: 'leaveItem'; - data: ItemInteractionData; + data: ItemInteractionData; } | { type: 'updateAxis'; @@ -37,7 +33,7 @@ type InteractionActions = }; type InteractionState = { - item: null | ItemInteractionData; + item: null | ItemInteractionData; axis: AxisInteractionData; dispatch: React.Dispatch; }; @@ -59,7 +55,7 @@ const dataReducer: React.Reducer, Interaction case 'leaveItem': if ( prevState.item === null || - (Object.keys(action.data) as (keyof ItemInteractionData)[]).some( + (Object.keys(action.data) as (keyof ItemInteractionData)[]).some( (key) => action.data[key] !== prevState.item![key], ) ) { diff --git a/packages/x-charts/src/hooks/useInteractionItemProps.ts b/packages/x-charts/src/hooks/useInteractionItemProps.ts index ec2692ae0d4c1..9483da5d7cda6 100644 --- a/packages/x-charts/src/hooks/useInteractionItemProps.ts +++ b/packages/x-charts/src/hooks/useInteractionItemProps.ts @@ -10,10 +10,10 @@ export const useInteractionItemProps = () => { const getInteractionItemProps = ( data: BarItemIdentifier | LineItemIdentifier | ScatterItemIdentifier, ) => { - const onMouseEnter = (event: React.MouseEvent) => { + const onMouseEnter = () => { dispatch({ type: 'enterItem', - data: { ...data, target: event?.target as SVGElement }, + data, }); }; const onMouseLeave = () => { diff --git a/packages/x-charts/src/internals/defaultizeValueFormatter.ts b/packages/x-charts/src/internals/defaultizeValueFormatter.ts new file mode 100644 index 0000000000000..9147c143113df --- /dev/null +++ b/packages/x-charts/src/internals/defaultizeValueFormatter.ts @@ -0,0 +1,25 @@ +function defaultizeValueFormatter string>( + series: { + [id: string]: ISeries; + }, + defaultValueFormatter: IFormatter, +): { + [id: string]: ISeries & { + valueFormatter: IFormatter; + }; +} { + const defaultizedSeries: { + [id: string]: ISeries & { + valueFormatter: IFormatter; + }; + } = {}; + Object.keys(series).forEach((seriesId) => { + defaultizedSeries[seriesId] = { + valueFormatter: defaultValueFormatter, + ...series[seriesId], + }; + }); + return defaultizedSeries; +} + +export default defaultizeValueFormatter; diff --git a/packages/x-charts/src/models/axis.ts b/packages/x-charts/src/models/axis.ts index fe436c833fc5b..61fd13e1f3dff 100644 --- a/packages/x-charts/src/models/axis.ts +++ b/packages/x-charts/src/models/axis.ts @@ -35,17 +35,19 @@ export type AxisScaleMapping = scale: ScaleLinear; }; -export type AxisConfig = { +export type AxisConfig = { id: string; scaleName?: ScaleName; min?: number; max?: number; - data?: any[]; + data?: V[]; + valueFormatter?: (value: V) => string; }; -export type AxisDefaultized = { +export type AxisDefaultized = { id: string; min?: number; max?: number; - data?: any[]; + data?: V[]; + valueFormatter?: (value: V) => string; } & AxisScaleMapping; diff --git a/packages/x-charts/src/models/seriesType/bar.ts b/packages/x-charts/src/models/seriesType/bar.ts index ccabb1b9d0b70..b8b17478561b2 100644 --- a/packages/x-charts/src/models/seriesType/bar.ts +++ b/packages/x-charts/src/models/seriesType/bar.ts @@ -5,6 +5,7 @@ export interface BarSeriesType extends CommonSeriesType, CartesianSeriesType { type: 'bar'; data: number[]; stack?: string; + label?: string; } /** @@ -19,4 +20,4 @@ export type BarItemIdentifier = { export interface DefaultizedBarSeriesType extends DefaultizedProps, - DefaultizedCommonSeriesType {} + DefaultizedCommonSeriesType {} diff --git a/packages/x-charts/src/models/seriesType/common.ts b/packages/x-charts/src/models/seriesType/common.ts index 23c6fbcadd607..e9b83e60935a2 100644 --- a/packages/x-charts/src/models/seriesType/common.ts +++ b/packages/x-charts/src/models/seriesType/common.ts @@ -2,8 +2,14 @@ export type CommonSeriesType = { id: string; }; -export type DefaultizedCommonSeriesType = { +export type DefaultizedCommonSeriesType = { color: string; + /** + * Formatter used to render values in tooltip or other data display. + * @param {TValue} value The series' value to render. + * @returns {string} The string to dispaly. + */ + valueFormatter: (value: TValue) => string; }; export type CartesianSeriesType = { diff --git a/packages/x-charts/src/models/seriesType/config.ts b/packages/x-charts/src/models/seriesType/config.ts index e76dfdb5dd33b..fd02092ae7332 100644 --- a/packages/x-charts/src/models/seriesType/config.ts +++ b/packages/x-charts/src/models/seriesType/config.ts @@ -1,6 +1,6 @@ -import { ScatterSeriesType, DefaultizedScatterSeriesType } from './scatter'; -import { LineSeriesType, DefaultizedLineSeriesType } from './line'; -import { BarSeriesType, DefaultizedBarSeriesType } from './bar'; +import { ScatterSeriesType, DefaultizedScatterSeriesType, ScatterItemIdentifier } from './scatter'; +import { LineSeriesType, DefaultizedLineSeriesType, LineItemIdentifier } from './line'; +import { BarItemIdentifier, BarSeriesType, DefaultizedBarSeriesType } from './bar'; import { AxisConfig } from '../axis'; interface ChartsSeriesConfig { @@ -8,15 +8,18 @@ interface ChartsSeriesConfig { seriesInput: BarSeriesType & { color: string }; series: DefaultizedBarSeriesType; canBeStacked: true; + itemIdentifier: BarItemIdentifier; }; line: { seriesInput: LineSeriesType & { color: string }; series: DefaultizedLineSeriesType; canBeStacked: true; + itemIdentifier: LineItemIdentifier; }; scatter: { seriesInput: ScatterSeriesType & { color: string }; series: DefaultizedScatterSeriesType; + itemIdentifier: ScatterItemIdentifier; }; } @@ -28,6 +31,12 @@ export type ChartSeries = ChartsSeriesConfig[T] exten ? ChartsSeriesConfig[T]['seriesInput'] & { stackedData: [number, number][] } : ChartsSeriesConfig[T]['seriesInput']; +export type ChartSeriesDefaultized = ChartsSeriesConfig[T]['series'] & + ChartSeries; + +export type ChartItemIdentifier = + ChartsSeriesConfig[T]['itemIdentifier']; + type ExtremumGetterParams = { series: { [id: string]: ChartSeries }; axis: AxisConfig; @@ -45,7 +54,7 @@ export type FormatterParams = { }; export type FormatterResult = { - series: { [id: string]: ChartSeries }; + series: { [id: string]: ChartSeriesDefaultized }; seriesOrder: string[]; } & (ChartsSeriesConfig[T] extends { canBeStacked: true; diff --git a/packages/x-charts/src/models/seriesType/line.ts b/packages/x-charts/src/models/seriesType/line.ts index 146b69de25622..d66d510f6cbba 100644 --- a/packages/x-charts/src/models/seriesType/line.ts +++ b/packages/x-charts/src/models/seriesType/line.ts @@ -7,6 +7,7 @@ export interface LineSeriesType extends CommonSeriesType, CartesianSeriesType { data: number[]; stack?: string; area?: any; + label?: string; curve?: CurveType; } @@ -25,4 +26,4 @@ export type LineItemIdentifier = { export interface DefaultizedLineSeriesType extends DefaultizedProps, - DefaultizedCommonSeriesType {} + DefaultizedCommonSeriesType {} diff --git a/packages/x-charts/src/models/seriesType/scatter.ts b/packages/x-charts/src/models/seriesType/scatter.ts index 4b835ae09ceac..e3c8553a75268 100644 --- a/packages/x-charts/src/models/seriesType/scatter.ts +++ b/packages/x-charts/src/models/seriesType/scatter.ts @@ -1,10 +1,13 @@ import { DefaultizedProps } from '../helpers'; import { CartesianSeriesType, CommonSeriesType, DefaultizedCommonSeriesType } from './common'; +type ScatterValueType = { x: unknown; y: unknown; id: string | number }; + export interface ScatterSeriesType extends CommonSeriesType, CartesianSeriesType { type: 'scatter'; - data: { x: unknown; y: unknown; id: string | number }[]; + data: ScatterValueType[]; markerSize?: number; + label?: string; } /** @@ -19,4 +22,4 @@ export type ScatterItemIdentifier = { export interface DefaultizedScatterSeriesType extends DefaultizedProps, - DefaultizedCommonSeriesType {} + DefaultizedCommonSeriesType {} From a36725c4f923297372d8b02e083c8bcd40ebb815 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 28 Apr 2023 15:36:16 +0300 Subject: [PATCH 34/80] [pickers] Fix `minutesStep` validation prop behavior (#8794) --- .../src/internal/models/dateTimeRange.ts | 10 ++----- .../src/DateTimeField/DateTimeField.types.ts | 12 ++------ .../src/DateTimePicker/shared.tsx | 17 +++++------ .../x-date-pickers/src/internals/index.ts | 1 + .../src/internals/models/validation.ts | 14 +++++++++ .../validation/extractValidationProps.ts | 24 +++++++++++++-- .../testTextFieldValidation.tsx | 29 +++++++++++++++++++ 7 files changed, 77 insertions(+), 30 deletions(-) diff --git a/packages/x-date-pickers-pro/src/internal/models/dateTimeRange.ts b/packages/x-date-pickers-pro/src/internal/models/dateTimeRange.ts index b8a493afcf86c..48223acd9672c 100644 --- a/packages/x-date-pickers-pro/src/internal/models/dateTimeRange.ts +++ b/packages/x-date-pickers-pro/src/internal/models/dateTimeRange.ts @@ -4,6 +4,7 @@ import { DefaultizedProps, MakeOptional, UseFieldInternalProps, + DateTimeValidationProps, } from '@mui/x-date-pickers/internals'; import { BaseRangeProps, DayRangeValidationProps } from './dateRange'; import { DateRange } from './range'; @@ -18,15 +19,8 @@ export interface UseDateTimeRangeFieldProps DayRangeValidationProps, TimeValidationProps, BaseDateValidationProps, + DateTimeValidationProps, BaseRangeProps { - /** - * Minimal selectable moment of time with binding to date, to set min time in each day use `minTime`. - */ - minDateTime?: TDate; - /** - * Maximal selectable moment of time with binding to date, to set max time in each day use `maxTime`. - */ - maxDateTime?: TDate; /** * 12h/24h view for hour selection clock. * @default `utils.is12HourCycleInCurrentLocale()` diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts b/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts index 7382edbd6f575..c4ecd06b66d3f 100644 --- a/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts +++ b/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts @@ -7,6 +7,7 @@ import { DefaultizedProps, MakeOptional } from '../internals/models/helpers'; import { BaseDateValidationProps, BaseTimeValidationProps, + DateTimeValidationProps, DayValidationProps, MonthValidationProps, TimeValidationProps, @@ -30,20 +31,13 @@ export interface UseDateTimeFieldProps YearValidationProps, BaseDateValidationProps, TimeValidationProps, - BaseTimeValidationProps { + BaseTimeValidationProps, + DateTimeValidationProps { /** * 12h/24h view for hour selection clock. * @default `utils.is12HourCycleInCurrentLocale()` */ ampm?: boolean; - /** - * Minimal selectable moment of time with binding to date, to set min time in each day use `minTime`. - */ - minDateTime?: TDate; - /** - * Maximal selectable moment of time with binding to date, to set max time in each day use `maxTime`. - */ - maxDateTime?: TDate; } export type UseDateTimeFieldDefaultizedProps = DefaultizedProps< diff --git a/packages/x-date-pickers/src/DateTimePicker/shared.tsx b/packages/x-date-pickers/src/DateTimePicker/shared.tsx index 12f8a4b7f2e35..8ae7298e17e16 100644 --- a/packages/x-date-pickers/src/DateTimePicker/shared.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/shared.tsx @@ -19,7 +19,11 @@ import { DateTimePickerTabsProps, ExportedDateTimePickerTabsProps, } from './DateTimePickerTabs'; -import { BaseDateValidationProps, BaseTimeValidationProps } from '../internals/models/validation'; +import { + BaseDateValidationProps, + BaseTimeValidationProps, + DateTimeValidationProps, +} from '../internals/models/validation'; import { LocalizedComponent, PickersInputLocaleText } from '../locales/utils/pickersLocaleTextApi'; import { DateTimePickerToolbar, @@ -64,20 +68,13 @@ export interface BaseDateTimePickerSlotsComponentsProps export interface BaseDateTimePickerProps extends BasePickerInputProps, Omit, 'onViewChange'>, - ExportedBaseClockProps { + ExportedBaseClockProps, + DateTimeValidationProps { /** * Display ampm controls under the clock (instead of in the toolbar). * @default true on desktop, false on mobile */ ampmInClock?: boolean; - /** - * Minimal selectable moment of time with binding to date, to set min time in each day use `minTime`. - */ - minDateTime?: TDate; - /** - * Maximal selectable moment of time with binding to date, to set max time in each day use `maxTime`. - */ - maxDateTime?: TDate; /** * Overridable components. * @default {} diff --git a/packages/x-date-pickers/src/internals/index.ts b/packages/x-date-pickers/src/internals/index.ts index 76fb2d832f933..a0e796bb7df00 100644 --- a/packages/x-date-pickers/src/internals/index.ts +++ b/packages/x-date-pickers/src/internals/index.ts @@ -113,6 +113,7 @@ export type { MonthValidationProps, YearValidationProps, DayValidationProps, + DateTimeValidationProps, } from './models/validation'; export { applyDefaultDate, replaceInvalidDateByNull, areDatesEqual } from './utils/date-utils'; diff --git a/packages/x-date-pickers/src/internals/models/validation.ts b/packages/x-date-pickers/src/internals/models/validation.ts index 23af5a8b5f1ab..e33e00ebf5b7e 100644 --- a/packages/x-date-pickers/src/internals/models/validation.ts +++ b/packages/x-date-pickers/src/internals/models/validation.ts @@ -114,3 +114,17 @@ export interface YearValidationProps { */ shouldDisableYear?: (year: TDate) => boolean; } + +/** + * Props used to validate a date time value. + */ +export interface DateTimeValidationProps { + /** + * Minimal selectable moment of time with binding to date, to set min time in each day use `minTime`. + */ + minDateTime?: TDate; + /** + * Maximal selectable moment of time with binding to date, to set max time in each day use `maxTime`. + */ + maxDateTime?: TDate; +} diff --git a/packages/x-date-pickers/src/internals/utils/validation/extractValidationProps.ts b/packages/x-date-pickers/src/internals/utils/validation/extractValidationProps.ts index a97c57516bdb5..88cb7f7739a5e 100644 --- a/packages/x-date-pickers/src/internals/utils/validation/extractValidationProps.ts +++ b/packages/x-date-pickers/src/internals/utils/validation/extractValidationProps.ts @@ -1,4 +1,22 @@ -const VALIDATION_PROP_NAMES = [ +import { + BaseDateValidationProps, + BaseTimeValidationProps, + DateTimeValidationProps, + DayValidationProps, + MonthValidationProps, + TimeValidationProps, + YearValidationProps, +} from '../../models/validation'; + +const VALIDATION_PROP_NAMES: ( + | keyof BaseTimeValidationProps + | keyof BaseDateValidationProps + | keyof TimeValidationProps + | keyof YearValidationProps + | keyof MonthValidationProps + | keyof DayValidationProps + | keyof DateTimeValidationProps +)[] = [ 'disablePast', 'disableFuture', 'minDate', @@ -12,8 +30,8 @@ const VALIDATION_PROP_NAMES = [ 'shouldDisableYear', 'shouldDisableClock', 'shouldDisableTime', - 'minuteStep', -] as const; + 'minutesStep', +]; type ValidationPropNames = (typeof VALIDATION_PROP_NAMES)[number]; diff --git a/packages/x-date-pickers/src/tests/describeValidation/testTextFieldValidation.tsx b/packages/x-date-pickers/src/tests/describeValidation/testTextFieldValidation.tsx index 04dc82511da06..b15222430ba42 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testTextFieldValidation.tsx +++ b/packages/x-date-pickers/src/tests/describeValidation/testTextFieldValidation.tsx @@ -449,5 +449,34 @@ export const testTextFieldValidation: DescribeValidationTestSuite = (ElementToTe expect(onErrorMock.lastCall.args[0]).to.equal(null); expect(screen.getByRole('textbox')).to.have.attribute('aria-invalid', 'false'); }); + + it('should apply minutesStep', function test() { + if (['picker', 'field'].includes(componentFamily) && !withTime) { + return; + } + + const onErrorMock = spy(); + const { setProps } = render( + , + ); + if (withTime) { + expect(onErrorMock.callCount).to.equal(1); + expect(onErrorMock.lastCall.args[0]).to.equal('minutesStep'); + expect(screen.getByRole('textbox')).to.have.attribute('aria-invalid', 'true'); + + setProps({ value: adapterToUse.date(new Date(2019, 5, 15, 10, 30)) }); + + expect(onErrorMock.callCount).to.equal(2); + expect(onErrorMock.lastCall.args[0]).to.equal(null); + expect(screen.getByRole('textbox')).to.have.attribute('aria-invalid', 'false'); + } else { + expect(onErrorMock.callCount).to.equal(0); + expect(screen.getByRole('textbox')).to.have.attribute('aria-invalid', 'false'); + } + }); }); }; From a2413971d0bdc9e0c6d0aae55698b5200a91969a Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Fri, 28 Apr 2023 14:59:09 +0200 Subject: [PATCH 35/80] [charts] Improvement and docs on axis (#8654) Co-authored-by: Lukas --- .../charts/axis/AxisCustomizationNoSnap.js | 88 ++++ docs/data/charts/axis/AxisWithComposition.js | 48 ++ docs/data/charts/axis/AxisWithComposition.tsx | 48 ++ docs/data/charts/axis/MinMaxExample.js | 66 +++ docs/data/charts/axis/MinMaxExample.tsx | 70 +++ docs/data/charts/axis/ModifyAxisPosition.js | 31 ++ docs/data/charts/axis/ModifyAxisPosition.tsx | 31 ++ .../axis/ModifyAxisPosition.tsx.preview | 15 + docs/data/charts/axis/ScaleExample.js | 47 ++ docs/data/charts/axis/ScaleExample.tsx | 47 ++ docs/data/charts/axis/axis.md | 98 ++++ docs/data/charts/bars/Composition.js | 6 +- docs/data/charts/bars/Composition.tsx | 6 +- .../data/charts/bars/CompositionResponsive.js | 8 +- .../charts/bars/CompositionResponsive.tsx | 8 +- docs/data/charts/bars/TestBars.js | 4 +- docs/data/charts/bars/TestBars.tsx | 4 +- docs/data/charts/lines/CSSCustomization.js | 2 +- docs/data/charts/lines/CSSCustomization.tsx | 2 +- docs/data/charts/lines/StackedAreas.js | 2 +- docs/data/charts/lines/StackedAreas.tsx | 2 +- docs/data/charts/lines/TestLines.js | 2 +- docs/data/charts/lines/TestLines.tsx | 2 +- docs/data/charts/lines/TestLinesTime.js | 4 +- docs/data/charts/lines/TestLinesTime.tsx | 4 +- docs/data/charts/overview/Combining.js | 6 +- docs/data/charts/overview/Combining.tsx | 6 +- docs/data/charts/overview/SimpleCharts.js | 2 +- docs/data/charts/overview/SimpleCharts.tsx | 2 +- docs/data/charts/scatter/Composition.js | 7 +- docs/data/charts/scatter/Composition.tsx | 7 +- docs/data/charts/tooltip/tooltip.md | 14 + docs/data/pages.ts | 10 +- docs/pages/x/react-charts/axis.js | 7 + docs/pages/x/react-charts/tooltip.js | 7 + .../src/modules/components/ChartsUsageDemo.js | 82 +++ docs/src/modules/components/DemoPropsForm.tsx | 466 ++++++++++++++++++ packages/x-charts/src/Axis/Axis.tsx | 99 ++++ packages/x-charts/src/Axis/index.tsx | 1 + packages/x-charts/src/BarChart/BarChart.tsx | 44 +- packages/x-charts/src/LineChart/LineChart.tsx | 41 +- packages/x-charts/src/LineChart/LinePlot.tsx | 204 ++++---- .../src/ResponsiveChartContainer/index.tsx | 24 +- .../src/ScatterChart/ScatterChart.tsx | 42 +- packages/x-charts/src/XAxis/XAxis.tsx | 4 +- packages/x-charts/src/YAxis/YAxis.tsx | 2 +- .../src/context/CartesianContextProvider.tsx | 20 +- packages/x-charts/src/hooks/useScale.ts | 4 +- packages/x-charts/src/index.ts | 10 + packages/x-charts/src/models/axis.ts | 14 +- 50 files changed, 1523 insertions(+), 247 deletions(-) create mode 100644 docs/data/charts/axis/AxisCustomizationNoSnap.js create mode 100644 docs/data/charts/axis/AxisWithComposition.js create mode 100644 docs/data/charts/axis/AxisWithComposition.tsx create mode 100644 docs/data/charts/axis/MinMaxExample.js create mode 100644 docs/data/charts/axis/MinMaxExample.tsx create mode 100644 docs/data/charts/axis/ModifyAxisPosition.js create mode 100644 docs/data/charts/axis/ModifyAxisPosition.tsx create mode 100644 docs/data/charts/axis/ModifyAxisPosition.tsx.preview create mode 100644 docs/data/charts/axis/ScaleExample.js create mode 100644 docs/data/charts/axis/ScaleExample.tsx create mode 100644 docs/data/charts/axis/axis.md create mode 100644 docs/data/charts/tooltip/tooltip.md create mode 100644 docs/pages/x/react-charts/axis.js create mode 100644 docs/pages/x/react-charts/tooltip.js create mode 100644 docs/src/modules/components/ChartsUsageDemo.js create mode 100644 docs/src/modules/components/DemoPropsForm.tsx create mode 100644 packages/x-charts/src/Axis/Axis.tsx create mode 100644 packages/x-charts/src/Axis/index.tsx diff --git a/docs/data/charts/axis/AxisCustomizationNoSnap.js b/docs/data/charts/axis/AxisCustomizationNoSnap.js new file mode 100644 index 0000000000000..8dfd08082cadb --- /dev/null +++ b/docs/data/charts/axis/AxisCustomizationNoSnap.js @@ -0,0 +1,88 @@ +import * as React from 'react'; +import ChartsUsageDemo from 'docsx/src/modules/components/ChartsUsageDemo'; +import { DEFAULT_X_AXIS_KEY } from '@mui/x-charts/constants'; +import { ScatterChart } from '@mui/x-charts/ScatterChart'; +import { Chance } from 'chance'; + +const chance = new Chance(42); + +const data = Array.from({ length: 200 }, () => ({ + x: chance.floating({ min: -25, max: 25 }), + y: chance.floating({ min: -25, max: 25 }), +})).map((d, index) => ({ ...d, id: index })); + +const defaultXAxis = { + disableLine: false, + disableTicks: false, + fill: 'currentColor', + fontSize: 12, + label: 'my axis', + labelFontSize: 14, + stroke: 'currentColor', + tickSize: 6, +}; +export default function AxisCustomizationNoSnap() { + return ( + ( + + )} + getCode={({ props }) => + [ + `import { ScatterChart } from '@mui/x-charts/ScatterChart';`, + '', + ` props[prop] !== defaultXAxis[prop]) + .map( + (prop) => + ` ${prop}: ${ + typeof props[prop] === 'string' ? `"${props[prop]}"` : props[prop] + },`, + ), + ' }}', + '/>', + ].join('\n') + } + /> + ); +} diff --git a/docs/data/charts/axis/AxisWithComposition.js b/docs/data/charts/axis/AxisWithComposition.js new file mode 100644 index 0000000000000..8630d179494a7 --- /dev/null +++ b/docs/data/charts/axis/AxisWithComposition.js @@ -0,0 +1,48 @@ +import * as React from 'react'; +import { ChartContainer, BarPlot, LinePlot, XAxis, YAxis } from '@mui/x-charts'; + +export default function AxisWithComposition() { + return ( + + + + + + + + ); +} diff --git a/docs/data/charts/axis/AxisWithComposition.tsx b/docs/data/charts/axis/AxisWithComposition.tsx new file mode 100644 index 0000000000000..8630d179494a7 --- /dev/null +++ b/docs/data/charts/axis/AxisWithComposition.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import { ChartContainer, BarPlot, LinePlot, XAxis, YAxis } from '@mui/x-charts'; + +export default function AxisWithComposition() { + return ( + + + + + + + + ); +} diff --git a/docs/data/charts/axis/MinMaxExample.js b/docs/data/charts/axis/MinMaxExample.js new file mode 100644 index 0000000000000..b44bf5d6b57bd --- /dev/null +++ b/docs/data/charts/axis/MinMaxExample.js @@ -0,0 +1,66 @@ +import * as React from 'react'; +import Slider from '@mui/material/Slider'; +import { ScatterChart } from '@mui/x-charts/ScatterChart'; +import { Chance } from 'chance'; + +const chance = new Chance(42); + +const data = Array.from({ length: 200 }, () => ({ + x: chance.floating({ min: -25, max: 25 }), + y: chance.floating({ min: -25, max: 25 }), +})).map((d, index) => ({ ...d, id: index })); + +const minDistance = 10; + +export default function MinMaxExample() { + const [value, setValue] = React.useState([-25, 25]); + + const handleChange = (event, newValue, activeThumb) => { + if (!Array.isArray(newValue)) { + return; + } + + if (newValue[1] - newValue[0] < minDistance) { + if (activeThumb === 0) { + const clamped = Math.min(newValue[0], 100 - minDistance); + setValue([clamped, clamped + minDistance]); + } else { + const clamped = Math.max(newValue[1], minDistance); + setValue([clamped - minDistance, clamped]); + } + } else { + setValue(newValue); + } + }; + + return ( +
+ + +
+ ); +} diff --git a/docs/data/charts/axis/MinMaxExample.tsx b/docs/data/charts/axis/MinMaxExample.tsx new file mode 100644 index 0000000000000..80626cce68a41 --- /dev/null +++ b/docs/data/charts/axis/MinMaxExample.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import Slider from '@mui/material/Slider'; +import { ScatterChart } from '@mui/x-charts/ScatterChart'; +import { Chance } from 'chance'; + +const chance = new Chance(42); + +const data = Array.from({ length: 200 }, () => ({ + x: chance.floating({ min: -25, max: 25 }), + y: chance.floating({ min: -25, max: 25 }), +})).map((d, index) => ({ ...d, id: index })); + +const minDistance = 10; + +export default function MinMaxExample() { + const [value, setValue] = React.useState([-25, 25]); + + const handleChange = ( + event: Event, + newValue: number | number[], + activeThumb: number, + ) => { + if (!Array.isArray(newValue)) { + return; + } + + if (newValue[1] - newValue[0] < minDistance) { + if (activeThumb === 0) { + const clamped = Math.min(newValue[0], 100 - minDistance); + setValue([clamped, clamped + minDistance]); + } else { + const clamped = Math.max(newValue[1], minDistance); + setValue([clamped - minDistance, clamped]); + } + } else { + setValue(newValue as number[]); + } + }; + + return ( +
+ + +
+ ); +} diff --git a/docs/data/charts/axis/ModifyAxisPosition.js b/docs/data/charts/axis/ModifyAxisPosition.js new file mode 100644 index 0000000000000..49f15d8a414b7 --- /dev/null +++ b/docs/data/charts/axis/ModifyAxisPosition.js @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '@mui/x-charts/constants'; +import { ScatterChart } from '@mui/x-charts/ScatterChart'; +import { Chance } from 'chance'; + +const chance = new Chance(42); + +const data = Array.from({ length: 200 }, () => ({ + x: chance.floating({ min: -25, max: 25 }), + y: chance.floating({ min: -25, max: 25 }), +})).map((d, index) => ({ ...d, id: index })); + +export default function ModifyAxisPosition() { + return ( + + ); +} diff --git a/docs/data/charts/axis/ModifyAxisPosition.tsx b/docs/data/charts/axis/ModifyAxisPosition.tsx new file mode 100644 index 0000000000000..49f15d8a414b7 --- /dev/null +++ b/docs/data/charts/axis/ModifyAxisPosition.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '@mui/x-charts/constants'; +import { ScatterChart } from '@mui/x-charts/ScatterChart'; +import { Chance } from 'chance'; + +const chance = new Chance(42); + +const data = Array.from({ length: 200 }, () => ({ + x: chance.floating({ min: -25, max: 25 }), + y: chance.floating({ min: -25, max: 25 }), +})).map((d, index) => ({ ...d, id: index })); + +export default function ModifyAxisPosition() { + return ( + + ); +} diff --git a/docs/data/charts/axis/ModifyAxisPosition.tsx.preview b/docs/data/charts/axis/ModifyAxisPosition.tsx.preview new file mode 100644 index 0000000000000..9b5216339ff09 --- /dev/null +++ b/docs/data/charts/axis/ModifyAxisPosition.tsx.preview @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/docs/data/charts/axis/ScaleExample.js b/docs/data/charts/axis/ScaleExample.js new file mode 100644 index 0000000000000..f41473be6234a --- /dev/null +++ b/docs/data/charts/axis/ScaleExample.js @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function ScaleExample() { + return ( + + ); +} diff --git a/docs/data/charts/axis/ScaleExample.tsx b/docs/data/charts/axis/ScaleExample.tsx new file mode 100644 index 0000000000000..f41473be6234a --- /dev/null +++ b/docs/data/charts/axis/ScaleExample.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function ScaleExample() { + return ( + + ); +} diff --git a/docs/data/charts/axis/axis.md b/docs/data/charts/axis/axis.md new file mode 100644 index 0000000000000..4f70e545f8630 --- /dev/null +++ b/docs/data/charts/axis/axis.md @@ -0,0 +1,98 @@ +--- +product: charts +title: Charts - Axis +--- + +# Charts - Axis + +

Axis provides associate values to element positions.

+ +Axes are used in the following charts: ``, ``, ``. + +## Defining axis + +Like your data, axis definition plays a central role in the chart rendering. +It's responsible for the mapping between your data and element positions. + +You can define custom axes by using `xAxis` and `yAxis` props. +Those props expect an array of objects. + +Here is a demonstration with two lines with the same data. +But one uses a linear, and the other a log axis. + +Each axis definition is identified by its property `id`. +And series specify the axis they use with `xAxisKey` and `yAxisKey` properties. + +{{"demo": "ScaleExample.js", "bg": "inline"}} + +### Axis type + +The axis type is specified by its property `scaleType` which expect one of the following values: + +- `'band'`: Split the axis in equal band. Mostly used for bar charts. +- `'linear'`, `'log'`, `'sqrt'`: Map numerical values to the space available for the chart. `'linear'` is the default behavior. +- `'time'`, `'utc'`: Map JavaScript `Date()` object to the space available for the chart. + +### Axis data + +The axis definition object also includes a `data` property. +Which expects an array of value coherent with the `scaleType`: + +- For `'linear'`, `'log'`, or `'sqrt'` it should contain numerical values +- For `'time'` or `'utc'` it should contain `Date()` objects +- For `'band'` it can contain `string`, or numerical values + +Some series types also require specific axis attributes: + +- line plots require an `xAxis` to have `data` provided +- bar plots require an `xAxis` with `scaleType='band'` and some `data` provided. + +### Axis sub domain + +By default, the axis domain is computed such that all your data is visible. +To show a specific range of values, you can provide properties `min` and/or `max` to the axis definition. + +```js +xAxis={[ + { + id: 'axisId', + min: 10, + max: 50, + } +]} +``` + +{{"demo": "MinMaxExample.js", "bg": "inline"}} + +## Axis customization + +You can further customize the axis rendering besides the axis definition. + +### Position + +Charts components provide 4 props: `topAxis`, `rightAxis`, `bottomAxis`, and `leftAxis` allowing to define the 4 axes of the chart. +Those pros can accept three type of value: + +- `null` to not display the axis +- `string` which should correspond to the id of a `xAxis` for top and bottom. Or to the id of a `yAxis` for left and right. +- `object` which will be passed as props to `` or ``. It allows to specify which axis should be represent, and to customize the design of the axis. + +{{"demo": "ModifyAxisPosition.js", "bg": "inline"}} + +### Rendering + +Axes rendering can be further customized. Below is an interactive demonstration of the axis props. + +{{"demo": "AxisCustomizationNoSnap.js", "hideToolbar": true, "bg": "inline"}} + +### Composition + +If you are using composition, you have to provide the axis settings in the `` by using `xAxis` and `yAxis` props. + +It will provide all the scaling properties to its children, and allows you to use `` and `` components as children. +Those components require an `axisId` prop to link them to an axis you defined in the ``. + +You can choose their position with `position` props which accept `'top'`/`'bottom'` for `` and `'left'`/`'right'` for ``. +Other props are similar to the ones defined in the [previous section](/x/react-charts/axis/#rendering). + +{{"demo": "AxisWithComposition.js", "bg": "inline"}} diff --git a/docs/data/charts/bars/Composition.js b/docs/data/charts/bars/Composition.js index 2cfe4b8233195..1569961f1ab81 100644 --- a/docs/data/charts/bars/Composition.js +++ b/docs/data/charts/bars/Composition.js @@ -45,17 +45,17 @@ export default function Composition() { { id: 'years', data: [2010, 2011, 2012, 2013, 2014], - scaleName: 'band', + scaleType: 'band', }, ]} yAxis={[ { id: 'eco', - scaleName: 'linear', + scaleType: 'linear', }, { id: 'pib', - scaleName: 'log', + scaleType: 'log', }, ]} > diff --git a/docs/data/charts/bars/Composition.tsx b/docs/data/charts/bars/Composition.tsx index 15cfd35851296..7f8a9226c5679 100644 --- a/docs/data/charts/bars/Composition.tsx +++ b/docs/data/charts/bars/Composition.tsx @@ -45,17 +45,17 @@ export default function Composition() { { id: 'years', data: [2010, 2011, 2012, 2013, 2014], - scaleName: 'band', + scaleType: 'band', }, ]} yAxis={[ { id: 'eco', - scaleName: 'linear', + scaleType: 'linear', }, { id: 'pib', - scaleName: 'log', + scaleType: 'log', }, ]} > diff --git a/docs/data/charts/bars/CompositionResponsive.js b/docs/data/charts/bars/CompositionResponsive.js index 8241169236c3d..87b1a1264cc77 100644 --- a/docs/data/charts/bars/CompositionResponsive.js +++ b/docs/data/charts/bars/CompositionResponsive.js @@ -39,23 +39,21 @@ export default function CompositionResponsive() {
diff --git a/docs/data/charts/bars/CompositionResponsive.tsx b/docs/data/charts/bars/CompositionResponsive.tsx index 6443d58e5f668..7fb76cac764ec 100644 --- a/docs/data/charts/bars/CompositionResponsive.tsx +++ b/docs/data/charts/bars/CompositionResponsive.tsx @@ -39,23 +39,21 @@ export default function CompositionResponsive() {
diff --git a/docs/data/charts/bars/TestBars.js b/docs/data/charts/bars/TestBars.js index 27f3779b1a7a9..aff9b36e643cf 100644 --- a/docs/data/charts/bars/TestBars.js +++ b/docs/data/charts/bars/TestBars.js @@ -8,13 +8,13 @@ export default function TestBars() { { id: 'barCategories', data: [2, 5, 20, 23, 25], - scaleName: 'band', + scaleType: 'band', }, ]} yAxis={[ { id: 'barCategoriesY', - scaleName: 'linear', + scaleType: 'linear', }, ]} series={[ diff --git a/docs/data/charts/bars/TestBars.tsx b/docs/data/charts/bars/TestBars.tsx index 27f3779b1a7a9..aff9b36e643cf 100644 --- a/docs/data/charts/bars/TestBars.tsx +++ b/docs/data/charts/bars/TestBars.tsx @@ -8,13 +8,13 @@ export default function TestBars() { { id: 'barCategories', data: [2, 5, 20, 23, 25], - scaleName: 'band', + scaleType: 'band', }, ]} yAxis={[ { id: 'barCategoriesY', - scaleName: 'linear', + scaleType: 'linear', }, ]} series={[ diff --git a/docs/data/charts/lines/CSSCustomization.js b/docs/data/charts/lines/CSSCustomization.js index 8454cb2adad67..61ed3da3f1cf0 100644 --- a/docs/data/charts/lines/CSSCustomization.js +++ b/docs/data/charts/lines/CSSCustomization.js @@ -69,7 +69,7 @@ export default function CSSCustomization() { { id: 'Years', data: years, - scaleName: 'time', + scaleType: 'time', valueFormatter: (date) => date.getFullYear(), }, ]} diff --git a/docs/data/charts/lines/CSSCustomization.tsx b/docs/data/charts/lines/CSSCustomization.tsx index 8454cb2adad67..61ed3da3f1cf0 100644 --- a/docs/data/charts/lines/CSSCustomization.tsx +++ b/docs/data/charts/lines/CSSCustomization.tsx @@ -69,7 +69,7 @@ export default function CSSCustomization() { { id: 'Years', data: years, - scaleName: 'time', + scaleType: 'time', valueFormatter: (date) => date.getFullYear(), }, ]} diff --git a/docs/data/charts/lines/StackedAreas.js b/docs/data/charts/lines/StackedAreas.js index cef4fd3c8daf0..65f3b963fea8b 100644 --- a/docs/data/charts/lines/StackedAreas.js +++ b/docs/data/charts/lines/StackedAreas.js @@ -61,7 +61,7 @@ export default function StackedAreas() { { id: 'Years', data: years, - scaleName: 'time', + scaleType: 'time', valueFormatter: (date) => date.getFullYear(), }, ]} diff --git a/docs/data/charts/lines/StackedAreas.tsx b/docs/data/charts/lines/StackedAreas.tsx index cef4fd3c8daf0..65f3b963fea8b 100644 --- a/docs/data/charts/lines/StackedAreas.tsx +++ b/docs/data/charts/lines/StackedAreas.tsx @@ -61,7 +61,7 @@ export default function StackedAreas() { { id: 'Years', data: years, - scaleName: 'time', + scaleType: 'time', valueFormatter: (date) => date.getFullYear(), }, ]} diff --git a/docs/data/charts/lines/TestLines.js b/docs/data/charts/lines/TestLines.js index 324bb20c84e71..72415a2c46656 100644 --- a/docs/data/charts/lines/TestLines.js +++ b/docs/data/charts/lines/TestLines.js @@ -13,7 +13,7 @@ export default function TestLines() { yAxis={[ { id: 'lineCategoriesY', - scaleName: 'linear', + scaleType: 'linear', }, ]} series={[ diff --git a/docs/data/charts/lines/TestLines.tsx b/docs/data/charts/lines/TestLines.tsx index 324bb20c84e71..72415a2c46656 100644 --- a/docs/data/charts/lines/TestLines.tsx +++ b/docs/data/charts/lines/TestLines.tsx @@ -13,7 +13,7 @@ export default function TestLines() { yAxis={[ { id: 'lineCategoriesY', - scaleName: 'linear', + scaleType: 'linear', }, ]} series={[ diff --git a/docs/data/charts/lines/TestLinesTime.js b/docs/data/charts/lines/TestLinesTime.js index 75e9764a4044e..8df283a4334ab 100644 --- a/docs/data/charts/lines/TestLinesTime.js +++ b/docs/data/charts/lines/TestLinesTime.js @@ -14,13 +14,13 @@ export default function TestLinesTime() { new Date(2012, 2, 10), new Date(2012, 2, 15), ], - scaleName: 'time', + scaleType: 'time', }, ]} yAxis={[ { id: 'lineCategoriesY', - scaleName: 'linear', + scaleType: 'linear', }, ]} series={[ diff --git a/docs/data/charts/lines/TestLinesTime.tsx b/docs/data/charts/lines/TestLinesTime.tsx index 75e9764a4044e..8df283a4334ab 100644 --- a/docs/data/charts/lines/TestLinesTime.tsx +++ b/docs/data/charts/lines/TestLinesTime.tsx @@ -14,13 +14,13 @@ export default function TestLinesTime() { new Date(2012, 2, 10), new Date(2012, 2, 15), ], - scaleName: 'time', + scaleType: 'time', }, ]} yAxis={[ { id: 'lineCategoriesY', - scaleName: 'linear', + scaleType: 'linear', }, ]} series={[ diff --git a/docs/data/charts/overview/Combining.js b/docs/data/charts/overview/Combining.js index 65cfadcc5fd81..5eb9d1cdb9f32 100644 --- a/docs/data/charts/overview/Combining.js +++ b/docs/data/charts/overview/Combining.js @@ -44,17 +44,17 @@ export default function Combining() { { id: 'years', data: [2010, 2011, 2012, 2013, 2014], - scaleName: 'band', + scaleType: 'band', }, ]} yAxis={[ { id: 'eco', - scaleName: 'linear', + scaleType: 'linear', }, { id: 'pib', - scaleName: 'log', + scaleType: 'log', }, ]} > diff --git a/docs/data/charts/overview/Combining.tsx b/docs/data/charts/overview/Combining.tsx index 4de1a9dab00ef..d0e0225cf89f2 100644 --- a/docs/data/charts/overview/Combining.tsx +++ b/docs/data/charts/overview/Combining.tsx @@ -44,17 +44,17 @@ export default function Combining() { { id: 'years', data: [2010, 2011, 2012, 2013, 2014], - scaleName: 'band', + scaleType: 'band', }, ]} yAxis={[ { id: 'eco', - scaleName: 'linear', + scaleType: 'linear', }, { id: 'pib', - scaleName: 'log', + scaleType: 'log', }, ]} > diff --git a/docs/data/charts/overview/SimpleCharts.js b/docs/data/charts/overview/SimpleCharts.js index ae22000ce0d4f..6a87c341498f7 100644 --- a/docs/data/charts/overview/SimpleCharts.js +++ b/docs/data/charts/overview/SimpleCharts.js @@ -8,7 +8,7 @@ export default function SimpleCharts() { { id: 'barCategories', data: ['bar A', 'bar B', 'bar C'], - scaleName: 'band', + scaleType: 'band', }, ]} series={[ diff --git a/docs/data/charts/overview/SimpleCharts.tsx b/docs/data/charts/overview/SimpleCharts.tsx index ae22000ce0d4f..6a87c341498f7 100644 --- a/docs/data/charts/overview/SimpleCharts.tsx +++ b/docs/data/charts/overview/SimpleCharts.tsx @@ -8,7 +8,7 @@ export default function SimpleCharts() { { id: 'barCategories', data: ['bar A', 'bar B', 'bar C'], - scaleName: 'band', + scaleType: 'band', }, ]} series={[ diff --git a/docs/data/charts/scatter/Composition.js b/docs/data/charts/scatter/Composition.js index 782b42691847f..613259386e369 100644 --- a/docs/data/charts/scatter/Composition.js +++ b/docs/data/charts/scatter/Composition.js @@ -1,6 +1,7 @@ import * as React from 'react'; import { ScatterPlot } from '@mui/x-charts/ScatterChart'; import { ChartContainer } from '@mui/x-charts/ChartContainer'; +import { DEFAULT_X_AXIS_KEY } from '@mui/x-charts/constants'; import { XAxis } from '@mui/x-charts/XAxis'; import { YAxis } from '@mui/x-charts/YAxis'; @@ -43,15 +44,13 @@ export default function Composition() { { id: 'leftAxis', max: 4, - scaleName: 'sqrt', + scaleType: 'sqrt', }, ]} > - - + - ); } diff --git a/docs/data/charts/scatter/Composition.tsx b/docs/data/charts/scatter/Composition.tsx index 5602b72cadc98..fdbd334ad9b37 100644 --- a/docs/data/charts/scatter/Composition.tsx +++ b/docs/data/charts/scatter/Composition.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { ScatterPlot } from '@mui/x-charts/ScatterChart'; import { ChartContainer } from '@mui/x-charts/ChartContainer'; +import { DEFAULT_X_AXIS_KEY } from '@mui/x-charts/constants'; import { XAxis } from '@mui/x-charts/XAxis'; import { YAxis } from '@mui/x-charts/YAxis'; @@ -44,15 +45,13 @@ export default function Composition() { id: 'leftAxis', max: 4, - scaleName: 'sqrt', + scaleType: 'sqrt', }, ]} > - - + - ); } diff --git a/docs/data/charts/tooltip/tooltip.md b/docs/data/charts/tooltip/tooltip.md new file mode 100644 index 0000000000000..97b6a777853e0 --- /dev/null +++ b/docs/data/charts/tooltip/tooltip.md @@ -0,0 +1,14 @@ +--- +product: charts +title: Charts - Tooltip +--- + +# Charts - Tooltip + +

Tooltip provides extra data on charts item.

+ +## Interactions + +## Highlights + +## Rendered Tooltip diff --git a/docs/data/pages.ts b/docs/data/pages.ts index 7d33f83ebb461..1a54906533655 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -455,7 +455,15 @@ const pages: MuiPage[] = [ icon: ChartIcon, children: [ { pathname: '/x/react-charts', title: '🚧 Overview' }, - { pathname: '/x/react-charts/styling', title: 'Styling' }, + { + pathname: '', + title: 'Common components', + children: [ + { pathname: '/x/react-charts/axis', title: 'Axis' }, + { pathname: '/x/react-charts/tooltip', title: 'Tooltip' }, + { pathname: '/x/react-charts/styling', title: 'Styling' }, + ], + }, { pathname: '/x/react-charts/bars', title: '🚧 Bars' }, { pathname: '/x/react-charts/lines', title: '🚧 Lines' }, { pathname: '/x/react-charts/areas', title: '🚧 Areas' }, diff --git a/docs/pages/x/react-charts/axis.js b/docs/pages/x/react-charts/axis.js new file mode 100644 index 0000000000000..bb865f7f75da8 --- /dev/null +++ b/docs/pages/x/react-charts/axis.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/charts/axis/axis.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-charts/tooltip.js b/docs/pages/x/react-charts/tooltip.js new file mode 100644 index 0000000000000..5d8df6cec858a --- /dev/null +++ b/docs/pages/x/react-charts/tooltip.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/charts/tooltip/tooltip.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/src/modules/components/ChartsUsageDemo.js b/docs/src/modules/components/ChartsUsageDemo.js new file mode 100644 index 0000000000000..d50b7ba83aa8d --- /dev/null +++ b/docs/src/modules/components/ChartsUsageDemo.js @@ -0,0 +1,82 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/joy/Box'; +import BrandingProvider from 'docs/src/BrandingProvider'; +import HighlightedCode from 'docs/src/modules/components/HighlightedCode'; +import DemoPropsForm from './DemoPropsForm'; + +export default function ChartsUsageDemo({ + componentName, + childrenAccepted = false, + data, + renderDemo, + getCode, +}) { + const initialProps = {}; + let demoProps = {}; + let codeBlockProps = {}; + data.forEach((p) => { + demoProps[p.propName] = p.defaultValue; + if (p.codeBlockDisplay) { + initialProps[p.propName] = p.defaultValue; + } + if (!p.knob) { + codeBlockProps[p.propName] = p.defaultValue; + } + }); + const [props, setProps] = React.useState(initialProps); + demoProps = { ...demoProps, ...props }; + codeBlockProps = { ...props, ...codeBlockProps }; + data.forEach((p) => { + if (p.codeBlockDisplay === false) { + delete codeBlockProps[p.propName]; + } + }); + return ( + + + + {renderDemo(demoProps)} + + + + + + + + ); +} + +ChartsUsageDemo.propTypes = { + childrenAccepted: PropTypes.any, + componentName: PropTypes.any, + data: PropTypes.any, + getCode: PropTypes.any, + renderDemo: PropTypes.any, +}; diff --git a/docs/src/modules/components/DemoPropsForm.tsx b/docs/src/modules/components/DemoPropsForm.tsx new file mode 100644 index 0000000000000..955d54222befa --- /dev/null +++ b/docs/src/modules/components/DemoPropsForm.tsx @@ -0,0 +1,466 @@ +import * as React from 'react'; +import Check from '@mui/icons-material/Check'; + +import ReplayRoundedIcon from '@mui/icons-material/ReplayRounded'; +import Box from '@mui/material/Box'; + +import FormControl from '@mui/material/FormControl'; +import FormLabel, { formLabelClasses } from '@mui/material/FormLabel'; +import IconButton from '@mui/material/IconButton'; +import Input, { inputClasses } from '@mui/material/Input'; +import MenuItem from '@mui/material/MenuItem'; + +import FormControlLabel, { formControlLabelClasses } from '@mui/material/FormControlLabel'; +import Radio from '@mui/material/Radio'; +import RadioGroup from '@mui/material/RadioGroup'; +import Select from '@mui/material/Select'; +import Paper from '@mui/material/Paper'; +import Switch from '@mui/material/Switch'; +import Typography from '@mui/material/Typography'; + +const shallowEqual = (item1: { [k: string]: any }, item2: { [k: string]: any }) => { + let equal = true; + Object.entries(item1).forEach(([key, value]: [string, any]) => { + if (item2[key] !== value) { + equal = false; + } + }); + return equal; +}; + +type DataType = { + /** + * Name of the prop, e.g. 'children' + */ + propName: Extract; + /** + * The controller to be used: + * - `switch`: render the switch component for boolean + * - `color`: render the built-in color selector + * - `select`: render + * - `radio`: render group of radios + */ + knob?: 'switch' | 'color' | 'select' | 'input' | 'radio' | 'controlled' | 'number' | 'placement'; + /** + * The options for these knobs: `select` and `radio` + */ + options?: Array; + /** + * The labels for these knobs: `radio` + */ + labels?: Array; + /** + * The default value to be used by the components. + * If exists, it will be injected to the `renderDemo` callback but it will not show + * in the code block. + * + * To make it appears in the code block, specified `codeBlockDisplay: true` + */ + defaultValue?: string | number | boolean; + /** + * If not specify (`undefined`), the prop displays when user change the value + * If `true`, the prop with defaultValue will always display in the code block. + * If `false`, the prop does not display in the code block. + */ + codeBlockDisplay?: boolean; +}[]; + +interface ChartDemoPropsFormProps { + /** + * Name of the component to show in the code block. + */ + componentName: string; + /** + * Configuration + */ + data: DataType; + onPropsChange: (data: any) => void; +} + +function ControlledColorRadio(props: any) { + const { value, ...other } = props; + return ( + + } + checkedIcon={ + theme.palette.background.default, + }} + /> + } + sx={{ width: '100%', height: '100%', margin: 0 }} + /> + + ); +} + +export default function ChartDemoPropsForm({ + componentName, + data, + onPropsChange, +}: ChartDemoPropsFormProps) { + const initialProps = {} as { [k in keyof T]: any }; + let demoProps = {} as { [k in keyof T]: any }; + + data.forEach((p) => { + demoProps[p.propName] = p.defaultValue; + + initialProps[p.propName] = p.defaultValue; + }); + const [props, setProps] = React.useState(initialProps as T); + + React.useEffect(() => { + onPropsChange(props); + }, [props, onPropsChange]); + + demoProps = { ...demoProps, ...props }; + + return ( + + + + Playground + + setProps(initialProps as T)} + sx={{ + visibility: !shallowEqual(props, initialProps) ? 'visible' : 'hidden', + '--IconButton-size': '30px', + }} + > + + + + + {data.map(({ propName, knob, options = [], defaultValue, labels }) => { + const resolvedValue = props[propName] ?? defaultValue; + if (!knob) { + return null; + } + if (knob === 'switch') { + return ( + + {propName} + + setProps((latestProps) => ({ + ...latestProps, + [propName]: event.target.checked, + })) + } + /> + + ); + } + if (knob === 'radio') { + const labelId = `${componentName}-${propName}`; + return ( + + {propName} + { + let value: string | boolean | undefined = event.target.value; + if (value === 'true') { + value = true; + } else if (value === 'false') { + value = false; + } else if (value === 'undefined') { + value = undefined; + } + setProps((latestProps) => ({ + ...latestProps, + [propName]: value, + })); + }} + sx={{ flexWrap: 'wrap', gap: 1 }} + > + {options.map((value: string, index: number) => { + const checked = String(resolvedValue) === value; + return ( + } + // variant={checked ? 'solid' : 'outlined'} + color={checked ? 'primary' : 'info'} + label={{labels?.[index] || value}} + value={value} + /> + ); + })} + + + ); + } + if (knob === 'color') { + return ( + + {propName} + + setProps((latestProps) => ({ + ...latestProps, + [propName]: event.target.value, + })) + } + sx={{ flexWrap: 'wrap', gap: 1.5, display: 'flex', flexDirection: 'row' }} + > + {options.map((value) => { + return ( + } + label={value} + value={value} + labelPlacement="bottom" + sx={{ + [`& .${formControlLabelClasses.label}`]: { + fontSize: '10px', + color: 'text.secondary', + }, + }} + /> + ); + })} + + + ); + } + if (knob === 'select') { + return ( + + {propName} + + + ); + } + if (knob === 'input') { + return ( + + {propName} + + setProps((latestProps) => ({ + ...latestProps, + [propName]: event.target.value, + })) + } + sx={{ + textTransform: 'capitalize', + [`& .${inputClasses.root}`]: { + bgcolor: 'background.body', + }, + }} + /> + + ); + } + if (knob === 'number') { + return ( + + {propName} + + setProps((latestProps) => ({ + ...latestProps, + [propName]: Number.isNaN(event.target.value) + ? undefined + : Number.parseInt(event.target.value, 10), + })) + } + sx={{ + textTransform: 'capitalize', + [`& .${inputClasses.root}`]: { + bgcolor: 'background.body', + }, + }} + /> + + ); + } + if (knob === 'placement') { + return ( + + Placement + + setProps((latestProps) => ({ + ...latestProps, + [propName]: event.target.value, + })) + } + > + + + {resolvedValue} + + {/* void */} + + + + {/* void */} + {[ + 'top-start', + 'top', + 'top-end', + 'left-start', + 'right-start', + 'left', + 'right', + 'left-end', + 'right-end', + 'bottom-start', + 'bottom', + 'bottom-end', + ].map((placement) => ( + + ({ + // sx: (theme) => ({ + // ...(checked && { + // ...theme.variants.solid.primary, + // '&:hover': theme.variants.solid.primary, + // }), + // }), + // }), + // }} + /> + + ))} + + + + ); + } + return null; + })} + + + ); +} diff --git a/packages/x-charts/src/Axis/Axis.tsx b/packages/x-charts/src/Axis/Axis.tsx new file mode 100644 index 0000000000000..e9f81dec252ec --- /dev/null +++ b/packages/x-charts/src/Axis/Axis.tsx @@ -0,0 +1,99 @@ +import * as React from 'react'; + +import { CartesianContext } from '../context/CartesianContextProvider'; +import { XAxis, XAxisProps } from '../XAxis'; +import { YAxis, YAxisProps } from '../YAxis'; + +export interface AxisProps { + /** + * Indicate which axis to display the the top of the charts. + * Can be a string (the id of the axis) or an object `XAxisProps` + * @default null + */ + topAxis?: null | string | XAxisProps; + /** + * Indicate which axis to display the the bottom of the charts. + * Can be a string (the id of the axis) or an object `XAxisProps` + * @default xAxisIds[0] The id of the first provided axis + */ + bottomAxis?: null | string | XAxisProps; + /** + * Indicate which axis to display the the left of the charts. + * Can be a string (the id of the axis) or an object `YAxisProps` + * @default yAxisIds[0] The id of the first provided axis + */ + leftAxis?: null | string | YAxisProps; + /** + * Indicate which axis to display the the right of the charts. + * Can be a string (the id of the axis) or an object `YAxisProps` + * @default null + */ + rightAxis?: null | string | YAxisProps; +} + +const getAxisId = ( + propsValue: undefined | null | string | XAxisProps | YAxisProps, +): string | null => { + if (propsValue == null) { + return null; + } + if (typeof propsValue === 'object') { + return propsValue.axisId; + } + return propsValue; +}; + +export function Axis(props: AxisProps) { + const { topAxis, leftAxis, rightAxis, bottomAxis } = props; + const { xAxis, xAxisIds, yAxis, yAxisIds } = React.useContext(CartesianContext); + + // TODO: use for plotting line without ticks or any thing + // const drawingArea = React.useContext(DrawingContext); + + const leftId = getAxisId(leftAxis === undefined ? yAxisIds[0] : leftAxis); + const bottomId = getAxisId(bottomAxis === undefined ? xAxisIds[0] : bottomAxis); + const topId = getAxisId(topAxis); + const rightId = getAxisId(rightAxis); + + if (topId !== null && !xAxis[topId]) { + throw Error(`MUI: id used for top axis "${topId}" is not defined`); + } + if (leftId !== null && !yAxis[leftId]) { + throw Error(`MUI: id used for left axis "${leftId}" is not defined`); + } + if (rightId !== null && !yAxis[rightId]) { + throw Error(`MUI: id used for right axis "${rightId}" is not defined`); + } + if (bottomId !== null && !xAxis[bottomId]) { + throw Error(`MUI: id used for bottom axis "${bottomId}" is not defined`); + } + + return ( + + {topId && ( + + )} + {bottomId && ( + + )} + {leftId && ( + + )} + {rightId && ( + + )} + + ); +} diff --git a/packages/x-charts/src/Axis/index.tsx b/packages/x-charts/src/Axis/index.tsx new file mode 100644 index 0000000000000..e6f8a1c4788db --- /dev/null +++ b/packages/x-charts/src/Axis/index.tsx @@ -0,0 +1 @@ +export * from './Axis'; diff --git a/packages/x-charts/src/BarChart/BarChart.tsx b/packages/x-charts/src/BarChart/BarChart.tsx index baf940658747f..22d5e6e5bf5d6 100644 --- a/packages/x-charts/src/BarChart/BarChart.tsx +++ b/packages/x-charts/src/BarChart/BarChart.tsx @@ -1,12 +1,27 @@ import * as React from 'react'; import { BarPlot } from './BarPlot'; -import { XAxis } from '../XAxis/XAxis'; -import { YAxis } from '../YAxis/YAxis'; -import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; import { ChartContainer, ChartContainerProps } from '../ChartContainer'; +import { Axis, AxisProps } from '../Axis'; -export function BarChart(props: Omit) { - const { xAxis, yAxis, series, width, height, margin, colors, tooltip } = props; +export interface BarChartProps extends ChartContainerProps, AxisProps {} + +export function BarChart(props: BarChartProps) { + const { + xAxis, + yAxis, + series, + width, + height, + margin, + colors, + sx, + tooltip, + topAxis, + leftAxis, + rightAxis, + bottomAxis, + children, + } = props; return ( ) { xAxis={xAxis} yAxis={yAxis} colors={colors} + sx={sx} tooltip={tooltip} > - - - - + + {children} ); } diff --git a/packages/x-charts/src/LineChart/LineChart.tsx b/packages/x-charts/src/LineChart/LineChart.tsx index c303a0a4a1186..c5a90c7160122 100644 --- a/packages/x-charts/src/LineChart/LineChart.tsx +++ b/packages/x-charts/src/LineChart/LineChart.tsx @@ -1,12 +1,26 @@ import * as React from 'react'; import { LinePlot } from './LinePlot'; -import { XAxis } from '../XAxis/XAxis'; -import { YAxis } from '../YAxis/YAxis'; -import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; import { ChartContainer, ChartContainerProps } from '../ChartContainer'; +import { Axis, AxisProps } from '../Axis/Axis'; -export function LineChart(props: ChartContainerProps) { - const { xAxis, yAxis, series, width, height, margin, colors, sx, tooltip, children } = props; +export interface LineChartProps extends ChartContainerProps, AxisProps {} +export function LineChart(props: LineChartProps) { + const { + xAxis, + yAxis, + series, + width, + height, + margin, + colors, + sx, + tooltip, + topAxis, + leftAxis, + rightAxis, + bottomAxis, + children, + } = props; return ( - - - - + {children} ); diff --git a/packages/x-charts/src/LineChart/LinePlot.tsx b/packages/x-charts/src/LineChart/LinePlot.tsx index 2a97bc9cc84ce..65928d6e80950 100644 --- a/packages/x-charts/src/LineChart/LinePlot.tsx +++ b/packages/x-charts/src/LineChart/LinePlot.tsx @@ -7,6 +7,7 @@ import { LineElement } from './LineElement'; import { AreaElement } from './AreaElement'; import { useInteractionItemProps } from '../hooks/useInteractionItemProps'; import { MarkElement } from './MarkElement'; +import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; import { getValueToPositionMapper } from '../hooks/useScale'; import getCurveFactory from '../internals/getCurve'; @@ -40,122 +41,125 @@ export function LinePlot() { return ( - {Object.keys(seriesPerAxis).flatMap((key) => { - const [xAxisKey, yAxisKey] = key.split('-'); - - const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); - const yScale = yAxis[yAxisKey].scale; - const xData = xAxis[xAxisKey].data; - - if (xData === undefined) { - throw new Error( - `Axis of id "${xAxisKey}" should have data property to be able to display a line plot`, - ); - } - - const areaPath = d3Area<{ - x: any; - y: any[]; - }>() - .x((d) => xScale(d.x)) - .y0((d) => yScale(d.y[0])) - .y1((d) => yScale(d.y[1])); - return stackingGroups.flatMap((groupIds) => { - return groupIds.flatMap((seriesId) => { - const stackedData = series[seriesId].stackedData; - const curve = getCurveFactory(series[seriesId].curve); - const d3Data = xData?.map((x, index) => ({ x, y: stackedData[index] })); - - return ( - !!series[seriesId].area && ( - - ) + {stackingGroups.flatMap((groupIds) => { + return groupIds.flatMap((seriesId) => { + const { + xAxisKey = DEFAULT_X_AXIS_KEY, + yAxisKey = DEFAULT_Y_AXIS_KEY, + stackedData, + } = series[seriesId]; + + const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); + const yScale = yAxis[yAxisKey].scale; + const xData = xAxis[xAxisKey].data; + + if (xData === undefined) { + throw new Error( + `Axis of id "${xAxisKey}" should have data property to be able to display a line plot.`, ); - }); + } + + const areaPath = d3Area<{ + x: any; + y: any[]; + }>() + .x((d) => xScale(d.x)) + .y0((d) => yScale(d.y[0])) + .y1((d) => yScale(d.y[1])); + + const curve = getCurveFactory(series[seriesId].curve); + const d3Data = xData?.map((x, index) => ({ x, y: stackedData[index] })); + + return ( + !!series[seriesId].area && ( + + ) + ); }); })} - {Object.keys(seriesPerAxis).flatMap((key) => { - const [xAxisKey, yAxisKey] = key.split('-'); - - const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); - const yScale = yAxis[yAxisKey].scale; - const xData = xAxis[xAxisKey].data; - - if (xData === undefined) { - throw new Error( - `Axis of id "${xAxisKey}" should have data property to be able to display a line plot`, + {stackingGroups.flatMap((groupIds) => { + return groupIds.flatMap((seriesId) => { + const { + xAxisKey = DEFAULT_X_AXIS_KEY, + yAxisKey = DEFAULT_Y_AXIS_KEY, + stackedData, + } = series[seriesId]; + + const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); + const yScale = yAxis[yAxisKey].scale; + const xData = xAxis[xAxisKey].data; + + if (xData === undefined) { + throw new Error( + `Axis of id "${xAxisKey}" should have data property to be able to display a line plot`, + ); + } + + const linePath = d3Line<{ + x: any; + y: any[]; + }>() + .x((d) => xScale(d.x)) + .y((d) => yScale(d.y[1])); + + const d3Data = xData?.map((x, index) => ({ x, y: stackedData[index] })); + + return ( + ); - } - - const linePath = d3Line<{ - x: any; - y: any[]; - }>() - .x((d) => xScale(d.x)) - .y((d) => yScale(d.y[1])); - - return stackingGroups.flatMap((groupIds) => { - return groupIds.flatMap((seriesId) => { - const stackedData = series[seriesId].stackedData; - const curve = getCurveFactory(series[seriesId].curve); - const d3Data = xData?.map((x, index) => ({ x, y: stackedData[index] })); + }); + })} + + + {stackingGroups.flatMap((groupIds) => { + return groupIds.flatMap((seriesId) => { + const { + xAxisKey = DEFAULT_X_AXIS_KEY, + yAxisKey = DEFAULT_Y_AXIS_KEY, + stackedData, + } = series[seriesId]; + + const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); + const yScale = yAxis[yAxisKey].scale; + const xData = xAxis[xAxisKey].data; + + if (xData === undefined) { + throw new Error( + `Axis of id "${xAxisKey}" should have data property to be able to display a line plot`, + ); + } + return xData?.map((x, index) => { + const y = stackedData[index][1]; return ( - ); }); }); })} - - {Object.keys(seriesPerAxis).flatMap((key) => { - const [xAxisKey, yAxisKey] = key.split('-'); - - const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); - const yScale = yAxis[yAxisKey].scale; - const xData = xAxis[xAxisKey].data; - - if (xData === undefined) { - throw new Error( - `Axis of id "${xAxisKey}" should have data property to be able to display a line plot`, - ); - } - - return stackingGroups.flatMap((groupIds) => { - return groupIds.flatMap((seriesId) => { - const stackedData = series[seriesId].stackedData; - - return xData?.map((x, index) => { - const y = stackedData[index][1]; - return ( - - ); - }); - }); - }); - })} - ); } diff --git a/packages/x-charts/src/ResponsiveChartContainer/index.tsx b/packages/x-charts/src/ResponsiveChartContainer/index.tsx index 117b94fe8eb6f..1926622ecf7be 100644 --- a/packages/x-charts/src/ResponsiveChartContainer/index.tsx +++ b/packages/x-charts/src/ResponsiveChartContainer/index.tsx @@ -1,17 +1,12 @@ import * as React from 'react'; import { ResizeObserver } from '@juggle/resize-observer'; import { DrawingProvider } from '../context/DrawingProvider'; -import { - SeriesContextProvider, - SeriesContextProviderProps, -} from '../context/SeriesContextProvider'; -import { LayoutConfig } from '../models/layout'; +import { SeriesContextProvider } from '../context/SeriesContextProvider'; import { Surface } from '../Surface'; -import { - CartesianContextProvider, - CartesianContextProviderProps, -} from '../context/CartesianContextProvider'; +import { CartesianContextProvider } from '../context/CartesianContextProvider'; import { InteractionProvider } from '../context/InteractionProvider'; +import { ChartContainerProps } from '../ChartContainer'; +import { Tooltip } from '../Tooltip'; const useChartDimensions = (): [React.MutableRefObject, number, number] => { const ref = React.useRef(null); @@ -38,12 +33,10 @@ const useChartDimensions = (): [React.MutableRefObject, number, return [ref, width, height]; }; -type ChartContainerProps = LayoutConfig & - SeriesContextProviderProps & - CartesianContextProviderProps; +export type ResponsiveChartContainerProps = Omit; -export function ResponsiveChartContainer(props: ChartContainerProps) { - const { series, margin, xAxis, colors, yAxis, children } = props; +export function ResponsiveChartContainer(props: ResponsiveChartContainerProps) { + const { series, margin, xAxis, yAxis, colors, sx, title, desc, tooltip, children } = props; const ref = React.useRef(null); const [containerRef, width, height] = useChartDimensions(); @@ -54,8 +47,9 @@ export function ResponsiveChartContainer(props: ChartContainerProps) { - + {children} + diff --git a/packages/x-charts/src/ScatterChart/ScatterChart.tsx b/packages/x-charts/src/ScatterChart/ScatterChart.tsx index 5e37d5d81fecf..5725d5843001f 100644 --- a/packages/x-charts/src/ScatterChart/ScatterChart.tsx +++ b/packages/x-charts/src/ScatterChart/ScatterChart.tsx @@ -1,12 +1,25 @@ import * as React from 'react'; import { ScatterPlot } from './ScatterPlot'; -import { XAxis } from '../XAxis/XAxis'; -import { YAxis } from '../YAxis/YAxis'; -import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; import { ChartContainer, ChartContainerProps } from '../ChartContainer'; +import { Axis, AxisProps } from '../Axis'; -export function ScatterChart(props: Omit) { - const { xAxis, yAxis, series, width, height, margin, colors } = props; +export interface ScatterChartProps extends ChartContainerProps, AxisProps {} +export function ScatterChart(props: ScatterChartProps) { + const { + xAxis, + yAxis, + series, + width, + height, + margin, + colors, + sx, + topAxis, + leftAxis, + rightAxis, + bottomAxis, + children, + } = props; return ( ) { colors={colors} xAxis={xAxis} yAxis={yAxis} + sx={sx} tooltip={{ trigger: 'item' }} > - - - - + + {children} ); } diff --git a/packages/x-charts/src/XAxis/XAxis.tsx b/packages/x-charts/src/XAxis/XAxis.tsx index e766ddfbe24ac..cef03be632f01 100644 --- a/packages/x-charts/src/XAxis/XAxis.tsx +++ b/packages/x-charts/src/XAxis/XAxis.tsx @@ -12,7 +12,7 @@ export interface XAxisProps { /** * Id of the axis to render. */ - axisId?: string; + axisId: string; /** * If true, the axis line is disabled. * @default false @@ -102,7 +102,7 @@ export function XAxis(props: XAxisProps) { textAnchor="middle" fontSize={fontSize} > - {value} + {value.toLocaleString()} ))} diff --git a/packages/x-charts/src/YAxis/YAxis.tsx b/packages/x-charts/src/YAxis/YAxis.tsx index 33f1e2aa7cf92..8a4d11cf012d0 100644 --- a/packages/x-charts/src/YAxis/YAxis.tsx +++ b/packages/x-charts/src/YAxis/YAxis.tsx @@ -12,7 +12,7 @@ export interface YAxisProps { /** * Id of the axis to render. */ - axisId?: string; + axisId: string; /** * If true, the axis line is disabled. * @default false diff --git a/packages/x-charts/src/context/CartesianContextProvider.tsx b/packages/x-charts/src/context/CartesianContextProvider.tsx index f30bc20ba6a9b..356771f9f43c3 100644 --- a/packages/x-charts/src/context/CartesianContextProvider.tsx +++ b/packages/x-charts/src/context/CartesianContextProvider.tsx @@ -111,20 +111,20 @@ export function CartesianContextProvider({ const allXAxis: AxisConfig[] = [ ...(xAxis ?? []), - { id: DEFAULT_X_AXIS_KEY, scaleName: 'linear' }, + { id: DEFAULT_X_AXIS_KEY, scaleType: 'linear' }, ]; const completedXAxis: DefaultizedAxisConfig = {}; allXAxis.forEach((axis) => { const [minData, maxData] = getAxisExtremum(axis, xExtremumGetters); - const scaleName = axis.scaleName ?? 'linear'; + const scaleType = axis.scaleType ?? 'linear'; completedXAxis[axis.id] = { ...axis, - scaleName, - scale: getScale(scaleName) + scaleType, + scale: getScale(scaleType) // @ts-ignore - .domain(scaleName === 'band' ? axis.data : [axis.min ?? minData, axis.max ?? maxData]) + .domain(scaleType === 'band' ? axis.data : [axis.min ?? minData, axis.max ?? maxData]) // @ts-ignore .range([drawingArea.left, drawingArea.left + drawingArea.width]), }; @@ -132,20 +132,20 @@ export function CartesianContextProvider({ const allYAxis: AxisConfig[] = [ ...(yAxis ?? []), - { id: DEFAULT_Y_AXIS_KEY, scaleName: 'linear' }, + { id: DEFAULT_Y_AXIS_KEY, scaleType: 'linear' }, ]; const completedYAxis: DefaultizedAxisConfig = {}; allYAxis.forEach((axis) => { const [minData, maxData] = getAxisExtremum(axis, yExtremumGetters); - const scaleName: ScaleName = axis.scaleName ?? 'linear'; + const scaleType: ScaleName = axis.scaleType ?? 'linear'; completedYAxis[axis.id] = { ...axis, - scaleName, - scale: getScale(scaleName) + scaleType, + scale: getScale(scaleType) // @ts-ignore - .domain(scaleName === 'band' ? axis.data : [axis.min ?? minData, axis.max ?? maxData]) + .domain(scaleType === 'band' ? axis.data : [axis.min ?? minData, axis.max ?? maxData]) // @ts-ignore .range([drawingArea.top + drawingArea.height, drawingArea.top]), }; diff --git a/packages/x-charts/src/hooks/useScale.ts b/packages/x-charts/src/hooks/useScale.ts index bddd2ac03b321..394bed1b7ca9f 100644 --- a/packages/x-charts/src/hooks/useScale.ts +++ b/packages/x-charts/src/hooks/useScale.ts @@ -26,8 +26,8 @@ export type D3Scale = | ScaleTime | ScaleLinear; -export function getScale(scaleName: ScaleName | undefined): D3Scale { - switch (scaleName) { +export function getScale(scaleType: ScaleName | undefined): D3Scale { + switch (scaleType) { case 'band': return scaleBand(); case 'log': diff --git a/packages/x-charts/src/index.ts b/packages/x-charts/src/index.ts index e69de29bb2d1d..822f630854fbc 100644 --- a/packages/x-charts/src/index.ts +++ b/packages/x-charts/src/index.ts @@ -0,0 +1,10 @@ +export * from './constants'; +export * from './Axis'; +export * from './XAxis'; +export * from './YAxis'; +export * from './Tooltip'; +export * from './BarChart'; +export * from './LineChart'; +export * from './ScatterChart'; +export * from './ChartContainer'; +export * from './ResponsiveChartContainer'; diff --git a/packages/x-charts/src/models/axis.ts b/packages/x-charts/src/models/axis.ts index 61fd13e1f3dff..9a08f435614ac 100644 --- a/packages/x-charts/src/models/axis.ts +++ b/packages/x-charts/src/models/axis.ts @@ -11,33 +11,33 @@ export type ScaleName = 'linear' | 'band' | 'log' | 'point' | 'pow' | 'sqrt' | ' export type AxisScaleMapping = | { - scaleName: 'band'; + scaleType: 'band'; scale: ScaleBand; } | { - scaleName: 'log'; + scaleType: 'log'; scale: ScaleLogarithmic; } | { - scaleName: 'point'; + scaleType: 'point'; scale: ScalePoint; } | { - scaleName: 'pow' | 'sqrt'; + scaleType: 'pow' | 'sqrt'; scale: ScalePower; } | { - scaleName: 'time' | 'utc'; + scaleType: 'time' | 'utc'; scale: ScaleTime; } | { - scaleName: 'linear'; + scaleType: 'linear'; scale: ScaleLinear; }; export type AxisConfig = { id: string; - scaleName?: ScaleName; + scaleType?: ScaleName; min?: number; max?: number; data?: V[]; From 139e1cca6afab1c298ce7c00363321e1da5a4d85 Mon Sep 17 00:00:00 2001 From: ithrforu <81531000+ithrforu@users.noreply.github.com> Date: Mon, 1 May 2023 14:33:11 +0500 Subject: [PATCH 36/80] [DataGrid] Fix falsy filter values not showing in filter button tooltip (#8550) Co-authored-by: v.kolesnikov Co-authored-by: Bilal Shafi --- .../toolbar/GridToolbarFilterButton.tsx | 5 +- .../src/tests/filtering.DataGrid.test.tsx | 53 ++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/packages/grid/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx b/packages/grid/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx index ee35194e02609..36f92bcd8dfc1 100644 --- a/packages/grid/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx +++ b/packages/grid/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx @@ -93,7 +93,10 @@ const GridToolbarFilterButton = React.forwardRef {`${lookup[item.field!].headerName || item.field} ${getOperatorLabel(item)} - ${item.value ? getFilterItemValue(item) : ''}`} + ${ + // implicit check for null and undefined + item.value != null ? getFilterItemValue(item) : '' + }`} )), }))} diff --git a/packages/grid/x-data-grid/src/tests/filtering.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/filtering.DataGrid.test.tsx index 8a96937738ccb..eabad01e11f18 100644 --- a/packages/grid/x-data-grid/src/tests/filtering.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/filtering.DataGrid.test.tsx @@ -1105,8 +1105,59 @@ describe(' - Filter', () => { }); }); + describe('filter button tooltip', () => { + it('should display `falsy` value', () => { + const { setProps } = render( + , + ); + + const filterButton = document.querySelector('button[aria-label="Show filters"]')!; + expect(screen.queryByRole('tooltip')).to.equal(null); + + fireEvent.mouseOver(filterButton); + clock.tick(1000); // tooltip display delay + + const tooltip = screen.getByRole('tooltip'); + + expect(tooltip).toBeVisible(); + expect(tooltip.textContent).to.contain('false'); + + setProps({ filterModel: { items: [{ id: 0, field: 'level', operator: '=', value: 0 }] } }); + expect(tooltip.textContent).to.contain('0'); + }); + }); + describe('custom `filterOperators`', () => { - it('should allow to cutomize filter tooltip using `filterOperator.getValueAsString`', () => { + it('should allow to customize filter tooltip using `filterOperator.getValueAsString`', () => { render(
Date: Mon, 1 May 2023 22:04:27 +0530 Subject: [PATCH 37/80] [docs] Update demo to support agregation on popular feature cell (#8617) Co-authored-by: alexandre Co-authored-by: Jose Rodolfo Freitas --- .../data/data-grid/demo/PopularFeaturesDemo.js | 18 ++++++++++++++++++ .../data-grid/demo/PopularFeaturesDemo.tsx | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/docs/data/data-grid/demo/PopularFeaturesDemo.js b/docs/data/data-grid/demo/PopularFeaturesDemo.js index b06e3b809d28f..4f7e27c284af9 100644 --- a/docs/data/data-grid/demo/PopularFeaturesDemo.js +++ b/docs/data/data-grid/demo/PopularFeaturesDemo.js @@ -246,6 +246,18 @@ RowDemo.propTypes = { row: PropTypes.object.isRequired, }; +function CustomSizeAggregationFooter(props) { + return ( + + Total: {props.value} + + ); +} + +CustomSizeAggregationFooter.propTypes = { + value: PropTypes.string, +}; + const columns = [ { field: 'name', @@ -255,6 +267,9 @@ const columns = [ minWidth: 100, groupable: false, renderCell: (params) => { + if (params.aggregation) { + return ; + } if (!params.value) { return ''; } @@ -315,6 +330,9 @@ const columns = [ type: 'singleSelect', valueOptions: ['Premium', 'Pro', 'Community'], renderCell: (params) => { + if (params.aggregation) { + return ; + } if (!params.value) { return ''; } diff --git a/docs/data/data-grid/demo/PopularFeaturesDemo.tsx b/docs/data/data-grid/demo/PopularFeaturesDemo.tsx index 68570e874a929..cef3d05d44968 100644 --- a/docs/data/data-grid/demo/PopularFeaturesDemo.tsx +++ b/docs/data/data-grid/demo/PopularFeaturesDemo.tsx @@ -251,6 +251,14 @@ function RowDemo(props: { row: Row }) { ); } +function CustomSizeAggregationFooter(props: { value: string | undefined }) { + return ( + + Total: {props.value} + + ); +} + const columns: GridColDef[] = [ { field: 'name', @@ -260,6 +268,9 @@ const columns: GridColDef[] = [ minWidth: 100, groupable: false, renderCell: (params) => { + if (params.aggregation) { + return ; + } if (!params.value) { return ''; } @@ -320,6 +331,9 @@ const columns: GridColDef[] = [ type: 'singleSelect', valueOptions: ['Premium', 'Pro', 'Community'], renderCell: (params: GridRenderCellParams) => { + if (params.aggregation) { + return ; + } if (!params.value) { return ''; } From 1099111eb455dc8ab744388517fc2fe4a12bab2b Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Tue, 2 May 2023 13:14:07 +0200 Subject: [PATCH 38/80] [core] Do not include playground pages in `yarn typescript` script (#8822) --- .gitignore | 3 ++- docs/pages/playground/tsconfig.json | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 docs/pages/playground/tsconfig.json diff --git a/.gitignore b/.gitignore index f041afe1dc79f..a0f281bd18190 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,8 @@ __diff_output__ /.nyc_output /coverage /docs/.next -/docs/pages/playground/ +/docs/pages/playground/* +!/docs/pages/playground/tsconfig.json /docs/export /test/regressions/screenshots build diff --git a/docs/pages/playground/tsconfig.json b/docs/pages/playground/tsconfig.json new file mode 100644 index 0000000000000..a1dce267bbdef --- /dev/null +++ b/docs/pages/playground/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*"], + "exclude": [] +} From 8bd635067b9b1284109209fbc8ff5cc00a7f22d6 Mon Sep 17 00:00:00 2001 From: yared tsegaye <34584266+yaredtsy@users.noreply.github.com> Date: Tue, 2 May 2023 17:44:01 +0300 Subject: [PATCH 39/80] [docs] Fix controlled mode demo on Editing page (#8800) Co-authored-by: Yared Tsegaye --- docs/data/data-grid/editing/StartEditButtonGrid.js | 5 +++++ docs/data/data-grid/editing/StartEditButtonGrid.tsx | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/data/data-grid/editing/StartEditButtonGrid.js b/docs/data/data-grid/editing/StartEditButtonGrid.js index 35eb09f832280..65dca1af9af0b 100644 --- a/docs/data/data-grid/editing/StartEditButtonGrid.js +++ b/docs/data/data-grid/editing/StartEditButtonGrid.js @@ -117,6 +117,10 @@ export default function StartEditButtonGrid() { [cellMode], ); + const handleCellEditStop = React.useCallback((params, event) => { + event.defaultMuiPrevented = true; + }, []); + return (
setCellModesModel(model)} slots={{ toolbar: EditToolbar, diff --git a/docs/data/data-grid/editing/StartEditButtonGrid.tsx b/docs/data/data-grid/editing/StartEditButtonGrid.tsx index 777a4500c8084..90248a37e6aca 100644 --- a/docs/data/data-grid/editing/StartEditButtonGrid.tsx +++ b/docs/data/data-grid/editing/StartEditButtonGrid.tsx @@ -130,6 +130,13 @@ export default function StartEditButtonGrid() { [cellMode], ); + const handleCellEditStop = React.useCallback>( + (params, event) => { + event.defaultMuiPrevented = true; + }, + [], + ); + return (
setCellModesModel(model)} slots={{ toolbar: EditToolbar, From 4d4fcb42d9c04050f61750607e32d295dd1bf0b7 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Tue, 2 May 2023 17:54:22 +0200 Subject: [PATCH 40/80] [docs] Fix scrolling demo to work with React 18 (#6489) --- .../src/tests/cellSelection.DataGridPremium.test.tsx | 2 +- .../src/tests/cellEditing.DataGridPro.test.tsx | 6 +++--- .../src/tests/editComponents.DataGridPro.test.tsx | 2 +- .../src/tests/rowEditing.DataGridPro.test.tsx | 6 +++--- .../x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx | 4 ++-- .../x-data-grid/src/hooks/features/focus/useGridFocus.ts | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/grid/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx index 6b2331f75378f..985d03838ab97 100644 --- a/packages/grid/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx @@ -91,7 +91,7 @@ describe(' - Cell Selection', () => { cell.focus(); userEvent.mousePress(cell); fireEvent.keyDown(cell, { key: 'Shift' }); - fireEvent.click(getCell(2, 1), { shiftKey: true }); + userEvent.mousePress(getCell(2, 1), { shiftKey: true }); expect(document.querySelectorAll('.Mui-selected')).to.have.length(3 * 2); // 3 rows with 2 cells each }); diff --git a/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx index cb823b2c6b272..9d3a9157412b0 100644 --- a/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx @@ -963,7 +963,7 @@ describe(' - Cell Editing', () => { apiRef.current.subscribeEvent('cellEditStop', listener); fireEvent.doubleClick(getCell(0, 1)); expect(listener.callCount).to.equal(0); - fireEvent.click(getCell(1, 1)); + userEvent.mousePress(getCell(1, 1)); expect(listener.lastCall.args[0].reason).to.equal('cellFocusOut'); }); @@ -971,7 +971,7 @@ describe(' - Cell Editing', () => { render(); const spiedStopCellEditMode = spy(apiRef.current, 'stopCellEditMode'); fireEvent.doubleClick(getCell(0, 1)); - fireEvent.click(getCell(1, 1)); + userEvent.mousePress(getCell(1, 1)); expect(spiedStopCellEditMode.callCount).to.equal(1); expect(spiedStopCellEditMode.lastCall.args[0]).to.deep.equal({ id: 0, @@ -993,7 +993,7 @@ describe(' - Cell Editing', () => { resolve(); }), ); - fireEvent.click(getCell(1, 1)); + userEvent.mousePress(getCell(1, 1)); expect(spiedStopCellEditMode.callCount).to.equal(1); expect(spiedStopCellEditMode.lastCall.args[0].ignoreModifications).to.equal(false); }); diff --git a/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx index 40b4d307795da..1eab1604e4a58 100644 --- a/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx @@ -583,7 +583,7 @@ describe(' - Edit Components', () => { const cell = getCell(0, 0); fireEvent.doubleClick(cell); fireEvent.click(screen.queryAllByRole('option')[1]); - await Promise.resolve(); + await act(() => Promise.resolve()); expect(onValueChange.callCount).to.equal(1); expect(onValueChange.lastCall.args[1]).to.equal('Adidas'); diff --git a/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx index ed1eef5a3f05e..16abc7fa74224 100644 --- a/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx @@ -959,7 +959,7 @@ describe(' - Row Editing', () => { apiRef.current.subscribeEvent('rowEditStop', listener); fireEvent.doubleClick(getCell(0, 1)); expect(listener.callCount).to.equal(0); - fireEvent.click(getCell(1, 1)); + userEvent.mousePress(getCell(1, 1)); clock.runToLast(); expect(listener.lastCall.args[0].reason).to.equal('rowFocusOut'); }); @@ -968,7 +968,7 @@ describe(' - Row Editing', () => { render(); const spiedStopRowEditMode = spy(apiRef.current, 'stopRowEditMode'); fireEvent.doubleClick(getCell(0, 1)); - fireEvent.click(getCell(1, 1)); + userEvent.mousePress(getCell(1, 1)); clock.runToLast(); expect(spiedStopRowEditMode.callCount).to.equal(1); expect(spiedStopRowEditMode.lastCall.args[0]).to.deep.equal({ @@ -987,7 +987,7 @@ describe(' - Row Editing', () => { act(() => { apiRef.current.setEditCellValue({ id: 0, field: 'currencyPair', value: 'USD GBP' }); }); - fireEvent.click(getCell(1, 1)); + userEvent.mousePress(getCell(1, 1)); clock.runToLast(); expect(spiedStopRowEditMode.callCount).to.equal(1); expect(spiedStopRowEditMode.lastCall.args[0].ignoreModifications).to.equal(false); diff --git a/packages/grid/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx index 7e95cd22b690f..b3cf08ab69b37 100644 --- a/packages/grid/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx @@ -818,7 +818,7 @@ describe(' - Rows', () => { id: baselineProps.rows[1].id, field: baselineProps.columns[0].field, }); - fireEvent.click(document.body); + userEvent.mousePress(document.body); expect(gridFocusCellSelector(apiRef)).to.deep.equal(null); }); @@ -828,7 +828,7 @@ describe(' - Rows', () => { apiRef.current.subscribeEvent('cellFocusOut', handleCellFocusOut); userEvent.mousePress(getCell(1, 0)); expect(handleCellFocusOut.callCount).to.equal(0); - fireEvent.click(document.body); + userEvent.mousePress(document.body); expect(handleCellFocusOut.callCount).to.equal(1); expect(handleCellFocusOut.args[0][0].id).to.equal(baselineProps.rows[1].id); expect(handleCellFocusOut.args[0][0].field).to.equal(baselineProps.columns[0].field); diff --git a/packages/grid/x-data-grid/src/hooks/features/focus/useGridFocus.ts b/packages/grid/x-data-grid/src/hooks/features/focus/useGridFocus.ts index d78194e5d0578..c0d92c62991aa 100644 --- a/packages/grid/x-data-grid/src/hooks/features/focus/useGridFocus.ts +++ b/packages/grid/x-data-grid/src/hooks/features/focus/useGridFocus.ts @@ -353,10 +353,10 @@ export const useGridFocus = ( React.useEffect(() => { const doc = ownerDocument(apiRef.current.rootElementRef!.current); - doc.addEventListener('click', handleDocumentClick); + doc.addEventListener('mouseup', handleDocumentClick); return () => { - doc.removeEventListener('click', handleDocumentClick); + doc.removeEventListener('mouseup', handleDocumentClick); }; }, [apiRef, handleDocumentClick]); From 95d6fc5b81f1e48a932526e7aa1da9429cde0f99 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Tue, 2 May 2023 18:50:37 +0200 Subject: [PATCH 41/80] [DataGrid] Add Joy UI slots (Select, SelectOption, InputLabel, FormControl) (#8747) Co-authored-by: siriwatknp --- docs/data/data-grid/joy-ui/GridJoyUISlots.js | 18 ++- docs/data/data-grid/joy-ui/GridJoyUISlots.tsx | 18 ++- .../grid/x-data-grid/src/joy/joySlots.tsx | 146 +++++++++++++++--- 3 files changed, 151 insertions(+), 31 deletions(-) diff --git a/docs/data/data-grid/joy-ui/GridJoyUISlots.js b/docs/data/data-grid/joy-ui/GridJoyUISlots.js index b811e494ba6b1..8f11ba1a3c779 100644 --- a/docs/data/data-grid/joy-ui/GridJoyUISlots.js +++ b/docs/data/data-grid/joy-ui/GridJoyUISlots.js @@ -30,16 +30,18 @@ const materialTheme = materialExtendTheme({ }); const columns = [ - { field: 'name', editable: true, type: 'string' }, - { field: 'number', editable: true, type: 'number' }, - { field: 'date', editable: true, type: 'date' }, - { field: 'dateTime', editable: true, type: 'dateTime' }, - { field: 'boolean', editable: true, type: 'boolean' }, + { field: 'name', editable: true, type: 'string', minWidth: 140 }, + { field: 'number', editable: true, type: 'number', flex: 1 }, + { field: 'date', editable: true, type: 'date', minWidth: 100 }, + { field: 'dateTime', editable: true, type: 'dateTime', minWidth: 160 }, + { field: 'boolean', editable: true, type: 'boolean', flex: 1 }, { field: 'singleSelect', editable: true, type: 'singleSelect', valueOptions: ['Item1', 'Item2', 'Item3'], + minWidth: 100, + flex: 1, }, { field: 'actions', @@ -53,6 +55,8 @@ const columns = [ {}} label="Print" showInMenu />, , ], + minWidth: 80, + flex: 1, }, ]; @@ -89,6 +93,10 @@ export default function GridJoyUISlots() { '& .MuiDataGrid-filterForm': { alignItems: 'flex-end', }, + '& .MuiDataGrid-panelContent': { + // To prevent the Select popup being hidden by the panel + overflow: 'visible', + }, }, }, }} diff --git a/docs/data/data-grid/joy-ui/GridJoyUISlots.tsx b/docs/data/data-grid/joy-ui/GridJoyUISlots.tsx index ce4fb84abbbcd..3fd13ee64bdc0 100644 --- a/docs/data/data-grid/joy-ui/GridJoyUISlots.tsx +++ b/docs/data/data-grid/joy-ui/GridJoyUISlots.tsx @@ -32,16 +32,18 @@ const materialTheme = materialExtendTheme({ }); const columns: GridColDef[] = [ - { field: 'name', editable: true, type: 'string' }, - { field: 'number', editable: true, type: 'number' }, - { field: 'date', editable: true, type: 'date' }, - { field: 'dateTime', editable: true, type: 'dateTime' }, - { field: 'boolean', editable: true, type: 'boolean' }, + { field: 'name', editable: true, type: 'string', minWidth: 140 }, + { field: 'number', editable: true, type: 'number', flex: 1 }, + { field: 'date', editable: true, type: 'date', minWidth: 100 }, + { field: 'dateTime', editable: true, type: 'dateTime', minWidth: 160 }, + { field: 'boolean', editable: true, type: 'boolean', flex: 1 }, { field: 'singleSelect', editable: true, type: 'singleSelect', valueOptions: ['Item1', 'Item2', 'Item3'], + minWidth: 100, + flex: 1, }, { field: 'actions', @@ -55,6 +57,8 @@ const columns: GridColDef[] = [ {}} label="Print" showInMenu />, , ], + minWidth: 80, + flex: 1, }, ]; const rows = [ @@ -90,6 +94,10 @@ export default function GridJoyUISlots() { '& .MuiDataGrid-filterForm': { alignItems: 'flex-end', }, + '& .MuiDataGrid-panelContent': { + // To prevent the Select popup being hidden by the panel + overflow: 'visible', + }, }, }, }} diff --git a/packages/grid/x-data-grid/src/joy/joySlots.tsx b/packages/grid/x-data-grid/src/joy/joySlots.tsx index fd59d374db699..34d5e8e9cee6b 100644 --- a/packages/grid/x-data-grid/src/joy/joySlots.tsx +++ b/packages/grid/x-data-grid/src/joy/joySlots.tsx @@ -8,6 +8,9 @@ import JoyFormLabel from '@mui/joy/FormLabel'; import JoyButton from '@mui/joy/Button'; import JoyIconButton from '@mui/joy/IconButton'; import JoySwitch, { SwitchProps as JoySwitchProps } from '@mui/joy/Switch'; +import JoySelect, { SelectProps as JoySelectProps } from '@mui/joy/Select'; +import JoyOption, { OptionProps as JoyOptionProps } from '@mui/joy/Option'; +import { unstable_useForkRef as useForkRef } from '@mui/utils'; import type { UncapitalizeObjectKeys } from '../internals/utils'; import type { GridSlotsComponent, GridSlotsComponentsProps } from '../models'; @@ -44,18 +47,19 @@ function convertSize(size: T | undefined function convertVariant( variant: T | undefined, + defaultVariant: VariantProp = 'plain', ) { - return ( - variant - ? { - outlined: 'outlined', - contained: 'solid', - text: 'plain', - standard: 'plain', - filled: 'soft', - }[variant] - : variant - ) as VariantProp; + if (!variant) { + return defaultVariant; + } + + return ({ + standard: 'outlined', + outlined: 'outlined', + contained: 'solid', + text: 'plain', + filled: 'soft', + }[variant] || defaultVariant) as VariantProp; } const Checkbox = React.forwardRef< @@ -81,17 +85,24 @@ const Checkbox = React.forwardRef< const TextField = React.forwardRef< HTMLDivElement, NonNullable ->(({ onChange, label, placeholder, value, inputRef, type }, ref) => { +>(({ onChange, label, placeholder, value, inputRef, type, size, variant, ...props }, ref) => { + const rootRef = useForkRef(ref, props.InputProps?.ref); + const inputForkRef = useForkRef(inputRef, props?.inputProps?.ref); + const { startAdornment, endAdornment } = props.InputProps || {}; + return ( - - {label} + + {label} ); @@ -106,7 +117,7 @@ const Button = React.forwardRef< {...props} size={convertSize(size)} color={convertColor(color)} - variant={convertVariant(variant) ?? 'plain'} + variant={convertVariant(variant)} ref={ref} startDecorator={startIcon} endDecorator={endIcon} @@ -118,7 +129,7 @@ const Button = React.forwardRef< const IconButton = React.forwardRef< HTMLButtonElement, NonNullable ->(function IconButton({ color, size, sx, ...props }, ref) { +>(function IconButton({ color, size, sx, touchRippleRef, ...props }, ref) { return ( +>( + ( + { + open, + onOpen, + value, + onChange, + size, + color, + variant, + inputProps, + MenuProps, + inputRef, + error, + native, + fullWidth, + labelId, + ...props + }, + ref, + ) => { + const handleChange: JoySelectProps['onChange'] = (event, newValue) => { + if (event && onChange) { + // Same as in https://github.com/mui/material-ui/blob/e5558282a8f36856aef1299f3a36f3235e92e770/packages/mui-material/src/Select/SelectInput.js#L288-L300 + + // Redefine target to allow name and value to be read. + // This allows seamless integration with the most popular form libraries. + // https://github.com/mui/material-ui/issues/13485#issuecomment-676048492 + // Clone the event to not override `target` of the original event. + const nativeEvent = (event as React.SyntheticEvent).nativeEvent || event; + // @ts-ignore The nativeEvent is function, not object + const clonedEvent = new nativeEvent.constructor(nativeEvent.type, nativeEvent); + + Object.defineProperty(clonedEvent, 'target', { + writable: true, + value: { value: newValue, name: props.name }, + }); + onChange(clonedEvent, null); + } + }; + + return ( + )} + listboxOpen={open} + onListboxOpenChange={(isOpen) => { + if (isOpen) { + onOpen?.({} as React.SyntheticEvent); + } else { + MenuProps?.onClose?.({} as React.KeyboardEvent, undefined as any); + } + }} + size={convertSize(size)} + color={convertColor(color)} + variant={convertVariant(variant, 'outlined')} + ref={ref} + value={value} + onChange={handleChange} + slotProps={{ + button: { + 'aria-labelledby': labelId, + ref: inputRef, + }, + listbox: { + disablePortal: false, + sx: { + zIndex: 1350, + }, + }, + }} + /> + ); + }, +); + +const Option = React.forwardRef< + HTMLLIElement, + NonNullable +>(({ native, ...props }, ref) => { + return ; +}); + +const InputLabel = React.forwardRef< + HTMLLabelElement, + NonNullable +>(({ shrink, variant, sx, ...props }, ref) => { + return } />; +}); + const joySlots: UncapitalizeObjectKeys> = { baseCheckbox: Checkbox, baseTextField: TextField, baseButton: Button, baseIconButton: IconButton, baseSwitch: Switch, - // BaseFormControl: MUIFormControl, - // baseSelect: Select, - // baseSelectOption: Option, + baseSelect: Select, + baseSelectOption: Option, + baseInputLabel: InputLabel, + baseFormControl: JoyFormControl, // BaseTooltip: MUITooltip, // BasePopper: MUIPopper, }; From b497747f386e8ae33afa58386da67996373bc7ac Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Tue, 2 May 2023 19:22:12 +0200 Subject: [PATCH 42/80] [core] Upgrade monorepo (#8835) --- docs/.link-check-errors.txt | 3 +-- yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/.link-check-errors.txt b/docs/.link-check-errors.txt index d98a314ee5141..e231a14bb5d13 100644 --- a/docs/.link-check-errors.txt +++ b/docs/.link-check-errors.txt @@ -1,7 +1,6 @@ Broken links found by `yarn docs:link-check` that exist: -- https://mui.com/base/react-portal/components-api/#portal +- https://mui.com/base/api/portal/#props - https://mui.com/blog/material-ui-v4-is-out/#premium-themes-store-✨ -- https://mui.com/material-ui/guides/minimizing-bundle-size/#legacy-bundle - https://mui.com/size-snapshot - https://mui.com/x/react-data-grid/migration-v4 diff --git a/yarn.lock b/yarn.lock index 2202c6800b48d..5ab5c99f45906 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1845,8 +1845,8 @@ react-transition-group "^4.4.5" "@mui/monorepo@https://github.com/mui/material-ui.git#master": - version "5.12.1" - resolved "https://github.com/mui/material-ui.git#8e8442bc280321ac5fd4ee049e4def51c3dd7a70" + version "5.12.3" + resolved "https://github.com/mui/material-ui.git#9710ff34b16a0f271107a8619a28546ae3ca7c18" "@mui/private-theming@^5.12.0": version "5.12.0" From a51c651b0420b7c84a04607b56ae596f60ce2320 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Tue, 2 May 2023 22:46:58 +0200 Subject: [PATCH 43/80] [DataGrid] Fix broken filtering in the value formatter demo (#8621) --- .../column-definition/ValueFormatterGrid.js | 10 ++- .../column-definition/ValueFormatterGrid.tsx | 10 ++- .../column-definition/column-definition.md | 90 +++++++++++++------ 3 files changed, 75 insertions(+), 35 deletions(-) diff --git a/docs/data/data-grid/column-definition/ValueFormatterGrid.js b/docs/data/data-grid/column-definition/ValueFormatterGrid.js index c9dcbec204a84..f9fd6b171f2e6 100644 --- a/docs/data/data-grid/column-definition/ValueFormatterGrid.js +++ b/docs/data/data-grid/column-definition/ValueFormatterGrid.js @@ -27,13 +27,17 @@ export default function ValueFormatterGrid() { field: 'taxRate', headerName: 'Tax Rate', width: 150, + valueGetter: (params) => { + if (!params.value) { + return params.value; + } + return params.value * 100; + }, valueFormatter: (params) => { if (params.value == null) { return ''; } - - const valueFormatted = Number(params.value * 100).toLocaleString(); - return `${valueFormatted} %`; + return `${params.value.toLocaleString()} %`; }, }, ]} diff --git a/docs/data/data-grid/column-definition/ValueFormatterGrid.tsx b/docs/data/data-grid/column-definition/ValueFormatterGrid.tsx index 2af237889376e..24a4e6049fc05 100644 --- a/docs/data/data-grid/column-definition/ValueFormatterGrid.tsx +++ b/docs/data/data-grid/column-definition/ValueFormatterGrid.tsx @@ -27,13 +27,17 @@ export default function ValueFormatterGrid() { field: 'taxRate', headerName: 'Tax Rate', width: 150, + valueGetter: (params) => { + if (!params.value) { + return params.value; + } + return params.value * 100; + }, valueFormatter: (params: GridValueFormatterParams) => { if (params.value == null) { return ''; } - - const valueFormatted = Number(params.value * 100).toLocaleString(); - return `${valueFormatted} %`; + return `${params.value.toLocaleString()} %`; }, }, ]} diff --git a/docs/data/data-grid/column-definition/column-definition.md b/docs/data/data-grid/column-definition/column-definition.md index b320c61db8233..4a5efd364ce78 100644 --- a/docs/data/data-grid/column-definition/column-definition.md +++ b/docs/data/data-grid/column-definition/column-definition.md @@ -33,43 +33,75 @@ But for some columns, it can be useful to manually get and format the value to r ### Value getter -Sometimes a column might not have a corresponding value, or you might want to render a combination of different fields. - -To achieve that, set the `valueGetter` attribute of `GridColDef` as in the example below. - -```tsx -function getFullName(params) { - return `${params.row.firstName || ''} ${params.row.lastName || ''}`; -} - -const columns: GridColDef[] = [ - { field: 'firstName', headerName: 'First name', width: 130 }, - { field: 'lastName', headerName: 'Last name', width: 130 }, - { - field: 'fullName', - headerName: 'Full name', - width: 160, - valueGetter: getFullName, - }, -]; -``` - -{{"demo": "ValueGetterGrid.js", "bg": "inline"}} - -The value generated is used for filtering, sorting, rendering, etc. unless overridden by a more specific configuration. +Sometimes a column might not have a desired value. +You can use the `valueGetter` attribute of `GridColDef` to: + +1. Transform the value + + ```tsx + const columns: GridColDef[] = [ + { + field: 'taxRate', + valueGetter: (params) => { + if (!params.value) { + return params.value; + } + // Convert the decimal value to a percentage + return params.value * 100; + }, + }, + ]; + ``` + +2. Render a combination of different fields + + ```tsx + const columns: GridColDef[] = [ + { + field: 'fullName', + valueGetter: (params) => { + return `${params.row.firstName || ''} ${params.row.lastName || ''}`; + }, + }, + ]; + ``` + +3. Derive a value from a complex value + + ```tsx + const columns: GridColDef[] = [ + { + field: 'profit', + valueGetter: ({ row }) => { + if (!row.gross || !row.costs) { + return null; + } + return row.gross - row.costs; + }, + }, + ]; + ``` + +The value returned by `valueGetter` is used for: + +- Filtering +- Sorting +- Rendering (unless enhanced further by [`valueFormatter`](/x/react-data-grid/column-definition/#value-formatter) or [`renderCell`](/x/react-data-grid/column-definition/#rendering-cells)) + +{{"demo": "ValueGetterGrid.js", "bg": "inline", "defaultCodeOpen": false}} ### Value formatter The value formatter allows you to convert the value before displaying it. Common use cases include converting a JavaScript `Date` object to a date string or a `Number` into a formatted number (e.g. "1,000.50"). -In the following demo, a formatter is used to display the tax rate's decimal value (e.g. 0.2) as a percentage (e.g. 20%). +Note, that the value returned by `valueFormatter` is only used for rendering purposes. +Filtering and sorting are based on the raw value (`row[field]`) or the value returned by [`valueGetter`](/x/react-data-grid/column-definition/#value-getter). -{{"demo": "ValueFormatterGrid.js", "bg": "inline"}} +In the following demo, `valueGetter` is used to convert the tax rate (e.g. `0.2`) to a decimal value (e.g. `20`), +and `valueFormatter` is used to display it as a percentage (e.g. `20%`). -The value generated is only used for rendering purposes. -Filtering and sorting do not rely on the formatted value. -Use the [`valueParser`](/x/react-data-grid/editing/#value-parser-and-value-setter) to support filtering. +{{"demo": "ValueFormatterGrid.js", "bg": "inline"}} ## Rendering cells From 965915401afabf3ba585fd3992b076d2bed349e0 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Wed, 3 May 2023 14:32:26 +0200 Subject: [PATCH 44/80] [DataGrid] Fix missing watermark in pro and premium packages (#8797) --- .../tests/license.DataGridPremium.test.tsx | 14 ++++++++++++- .../src/tests/license.DataGridPro.test.tsx | 21 +++++++++++++++++++ .../src/components/base/GridBody.tsx | 6 +++++- 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 packages/grid/x-data-grid-pro/src/tests/license.DataGridPro.test.tsx diff --git a/packages/grid/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx index 058b4cad53116..7003adcc5a899 100644 --- a/packages/grid/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import addYears from 'date-fns/addYears'; import { expect } from 'chai'; -import { createRenderer } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, waitFor } from '@mui/monorepo/test/utils'; import { DataGridPremium } from '@mui/x-data-grid-premium'; import { generateLicense, LicenseInfo } from '@mui/x-license-pro'; @@ -20,4 +20,16 @@ describe(' - License', () => { 'MUI: License key plan mismatch', ]); }); + + it('should render watermark when the license is missing', async () => { + LicenseInfo.setLicenseKey(''); + + expect(() => render()).toErrorDev([ + 'MUI: Missing license key.', + ]); + + await waitFor(() => { + expect(screen.getByText('MUI X Missing license key')).to.not.equal(null); + }); + }); }); diff --git a/packages/grid/x-data-grid-pro/src/tests/license.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/license.DataGridPro.test.tsx new file mode 100644 index 0000000000000..7306eb5992d8c --- /dev/null +++ b/packages/grid/x-data-grid-pro/src/tests/license.DataGridPro.test.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { createRenderer, screen, waitFor } from '@mui/monorepo/test/utils'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { LicenseInfo } from '@mui/x-license-pro'; + +describe(' - License', () => { + const { render } = createRenderer(); + + it('should render watermark when the license is missing', async () => { + LicenseInfo.setLicenseKey(''); + + expect(() => render()).toErrorDev([ + 'MUI: Missing license key.', + ]); + + await waitFor(() => { + expect(screen.getByText('MUI X Missing license key')).to.not.equal(null); + }); + }); +}); diff --git a/packages/grid/x-data-grid/src/components/base/GridBody.tsx b/packages/grid/x-data-grid/src/components/base/GridBody.tsx index d21f4c5d29c5b..9be017f28e2c6 100644 --- a/packages/grid/x-data-grid/src/components/base/GridBody.tsx +++ b/packages/grid/x-data-grid/src/components/base/GridBody.tsx @@ -27,6 +27,7 @@ import { import { gridColumnMenuSelector } from '../../hooks/features/columnMenu/columnMenuSelector'; interface GridBodyProps { + children?: React.ReactNode; ColumnHeadersProps?: Record; VirtualScrollerComponent: React.JSXElementConstructor< React.HTMLAttributes & { @@ -37,7 +38,7 @@ interface GridBodyProps { } function GridBody(props: GridBodyProps) { - const { VirtualScrollerComponent, ColumnHeadersProps } = props; + const { VirtualScrollerComponent, ColumnHeadersProps, children } = props; const apiRef = useGridPrivateApiContext(); const rootProps = useGridRootProps(); const rootRef = React.useRef(null); @@ -175,6 +176,8 @@ function GridBody(props: GridBodyProps) { disableVirtualization={isVirtualizationDisabled} /> )} + + {children} ); } @@ -184,6 +187,7 @@ GridBody.propTypes = { // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- + children: PropTypes.node, ColumnHeadersProps: PropTypes.object, VirtualScrollerComponent: PropTypes.elementType.isRequired, } as any; From d7d517930b6f544f7e7ae5c72d041d6202c3a155 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Wed, 3 May 2023 16:11:19 +0200 Subject: [PATCH 45/80] [pickers] Remove last additional character when using LTR (#8848) --- .../src/internal/utils/valueManagers.ts | 2 +- .../hooks/useField/useField.utils.ts | 3 +- .../internals/hooks/useField/useFieldState.ts | 2 ++ test/utils/pickers-utils.tsx | 33 +++++++++++++++---- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/packages/x-date-pickers-pro/src/internal/utils/valueManagers.ts b/packages/x-date-pickers-pro/src/internal/utils/valueManagers.ts index b7a5fec403029..8b53f7728de16 100644 --- a/packages/x-date-pickers-pro/src/internal/utils/valueManagers.ts +++ b/packages/x-date-pickers-pro/src/internal/utils/valueManagers.ts @@ -81,7 +81,7 @@ export const rangeFieldValueManager: FieldValueManager, any, Rang return { ...section, dateName: position, - endSeparator: `${section.endSeparator}\u2069 – \u2066`, + endSeparator: `${section.endSeparator}${isRTL ? '\u2069 – \u2066' : ' – '}`, }; } diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts index b049ec8835a0a..b531e0358179d 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts @@ -463,6 +463,7 @@ export const splitFormatIntoSections = ( date: TDate | null, formatDensity: 'dense' | 'spacious', shouldRespectLeadingZeros: boolean, + isRTL: boolean, ) => { let startSeparator: string = ''; const sections: FieldSectionWithoutPosition[] = []; @@ -585,7 +586,7 @@ export const splitFormatIntoSections = ( return sections.map((section) => { const cleanSeparator = (separator: string) => { let cleanedSeparator = separator; - if (cleanedSeparator !== null && cleanedSeparator.includes(' ')) { + if (isRTL && cleanedSeparator !== null && cleanedSeparator.includes(' ')) { cleanedSeparator = `\u2069${cleanedSeparator}\u2066`; } diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts b/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts index b1b1848cfb831..14343b895869b 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts @@ -85,6 +85,7 @@ export const useFieldState = < date, formatDensity, shouldRespectLeadingZeros, + isRTL, ), ), [fieldValueManager, format, localeText, isRTL, shouldRespectLeadingZeros, utils, formatDensity], @@ -257,6 +258,7 @@ export const useFieldState = < date, formatDensity, shouldRespectLeadingZeros, + isRTL, ); return mergeDateIntoReferenceDate(utils, date, sections, referenceDate, false); }; diff --git a/test/utils/pickers-utils.tsx b/test/utils/pickers-utils.tsx index 9a7ed49af244f..22f29ba0fca05 100644 --- a/test/utils/pickers-utils.tsx +++ b/test/utils/pickers-utils.tsx @@ -311,18 +311,35 @@ export const stubMatchMedia = (matches = true) => export const getPickerDay = (name: string, picker = 'January 2018') => getByRole(screen.getByText(picker)?.parentElement?.parentElement!, 'gridcell', { name }); -export const cleanText = (text) => text.replace(/\u200e|\u2066|\u2067|\u2068|\u2069/g, ''); +export const cleanText = (text, specialCase?: 'singleDigit' | 'RTL') => { + switch (specialCase) { + case 'singleDigit': + return text.replace(/\u200e/g, ''); + case 'RTL': + return text.replace(/\u2066|\u2067|\u2068|\u2069/g, ''); + default: + return text; + } +}; export const getCleanedSelectedContent = (input: HTMLInputElement) => cleanText(input.value.slice(input.selectionStart ?? 0, input.selectionEnd ?? 0)); -export const expectInputValue = (input: HTMLInputElement, expectedValue: string) => { - const value = cleanText(input.value); +export const expectInputValue = ( + input: HTMLInputElement, + expectedValue: string, + specialCase?: 'singleDigit' | 'RTL', +) => { + const value = cleanText(input.value, specialCase); return expect(value).to.equal(expectedValue); }; -export const expectInputPlaceholder = (input: HTMLInputElement, placeholder: string) => { - const cleanPlaceholder = cleanText(input.placeholder); +export const expectInputPlaceholder = ( + input: HTMLInputElement, + placeholder: string, + specialCase?: 'singleDigit' | 'RTL', +) => { + const cleanPlaceholder = cleanText(input.placeholder, specialCase); return expect(cleanPlaceholder).to.equal(placeholder); }; @@ -443,7 +460,11 @@ export const buildFieldInteractions =

({ keyStrokes.forEach((keyStroke) => { fireEvent.change(input, { target: { value: keyStroke.value } }); - expectInputValue(input, keyStroke.expected); + expectInputValue( + input, + keyStroke.expected, + (props as any).shouldRespectLeadingZeros ? 'singleDigit' : undefined, + ); }); }; From cd5270652c67b1507ce428243f66faae7e25967a Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 4 May 2023 09:10:13 +0300 Subject: [PATCH 46/80] [core] Limit `typescript:ci` step memory limit (#8796) --- .circleci/config.yml | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 56ec43eb64915..5a676fdaa0d70 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -217,6 +217,8 @@ jobs: - run: name: Tests TypeScript definitions command: yarn typescript:ci + environment: + NODE_OPTIONS: --max-old-space-size=3072 test_e2e: <<: *defaults docker: diff --git a/package.json b/package.json index 6e7e6227d800d..48bca22a29f1b 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "test:performance:server": "serve test/performance -p 5001", "test:argos": "node ./scripts/pushArgos.mjs", "typescript": "lerna run --no-bail --parallel typescript", - "typescript:ci": "lerna run --concurrency 4 --no-bail --no-sort typescript", + "typescript:ci": "lerna run --concurrency 3 --no-bail --no-sort typescript", "build:codesandbox": "yarn release:build", "install:codesandbox": "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 yarn install --ignore-engines", "release:changelog": "node scripts/releaseChangelog.mjs", From f2f1eae08ba93e6cbecbcee078b6794414c095e4 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 4 May 2023 09:10:56 +0300 Subject: [PATCH 47/80] [pickers] Fix time picker `viewRenderers` overriding (#8830) --- .../x-date-pickers/src/DateTimePicker/shared.tsx | 5 +++-- .../src/DesktopTimePicker/DesktopTimePicker.tsx | 10 +++++++--- .../src/TimeClock/TimeClock.types.ts | 5 +++-- .../internals/hooks/usePicker/usePickerViews.ts | 4 ++-- .../src/internals/models/props/clock.ts | 2 +- .../src/timeViewRenderers/timeViewRenderers.tsx | 15 +++++---------- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/x-date-pickers/src/DateTimePicker/shared.tsx b/packages/x-date-pickers/src/DateTimePicker/shared.tsx index 8ae7298e17e16..970fac3ced05f 100644 --- a/packages/x-date-pickers/src/DateTimePicker/shared.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/shared.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { useThemeProps } from '@mui/material/styles'; import { DefaultizedProps } from '../internals/models/helpers'; -import { DateOrTimeView, DateTimeValidationError, TimeView } from '../models'; +import { DateOrTimeView, DateTimeValidationError } from '../models'; import { useDefaultDates, useUtils } from '../internals/hooks/useUtils'; import { DateCalendarSlotsComponent, @@ -36,6 +36,7 @@ import { TimeViewRendererProps } from '../timeViewRenderers'; import { applyDefaultViewProps } from '../internals/utils/views'; import { uncapitalizeObjectKeys, UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { BaseClockProps, ExportedBaseClockProps } from '../internals/models/props/clock'; +import { TimeViewWithMeridiem } from '../internals/models'; export interface BaseDateTimePickerSlotsComponent extends DateCalendarSlotsComponent, @@ -107,7 +108,7 @@ export interface BaseDateTimePickerProps TDate | null, DateOrTimeView, DateViewRendererProps & - TimeViewRendererProps>, + TimeViewRendererProps>, {} > >; diff --git a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx index 13615e9f24281..841d39477bb5a 100644 --- a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx +++ b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx @@ -56,9 +56,13 @@ const DesktopTimePicker = React.forwardRef(function DesktopTimePicker( const actionBarActions: PickersActionBarAction[] = shouldRenderTimeInASingleColumn ? [] : ['accept']; - const views: readonly TimeViewWithMeridiem[] = defaultizedProps.ampm - ? [...defaultizedProps.views, 'meridiem'] - : defaultizedProps.views; + // Need to avoid adding the `meridiem` view when unexpected renderer is specified + const shouldHoursRendererContainMeridiemView = + viewRenderers.hours?.name === renderMultiSectionDigitalClockTimeView.name; + const views: readonly TimeViewWithMeridiem[] = + defaultizedProps.ampm && shouldHoursRendererContainMeridiemView + ? [...defaultizedProps.views, 'meridiem'] + : defaultizedProps.views; // Props with the default values specific to the desktop variant const props = { diff --git a/packages/x-date-pickers/src/TimeClock/TimeClock.types.ts b/packages/x-date-pickers/src/TimeClock/TimeClock.types.ts index 8f02aa2d09a1b..3a2b058589297 100644 --- a/packages/x-date-pickers/src/TimeClock/TimeClock.types.ts +++ b/packages/x-date-pickers/src/TimeClock/TimeClock.types.ts @@ -6,6 +6,7 @@ import { import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { BaseClockProps, ExportedBaseClockProps } from '../internals/models/props/clock'; import { TimeView } from '../models'; +import { TimeViewWithMeridiem } from '../internals/models'; export interface ExportedTimeClockProps extends ExportedBaseClockProps { /** @@ -19,9 +20,9 @@ export interface TimeClockSlotsComponent extends PickersArrowSwitcherSlotsCompon export interface TimeClockSlotsComponentsProps extends PickersArrowSwitcherSlotsComponentsProps {} -export interface TimeClockProps +export interface TimeClockProps extends ExportedTimeClockProps, - BaseClockProps { + BaseClockProps { /** * Override or extend the styles applied to the component. */ diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts index ac9a36cbd5b4f..c02cb92a22f17 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.ts @@ -37,10 +37,10 @@ type PickerViewRenderer< export type PickerViewRendererLookup< TValue, TView extends DateOrTimeViewWithMeridiem, - TExternalProps extends PickerViewsRendererBaseExternalProps, + TExternalProps extends PickerViewsRendererBaseExternalProps, TAdditionalProps extends {}, > = { - [K in TView]: PickerViewRenderer | null; + [K in TView]: PickerViewRenderer | null; }; /** diff --git a/packages/x-date-pickers/src/internals/models/props/clock.ts b/packages/x-date-pickers/src/internals/models/props/clock.ts index 04a50a75da303..d0f6778560740 100644 --- a/packages/x-date-pickers/src/internals/models/props/clock.ts +++ b/packages/x-date-pickers/src/internals/models/props/clock.ts @@ -61,7 +61,7 @@ export interface BaseClockProps export interface DesktopOnlyTimePickerProps extends Omit, 'timeStep'>, - Omit, 'timeStep'> { + Omit, 'timeSteps'> { /** * Amount of time options below or at which the single column time renderer is used. * @default 24 diff --git a/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx b/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx index 5a1bc9963246e..0e7162d692db8 100644 --- a/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx +++ b/packages/x-date-pickers/src/timeViewRenderers/timeViewRenderers.tsx @@ -50,13 +50,13 @@ export const renderTimeViewClock = ({ autoFocus, showViewSwitcher, disableIgnoringDatePartForTimeValidation, -}: TimeViewRendererProps>) => ( +}: TimeViewRendererProps>) => ( - view={view} + view={view as TimeView} onViewChange={onViewChange} - focusedView={focusedView} + focusedView={focusedView as TimeView} onFocusedViewChange={onFocusedViewChange} - views={views.filter(isTimeView)} + views={views.filter(isTimeView) as TimeView[]} value={value} defaultValue={defaultValue} onChange={onChange} @@ -181,12 +181,7 @@ export const renderMultiSectionDigitalClockTimeView = ({ disableIgnoringDatePartForTimeValidation, timeSteps, skipDisabled, -}: TimeViewRendererProps< - TimeViewWithMeridiem, - Omit, 'timeSteps'> & - Omit, 'timeStep'> & - Pick, 'timeSteps'> ->) => ( +}: TimeViewRendererProps>) => ( view={view} onViewChange={onViewChange} From cd2e6bc4b50c5b0992078105cb163f801eaf4c99 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 09:14:25 +0300 Subject: [PATCH 48/80] Bump @docsearch/react to ^3.3.4 (#8801) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/package.json | 2 +- yarn.lock | 50 +++++++++++++++++++++++------------------------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/package.json b/docs/package.json index a1b9e0c5834ca..34b01334685bd 100644 --- a/docs/package.json +++ b/docs/package.json @@ -23,7 +23,7 @@ "@babel/core": "^7.21.4", "@babel/plugin-transform-object-assign": "^7.18.6", "@babel/runtime-corejs2": "^7.21.0", - "@docsearch/react": "^3.3.3", + "@docsearch/react": "^3.3.4", "@emotion/cache": "^11.10.7", "@emotion/react": "^11.10.6", "@emotion/server": "^11.10.0", diff --git a/yarn.lock b/yarn.lock index 5ab5c99f45906..a06cf669be3fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,24 +2,24 @@ # yarn lockfile v1 -"@algolia/autocomplete-core@1.7.4": - version "1.7.4" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.4.tgz#85ff36b2673654a393c8c505345eaedd6eaa4f70" - integrity sha512-daoLpQ3ps/VTMRZDEBfU8ixXd+amZcNJ4QSP3IERGyzqnL5Ch8uSRFt/4G8pUvW9c3o6GA4vtVv4I4lmnkdXyg== +"@algolia/autocomplete-core@1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.8.2.tgz#8d758c8652742e2761450d2b615a841fca24e10e" + integrity sha512-mTeshsyFhAqw/ebqNsQpMtbnjr+qVOSKXArEj4K0d7sqc8It1XD0gkASwecm9mF/jlOQ4Z9RNg1HbdA8JPdRwQ== dependencies: - "@algolia/autocomplete-shared" "1.7.4" + "@algolia/autocomplete-shared" "1.8.2" -"@algolia/autocomplete-preset-algolia@1.7.4": - version "1.7.4" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.4.tgz#610ee1d887962f230b987cba2fd6556478000bc3" - integrity sha512-s37hrvLEIfcmKY8VU9LsAXgm2yfmkdHT3DnA3SgHaY93yjZ2qL57wzb5QweVkYuEBZkT2PIREvRoLXC2sxTbpQ== +"@algolia/autocomplete-preset-algolia@1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.8.2.tgz#706e87f94c5f198c0e90502b97af09adeeddcc79" + integrity sha512-J0oTx4me6ZM9kIKPuL3lyU3aB8DEvpVvR6xWmHVROx5rOYJGQcZsdG4ozxwcOyiiu3qxMkIbzntnV1S1VWD8yA== dependencies: - "@algolia/autocomplete-shared" "1.7.4" + "@algolia/autocomplete-shared" "1.8.2" -"@algolia/autocomplete-shared@1.7.4": - version "1.7.4" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.4.tgz#78aea1140a50c4d193e1f06a13b7f12c5e2cbeea" - integrity sha512-2VGCk7I9tA9Ge73Km99+Qg87w0wzW4tgUruvWAn/gfey1ZXgmxZtyIRBebk35R1O8TbK77wujVtCnpsGpRy1kg== +"@algolia/autocomplete-shared@1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.8.2.tgz#e6972df5c6935a241f16e4909aa82902338e029d" + integrity sha512-b6Z/X4MczChMcfhk6kfRmBzPgjoPzuS9KGR4AFsiLulLNRAAqhP+xZTKtMnZGhLuc61I20d5WqlId02AZvcO6g== "@algolia/cache-browser-local-storage@4.11.0": version "4.11.0" @@ -1238,19 +1238,19 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@docsearch/css@3.3.3": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.3.3.tgz#f9346c9e24602218341f51b8ba91eb9109add434" - integrity sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg== +"@docsearch/css@3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.3.4.tgz#533719eac0aa3934318074e7e981e633727ad2fd" + integrity sha512-vDwCDoVXDgopw/hvr0zEADew2wWaGP8Qq0Bxhgii1Ewz2t4fQeyJwIRN/mWADeLFYPVkpz8TpEbxya/i6Tm0WA== -"@docsearch/react@^3.3.3": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.3.3.tgz#907b6936a565f880b4c0892624b4f7a9f132d298" - integrity sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q== +"@docsearch/react@^3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.3.4.tgz#d49cf9e5d939145c9fe688113c5bdf41975d8ae7" + integrity sha512-aeOf1WC5zMzBEi2SI6WWznOmIo9rnpN4p7a3zHXxowVciqlI4HsZGtOR9nFOufLeolv7HibwLlaM0oyUqJxasw== dependencies: - "@algolia/autocomplete-core" "1.7.4" - "@algolia/autocomplete-preset-algolia" "1.7.4" - "@docsearch/css" "3.3.3" + "@algolia/autocomplete-core" "1.8.2" + "@algolia/autocomplete-preset-algolia" "1.8.2" + "@docsearch/css" "3.3.4" algoliasearch "^4.0.0" "@emotion/babel-plugin@^11.10.6": From ff1579e5063dd093d4ec1884104d64acca5a3613 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 09:18:15 +0300 Subject: [PATCH 49/80] Bump prettier to ^2.8.8 (#8810) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 48bca22a29f1b..98fca93e04bd0 100644 --- a/package.json +++ b/package.json @@ -158,7 +158,7 @@ "patch-package": "^6.5.1", "playwright": "^1.32.3", "postinstall-postinstall": "^2.1.0", - "prettier": "^2.8.7", + "prettier": "^2.8.8", "pretty-quick": "^3.1.3", "process": "^0.11.10", "react": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index a06cf669be3fa..5cba0efd71dde 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11551,10 +11551,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.8.3, prettier@^2.8.7: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== +prettier@^2.8.3, prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-error@^4.0.0: version "4.0.0" From ec5908d9d54a173e2ee2679e2b770b3ef1637112 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 09:18:27 +0300 Subject: [PATCH 50/80] Bump github/codeql-action action to v2.3.2 (#8807) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 543eedbea09de..751ba8b3f38e1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@b2c19fb9a2a485599ccf4ed5d65527d94bc57226 # v2.3.0 + uses: github/codeql-action/init@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # v2.3.2 with: languages: typescript # If you wish to specify custom queries, you can do so here or in a config file. @@ -29,4 +29,4 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b2c19fb9a2a485599ccf4ed5d65527d94bc57226 # v2.3.0 + uses: github/codeql-action/analyze@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # v2.3.2 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 7d74d28df203e..f1c5e82782735 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -44,6 +44,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@b2c19fb9a2a485599ccf4ed5d65527d94bc57226 # v2.3.0 + uses: github/codeql-action/upload-sarif@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # v2.3.2 with: sarif_file: results.sarif From 42f4b9c021c1f924931ffcd1689439849865d6a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 09:18:37 +0300 Subject: [PATCH 51/80] Bump AG Grid to ^29.3.3 (#8804) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- benchmark/package.json | 4 ++-- yarn.lock | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/benchmark/package.json b/benchmark/package.json index f256286d15bc2..22b4a94110061 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -12,8 +12,8 @@ "@material-ui/icons": "^5.0.0-beta.5", "@mui/material": "^5.12.2", "@mui/x-data-grid": "^4.0.0", - "ag-grid-community": "^29.3.2", - "ag-grid-react": "^29.3.2", + "ag-grid-community": "^29.3.3", + "ag-grid-react": "^29.3.3", "css-loader": "^6.7.3", "html-webpack-plugin": "^5.5.1", "mui-plus": "^0.0.15", diff --git a/yarn.lock b/yarn.lock index 5cba0efd71dde..c9fffd62f7c27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3484,15 +3484,15 @@ add-stream@^1.0.0: resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= -ag-grid-community@^29.3.2: - version "29.3.2" - resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-29.3.2.tgz#3b94fd1769b142cd1b87178b6a45f619b5f1b2da" - integrity sha512-D2to2YcYzNETy7m4hM03uQiMHodrFiddaktceurryKEuDRtBYKO9MKk/6JXYkfP3vdDhnmeXBcAgEAJXEprNxg== - -ag-grid-react@^29.3.2: - version "29.3.2" - resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-29.3.2.tgz#46c584887f6dde9f885f5fafd474c23069d76dcb" - integrity sha512-PcQJprPUafk0iz8e+O4GPjDH1I4qZ/10W3uhA6UED2nEkm9p8DaK+zAZKcynMi4Le6DC984Y3Anvryh326+vVw== +ag-grid-community@^29.3.3: + version "29.3.3" + resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-29.3.3.tgz#87a649d3c549e3e3b8316ac2e262dc566788ef1f" + integrity sha512-5XHG2NtXfUFroST/IvWyIYzM7GnbAM1mX7YsKvUHRWk0iMY1kAMJMk6AOoNKe1BBj7jg+Wgbig123T4X7bNZPw== + +ag-grid-react@^29.3.3: + version "29.3.3" + resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-29.3.3.tgz#ce5eef347dc8487dc694364bda1b6b1fa0bf8f30" + integrity sha512-xciimp9q8FXv2bgLOIyPT11dKEW0KNDTF8hyDuAKuK3541YBTZpiycwtmYxVimUM4c007TRTEYgw6Kql/gqWiA== dependencies: prop-types "^15.8.1" From 17f54dfb60639db6a60316a400393d4196923d59 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 09:53:04 +0300 Subject: [PATCH 52/80] Bump markdownlint-cli2 to ^0.7.1 (#8808) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 98fca93e04bd0..55a7d43ba33dd 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "karma-sourcemap-loader": "^0.4.0", "karma-webpack": "^5.0.0", "lerna": "^6.6.1", - "markdownlint-cli2": "^0.7.0", + "markdownlint-cli2": "^0.7.1", "mocha": "^10.2.0", "nyc": "^15.1.0", "patch-package": "^6.5.1", diff --git a/yarn.lock b/yarn.lock index c9fffd62f7c27..1bd409e42aa31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9686,27 +9686,27 @@ markdownlint-cli2-formatter-default@0.0.4: resolved "https://registry.yarnpkg.com/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.4.tgz#81e26b0a50409c0357c6f0d38d8246946b236fab" integrity sha512-xm2rM0E+sWgjpPn1EesPXx5hIyrN2ddUnUwnbCsD/ONxYtw3PX6LydvdH6dciWAoFDpwzbHM1TO7uHfcMd6IYg== -markdownlint-cli2@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/markdownlint-cli2/-/markdownlint-cli2-0.7.0.tgz#4ce5444d79b6a0f1526fb77299a08d5df3e8c6af" - integrity sha512-67r1t9ep+z0fa6g9TgL3tiPQeWo297ip165Et2u54UquJAkXWnq6e+dXFBjSPft/iLaGJfU0fUHXhXueqNUkGQ== +markdownlint-cli2@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/markdownlint-cli2/-/markdownlint-cli2-0.7.1.tgz#42cd126f640c1bd0b820759c29b47c06fb2246cf" + integrity sha512-N58lw50Ws0WOfCc07B9dPKMnPMbIj6ZCMlszZLVfxBwKN/M+WZqXLdOHyRL2BWCZ3APBxQN9qDEw7Vf1PRqFkg== dependencies: globby "13.1.4" - markdownlint "0.28.1" + markdownlint "0.28.2" markdownlint-cli2-formatter-default "0.0.4" micromatch "4.0.5" strip-json-comments "5.0.0" - yaml "2.2.1" + yaml "2.2.2" markdownlint-micromark@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/markdownlint-micromark/-/markdownlint-micromark-0.1.2.tgz#5520e04febffa46741875a2f297509ffdb561f5c" integrity sha512-jRxlQg8KpOfM2IbCL9RXM8ZiYWz2rv6DlZAnGv8ASJQpUh6byTBnEsbuMZ6T2/uIgntyf7SKg/mEaEBo1164fQ== -markdownlint@0.28.1: - version "0.28.1" - resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.28.1.tgz#7978f7e4393edbe08527417d44ffdc2ab865a61f" - integrity sha512-8At2DbgGKT/RVBinkqIPqLETopPXyQFGWSiTCJSr9Y6wVVyY70cDJ9dw3FXePn4AkytIUclgrsgI6KVeqeHFoA== +markdownlint@0.28.2: + version "0.28.2" + resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.28.2.tgz#ea31586a02fe3a06403ecafbbe22d77e363c8ed5" + integrity sha512-yYaQXoKKPV1zgrFsyAuZPEQoe+JrY9GDag9ObKpk09twx4OCU5lut+0/kZPrQ3W7w82SmgKhd7D8m34aG1unVw== dependencies: markdown-it "13.0.1" markdownlint-micromark "0.1.2" @@ -14490,10 +14490,10 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" - integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== +yaml@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" + integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== yaml@^1.10.0, yaml@^1.10.2: version "1.10.2" From 4e7e4bb751da942a46d3cc45172c3f236dbd2016 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 09:54:17 +0300 Subject: [PATCH 53/80] Bump @types/react-dom to ^18.2.3 (#8814) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/package.json | 2 +- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/package.json b/docs/package.json index 34b01334685bd..13087e1411f54 100644 --- a/docs/package.json +++ b/docs/package.json @@ -36,7 +36,7 @@ "@trendmicro/react-interpolate": "^0.5.5", "@types/lodash": "^4.14.194", "@types/moment-hijri": "^2.1.0", - "@types/react-dom": "^18.0.11", + "@types/react-dom": "^18.2.3", "@types/react-router-dom": "^5.3.3", "ast-types": "^0.14.2", "autoprefixer": "^10.4.14", diff --git a/package.json b/package.json index 55a7d43ba33dd..aea18945bba3e 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "@types/node": "^18.16.0", "@types/prettier": "^2.7.2", "@types/react": "^18.0.38", - "@types/react-dom": "^18.0.11", + "@types/react-dom": "^18.2.3", "@types/react-test-renderer": "^18.0.0", "@types/requestidlecallback": "^0.3.5", "@types/sinon": "^10.0.14", diff --git a/yarn.lock b/yarn.lock index 1bd409e42aa31..bb8ee305b0e34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3051,10 +3051,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== -"@types/react-dom@^18.0.0", "@types/react-dom@^18.0.11": - version "18.0.11" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.11.tgz#321351c1459bc9ca3d216aefc8a167beec334e33" - integrity sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw== +"@types/react-dom@^18.0.0", "@types/react-dom@^18.2.3": + version "18.2.3" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.3.tgz#2fe492bb0e67047b7b43e18166d8f50d44e0525e" + integrity sha512-hxXEXWxFJXbY0LMj/T69mznqOZJXNtQMqVxIiirVAZnnpeYiD4zt+lPsgcr/cfWg2VLsxZ1y26vigG03prYB+Q== dependencies: "@types/react" "*" From 63c7a7dd3403e3a29e7527a38d80c608e30d7ad9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 09:56:39 +0300 Subject: [PATCH 54/80] Bump React router to ^6.11.1 (#8816) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/package.json | 4 ++-- yarn.lock | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/package.json b/docs/package.json index 13087e1411f54..eed626310b366 100644 --- a/docs/package.json +++ b/docs/package.json @@ -76,8 +76,8 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.43.9", "react-is": "^18.2.0", - "react-router": "^6.10.0", - "react-router-dom": "^6.10.0", + "react-router": "^6.11.1", + "react-router-dom": "^6.11.1", "react-runner": "^1.0.3", "react-simple-code-editor": "^0.13.1", "recast": "^0.22.0", diff --git a/yarn.lock b/yarn.lock index bb8ee305b0e34..55097492f9dde 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2658,10 +2658,10 @@ "@react-spring/shared" "~9.2.0" "@react-spring/types" "~9.2.0" -"@remix-run/router@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.5.0.tgz#57618e57942a5f0131374a9fdb0167e25a117fdc" - integrity sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg== +"@remix-run/router@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.6.1.tgz#3a3a408481a3796f45223a549c2571517bc8af2d" + integrity sha512-YUkWj+xs0oOzBe74OgErsuR3wVn+efrFhXBWrit50kOiED+pvQe2r6MWY0iJMQU/mSVKxvNzL4ZaYvjdX+G7ZA== "@sigstore/protobuf-specs@^0.1.0": version "0.1.0" @@ -11902,20 +11902,20 @@ react-reconciler@^0.29.0: loose-envify "^1.1.0" scheduler "^0.23.0" -react-router-dom@^6.10.0: - version "6.10.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.10.0.tgz#090ddc5c84dc41b583ce08468c4007c84245f61f" - integrity sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg== +react-router-dom@^6.11.1: + version "6.11.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.11.1.tgz#af226bae950deb437208a606a47cf5c2d72c55dc" + integrity sha512-dPC2MhoPeTQ1YUOt5uIK376SMNWbwUxYRWk2ZmTT4fZfwlOvabF8uduRKKJIyfkCZvMgiF0GSCQckmkGGijIrg== dependencies: - "@remix-run/router" "1.5.0" - react-router "6.10.0" + "@remix-run/router" "1.6.1" + react-router "6.11.1" -react-router@6.10.0, react-router@^6.10.0: - version "6.10.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.10.0.tgz#230f824fde9dd0270781b5cb497912de32c0a971" - integrity sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ== +react-router@6.11.1, react-router@^6.11.1: + version "6.11.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.11.1.tgz#6e58458c03e16834dda2b433c6adb9e7c2b1d7a8" + integrity sha512-OZINSdjJ2WgvAi7hgNLazrEV8SGn6xrKA+MkJe9wVDMZ3zQ6fdJocUjpCUCI0cNrelWjcvon0S/QK/j0NzL3KA== dependencies: - "@remix-run/router" "1.5.0" + "@remix-run/router" "1.6.1" react-runner@^1.0.3: version "1.0.3" From e17dea1e9b8225a8a4614b31cd7f15c9cd186629 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 09:59:58 +0300 Subject: [PATCH 55/80] Bump date-fns to ^2.30.0 (#8818) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/package.json | 2 +- packages/x-date-pickers-pro/package.json | 2 +- packages/x-date-pickers/package.json | 2 +- yarn.lock | 10 ++++++---- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/package.json b/docs/package.json index eed626310b366..58e2276d31533 100644 --- a/docs/package.json +++ b/docs/package.json @@ -50,7 +50,7 @@ "clsx": "^1.2.1", "core-js": "^2.6.12", "cross-env": "^7.0.3", - "date-fns": "^2.29.3", + "date-fns": "^2.30.0", "date-fns-jalali": "^2.21.3-1", "dayjs": "^1.11.7", "doctrine": "^3.0.0", diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index 31e739385f2be..e988300149402 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -96,7 +96,7 @@ }, "devDependencies": { "@types/luxon": "^3.3.0", - "date-fns": "^2.29.3", + "date-fns": "^2.30.0", "dayjs": "^1.11.7", "luxon": "^3.3.0", "moment": "^2.29.4" diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 78fa5f8ca6a82..93371a2ecdf00 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -99,7 +99,7 @@ "devDependencies": { "@types/luxon": "^3.3.0", "@types/moment-jalaali": "^0.7.6", - "date-fns": "^2.29.3", + "date-fns": "^2.30.0", "date-fns-jalali": "^2.13.0-0", "dayjs": "^1.11.7", "luxon": "^3.3.0", diff --git a/yarn.lock b/yarn.lock index 55097492f9dde..55898bbc712a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5571,10 +5571,12 @@ date-fns-jalali@^2.21.3-1: resolved "https://registry.yarnpkg.com/date-fns-jalali/-/date-fns-jalali-2.21.3-1.tgz#b87a01c1d7bb2fe827faed34b33478a5fe4aa027" integrity sha512-Sgw1IdgCgyWDKCpq6uwAu24vPMOtvmcXXXuETr1jQO/aVj4h23XAltcP7hLbo+osqoiJnPmiydbI/q1W7TYAjA== -date-fns@^2.29.3: - version "2.29.3" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" - integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== +date-fns@^2.29.3, date-fns@^2.30.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== + dependencies: + "@babel/runtime" "^7.21.0" date-format@^4.0.3: version "4.0.3" From d02e6e51b9d25a612ad7518a42fce4bbffc379b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 10:07:20 +0300 Subject: [PATCH 56/80] Bump stylis to ^4.1.4 & sync @emotion/* (#8811) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Lukas --- benchmark/package.json | 4 ++-- docs/package.json | 8 +++---- package.json | 6 ++--- yarn.lock | 50 +++++++++++++++++++++--------------------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/benchmark/package.json b/benchmark/package.json index 22b4a94110061..9706a8a33985b 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -6,8 +6,8 @@ "browser": "webpack --config browser/webpack.config.js && node browser/scripts/benchmark.js" }, "dependencies": { - "@emotion/react": "^11.10.6", - "@emotion/styled": "^11.10.6", + "@emotion/react": "^11.10.8", + "@emotion/styled": "^11.10.8", "@material-ui/core": "^5.0.0-beta.5", "@material-ui/icons": "^5.0.0-beta.5", "@mui/material": "^5.12.2", diff --git a/docs/package.json b/docs/package.json index 58e2276d31533..f7b3bbf722004 100644 --- a/docs/package.json +++ b/docs/package.json @@ -24,10 +24,10 @@ "@babel/plugin-transform-object-assign": "^7.18.6", "@babel/runtime-corejs2": "^7.21.0", "@docsearch/react": "^3.3.4", - "@emotion/cache": "^11.10.7", - "@emotion/react": "^11.10.6", + "@emotion/cache": "^11.10.8", + "@emotion/react": "^11.10.8", "@emotion/server": "^11.10.0", - "@emotion/styled": "^11.10.6", + "@emotion/styled": "^11.10.8", "@mui/icons-material": "^5.11.16", "@mui/joy": "^5.0.0-alpha.77", "@mui/material": "^5.12.2", @@ -83,7 +83,7 @@ "recast": "^0.22.0", "rimraf": "^5.0.0", "styled-components": "^5.3.10", - "stylis": "^4.1.3", + "stylis": "^4.1.4", "stylis-plugin-rtl": "^2.1.1", "stylis-plugin-rtl-sc": "npm:stylis-plugin-rtl@^2.1.1", "webpack-bundle-analyzer": "^4.8.0" diff --git a/package.json b/package.json index aea18945bba3e..c63bfe99baef6 100644 --- a/package.json +++ b/package.json @@ -81,9 +81,9 @@ "@babel/register": "^7.21.0", "@babel/traverse": "^7.21.4", "@babel/types": "^7.21.4", - "@emotion/cache": "^11.10.7", - "@emotion/react": "^11.10.6", - "@emotion/styled": "^11.10.6", + "@emotion/cache": "^11.10.8", + "@emotion/react": "^11.10.8", + "@emotion/styled": "^11.10.8", "@mnajdova/enzyme-adapter-react-18": "^0.2.0", "@mui/icons-material": "^5.11.16", "@mui/material": "^5.12.2", diff --git a/yarn.lock b/yarn.lock index 55898bbc712a6..3a361ab8f8a27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1253,10 +1253,10 @@ "@docsearch/css" "3.3.4" algoliasearch "^4.0.0" -"@emotion/babel-plugin@^11.10.6": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz#a68ee4b019d661d6f37dec4b8903255766925ead" - integrity sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ== +"@emotion/babel-plugin@^11.10.8": + version "11.10.8" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.8.tgz#bae325c902937665d00684038fd5294223ef9e1d" + integrity sha512-gxNky50AJL3AlkbjvTARiwAqei6/tNUxDZPSKd+3jqWVM3AmdVTTdpjHorR/an/M0VJqdsuq5oGcFH+rjtyujQ== dependencies: "@babel/helper-module-imports" "^7.16.7" "@babel/runtime" "^7.18.3" @@ -1268,18 +1268,18 @@ escape-string-regexp "^4.0.0" find-root "^1.1.0" source-map "^0.5.7" - stylis "4.1.3" + stylis "4.1.4" -"@emotion/cache@^11.10.5", "@emotion/cache@^11.10.7", "@emotion/cache@^11.4.0": - version "11.10.7" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.7.tgz#2e3b12d3c7c74db0a020ae79eefc52a1b03a6908" - integrity sha512-VLl1/2D6LOjH57Y8Vem1RoZ9haWF4jesHDGiHtKozDQuBIkJm2gimVo0I02sWCuzZtVACeixTVB4jeE8qvCBoQ== +"@emotion/cache@^11.10.7", "@emotion/cache@^11.10.8", "@emotion/cache@^11.4.0": + version "11.10.8" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.8.tgz#3b39b4761bea0ae2f4f07f0a425eec8b6977c03e" + integrity sha512-5fyqGHi51LU95o7qQ/vD1jyvC4uCY5GcBT+UgP4LHdpO9jPDlXqhrRr9/wCKmfoAvh5G/F7aOh4MwQa+8uEqhA== dependencies: "@emotion/memoize" "^0.8.0" "@emotion/sheet" "^1.2.1" "@emotion/utils" "^1.2.0" "@emotion/weak-memoize" "^0.3.0" - stylis "4.1.3" + stylis "4.1.4" "@emotion/hash@^0.9.0": version "0.9.0" @@ -1298,14 +1298,14 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== -"@emotion/react@^11.10.6": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.6.tgz#dbe5e650ab0f3b1d2e592e6ab1e006e75fd9ac11" - integrity sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw== +"@emotion/react@^11.10.8": + version "11.10.8" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.8.tgz#02e274ecb45e03ab9d7a8eb9f0f0c064613eaf7b" + integrity sha512-ZfGfiABtJ1P1OXqOBsW08EgCDp5fK6C5I8hUJauc/VcJBGSzqAirMnFslhFWnZJ/w5HxPI36XbvMV0l4KZHl+w== dependencies: "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.10.6" - "@emotion/cache" "^11.10.5" + "@emotion/babel-plugin" "^11.10.8" + "@emotion/cache" "^11.10.8" "@emotion/serialize" "^1.1.1" "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" "@emotion/utils" "^1.2.0" @@ -1338,13 +1338,13 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c" integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA== -"@emotion/styled@^11.10.6": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.6.tgz#d886afdc51ef4d66c787ebde848f3cc8b117ebba" - integrity sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og== +"@emotion/styled@^11.10.8": + version "11.10.8" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.8.tgz#a3fd68efd90bd7e8a06b82b95adec643d386fa69" + integrity sha512-gow0lF4Uw/QEdX2REMhI8v6wLOabPKJ+4HKNF0xdJ2DJdznN6fxaXpQOx6sNkyBhSUL558Rmcu1Lq/MYlVo4vw== dependencies: "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.10.6" + "@emotion/babel-plugin" "^11.10.8" "@emotion/is-prop-valid" "^1.2.0" "@emotion/serialize" "^1.1.1" "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" @@ -13299,10 +13299,10 @@ styled-jsx@5.1.1: dependencies: cssjanus "^2.0.1" -stylis@4.1.3, stylis@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7" - integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA== +stylis@4.1.4, stylis@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.4.tgz#9cb60e7153d8ac6d02d773552bf51c7a0344535b" + integrity sha512-USf5pszRYwuE6hg9by0OkKChkQYEXfkeTtm0xKw+jqQhwyjCVLdYyMBK7R+n7dhzsblAWJnGxju4vxq5eH20GQ== sucrase@^3.21.0: version "3.28.0" From bec36a107b13a89ed18cb8cf9f037d59c85e14c6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 08:33:59 +0000 Subject: [PATCH 57/80] Bump @types/react to ^18.2.3 (#8813) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index c63bfe99baef6..187ae50714d37 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "@types/mocha": "^10.0.1", "@types/node": "^18.16.0", "@types/prettier": "^2.7.2", - "@types/react": "^18.0.38", + "@types/react": "^18.2.4", "@types/react-dom": "^18.2.3", "@types/react-test-renderer": "^18.0.0", "@types/requestidlecallback": "^0.3.5", diff --git a/yarn.lock b/yarn.lock index 3a361ab8f8a27..5a1efaadaa80e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3096,10 +3096,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.0.38": - version "18.0.38" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.38.tgz#02a23bef8848b360a0d1dceef4432c15c21c600c" - integrity sha512-ExsidLLSzYj4cvaQjGnQCk4HFfVT9+EZ9XZsQ8Hsrcn8QNgXtpZ3m9vSIC2MWtx7jHictK6wYhQgGh6ic58oOw== +"@types/react@*", "@types/react@^18.2.4": + version "18.2.4" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.4.tgz#970e6d56f6d3fd8bd2cb1d1f042aef1d0426d08e" + integrity sha512-IvAIhJTmKAAJmCIcaa6+5uagjyh+9GvcJ/thPZcw+i+vx+22eHlTy2Q1bJg/prES57jehjebq9DnIhOTtIhmLw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" From 3a9c696ac0b39f1247b5adc7a33c1516c0467113 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 4 May 2023 11:41:56 +0300 Subject: [PATCH 58/80] [test] Use `fake` clock on `MobileDateRangePicker` (#8861) --- .../MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx index 2a1bdaa80e3d3..5c27332ed08dc 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx @@ -7,7 +7,7 @@ import { createPickerRenderer, adapterToUse, openPicker } from 'test/utils/picke import { DateRange } from '@mui/x-date-pickers-pro'; describe('', () => { - const { render } = createPickerRenderer(); + const { render } = createPickerRenderer({ clock: 'fake' }); describe('picker state', () => { it('should open when focusing the start input', () => { From 9e9c9d1f011524ca00abae657d2ed8f4e077b40c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 12:00:59 +0300 Subject: [PATCH 59/80] Bump @types/node to ^18.16.3 (#8803) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 187ae50714d37..456a3858c848d 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "@types/chai-dom": "^1.11.0", "@types/enzyme": "^3.10.12", "@types/mocha": "^10.0.1", - "@types/node": "^18.16.0", + "@types/node": "^18.16.3", "@types/prettier": "^2.7.2", "@types/react": "^18.2.4", "@types/react-dom": "^18.2.3", diff --git a/yarn.lock b/yarn.lock index 5a1efaadaa80e..18758d2509b6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3021,10 +3021,10 @@ dependencies: moment ">=2.14.0" -"@types/node@*", "@types/node@>= 8", "@types/node@>=10.0.0", "@types/node@^18.16.0": - version "18.16.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.0.tgz#4668bc392bb6938637b47e98b1f2ed5426f33316" - integrity sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ== +"@types/node@*", "@types/node@>= 8", "@types/node@>=10.0.0", "@types/node@^18.16.3": + version "18.16.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01" + integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q== "@types/node@^14.0.1": version "14.18.36" From 0fe6f0e5908bd21935bcac42d64172365977fcc7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 12:02:19 +0300 Subject: [PATCH 60/80] Bump Playwright (#8815) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .circleci/config.yml | 10 +++++----- benchmark/package.json | 2 +- package.json | 4 ++-- yarn.lock | 28 ++++++++++++++-------------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5a676fdaa0d70..aae2b9c95c5ce 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -189,7 +189,7 @@ jobs: test_browser: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.32.3-focal + - image: mcr.microsoft.com/playwright:v1.33.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -222,7 +222,7 @@ jobs: test_e2e: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.32.3-focal + - image: mcr.microsoft.com/playwright:v1.33.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -235,7 +235,7 @@ jobs: test_e2e_website: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.32.3-focal + - image: mcr.microsoft.com/playwright:v1.33.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -250,7 +250,7 @@ jobs: test_regressions: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.32.3-focal + - image: mcr.microsoft.com/playwright:v1.33.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -266,7 +266,7 @@ jobs: test_performance: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.32.3-focal + - image: mcr.microsoft.com/playwright:v1.33.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: diff --git a/benchmark/package.json b/benchmark/package.json index 9706a8a33985b..cf616997474c2 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -17,7 +17,7 @@ "css-loader": "^6.7.3", "html-webpack-plugin": "^5.5.1", "mui-plus": "^0.0.15", - "playwright": "^1.32.3", + "playwright": "^1.33.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-spring": "^9.2.4", diff --git a/package.json b/package.json index 456a3858c848d..0a65887987044 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "@mui/utils": "^5.12.0", "@octokit/plugin-retry": "^4.1.3", "@octokit/rest": "^19.0.7", - "@playwright/test": "1.32.3", + "@playwright/test": "1.33.0", "@testing-library/react": "^14.0.0", "@types/babel__core": "^7.20.0", "@types/chai": "^4.3.4", @@ -156,7 +156,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "patch-package": "^6.5.1", - "playwright": "^1.32.3", + "playwright": "^1.33.0", "postinstall-postinstall": "^2.1.0", "prettier": "^2.8.8", "pretty-quick": "^3.1.3", diff --git a/yarn.lock b/yarn.lock index 18758d2509b6a..59634e89721f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2553,13 +2553,13 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@playwright/test@1.32.3": - version "1.32.3" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.32.3.tgz#75be8346d4ef289896835e1d2a86fdbe3d9be92a" - integrity sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ== +"@playwright/test@1.33.0": + version "1.33.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.33.0.tgz#669ef859efb81b143dfc624eef99d1dd92a81b67" + integrity sha512-YunBa2mE7Hq4CfPkGzQRK916a4tuZoVx/EpLjeWlTVOnD4S2+fdaQZE0LJkbfhN5FTSKNLdcl7MoT5XB37bTkg== dependencies: "@types/node" "*" - playwright-core "1.32.3" + playwright-core "1.33.0" optionalDependencies: fsevents "2.3.2" @@ -11437,17 +11437,17 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -playwright-core@1.32.3: - version "1.32.3" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.32.3.tgz#e6dc7db0b49e9b6c0b8073c4a2d789a96f519c48" - integrity sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg== +playwright-core@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.33.0.tgz#269efe29a927cd6d144d05f3c2d2f72bd72447a1" + integrity sha512-aizyPE1Cj62vAECdph1iaMILpT0WUDCq3E6rW6I+dleSbBoGbktvJtzS6VHkZ4DKNEOG9qJpiom/ZxO+S15LAw== -playwright@^1.32.3: - version "1.32.3" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.32.3.tgz#88583167880e42ca04fa8c4cc303680f4ff457d0" - integrity sha512-h/ylpgoj6l/EjkfUDyx8cdOlfzC96itPpPe8BXacFkqpw/YsuxkpPyVbzEq4jw+bAJh5FLgh31Ljg2cR6HV3uw== +playwright@^1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.33.0.tgz#88df1cffe97718ab8a02303e12c9133681ec7fab" + integrity sha512-+zzU3V2TslRX2ETBRgQKsKytYBkJeLZ2xzUj4JohnZnxQnivoUvOvNbRBYWSYykQTO0Y4zb8NwZTYFUO+EpPBQ== dependencies: - playwright-core "1.32.3" + playwright-core "1.33.0" posix-character-classes@^0.1.0: version "0.1.1" From 1274b0eef965d0f1e9dc4b09667c0ff790cd9ae9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 12:02:44 +0300 Subject: [PATCH 61/80] Bump @types/chai to ^4.3.5 (#8802) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0a65887987044..36ab4ddb97ec7 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "@playwright/test": "1.33.0", "@testing-library/react": "^14.0.0", "@types/babel__core": "^7.20.0", - "@types/chai": "^4.3.4", + "@types/chai": "^4.3.5", "@types/chai-dom": "^1.11.0", "@types/enzyme": "^3.10.12", "@types/mocha": "^10.0.1", diff --git a/yarn.lock b/yarn.lock index 59634e89721f9..98da1ac2c2511 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2830,10 +2830,10 @@ dependencies: "@types/chai" "*" -"@types/chai@*", "@types/chai@^4.3.4": - version "4.3.4" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4" - integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw== +"@types/chai@*", "@types/chai@^4.3.5": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b" + integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng== "@types/chance@^1.1.3": version "1.1.3" From 5707693c03327d6fd9255b520e01181dbc55a3db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 12:47:46 +0300 Subject: [PATCH 62/80] Bump yargs to ^17.7.2 (#8812) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- packages/x-codemod/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 36ab4ddb97ec7..d6c57b254cea7 100644 --- a/package.json +++ b/package.json @@ -171,7 +171,7 @@ "util": "^0.12.5", "webpack": "^5.81.0", "webpack-cli": "^5.0.2", - "yargs": "^17.7.1", + "yargs": "^17.7.2", "yarn-deduplicate": "^6.0.1" }, "setupFiles": [ diff --git a/packages/x-codemod/package.json b/packages/x-codemod/package.json index 3e579869cbc88..2edf295c2faca 100644 --- a/packages/x-codemod/package.json +++ b/packages/x-codemod/package.json @@ -36,7 +36,7 @@ "@babel/traverse": "^7.21.4", "jscodeshift": "0.13.1", "jscodeshift-add-imports": "^1.0.10", - "yargs": "^17.7.1" + "yargs": "^17.7.2" }, "devDependencies": { "@types/jscodeshift": "^0.11.5" diff --git a/yarn.lock b/yarn.lock index 98da1ac2c2511..0a8535b07518d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14565,10 +14565,10 @@ yargs@^15.0.2: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^17.6.2, yargs@^17.7.1: - version "17.7.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" - integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== +yargs@^17.6.2, yargs@^17.7.1, yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" escalade "^3.1.1" From 739cc87c58bc5d64dc9147da457f1e386b212a5c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 13:35:13 +0300 Subject: [PATCH 63/80] Bump patch-package to ^7.0.0 (#8817) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index d6c57b254cea7..e307426516aba 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "markdownlint-cli2": "^0.7.1", "mocha": "^10.2.0", "nyc": "^15.1.0", - "patch-package": "^6.5.1", + "patch-package": "^7.0.0", "playwright": "^1.33.0", "postinstall-postinstall": "^2.1.0", "prettier": "^2.8.8", diff --git a/yarn.lock b/yarn.lock index 0a8535b07518d..6c503ec90c560 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4701,6 +4701,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.7.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -5319,7 +5324,7 @@ cross-spawn@^4.0.0: lru-cache "^4.0.1" which "^1.2.9" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -8176,7 +8181,7 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-ci@2.0.0, is-ci@^2.0.0: +is-ci@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== @@ -11274,17 +11279,17 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== -patch-package@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.5.1.tgz#3e5d00c16997e6160291fee06a521c42ac99b621" - integrity sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA== +patch-package@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-7.0.0.tgz#5c646b6b4b4bf37e5184a6950777b21dea6bb66e" + integrity sha512-eYunHbnnB2ghjTNc5iL1Uo7TsGMuXk0vibX3RFcE/CdVdXzmdbMsG/4K4IgoSuIkLTI5oHrMQk4+NkFqSed0BQ== dependencies: "@yarnpkg/lockfile" "^1.1.0" chalk "^4.1.2" - cross-spawn "^6.0.5" + ci-info "^3.7.0" + cross-spawn "^7.0.3" find-yarn-workspace-root "^2.0.0" fs-extra "^9.0.0" - is-ci "^2.0.0" klaw-sync "^6.0.0" minimist "^1.2.6" open "^7.4.2" @@ -11292,7 +11297,7 @@ patch-package@^6.5.1: semver "^5.6.0" slash "^2.0.0" tmp "^0.0.33" - yaml "^1.10.2" + yaml "^2.2.2" path-exists@^3.0.0: version "3.0.0" @@ -14492,12 +14497,12 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@2.2.2: +yaml@2.2.2, yaml@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== -yaml@^1.10.0, yaml@^1.10.2: +yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== From 2f396d761daaca90e3c2bac3f95d12ca1ea13d44 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 13:46:33 +0200 Subject: [PATCH 64/80] Bump MUI Core (#8859) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: alexandre --- benchmark/package.json | 2 +- docs/package.json | 8 +- package.json | 4 +- .../grid/x-data-grid-generator/package.json | 2 +- .../grid/x-data-grid-premium/package.json | 2 +- packages/grid/x-data-grid-pro/package.json | 2 +- packages/grid/x-data-grid/package.json | 2 +- packages/x-date-pickers-pro/package.json | 2 +- .../SingleInputDateRangeField.tsx | 4 + .../SingleInputDateTimeRangeField.tsx | 4 + .../SingleInputTimeRangeField.tsx | 4 + packages/x-date-pickers/package.json | 2 +- .../src/DateField/DateField.tsx | 4 + .../src/DateTimeField/DateTimeField.tsx | 4 + .../src/TimeField/TimeField.tsx | 4 + packages/x-license-pro/package.json | 2 +- yarn.lock | 106 +++++++++--------- 17 files changed, 91 insertions(+), 67 deletions(-) diff --git a/benchmark/package.json b/benchmark/package.json index cf616997474c2..b9a84d7871d27 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -10,7 +10,7 @@ "@emotion/styled": "^11.10.8", "@material-ui/core": "^5.0.0-beta.5", "@material-ui/icons": "^5.0.0-beta.5", - "@mui/material": "^5.12.2", + "@mui/material": "^5.12.3", "@mui/x-data-grid": "^4.0.0", "ag-grid-community": "^29.3.3", "ag-grid-react": "^29.3.3", diff --git a/docs/package.json b/docs/package.json index f7b3bbf722004..509a2c568abcf 100644 --- a/docs/package.json +++ b/docs/package.json @@ -29,10 +29,10 @@ "@emotion/server": "^11.10.0", "@emotion/styled": "^11.10.8", "@mui/icons-material": "^5.11.16", - "@mui/joy": "^5.0.0-alpha.77", - "@mui/material": "^5.12.2", - "@mui/styles": "^5.12.0", - "@mui/utils": "^5.12.0", + "@mui/joy": "^5.0.0-alpha.78", + "@mui/material": "^5.12.3", + "@mui/styles": "^5.12.3", + "@mui/utils": "^5.12.3", "@trendmicro/react-interpolate": "^0.5.5", "@types/lodash": "^4.14.194", "@types/moment-hijri": "^2.1.0", diff --git a/package.json b/package.json index e307426516aba..cfd0092e90c2c 100644 --- a/package.json +++ b/package.json @@ -86,9 +86,9 @@ "@emotion/styled": "^11.10.8", "@mnajdova/enzyme-adapter-react-18": "^0.2.0", "@mui/icons-material": "^5.11.16", - "@mui/material": "^5.12.2", + "@mui/material": "^5.12.3", "@mui/monorepo": "https://github.com/mui/material-ui.git#master", - "@mui/utils": "^5.12.0", + "@mui/utils": "^5.12.3", "@octokit/plugin-retry": "^4.1.3", "@octokit/rest": "^19.0.7", "@playwright/test": "1.33.0", diff --git a/packages/grid/x-data-grid-generator/package.json b/packages/grid/x-data-grid-generator/package.json index 4cfc78d7b4252..d7a5be80a6c93 100644 --- a/packages/grid/x-data-grid-generator/package.json +++ b/packages/grid/x-data-grid-generator/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/base": "^5.0.0-alpha.127", + "@mui/base": "^5.0.0-alpha.128", "@mui/x-data-grid-premium": "6.3.0", "chance": "^1.1.11", "clsx": "^1.2.1", diff --git a/packages/grid/x-data-grid-premium/package.json b/packages/grid/x-data-grid-premium/package.json index fc3d4bce897f2..b4111ec02335d 100644 --- a/packages/grid/x-data-grid-premium/package.json +++ b/packages/grid/x-data-grid-premium/package.json @@ -43,7 +43,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.12.0", + "@mui/utils": "^5.12.3", "@mui/x-data-grid": "6.3.0", "@mui/x-data-grid-pro": "6.3.0", "@mui/x-license-pro": "6.0.4", diff --git a/packages/grid/x-data-grid-pro/package.json b/packages/grid/x-data-grid-pro/package.json index 294cc171e8bf1..2bf4223acbf6d 100644 --- a/packages/grid/x-data-grid-pro/package.json +++ b/packages/grid/x-data-grid-pro/package.json @@ -43,7 +43,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.12.0", + "@mui/utils": "^5.12.3", "@mui/x-data-grid": "6.3.0", "@mui/x-license-pro": "6.0.4", "@types/format-util": "^1.0.2", diff --git a/packages/grid/x-data-grid/package.json b/packages/grid/x-data-grid/package.json index 201ff4d0e95c7..3b8c3a484a71a 100644 --- a/packages/grid/x-data-grid/package.json +++ b/packages/grid/x-data-grid/package.json @@ -47,7 +47,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.12.0", + "@mui/utils": "^5.12.3", "clsx": "^1.2.1", "prop-types": "^15.8.1", "reselect": "^4.1.8" diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index e988300149402..225a0999b5ed5 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -42,7 +42,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.12.0", + "@mui/utils": "^5.12.3", "@mui/x-date-pickers": "6.3.0", "@mui/x-license-pro": "6.0.4", "clsx": "^1.2.1", diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx index 106b5c17bddfd..42834dce277a0 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx @@ -203,6 +203,10 @@ SingleInputDateRangeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * @ignore + */ + onClick: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx index 67c154faceed8..23b2245f72ec0 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx @@ -237,6 +237,10 @@ SingleInputDateTimeRangeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * @ignore + */ + onClick: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. diff --git a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx index 44509264e5a43..669ee8d2d48bd 100644 --- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx @@ -220,6 +220,10 @@ SingleInputTimeRangeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * @ignore + */ + onClick: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 93371a2ecdf00..791d47c9d9620 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -45,7 +45,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.12.0", + "@mui/utils": "^5.12.3", "@types/react-transition-group": "^4.4.5", "clsx": "^1.2.1", "prop-types": "^15.8.1", diff --git a/packages/x-date-pickers/src/DateField/DateField.tsx b/packages/x-date-pickers/src/DateField/DateField.tsx index 00aae17c15703..89ea32a651c3a 100644 --- a/packages/x-date-pickers/src/DateField/DateField.tsx +++ b/packages/x-date-pickers/src/DateField/DateField.tsx @@ -200,6 +200,10 @@ DateField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * @ignore + */ + onClick: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx index 0ac7ecd10c973..c4a1e3775a6e4 100644 --- a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx +++ b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx @@ -235,6 +235,10 @@ DateTimeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * @ignore + */ + onClick: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. diff --git a/packages/x-date-pickers/src/TimeField/TimeField.tsx b/packages/x-date-pickers/src/TimeField/TimeField.tsx index 5d71d43ff26e0..5c4f79f05570b 100644 --- a/packages/x-date-pickers/src/TimeField/TimeField.tsx +++ b/packages/x-date-pickers/src/TimeField/TimeField.tsx @@ -217,6 +217,10 @@ TimeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * @ignore + */ + onClick: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. diff --git a/packages/x-license-pro/package.json b/packages/x-license-pro/package.json index 966987cfe5c35..ba461366e79bc 100644 --- a/packages/x-license-pro/package.json +++ b/packages/x-license-pro/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.12.0" + "@mui/utils": "^5.12.3" }, "peerDependencies": { "react": "^17.0.2 || ^18.0.0" diff --git a/yarn.lock b/yarn.lock index 6c503ec90c560..7806c2ce6aea7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1270,7 +1270,7 @@ source-map "^0.5.7" stylis "4.1.4" -"@emotion/cache@^11.10.7", "@emotion/cache@^11.10.8", "@emotion/cache@^11.4.0": +"@emotion/cache@^11.10.8", "@emotion/cache@^11.4.0": version "11.10.8" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.8.tgz#3b39b4761bea0ae2f4f07f0a425eec8b6977c03e" integrity sha512-5fyqGHi51LU95o7qQ/vD1jyvC4uCY5GcBT+UgP4LHdpO9jPDlXqhrRr9/wCKmfoAvh5G/F7aOh4MwQa+8uEqhA== @@ -1784,24 +1784,24 @@ react-test-renderer "^18.0.0" semver "^5.7.0" -"@mui/base@5.0.0-alpha.127", "@mui/base@^5.0.0-alpha.127": - version "5.0.0-alpha.127" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.127.tgz#bc61eaf1fd31094c939b6521cfea21643207c3b4" - integrity sha512-FoRQd0IOH9MnfyL5yXssyQRnC4vXI+1bwkU1idr+wNkP1ZfxE+JsThHcfl1dy5azLssVUGTtQFD9edQLdbyJog== +"@mui/base@5.0.0-alpha.128", "@mui/base@^5.0.0-alpha.128": + version "5.0.0-alpha.128" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.128.tgz#8ce4beb971ac989df0b1d3b2bd3e9274dbfa604f" + integrity sha512-wub3wxNN+hUp8hzilMlXX3sZrPo75vsy1cXEQpqdTfIFlE9HprP1jlulFiPg5tfPst2OKmygXr2hhmgvAKRrzQ== dependencies: "@babel/runtime" "^7.21.0" "@emotion/is-prop-valid" "^1.2.0" "@mui/types" "^7.2.4" - "@mui/utils" "^5.12.0" + "@mui/utils" "^5.12.3" "@popperjs/core" "^2.11.7" clsx "^1.2.1" prop-types "^15.8.1" react-is "^18.2.0" -"@mui/core-downloads-tracker@^5.12.2": - version "5.12.2" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.12.2.tgz#4a0186d25b01d693171366e1c00de0e7c8c35f6a" - integrity sha512-Qn7dy8tql6T0hY6gTFPkpWlnqVVFGu5Z6QzEzUSzzmLZpfAx4kf8sFz0PHiB7gU5yrqcZF9picMx1shpRY/rXw== +"@mui/core-downloads-tracker@^5.12.3": + version "5.12.3" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.12.3.tgz#3dffe62dccc065ddd7338e97d7be4b917004287e" + integrity sha512-yiJZ+knaknPHuRKhRk4L6XiwppwkAahVal3LuYpvBH7GkA2g+D9WLEXOEnNYtVFUggyKf6fWGLGnx0iqzkU5YA== "@mui/icons-material@^5.11.16": version "5.11.16" @@ -1810,33 +1810,33 @@ dependencies: "@babel/runtime" "^7.21.0" -"@mui/joy@^5.0.0-alpha.77": - version "5.0.0-alpha.77" - resolved "https://registry.yarnpkg.com/@mui/joy/-/joy-5.0.0-alpha.77.tgz#d1af526866366a63ed30e7dc893657a4195bb172" - integrity sha512-X0XbswA90mmwJwOaJNxm3sQgA24NOwMEoCxQ5VbwO/O+8ZoDQ5Ngb6l/VI0FfXJJDL52p793QMAuiDHLef52hQ== +"@mui/joy@^5.0.0-alpha.78": + version "5.0.0-alpha.78" + resolved "https://registry.yarnpkg.com/@mui/joy/-/joy-5.0.0-alpha.78.tgz#8e977233e0d83c6df0f307e906a1dc5e111a03f8" + integrity sha512-Esxxov9lWmYGHl9xEqxf2uAEFYSj2VnjcieP2J6rubygi2n2mX3Nw/H28pMrde4sT7xcPYRgnp1NnTyizSlDkw== dependencies: "@babel/runtime" "^7.21.0" - "@mui/base" "5.0.0-alpha.127" - "@mui/core-downloads-tracker" "^5.12.2" - "@mui/system" "^5.12.1" + "@mui/base" "5.0.0-alpha.128" + "@mui/core-downloads-tracker" "^5.12.3" + "@mui/system" "^5.12.3" "@mui/types" "^7.2.4" - "@mui/utils" "^5.12.0" + "@mui/utils" "^5.12.3" clsx "^1.2.1" csstype "^3.1.2" prop-types "^15.8.1" react-is "^18.2.0" -"@mui/material@^5.12.2": - version "5.12.2" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.12.2.tgz#c3fcc94e523d9e673e2e045dfad04d12ab454a80" - integrity sha512-XOVy6fVC0rI2dEwDq/1s4Te2hewTUe6lznzeVnruyATGkdmM06WnHqkZOoLVIWo9hWwAxpcgTDcAIVpFtt1nrw== +"@mui/material@^5.12.3": + version "5.12.3" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.12.3.tgz#398c1b123fb065763558bc1f9fc47d1f8cb87d0c" + integrity sha512-xNmKlrEN4HsTaKFNLZfc7ie7CXx2YqEeO//hsXZx2p3MGtDdeMr2sV3jC4hsFs57RhQlF79weY7uVvC8xSuVbg== dependencies: "@babel/runtime" "^7.21.0" - "@mui/base" "5.0.0-alpha.127" - "@mui/core-downloads-tracker" "^5.12.2" - "@mui/system" "^5.12.1" + "@mui/base" "5.0.0-alpha.128" + "@mui/core-downloads-tracker" "^5.12.3" + "@mui/system" "^5.12.3" "@mui/types" "^7.2.4" - "@mui/utils" "^5.12.0" + "@mui/utils" "^5.12.3" "@types/react-transition-group" "^4.4.5" clsx "^1.2.1" csstype "^3.1.2" @@ -1848,35 +1848,35 @@ version "5.12.3" resolved "https://github.com/mui/material-ui.git#9710ff34b16a0f271107a8619a28546ae3ca7c18" -"@mui/private-theming@^5.12.0": - version "5.12.0" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.12.0.tgz#5f1e6fd09b1447c387fdac1eef7f23efca5c6d69" - integrity sha512-w5dwMen1CUm1puAtubqxY9BIzrBxbOThsg2iWMvRJmWyJAPdf3Z583fPXpqeA2lhTW79uH2jajk5Ka4FuGlTPg== +"@mui/private-theming@^5.12.3": + version "5.12.3" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.12.3.tgz#f5e4704e25d9d91b906561cae573cda8f3801e10" + integrity sha512-o1e7Z1Bp27n4x2iUHhegV4/Jp6H3T6iBKHJdLivS5GbwsuAE/5l4SnZ+7+K+e5u9TuhwcAKZLkjvqzkDe8zqfA== dependencies: "@babel/runtime" "^7.21.0" - "@mui/utils" "^5.12.0" + "@mui/utils" "^5.12.3" prop-types "^15.8.1" -"@mui/styled-engine@^5.12.0": - version "5.12.0" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.12.0.tgz#44640cad961adcc9413ae32116237cd1c8f7ddb0" - integrity sha512-frh8L7CRnvD0RDmIqEv6jFeKQUIXqW90BaZ6OrxJ2j4kIsiVLu29Gss4SbBvvrWwwatR72sBmC3w1aG4fjp9mQ== +"@mui/styled-engine@^5.12.3": + version "5.12.3" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.12.3.tgz#3307643d52c81947a624cdd0437536cc8109c4f0" + integrity sha512-AhZtiRyT8Bjr7fufxE/mLS+QJ3LxwX1kghIcM2B2dvJzSSg9rnIuXDXM959QfUVIM3C8U4x3mgVoPFMQJvc4/g== dependencies: "@babel/runtime" "^7.21.0" - "@emotion/cache" "^11.10.7" + "@emotion/cache" "^11.10.8" csstype "^3.1.2" prop-types "^15.8.1" -"@mui/styles@^5.12.0": - version "5.12.0" - resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.12.0.tgz#a41a4337996db2cd23a828bb28721ea183819e87" - integrity sha512-X7obkgZTd9X+7igqwKKe8pEncyXYdUCNmyJfHruV9TSc6LThoI29OYs6hkN6n+7ueNli+YDKdZ+TCoC1GpJuOw== +"@mui/styles@^5.12.3": + version "5.12.3" + resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.12.3.tgz#2856fea1002199155bca02d4188b3771539481c6" + integrity sha512-y0GN1kTYO2FF/0LH8a0PpVxwLotlcunFqdJpCL5gza0w5Fqz9wxlwauPZW0bDt0+sF79CrohzdzWkh+fxB+oww== dependencies: "@babel/runtime" "^7.21.0" "@emotion/hash" "^0.9.0" - "@mui/private-theming" "^5.12.0" + "@mui/private-theming" "^5.12.3" "@mui/types" "^7.2.4" - "@mui/utils" "^5.12.0" + "@mui/utils" "^5.12.3" clsx "^1.2.1" csstype "^3.1.2" hoist-non-react-statics "^3.3.2" @@ -1890,16 +1890,16 @@ jss-plugin-vendor-prefixer "^10.10.0" prop-types "^15.8.1" -"@mui/system@^5.12.1": - version "5.12.1" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.12.1.tgz#8452bc03159f0a6725b96bde1dee1316e308231b" - integrity sha512-Po+sicdV3bbRYXdU29XZaHPZrW7HUYUqU1qCu77GCCEMbahC756YpeyefdIYuPMUg0OdO3gKIUfDISBrkjJL+w== +"@mui/system@^5.12.3": + version "5.12.3" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.12.3.tgz#306b3cdffa3046067640219c1e5dd7e3dae38ff9" + integrity sha512-JB/6sypHqeJCqwldWeQ1MKkijH829EcZAKKizxbU2MJdxGG5KSwZvTBa5D9qiJUA1hJFYYupjiuy9ZdJt6rV6w== dependencies: "@babel/runtime" "^7.21.0" - "@mui/private-theming" "^5.12.0" - "@mui/styled-engine" "^5.12.0" + "@mui/private-theming" "^5.12.3" + "@mui/styled-engine" "^5.12.3" "@mui/types" "^7.2.4" - "@mui/utils" "^5.12.0" + "@mui/utils" "^5.12.3" clsx "^1.2.1" csstype "^3.1.2" prop-types "^15.8.1" @@ -1909,10 +1909,10 @@ resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328" integrity sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA== -"@mui/utils@^5.12.0": - version "5.12.0" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.12.0.tgz#284db48b36ac26f3d34076379072c1dc8aed1ad0" - integrity sha512-RmQwgzF72p7Yr4+AAUO6j1v2uzt6wr7SWXn68KBsnfVpdOHyclCzH2lr/Xu6YOw9su4JRtdAIYfJFXsS6Cjkmw== +"@mui/utils@^5.12.3": + version "5.12.3" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.12.3.tgz#3fa3570dac7ec66bb9cc84ab7c16ab6e1b7200f2" + integrity sha512-D/Z4Ub3MRl7HiUccid7sQYclTr24TqUAQFFlxHQF8FR177BrCTQ0JJZom7EqYjZCdXhwnSkOj2ph685MSKNtIA== dependencies: "@babel/runtime" "^7.21.0" "@types/prop-types" "^15.7.5" From fc4392c20b1c9e5cf8b98cdcbcdd6870eda78bf9 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Thu, 4 May 2023 15:15:20 +0200 Subject: [PATCH 65/80] [charts] Defaultize attributes (#8788) --- docs/data/charts/axis/AxisWithComposition.js | 4 +- docs/data/charts/axis/AxisWithComposition.tsx | 4 +- docs/data/charts/axis/MinMaxExample.js | 2 - docs/data/charts/axis/MinMaxExample.tsx | 2 - docs/data/charts/axis/ModifyAxisPosition.js | 1 - docs/data/charts/axis/ModifyAxisPosition.tsx | 1 - .../axis/ModifyAxisPosition.tsx.preview | 1 - docs/data/charts/axis/ScaleExample.js | 4 - docs/data/charts/axis/ScaleExample.tsx | 4 - docs/data/charts/bars/Composition.js | 3 - docs/data/charts/bars/Composition.tsx | 3 - docs/data/charts/bars/TestBars.js | 39 +------- docs/data/charts/bars/TestBars.tsx | 39 +------- docs/data/charts/bars/TestBars.tsx.preview | 9 ++ docs/data/charts/lines/BasicLineChart.js | 2 - docs/data/charts/lines/BasicLineChart.tsx | 2 - .../charts/lines/BasicLineChart.tsx.preview | 2 - docs/data/charts/lines/CSSCustomization.js | 6 -- docs/data/charts/lines/CSSCustomization.tsx | 6 -- docs/data/charts/lines/StackedAreas.js | 12 +-- docs/data/charts/lines/StackedAreas.tsx | 12 +-- docs/data/charts/lines/TestLines.js | 12 --- docs/data/charts/lines/TestLines.tsx | 12 --- docs/data/charts/lines/TestLinesTime.js | 9 -- docs/data/charts/lines/TestLinesTime.tsx | 9 -- docs/data/charts/overview/Combining.js | 6 -- docs/data/charts/overview/Combining.tsx | 6 -- docs/data/charts/overview/SimpleCharts.js | 3 - docs/data/charts/overview/SimpleCharts.tsx | 3 - .../charts/overview/SimpleCharts.tsx.preview | 16 ++++ docs/data/charts/scatter/BasicScatterChart.js | 2 - .../data/charts/scatter/BasicScatterChart.tsx | 2 - .../scatter/BasicScatterChart.tsx.preview | 15 +++ docs/data/charts/scatter/Composition.js | 1 - docs/data/charts/scatter/Composition.tsx | 2 - docs/data/charts/scatter/ScatterChartAxis.js | 2 - docs/data/charts/scatter/ScatterChartAxis.tsx | 2 - packages/x-charts/src/Axis/Axis.tsx | 5 +- packages/x-charts/src/BarChart/BarChart.tsx | 21 ++++- packages/x-charts/src/BarChart/BarPlot.tsx | 93 ++++++++----------- packages/x-charts/src/BarChart/extremums.ts | 8 +- packages/x-charts/src/BarChart/formatter.ts | 5 +- packages/x-charts/src/LineChart/LineChart.tsx | 21 ++++- packages/x-charts/src/LineChart/LinePlot.tsx | 35 ++----- packages/x-charts/src/LineChart/extremums.ts | 8 +- packages/x-charts/src/LineChart/formatter.ts | 6 +- .../x-charts/src/ScatterChart/Scatter.tsx | 4 +- .../src/ScatterChart/ScatterChart.tsx | 9 +- .../x-charts/src/ScatterChart/ScatterPlot.tsx | 25 +---- .../x-charts/src/ScatterChart/extremums.ts | 16 +++- .../x-charts/src/ScatterChart/formatter.ts | 3 +- .../src/Tooltip/AxisTooltipContent.tsx | 3 +- packages/x-charts/src/XAxis/XAxis.tsx | 65 ++----------- packages/x-charts/src/YAxis/YAxis.tsx | 66 ++----------- .../src/context/CartesianContextProvider.tsx | 37 +++++--- .../src/context/SeriesContextProvider.tsx | 4 +- .../internals/defaultizeCartesianSeries.ts | 27 ------ packages/x-charts/src/models/axis.ts | 72 ++++++++++++-- .../x-charts/src/models/seriesType/bar.ts | 4 +- .../x-charts/src/models/seriesType/common.ts | 3 +- .../x-charts/src/models/seriesType/config.ts | 8 +- .../x-charts/src/models/seriesType/index.ts | 2 +- .../x-charts/src/models/seriesType/line.ts | 4 +- .../x-charts/src/models/seriesType/scatter.ts | 4 +- 64 files changed, 303 insertions(+), 515 deletions(-) create mode 100644 docs/data/charts/bars/TestBars.tsx.preview create mode 100644 docs/data/charts/overview/SimpleCharts.tsx.preview create mode 100644 docs/data/charts/scatter/BasicScatterChart.tsx.preview delete mode 100644 packages/x-charts/src/internals/defaultizeCartesianSeries.ts diff --git a/docs/data/charts/axis/AxisWithComposition.js b/docs/data/charts/axis/AxisWithComposition.js index 8630d179494a7..063f08f00bb05 100644 --- a/docs/data/charts/axis/AxisWithComposition.js +++ b/docs/data/charts/axis/AxisWithComposition.js @@ -9,6 +9,7 @@ export default function AxisWithComposition() { scaleType: 'band', data: ['Q1', 'Q2', 'Q3', 'Q4'], id: 'quarters', + label: 'Quarters', }, ]} yAxis={[{ id: 'money' }, { id: 'quantities' }]} @@ -16,21 +17,18 @@ export default function AxisWithComposition() { { type: 'line', id: 'revenue', - xAxisKey: 'quarters', yAxisKey: 'money', data: [5645, 7542, 9135, 12221], }, { type: 'bar', id: 'cookies', - xAxisKey: 'quarters', yAxisKey: 'quantities', data: [3205, 2542, 3135, 8374], }, { type: 'bar', id: 'icecream', - xAxisKey: 'quarters', yAxisKey: 'quantities', data: [1645, 5542, 5146, 3735], }, diff --git a/docs/data/charts/axis/AxisWithComposition.tsx b/docs/data/charts/axis/AxisWithComposition.tsx index 8630d179494a7..063f08f00bb05 100644 --- a/docs/data/charts/axis/AxisWithComposition.tsx +++ b/docs/data/charts/axis/AxisWithComposition.tsx @@ -9,6 +9,7 @@ export default function AxisWithComposition() { scaleType: 'band', data: ['Q1', 'Q2', 'Q3', 'Q4'], id: 'quarters', + label: 'Quarters', }, ]} yAxis={[{ id: 'money' }, { id: 'quantities' }]} @@ -16,21 +17,18 @@ export default function AxisWithComposition() { { type: 'line', id: 'revenue', - xAxisKey: 'quarters', yAxisKey: 'money', data: [5645, 7542, 9135, 12221], }, { type: 'bar', id: 'cookies', - xAxisKey: 'quarters', yAxisKey: 'quantities', data: [3205, 2542, 3135, 8374], }, { type: 'bar', id: 'icecream', - xAxisKey: 'quarters', yAxisKey: 'quantities', data: [1645, 5542, 5146, 3735], }, diff --git a/docs/data/charts/axis/MinMaxExample.js b/docs/data/charts/axis/MinMaxExample.js index b44bf5d6b57bd..cbae2eb0676db 100644 --- a/docs/data/charts/axis/MinMaxExample.js +++ b/docs/data/charts/axis/MinMaxExample.js @@ -45,9 +45,7 @@ export default function MinMaxExample() { ]} series={[ { - type: 'scatter', id: 'linear', - xAxisKey: 'x', data, }, ]} diff --git a/docs/data/charts/axis/MinMaxExample.tsx b/docs/data/charts/axis/MinMaxExample.tsx index 80626cce68a41..11e1fff8d9aac 100644 --- a/docs/data/charts/axis/MinMaxExample.tsx +++ b/docs/data/charts/axis/MinMaxExample.tsx @@ -49,9 +49,7 @@ export default function MinMaxExample() { ]} series={[ { - type: 'scatter', id: 'linear', - xAxisKey: 'x', data, }, ]} diff --git a/docs/data/charts/axis/ModifyAxisPosition.js b/docs/data/charts/axis/ModifyAxisPosition.js index 49f15d8a414b7..1772c680ce2cc 100644 --- a/docs/data/charts/axis/ModifyAxisPosition.js +++ b/docs/data/charts/axis/ModifyAxisPosition.js @@ -15,7 +15,6 @@ export default function ModifyAxisPosition() { \ No newline at end of file diff --git a/docs/data/charts/lines/BasicLineChart.js b/docs/data/charts/lines/BasicLineChart.js index 4bba528b48e08..ec57da2280b69 100644 --- a/docs/data/charts/lines/BasicLineChart.js +++ b/docs/data/charts/lines/BasicLineChart.js @@ -7,9 +7,7 @@ export default function BasicLineChart() { xAxis={[{ id: 'x', data: [1, 2, 3, 4, 8, 10] }]} series={[ { - type: 'line', id: 'y', - xAxisKey: 'x', data: [0, 5, 2, 8, 1, 1], }, ]} diff --git a/docs/data/charts/lines/BasicLineChart.tsx b/docs/data/charts/lines/BasicLineChart.tsx index 4bba528b48e08..ec57da2280b69 100644 --- a/docs/data/charts/lines/BasicLineChart.tsx +++ b/docs/data/charts/lines/BasicLineChart.tsx @@ -7,9 +7,7 @@ export default function BasicLineChart() { xAxis={[{ id: 'x', data: [1, 2, 3, 4, 8, 10] }]} series={[ { - type: 'line', id: 'y', - xAxisKey: 'x', data: [0, 5, 2, 8, 1, 1], }, ]} diff --git a/docs/data/charts/lines/BasicLineChart.tsx.preview b/docs/data/charts/lines/BasicLineChart.tsx.preview index 5bb74159d44aa..79f036458a192 100644 --- a/docs/data/charts/lines/BasicLineChart.tsx.preview +++ b/docs/data/charts/lines/BasicLineChart.tsx.preview @@ -2,9 +2,7 @@ xAxis={[{ id: 'x', data: [1, 2, 3, 4, 8, 10] }]} series={[ { - type: 'line', id: 'y', - xAxisKey: 'x', data: [0, 5, 2, 8, 1, 1], }, ]} diff --git a/docs/data/charts/lines/CSSCustomization.js b/docs/data/charts/lines/CSSCustomization.js index 61ed3da3f1cf0..c4e60aea7bc01 100644 --- a/docs/data/charts/lines/CSSCustomization.js +++ b/docs/data/charts/lines/CSSCustomization.js @@ -75,25 +75,19 @@ export default function CSSCustomization() { ]} series={[ { - type: 'line', id: 'France', - xAxisKey: 'Years', data: FranceGDPperCapita, stack: 'total', area: {}, }, { - type: 'line', id: 'Germany', - xAxisKey: 'Years', data: GermanyGDPperCapita, stack: 'total', area: {}, }, { - type: 'line', id: 'United Kingdom', - xAxisKey: 'Years', data: UKGDPperCapita, stack: 'total', area: {}, diff --git a/docs/data/charts/lines/CSSCustomization.tsx b/docs/data/charts/lines/CSSCustomization.tsx index 61ed3da3f1cf0..c4e60aea7bc01 100644 --- a/docs/data/charts/lines/CSSCustomization.tsx +++ b/docs/data/charts/lines/CSSCustomization.tsx @@ -75,25 +75,19 @@ export default function CSSCustomization() { ]} series={[ { - type: 'line', id: 'France', - xAxisKey: 'Years', data: FranceGDPperCapita, stack: 'total', area: {}, }, { - type: 'line', id: 'Germany', - xAxisKey: 'Years', data: GermanyGDPperCapita, stack: 'total', area: {}, }, { - type: 'line', id: 'United Kingdom', - xAxisKey: 'Years', data: UKGDPperCapita, stack: 'total', area: {}, diff --git a/docs/data/charts/lines/StackedAreas.js b/docs/data/charts/lines/StackedAreas.js index 65f3b963fea8b..12c483d42cf17 100644 --- a/docs/data/charts/lines/StackedAreas.js +++ b/docs/data/charts/lines/StackedAreas.js @@ -67,28 +67,22 @@ export default function StackedAreas() { ]} series={[ { - type: 'line', id: 'France', - label: 'French GDP per capite', - xAxisKey: 'Years', + label: 'French GDP per capita', data: FranceGDPperCapita, stack: 'total', area: {}, }, { - type: 'line', id: 'Germany', - label: 'German GDP per capite', - xAxisKey: 'Years', + label: 'German GDP per capita', data: GermanyGDPperCapita, stack: 'total', area: {}, }, { - type: 'line', id: 'United Kingdom', - label: 'UK GDP per capite', - xAxisKey: 'Years', + label: 'UK GDP per capita', data: UKGDPperCapita, stack: 'total', area: {}, diff --git a/docs/data/charts/lines/StackedAreas.tsx b/docs/data/charts/lines/StackedAreas.tsx index 65f3b963fea8b..12c483d42cf17 100644 --- a/docs/data/charts/lines/StackedAreas.tsx +++ b/docs/data/charts/lines/StackedAreas.tsx @@ -67,28 +67,22 @@ export default function StackedAreas() { ]} series={[ { - type: 'line', id: 'France', - label: 'French GDP per capite', - xAxisKey: 'Years', + label: 'French GDP per capita', data: FranceGDPperCapita, stack: 'total', area: {}, }, { - type: 'line', id: 'Germany', - label: 'German GDP per capite', - xAxisKey: 'Years', + label: 'German GDP per capita', data: GermanyGDPperCapita, stack: 'total', area: {}, }, { - type: 'line', id: 'United Kingdom', - label: 'UK GDP per capite', - xAxisKey: 'Years', + label: 'UK GDP per capita', data: UKGDPperCapita, stack: 'total', area: {}, diff --git a/docs/data/charts/lines/TestLines.js b/docs/data/charts/lines/TestLines.js index 72415a2c46656..8ebce118e9fb9 100644 --- a/docs/data/charts/lines/TestLines.js +++ b/docs/data/charts/lines/TestLines.js @@ -18,26 +18,14 @@ export default function TestLines() { ]} series={[ { - type: 'line', - id: 's1', stack: '1', area: {}, - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', data: [2, 5, 3, 4, 1], }, { - type: 'line', - id: 's2', - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', data: [10, 3, 1, 2, 10], }, { - type: 'line', - id: 's3', - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', stack: '1', area: {}, data: [10, 3, 1, 2, 10], diff --git a/docs/data/charts/lines/TestLines.tsx b/docs/data/charts/lines/TestLines.tsx index 72415a2c46656..8ebce118e9fb9 100644 --- a/docs/data/charts/lines/TestLines.tsx +++ b/docs/data/charts/lines/TestLines.tsx @@ -18,26 +18,14 @@ export default function TestLines() { ]} series={[ { - type: 'line', - id: 's1', stack: '1', area: {}, - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', data: [2, 5, 3, 4, 1], }, { - type: 'line', - id: 's2', - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', data: [10, 3, 1, 2, 10], }, { - type: 'line', - id: 's3', - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', stack: '1', area: {}, data: [10, 3, 1, 2, 10], diff --git a/docs/data/charts/lines/TestLinesTime.js b/docs/data/charts/lines/TestLinesTime.js index 8df283a4334ab..d7d5fb08d8483 100644 --- a/docs/data/charts/lines/TestLinesTime.js +++ b/docs/data/charts/lines/TestLinesTime.js @@ -25,26 +25,17 @@ export default function TestLinesTime() { ]} series={[ { - type: 'line', id: 's1', stack: '1', area: {}, - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', data: [2, 5, 3, 4, 1], }, { - type: 'line', id: 's2', - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', data: [10, 3, 1, 2, 10], }, { - type: 'line', id: 's3', - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', stack: '1', area: {}, data: [10, 3, 1, 2, 10], diff --git a/docs/data/charts/lines/TestLinesTime.tsx b/docs/data/charts/lines/TestLinesTime.tsx index 8df283a4334ab..d7d5fb08d8483 100644 --- a/docs/data/charts/lines/TestLinesTime.tsx +++ b/docs/data/charts/lines/TestLinesTime.tsx @@ -25,26 +25,17 @@ export default function TestLinesTime() { ]} series={[ { - type: 'line', id: 's1', stack: '1', area: {}, - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', data: [2, 5, 3, 4, 1], }, { - type: 'line', id: 's2', - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', data: [10, 3, 1, 2, 10], }, { - type: 'line', id: 's3', - xAxisKey: 'lineCategories', - yAxisKey: 'lineCategoriesY', stack: '1', area: {}, data: [10, 3, 1, 2, 10], diff --git a/docs/data/charts/overview/Combining.js b/docs/data/charts/overview/Combining.js index 5eb9d1cdb9f32..6f10c6845183f 100644 --- a/docs/data/charts/overview/Combining.js +++ b/docs/data/charts/overview/Combining.js @@ -9,26 +9,20 @@ import { YAxis } from '@mui/x-charts/YAxis'; const series = [ { type: 'bar', - id: 'Eco-1', stack: '', - xAxisKey: 'years', yAxisKey: 'eco', color: 'red', data: [2, 5, 3, 4, 1], }, { type: 'bar', - id: 'Eco-2', stack: '', - xAxisKey: 'years', yAxisKey: 'eco', color: 'blue', data: [5, 6, 2, 8, 9], }, { type: 'line', - id: 'pib', - xAxisKey: 'years', yAxisKey: 'pib', data: [1000, 1500, 3000, 5000, 10000], }, diff --git a/docs/data/charts/overview/Combining.tsx b/docs/data/charts/overview/Combining.tsx index d0e0225cf89f2..c06d932b1c767 100644 --- a/docs/data/charts/overview/Combining.tsx +++ b/docs/data/charts/overview/Combining.tsx @@ -9,26 +9,20 @@ import { YAxis } from '@mui/x-charts/YAxis'; const series = [ { type: 'bar', - id: 'Eco-1', stack: '', - xAxisKey: 'years', yAxisKey: 'eco', color: 'red', data: [2, 5, 3, 4, 1], }, { type: 'bar', - id: 'Eco-2', stack: '', - xAxisKey: 'years', yAxisKey: 'eco', color: 'blue', data: [5, 6, 2, 8, 9], }, { type: 'line', - id: 'pib', - xAxisKey: 'years', yAxisKey: 'pib', data: [1000, 1500, 3000, 5000, 10000], }, diff --git a/docs/data/charts/overview/SimpleCharts.js b/docs/data/charts/overview/SimpleCharts.js index 6a87c341498f7..668755a522daf 100644 --- a/docs/data/charts/overview/SimpleCharts.js +++ b/docs/data/charts/overview/SimpleCharts.js @@ -13,9 +13,6 @@ export default function SimpleCharts() { ]} series={[ { - type: 'bar', - id: 'bar-id-1', - xAxisKey: 'barCategories', data: [2, 5, 3], }, ]} diff --git a/docs/data/charts/overview/SimpleCharts.tsx b/docs/data/charts/overview/SimpleCharts.tsx index 6a87c341498f7..668755a522daf 100644 --- a/docs/data/charts/overview/SimpleCharts.tsx +++ b/docs/data/charts/overview/SimpleCharts.tsx @@ -13,9 +13,6 @@ export default function SimpleCharts() { ]} series={[ { - type: 'bar', - id: 'bar-id-1', - xAxisKey: 'barCategories', data: [2, 5, 3], }, ]} diff --git a/docs/data/charts/overview/SimpleCharts.tsx.preview b/docs/data/charts/overview/SimpleCharts.tsx.preview new file mode 100644 index 0000000000000..ea2a0b50f5448 --- /dev/null +++ b/docs/data/charts/overview/SimpleCharts.tsx.preview @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/docs/data/charts/scatter/BasicScatterChart.js b/docs/data/charts/scatter/BasicScatterChart.js index 8b2a46c60b839..cee6d338e9e90 100644 --- a/docs/data/charts/scatter/BasicScatterChart.js +++ b/docs/data/charts/scatter/BasicScatterChart.js @@ -172,14 +172,12 @@ export default function BasicScatterChart() { height={400} series={[ { - type: 'scatter', id: 's1', markerSize: 5, data: data.map(({ x1, y1, id }) => ({ x: x1, y: y1, id })), }, { id: 's2', - type: 'scatter', data: data.map(({ x1, y2, id }) => ({ x: x1, y: y2, id })), }, ]} diff --git a/docs/data/charts/scatter/BasicScatterChart.tsx b/docs/data/charts/scatter/BasicScatterChart.tsx index 8b2a46c60b839..cee6d338e9e90 100644 --- a/docs/data/charts/scatter/BasicScatterChart.tsx +++ b/docs/data/charts/scatter/BasicScatterChart.tsx @@ -172,14 +172,12 @@ export default function BasicScatterChart() { height={400} series={[ { - type: 'scatter', id: 's1', markerSize: 5, data: data.map(({ x1, y1, id }) => ({ x: x1, y: y1, id })), }, { id: 's2', - type: 'scatter', data: data.map(({ x1, y2, id }) => ({ x: x1, y: y2, id })), }, ]} diff --git a/docs/data/charts/scatter/BasicScatterChart.tsx.preview b/docs/data/charts/scatter/BasicScatterChart.tsx.preview new file mode 100644 index 0000000000000..0dec56c92c4ee --- /dev/null +++ b/docs/data/charts/scatter/BasicScatterChart.tsx.preview @@ -0,0 +1,15 @@ + ({ x: x1, y: y1, id })), + }, + { + id: 's2', + data: data.map(({ x1, y2, id }) => ({ x: x1, y: y2, id })), + }, + ]} +/> \ No newline at end of file diff --git a/docs/data/charts/scatter/Composition.js b/docs/data/charts/scatter/Composition.js index 613259386e369..090f9ec6f032e 100644 --- a/docs/data/charts/scatter/Composition.js +++ b/docs/data/charts/scatter/Composition.js @@ -12,7 +12,6 @@ export default function Composition() { { type: 'scatter', id: 's1', - yAxisKey: 'leftAxis', markerSize: 5, data: [ { x: 0, y: 0, id: 0 }, diff --git a/docs/data/charts/scatter/Composition.tsx b/docs/data/charts/scatter/Composition.tsx index fdbd334ad9b37..090f9ec6f032e 100644 --- a/docs/data/charts/scatter/Composition.tsx +++ b/docs/data/charts/scatter/Composition.tsx @@ -12,7 +12,6 @@ export default function Composition() { { type: 'scatter', id: 's1', - yAxisKey: 'leftAxis', markerSize: 5, data: [ { x: 0, y: 0, id: 0 }, @@ -43,7 +42,6 @@ export default function Composition() { yAxis={[ { id: 'leftAxis', - max: 4, scaleType: 'sqrt', }, diff --git a/docs/data/charts/scatter/ScatterChartAxis.js b/docs/data/charts/scatter/ScatterChartAxis.js index 8a980c7360b6f..9955ea5024c45 100644 --- a/docs/data/charts/scatter/ScatterChartAxis.js +++ b/docs/data/charts/scatter/ScatterChartAxis.js @@ -15,7 +15,6 @@ export default function ScatterChartAxis() { ]} series={[ { - type: 'scatter', id: 's1', yAxisKey: 'leftAxis', markerSize: 5, @@ -28,7 +27,6 @@ export default function ScatterChartAxis() { ], }, { - type: 'scatter', id: 's2', markerSize: 5, data: [ diff --git a/docs/data/charts/scatter/ScatterChartAxis.tsx b/docs/data/charts/scatter/ScatterChartAxis.tsx index 8a980c7360b6f..9955ea5024c45 100644 --- a/docs/data/charts/scatter/ScatterChartAxis.tsx +++ b/docs/data/charts/scatter/ScatterChartAxis.tsx @@ -15,7 +15,6 @@ export default function ScatterChartAxis() { ]} series={[ { - type: 'scatter', id: 's1', yAxisKey: 'leftAxis', markerSize: 5, @@ -28,7 +27,6 @@ export default function ScatterChartAxis() { ], }, { - type: 'scatter', id: 's2', markerSize: 5, data: [ diff --git a/packages/x-charts/src/Axis/Axis.tsx b/packages/x-charts/src/Axis/Axis.tsx index e9f81dec252ec..31c0a5e6cf403 100644 --- a/packages/x-charts/src/Axis/Axis.tsx +++ b/packages/x-charts/src/Axis/Axis.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { CartesianContext } from '../context/CartesianContextProvider'; -import { XAxis, XAxisProps } from '../XAxis'; -import { YAxis, YAxisProps } from '../YAxis'; +import { XAxis } from '../XAxis'; +import { YAxis } from '../YAxis'; +import { XAxisProps, YAxisProps } from '../models/axis'; export interface AxisProps { /** diff --git a/packages/x-charts/src/BarChart/BarChart.tsx b/packages/x-charts/src/BarChart/BarChart.tsx index 22d5e6e5bf5d6..11567f1cb7ab5 100644 --- a/packages/x-charts/src/BarChart/BarChart.tsx +++ b/packages/x-charts/src/BarChart/BarChart.tsx @@ -2,8 +2,13 @@ import * as React from 'react'; import { BarPlot } from './BarPlot'; import { ChartContainer, ChartContainerProps } from '../ChartContainer'; import { Axis, AxisProps } from '../Axis'; +import { BarSeriesType } from '../models/seriesType/bar'; +import { MakeOptional } from '../models/helpers'; +import { DEFAULT_X_AXIS_KEY } from '../constants'; -export interface BarChartProps extends ChartContainerProps, AxisProps {} +export interface BarChartProps extends Omit, AxisProps { + series: MakeOptional[]; +} export function BarChart(props: BarChartProps) { const { @@ -25,11 +30,21 @@ export function BarChart(props: BarChartProps) { return ( ({ type: 'bar', ...s }))} width={width} height={height} margin={margin} - xAxis={xAxis} + xAxis={ + xAxis ?? [ + { + id: DEFAULT_X_AXIS_KEY, + scaleType: 'band', + data: [...new Array(Math.max(...series.map((s) => s.data.length)))].map( + (_, index) => index, + ), + }, + ] + } yAxis={yAxis} colors={colors} sx={sx} diff --git a/packages/x-charts/src/BarChart/BarPlot.tsx b/packages/x-charts/src/BarChart/BarPlot.tsx index a9adb40baafb0..df86a154f30a4 100644 --- a/packages/x-charts/src/BarChart/BarPlot.tsx +++ b/packages/x-charts/src/BarChart/BarPlot.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { SeriesContext } from '../context/SeriesContextProvider'; -import { BarSeriesType } from '../models/seriesType'; import { CartesianContext } from '../context/CartesianContextProvider'; import { isBandScale } from '../hooks/useScale'; import { useInteractionItemProps } from '../hooks/useInteractionItemProps'; @@ -13,68 +12,54 @@ export function BarPlot() { if (seriesData === undefined) { return null; } - const { series, seriesOrder, stackingGroups } = seriesData; - const { xAxis, yAxis } = axisData; - - const seriesPerAxis: { [key: string]: BarSeriesType[] } = {}; - - seriesOrder.forEach((seriesId) => { - const xAxisKey = series[seriesId].xAxisKey; // ?? DEFAULT_X_AXIS_KEY; - const yAxisKey = series[seriesId].yAxisKey; // ?? DEFAULT_Y_AXIS_KEY; - - const key = `${xAxisKey}-${yAxisKey}`; - - if (seriesPerAxis[key] === undefined) { - seriesPerAxis[key] = [series[seriesId]]; - } else { - seriesPerAxis[key].push(series[seriesId]); - } - }); + const { series, stackingGroups } = seriesData; + const { xAxis, yAxis, xAxisIds, yAxisIds } = axisData; + const defaultXAxisId = xAxisIds[0]; + const defaultYAxisId = yAxisIds[0]; return ( - {Object.keys(seriesPerAxis).flatMap((key) => { - const [xAxisKey, yAxisKey] = key.split('-'); + {stackingGroups.flatMap((groupIds, groupIndex) => { + return groupIds.flatMap((seriesId) => { + const xAxisKey = series[seriesId].xAxisKey ?? defaultXAxisId; + const yAxisKey = series[seriesId].yAxisKey ?? defaultYAxisId; - const xScale = xAxis[xAxisKey].scale; - const yScale = yAxis[yAxisKey].scale; + const xScale = xAxis[xAxisKey].scale; + const yScale = yAxis[yAxisKey].scale; - if (!isBandScale(xScale)) { - throw new Error( - `Axis with id "${xAxisKey}" shoud be of type "band" to display the bar series ${stackingGroups}`, - ); - } + if (!isBandScale(xScale)) { + throw new Error( + `Axis with id "${xAxisKey}" shoud be of type "band" to display the bar series ${stackingGroups}`, + ); + } - if (xAxis[xAxisKey].data === undefined) { - throw new Error(`Axis with id "${xAxisKey}" shoud have data property`); - } + if (xAxis[xAxisKey].data === undefined) { + throw new Error(`Axis with id "${xAxisKey}" shoud have data property`); + } - // Currently assuming all bars are vertical - const bandWidth = xScale.bandwidth(); - const barWidth = (0.9 * bandWidth) / stackingGroups.length; - const offset = 0.05 * bandWidth; + // Currently assuming all bars are vertical + const bandWidth = xScale.bandwidth(); + const barWidth = (0.9 * bandWidth) / stackingGroups.length; + const offset = 0.05 * bandWidth; - return stackingGroups.flatMap((groupIds, groupIndex) => { - return groupIds.flatMap((seriesId) => { - // @ts-ignore TODO: fix when adding a correct API for customisation - const { stackedData, color } = series[seriesId]; + // @ts-ignore TODO: fix when adding a correct API for customisation + const { stackedData, color } = series[seriesId]; - return stackedData.map((values, dataIndex: number) => { - const baseline = Math.min(...values); - const value = Math.max(...values); - return ( - - ); - }); + return stackedData.map((values, dataIndex: number) => { + const baseline = Math.min(...values); + const value = Math.max(...values); + return ( + + ); }); }); })} diff --git a/packages/x-charts/src/BarChart/extremums.ts b/packages/x-charts/src/BarChart/extremums.ts index 0091f7fe67847..023d15e3ebc1d 100644 --- a/packages/x-charts/src/BarChart/extremums.ts +++ b/packages/x-charts/src/BarChart/extremums.ts @@ -9,10 +9,14 @@ export const getExtremumX: ExtremumGetter<'bar'> = (params) => { }; export const getExtremumY: ExtremumGetter<'bar'> = (params) => { - const { series, axis } = params; + const { series, axis, isDefaultAxis } = params; return Object.keys(series) - .filter((seriesId) => series[seriesId].yAxisKey === axis.id) + .filter( + (seriesId) => + series[seriesId].yAxisKey === axis.id || + (isDefaultAxis && series[seriesId].yAxisKey === undefined), + ) .reduce( (acc: ExtremumGetterResult, seriesId) => { const [seriesMin, seriesMax] = series[seriesId].stackedData.reduce( diff --git a/packages/x-charts/src/BarChart/formatter.ts b/packages/x-charts/src/BarChart/formatter.ts index a6d633e9faf51..554ef68e4c703 100644 --- a/packages/x-charts/src/BarChart/formatter.ts +++ b/packages/x-charts/src/BarChart/formatter.ts @@ -3,7 +3,6 @@ import { stackOrderNone as d3StackOrderNone, stackOffsetNone as d3StackOffsetNone, } from 'd3-shape'; -import defaultizeCartesianSeries from '../internals/defaultizeCartesianSeries'; import { getStackingGroups } from '../internals/stackSeries'; import { ChartSeries, Formatter } from '../models/seriesType/config'; import defaultizeValueFormatter from '../internals/defaultizeValueFormatter'; @@ -44,9 +43,7 @@ const formatter: Formatter<'bar'> = (params) => { return { seriesOrder, stackingGroups, - series: defaultizeValueFormatter(defaultizeCartesianSeries(completedSeries), (v) => - v.toLocaleString(), - ), + series: defaultizeValueFormatter(completedSeries, (v) => v.toLocaleString()), }; }; diff --git a/packages/x-charts/src/LineChart/LineChart.tsx b/packages/x-charts/src/LineChart/LineChart.tsx index c5a90c7160122..97a4e4f95babe 100644 --- a/packages/x-charts/src/LineChart/LineChart.tsx +++ b/packages/x-charts/src/LineChart/LineChart.tsx @@ -2,8 +2,13 @@ import * as React from 'react'; import { LinePlot } from './LinePlot'; import { ChartContainer, ChartContainerProps } from '../ChartContainer'; import { Axis, AxisProps } from '../Axis/Axis'; +import { LineSeriesType } from '../models/seriesType/line'; +import { MakeOptional } from '../models/helpers'; +import { DEFAULT_X_AXIS_KEY } from '../constants'; -export interface LineChartProps extends ChartContainerProps, AxisProps {} +export interface LineChartProps extends Omit, AxisProps { + series: MakeOptional[]; +} export function LineChart(props: LineChartProps) { const { xAxis, @@ -24,11 +29,21 @@ export function LineChart(props: LineChartProps) { return ( ({ type: 'line', ...s }))} width={width} height={height} margin={margin} - xAxis={xAxis} + xAxis={ + xAxis ?? [ + { + id: DEFAULT_X_AXIS_KEY, + scaleType: 'band', + data: [...new Array(Math.max(...series.map((s) => s.data.length)))].map( + (_, index) => index, + ), + }, + ] + } yAxis={yAxis} colors={colors} sx={sx} diff --git a/packages/x-charts/src/LineChart/LinePlot.tsx b/packages/x-charts/src/LineChart/LinePlot.tsx index 65928d6e80950..3f0c3cf5e0f5c 100644 --- a/packages/x-charts/src/LineChart/LinePlot.tsx +++ b/packages/x-charts/src/LineChart/LinePlot.tsx @@ -1,13 +1,11 @@ import * as React from 'react'; import { line as d3Line, area as d3Area } from 'd3-shape'; import { SeriesContext } from '../context/SeriesContextProvider'; -import { LineSeriesType } from '../models/seriesType'; import { CartesianContext } from '../context/CartesianContextProvider'; import { LineElement } from './LineElement'; import { AreaElement } from './AreaElement'; import { useInteractionItemProps } from '../hooks/useInteractionItemProps'; import { MarkElement } from './MarkElement'; -import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; import { getValueToPositionMapper } from '../hooks/useScale'; import getCurveFactory from '../internals/getCurve'; @@ -20,23 +18,10 @@ export function LinePlot() { if (seriesData === undefined) { return null; } - const { series, seriesOrder, stackingGroups } = seriesData; - const { xAxis, yAxis } = axisData; - - const seriesPerAxis: { [key: string]: LineSeriesType[] } = {}; - - seriesOrder.forEach((seriesId) => { - const xAxisKey = series[seriesId].xAxisKey; // ?? DEFAULT_X_AXIS_KEY; - const yAxisKey = series[seriesId].yAxisKey; // ?? DEFAULT_Y_AXIS_KEY; - - const key = `${xAxisKey}-${yAxisKey}`; - - if (seriesPerAxis[key] === undefined) { - seriesPerAxis[key] = [series[seriesId]]; - } else { - seriesPerAxis[key].push(series[seriesId]); - } - }); + const { series, stackingGroups } = seriesData; + const { xAxis, yAxis, xAxisIds, yAxisIds } = axisData; + const defaultXAxisId = xAxisIds[0]; + const defaultYAxisId = yAxisIds[0]; return ( @@ -44,8 +29,8 @@ export function LinePlot() { {stackingGroups.flatMap((groupIds) => { return groupIds.flatMap((seriesId) => { const { - xAxisKey = DEFAULT_X_AXIS_KEY, - yAxisKey = DEFAULT_Y_AXIS_KEY, + xAxisKey = defaultXAxisId, + yAxisKey = defaultYAxisId, stackedData, } = series[seriesId]; @@ -87,8 +72,8 @@ export function LinePlot() { {stackingGroups.flatMap((groupIds) => { return groupIds.flatMap((seriesId) => { const { - xAxisKey = DEFAULT_X_AXIS_KEY, - yAxisKey = DEFAULT_Y_AXIS_KEY, + xAxisKey = defaultXAxisId, + yAxisKey = defaultYAxisId, stackedData, } = series[seriesId]; @@ -127,8 +112,8 @@ export function LinePlot() { {stackingGroups.flatMap((groupIds) => { return groupIds.flatMap((seriesId) => { const { - xAxisKey = DEFAULT_X_AXIS_KEY, - yAxisKey = DEFAULT_Y_AXIS_KEY, + xAxisKey = defaultXAxisId, + yAxisKey = defaultYAxisId, stackedData, } = series[seriesId]; diff --git a/packages/x-charts/src/LineChart/extremums.ts b/packages/x-charts/src/LineChart/extremums.ts index 542e775ceaf29..19a89147438c9 100644 --- a/packages/x-charts/src/LineChart/extremums.ts +++ b/packages/x-charts/src/LineChart/extremums.ts @@ -9,10 +9,14 @@ export const getExtremumX: ExtremumGetter<'line'> = (params) => { }; export const getExtremumY: ExtremumGetter<'line'> = (params) => { - const { series, axis } = params; + const { series, axis, isDefaultAxis } = params; return Object.keys(series) - .filter((seriesId) => series[seriesId].yAxisKey === axis.id) + .filter( + (seriesId) => + series[seriesId].yAxisKey === axis.id || + (isDefaultAxis && series[seriesId].yAxisKey === undefined), + ) .reduce( (acc: ExtremumGetterResult, seriesId) => { const isArea = series[seriesId].area !== undefined; diff --git a/packages/x-charts/src/LineChart/formatter.ts b/packages/x-charts/src/LineChart/formatter.ts index 79a871600e112..6394f708e04d4 100644 --- a/packages/x-charts/src/LineChart/formatter.ts +++ b/packages/x-charts/src/LineChart/formatter.ts @@ -3,7 +3,6 @@ import { stackOrderNone as d3StackOrderNone, stackOffsetNone as d3StackOffsetNone, } from 'd3-shape'; -import defaultizeCartesianSeries from '../internals/defaultizeCartesianSeries'; import { getStackingGroups } from '../internals/stackSeries'; import { ChartSeries, Formatter } from '../models/seriesType/config'; import defaultizeValueFormatter from '../internals/defaultizeValueFormatter'; @@ -45,10 +44,7 @@ const formatter: Formatter<'line'> = (params) => { return { seriesOrder, stackingGroups, - series: defaultizeValueFormatter( - defaultizeCartesianSeries>(completedSeries), - (v) => v.toLocaleString(), - ), + series: defaultizeValueFormatter(completedSeries, (v) => v.toLocaleString()), }; }; diff --git a/packages/x-charts/src/ScatterChart/Scatter.tsx b/packages/x-charts/src/ScatterChart/Scatter.tsx index 8e91b7895bb40..faaa4c8560de6 100644 --- a/packages/x-charts/src/ScatterChart/Scatter.tsx +++ b/packages/x-charts/src/ScatterChart/Scatter.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { ScatterSeriesType } from '../models/seriesType/scatter'; +import { DefaultizedScatterSeriesType } from '../models/seriesType/scatter'; import { D3Scale, getValueToPositionMapper } from '../hooks/useScale'; import { useInteractionItemProps } from '../hooks/useInteractionItemProps'; export interface ScatterProps { - series: ScatterSeriesType; + series: DefaultizedScatterSeriesType; xScale: D3Scale; yScale: D3Scale; markerSize: number; diff --git a/packages/x-charts/src/ScatterChart/ScatterChart.tsx b/packages/x-charts/src/ScatterChart/ScatterChart.tsx index 5725d5843001f..38acce09c2429 100644 --- a/packages/x-charts/src/ScatterChart/ScatterChart.tsx +++ b/packages/x-charts/src/ScatterChart/ScatterChart.tsx @@ -2,8 +2,13 @@ import * as React from 'react'; import { ScatterPlot } from './ScatterPlot'; import { ChartContainer, ChartContainerProps } from '../ChartContainer'; import { Axis, AxisProps } from '../Axis'; +import { ScatterSeriesType } from '../models/seriesType/scatter'; +import { MakeOptional } from '../models/helpers'; + +export interface ScatterChartProps extends Omit, AxisProps { + series: MakeOptional[]; +} -export interface ScatterChartProps extends ChartContainerProps, AxisProps {} export function ScatterChart(props: ScatterChartProps) { const { xAxis, @@ -23,7 +28,7 @@ export function ScatterChart(props: ScatterChartProps) { return ( ({ type: 'scatter', ...s }))} width={width} height={height} margin={margin} diff --git a/packages/x-charts/src/ScatterChart/ScatterPlot.tsx b/packages/x-charts/src/ScatterChart/ScatterPlot.tsx index 4638e172bf55f..1d84543cce95e 100644 --- a/packages/x-charts/src/ScatterChart/ScatterPlot.tsx +++ b/packages/x-charts/src/ScatterChart/ScatterPlot.tsx @@ -1,9 +1,7 @@ import * as React from 'react'; import { Scatter } from './Scatter'; import { SeriesContext } from '../context/SeriesContextProvider'; -import { ScatterSeriesType } from '../models/seriesType'; import { CartesianContext } from '../context/CartesianContextProvider'; -import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; export function ScatterPlot() { const seriesData = React.useContext(SeriesContext).scatter; @@ -13,30 +11,17 @@ export function ScatterPlot() { return null; } const { series, seriesOrder } = seriesData; - const { xAxis, yAxis } = axisData; - - const seriesPerAxis: { [key: string]: ScatterSeriesType[] } = {}; - - seriesOrder.forEach((seriesId) => { - const xAxisKey = series[seriesId].xAxisKey; - const yAxisKey = series[seriesId].yAxisKey; - - const key = `${xAxisKey}-${yAxisKey}`; - - if (seriesPerAxis[key] === undefined) { - seriesPerAxis[key] = [series[seriesId]]; - } else { - seriesPerAxis[key].push(series[seriesId]); - } - }); + const { xAxis, yAxis, xAxisIds, yAxisIds } = axisData; + const defaultXAxisId = xAxisIds[0]; + const defaultYAxisId = yAxisIds[0]; return ( {seriesOrder.map((seriesId) => { const { id, xAxisKey, yAxisKey, markerSize, color } = series[seriesId]; - const xScale = xAxis[xAxisKey ?? DEFAULT_X_AXIS_KEY].scale; - const yScale = yAxis[yAxisKey ?? DEFAULT_Y_AXIS_KEY].scale; + const xScale = xAxis[xAxisKey ?? defaultXAxisId].scale; + const yScale = yAxis[yAxisKey ?? defaultYAxisId].scale; return ( = (params) => { - const { series, axis } = params; + const { series, axis, isDefaultAxis } = params; return Object.keys(series) - .filter((seriesId) => series[seriesId].xAxisKey === axis.id) + .filter( + (seriesId) => + series[seriesId].xAxisKey === axis.id || + (series[seriesId].xAxisKey === undefined && isDefaultAxis), + ) .reduce( (acc: ExtremumGetterResult, seriesId) => { const seriesMinMax = series[seriesId].data.reduce( @@ -34,10 +38,14 @@ export const getExtremumX: ExtremumGetter<'scatter'> = (params) => { }; export const getExtremumY: ExtremumGetter<'scatter'> = (params) => { - const { series, axis } = params; + const { series, axis, isDefaultAxis } = params; return Object.keys(series) - .filter((seriesId) => series[seriesId].yAxisKey === axis.id) + .filter( + (seriesId) => + series[seriesId].yAxisKey === axis.id || + (series[seriesId].yAxisKey === undefined && isDefaultAxis), + ) .reduce( (acc: ExtremumGetterResult, seriesId) => { const seriesMinMax = series[seriesId].data.reduce( diff --git a/packages/x-charts/src/ScatterChart/formatter.ts b/packages/x-charts/src/ScatterChart/formatter.ts index 7aad4fa359c52..d0db1a6ad0a5a 100644 --- a/packages/x-charts/src/ScatterChart/formatter.ts +++ b/packages/x-charts/src/ScatterChart/formatter.ts @@ -1,10 +1,9 @@ -import defaultizeCartesianSeries from '../internals/defaultizeCartesianSeries'; import defaultizeValueFormatter from '../internals/defaultizeValueFormatter'; import { Formatter } from '../models/seriesType/config'; const formatter: Formatter<'scatter'> = ({ series, seriesOrder }) => { return { - series: defaultizeValueFormatter(defaultizeCartesianSeries(series), (v) => `(${v.x}, ${v.y})`), + series: defaultizeValueFormatter(series, (v) => `(${v.x}, ${v.y})`), seriesOrder, }; }; diff --git a/packages/x-charts/src/Tooltip/AxisTooltipContent.tsx b/packages/x-charts/src/Tooltip/AxisTooltipContent.tsx index 82a3028ddcae2..ae80c687f6bce 100644 --- a/packages/x-charts/src/Tooltip/AxisTooltipContent.tsx +++ b/packages/x-charts/src/Tooltip/AxisTooltipContent.tsx @@ -86,7 +86,8 @@ export function AxisTooltipContent(props: { const rep: any[] = []; (Object.keys(series) as (keyof FormattedSeries)[]).forEach((seriesType) => { series[seriesType]!.seriesOrder.forEach((seriesId) => { - if (series[seriesType]!.series[seriesId].xAxisKey === USED_X_AXIS_ID) { + const axisKey = series[seriesType]!.series[seriesId].xAxisKey; + if (axisKey === undefined || axisKey === USED_X_AXIS_ID) { rep.push(series[seriesType]!.series[seriesId]); } }); diff --git a/packages/x-charts/src/XAxis/XAxis.tsx b/packages/x-charts/src/XAxis/XAxis.tsx index cef03be632f01..748040941c9a3 100644 --- a/packages/x-charts/src/XAxis/XAxis.tsx +++ b/packages/x-charts/src/XAxis/XAxis.tsx @@ -1,63 +1,17 @@ import * as React from 'react'; -import { DEFAULT_X_AXIS_KEY } from '../constants'; import { CartesianContext } from '../context/CartesianContextProvider'; import { DrawingContext } from '../context/DrawingProvider'; import useTicks from '../hooks/useTicks'; - -export interface XAxisProps { - /** - * Position of the axis. - */ - position?: 'top' | 'bottom'; - /** - * Id of the axis to render. - */ - axisId: string; - /** - * If true, the axis line is disabled. - * @default false - */ - disableLine?: boolean; - /** - * If true, the ticks are disabled. - * @default false - */ - disableTicks?: boolean; - /** - * The fill color of the axis text. - * @default 'currentColor' - */ - fill?: string; - /** - * The font size of the axis text. - * @default 12 - */ - fontSize?: number; - /** - * The label of the axis. - */ - label?: string; - /** - * The font size of the axis label. - * @default 14 - */ - labelFontSize?: number; - /** - * The stroke color of the axis line. - * @default 'currentColor' - */ - stroke?: string; - /** - * The size of the ticks. - * @default 6 - */ - tickSize?: number; -} +import { XAxisProps } from '../models/axis'; export function XAxis(props: XAxisProps) { + const { + xAxis: { + [props.axisId]: { scale: xScale, ...settings }, + }, + } = React.useContext(CartesianContext); const { position = 'bottom', - axisId = DEFAULT_X_AXIS_KEY, disableLine = false, disableTicks = false, fill = 'currentColor', @@ -66,12 +20,7 @@ export function XAxis(props: XAxisProps) { labelFontSize = 14, stroke = 'currentColor', tickSize: tickSizeProp = 6, - } = props; - const { - xAxis: { - [axisId]: { scale: xScale }, - }, - } = React.useContext(CartesianContext) as any; + } = { ...settings, ...props }; const { left, top, width, height } = React.useContext(DrawingContext); diff --git a/packages/x-charts/src/YAxis/YAxis.tsx b/packages/x-charts/src/YAxis/YAxis.tsx index 8a4d11cf012d0..56949b3279640 100644 --- a/packages/x-charts/src/YAxis/YAxis.tsx +++ b/packages/x-charts/src/YAxis/YAxis.tsx @@ -1,63 +1,17 @@ import * as React from 'react'; -import { DEFAULT_Y_AXIS_KEY } from '../constants'; import { CartesianContext } from '../context/CartesianContextProvider'; import { DrawingContext } from '../context/DrawingProvider'; import useTicks from '../hooks/useTicks'; - -export interface YAxisProps { - /** - * Position of the axis. - */ - position?: 'left' | 'right'; - /** - * Id of the axis to render. - */ - axisId: string; - /** - * If true, the axis line is disabled. - * @default false - */ - disableLine?: boolean; - /** - * If true, the ticks are disabled. - * @default false - */ - disableTicks?: boolean; - /** - * The fill color of the axis text. - * @default 'currentColor' - */ - fill?: string; - /** - * The font size of the axis text. - * @default 12 - */ - fontSize?: number; - /** - * The label of the axis. - */ - label?: string; - /** - * The font size of the axis label. - * @default 14 - */ - labelFontSize?: number; - /** - * The stroke color of the axis line. - * @default 'currentColor' - */ - stroke?: string; - /** - * The size of the ticks. - * @default 6 - */ - tickSize?: number; -} +import { YAxisProps } from '../models/axis'; export function YAxis(props: YAxisProps) { + const { + yAxis: { + [props.axisId]: { scale: yScale, ...settings }, + }, + } = React.useContext(CartesianContext); const { position = 'left', - axisId = DEFAULT_Y_AXIS_KEY, disableLine = false, disableTicks = false, fill = 'currentColor', @@ -66,13 +20,7 @@ export function YAxis(props: YAxisProps) { labelFontSize = 14, stroke = 'currentColor', tickSize: tickSizeProp = 6, - } = props; - - const { - yAxis: { - [axisId]: { scale: yScale }, - }, - } = React.useContext(CartesianContext) as any; + } = { ...settings, ...props }; const { left, top, width, height } = React.useContext(DrawingContext); diff --git a/packages/x-charts/src/context/CartesianContextProvider.tsx b/packages/x-charts/src/context/CartesianContextProvider.tsx index 356771f9f43c3..546a22bde47e1 100644 --- a/packages/x-charts/src/context/CartesianContextProvider.tsx +++ b/packages/x-charts/src/context/CartesianContextProvider.tsx @@ -22,10 +22,11 @@ import { ExtremumGetter, ExtremumGetterResult, } from '../models/seriesType/config'; +import { MakeOptional } from '../models/helpers'; export type CartesianContextProviderProps = { - xAxis?: AxisConfig[]; - yAxis?: AxisConfig[]; + xAxis?: MakeOptional[]; + yAxis?: MakeOptional[]; children: React.ReactNode; }; @@ -75,6 +76,7 @@ export function CartesianContextProvider({ chartType: T, axis: AxisConfig, getters: { [T2 in ChartSeriesType]: ExtremumGetter }, + isDefaultAxis: boolean, ): ExtremumGetterResult => { const getter = getters[chartType]; const series = (formattedSeries[chartType]?.series as { [id: string]: ChartSeries }) ?? {}; @@ -82,6 +84,7 @@ export function CartesianContextProvider({ const [minChartTypeData, maxChartTypeData] = getter({ series, axis, + isDefaultAxis, }); const [minData, maxData] = acc; @@ -100,23 +103,28 @@ export function CartesianContextProvider({ const getAxisExtremum = ( axis: AxisConfig, getters: { [T in ChartSeriesType]: ExtremumGetter }, + isDefaultAxis: boolean, ) => { const charTypes = Object.keys(getters) as ChartSeriesType[]; return charTypes.reduce( - (acc, charType) => axisExtremumCallback(acc, charType, axis, getters), + (acc, charType) => axisExtremumCallback(acc, charType, axis, getters, isDefaultAxis), [null, null] as ExtremumGetterResult, ); }; const allXAxis: AxisConfig[] = [ - ...(xAxis ?? []), - { id: DEFAULT_X_AXIS_KEY, scaleType: 'linear' }, + ...(xAxis?.map((axis, index) => ({ id: `deaultized-x-axis-${index}`, ...axis })) ?? []), + // Allows to specify an axis with id=DEFAULT_X_AXIS_KEY + ...(xAxis === undefined || xAxis.findIndex(({ id }) => id === DEFAULT_X_AXIS_KEY) === -1 + ? [{ id: DEFAULT_X_AXIS_KEY, scaleType: 'linear' } as AxisConfig] + : []), ]; const completedXAxis: DefaultizedAxisConfig = {}; - allXAxis.forEach((axis) => { - const [minData, maxData] = getAxisExtremum(axis, xExtremumGetters); + allXAxis.forEach((axis, axisIndex) => { + const isDefaultAxis = axisIndex === 0; + const [minData, maxData] = getAxisExtremum(axis, xExtremumGetters, isDefaultAxis); const scaleType = axis.scaleType ?? 'linear'; completedXAxis[axis.id] = { @@ -131,13 +139,16 @@ export function CartesianContextProvider({ }); const allYAxis: AxisConfig[] = [ - ...(yAxis ?? []), - { id: DEFAULT_Y_AXIS_KEY, scaleType: 'linear' }, + ...(yAxis?.map((axis, index) => ({ id: `deaultized-y-axis-${index}`, ...axis })) ?? []), + ...(yAxis === undefined || yAxis.findIndex(({ id }) => id === DEFAULT_Y_AXIS_KEY) === -1 + ? [{ id: DEFAULT_Y_AXIS_KEY, scaleType: 'linear' } as AxisConfig] + : []), ]; const completedYAxis: DefaultizedAxisConfig = {}; - allYAxis.forEach((axis) => { - const [minData, maxData] = getAxisExtremum(axis, yExtremumGetters); + allYAxis.forEach((axis, axisIndex) => { + const isDefaultAxis = axisIndex === 0; + const [minData, maxData] = getAxisExtremum(axis, yExtremumGetters, isDefaultAxis); const scaleType: ScaleName = axis.scaleType ?? 'linear'; completedYAxis[axis.id] = { @@ -154,8 +165,8 @@ export function CartesianContextProvider({ return { xAxis: completedXAxis, yAxis: completedYAxis, - xAxisIds: [...(xAxis ?? []), { id: DEFAULT_X_AXIS_KEY }]?.map(({ id }) => id), - yAxisIds: [...(yAxis ?? []), { id: DEFAULT_Y_AXIS_KEY }]?.map(({ id }) => id), + xAxisIds: allXAxis.map(({ id }) => id), + yAxisIds: allYAxis.map(({ id }) => id), }; }, [ drawingArea.height, diff --git a/packages/x-charts/src/context/SeriesContextProvider.tsx b/packages/x-charts/src/context/SeriesContextProvider.tsx index db4e8b24c5350..c3c9731e9a1c9 100644 --- a/packages/x-charts/src/context/SeriesContextProvider.tsx +++ b/packages/x-charts/src/context/SeriesContextProvider.tsx @@ -26,7 +26,7 @@ const formatSeries = (series: AllSeriesType[], colors?: string[]) => { // Group series by type const seriesGroups: { [type in ChartSeriesType]?: FormatterParams } = {}; series.forEach((seriesData, seriesIndex: number) => { - const { id, type } = seriesData; + const { id = `auto-generated-id-${seriesIndex}`, type } = seriesData; if (seriesGroups[type] === undefined) { seriesGroups[type] = { series: {}, seriesOrder: [] }; @@ -34,7 +34,7 @@ const formatSeries = (series: AllSeriesType[], colors?: string[]) => { if (seriesGroups[type]?.series[id] !== undefined) { throw new Error(`MUI: series' id "${id}" is not unique`); } - seriesGroups[type]!.series[id] = defaultizeColor(seriesData, seriesIndex, colors); + seriesGroups[type]!.series[id] = { id, ...defaultizeColor(seriesData, seriesIndex, colors) }; seriesGroups[type]!.seriesOrder.push(id); }); diff --git a/packages/x-charts/src/internals/defaultizeCartesianSeries.ts b/packages/x-charts/src/internals/defaultizeCartesianSeries.ts deleted file mode 100644 index c97f77bb76658..0000000000000 --- a/packages/x-charts/src/internals/defaultizeCartesianSeries.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; - -function defaultizeCartesianSeries(series: { - [id: string]: ISeries; -}): { - [id: string]: ISeries & { - xAxisKey: string; - yAxisKey: string; - }; -} { - const defaultizedSeries: { - [id: string]: ISeries & { - xAxisKey: string; - yAxisKey: string; - }; - } = {}; - Object.keys(series).forEach((seriesId) => { - defaultizedSeries[seriesId] = { - xAxisKey: DEFAULT_X_AXIS_KEY, - yAxisKey: DEFAULT_Y_AXIS_KEY, - ...series[seriesId], - }; - }); - return defaultizedSeries; -} - -export default defaultizeCartesianSeries; diff --git a/packages/x-charts/src/models/axis.ts b/packages/x-charts/src/models/axis.ts index 9a08f435614ac..e993401daaed9 100644 --- a/packages/x-charts/src/models/axis.ts +++ b/packages/x-charts/src/models/axis.ts @@ -6,6 +6,67 @@ import type { ScaleTime, ScaleLinear, } from 'd3-scale'; +import { DefaultizedProps } from './helpers'; + +export interface AxisProps { + /** + * Id of the axis to render. + */ + axisId: string; + /** + * If true, the axis line is disabled. + * @default false + */ + disableLine?: boolean; + /** + * If true, the ticks are disabled. + * @default false + */ + disableTicks?: boolean; + /** + * The fill color of the axis text. + * @default 'currentColor' + */ + fill?: string; + /** + * The font size of the axis text. + * @default 12 + */ + fontSize?: number; + /** + * The label of the axis. + */ + label?: string; + /** + * The font size of the axis label. + * @default 14 + */ + labelFontSize?: number; + /** + * The stroke color of the axis line. + * @default 'currentColor' + */ + stroke?: string; + /** + * The size of the ticks. + * @default 6 + */ + tickSize?: number; +} + +export interface YAxisProps extends AxisProps { + /** + * Position of the axis. + */ + position?: 'left' | 'right'; +} + +export interface XAxisProps extends AxisProps { + /** + * Position of the axis. + */ + position?: 'top' | 'bottom'; +} export type ScaleName = 'linear' | 'band' | 'log' | 'point' | 'pow' | 'sqrt' | 'time' | 'utc'; @@ -42,12 +103,7 @@ export type AxisConfig = { max?: number; data?: V[]; valueFormatter?: (value: V) => string; -}; +} & Partial; -export type AxisDefaultized = { - id: string; - min?: number; - max?: number; - data?: V[]; - valueFormatter?: (value: V) => string; -} & AxisScaleMapping; +export type AxisDefaultized = DefaultizedProps, 'scaleType'> & + AxisScaleMapping; diff --git a/packages/x-charts/src/models/seriesType/bar.ts b/packages/x-charts/src/models/seriesType/bar.ts index b8b17478561b2..ef440a30dc0d2 100644 --- a/packages/x-charts/src/models/seriesType/bar.ts +++ b/packages/x-charts/src/models/seriesType/bar.ts @@ -14,10 +14,10 @@ export interface BarSeriesType extends CommonSeriesType, CartesianSeriesType { */ export type BarItemIdentifier = { type: 'bar'; - seriesId: BarSeriesType['id']; + seriesId: DefaultizedBarSeriesType['id']; dataIndex: number; }; export interface DefaultizedBarSeriesType - extends DefaultizedProps, + extends DefaultizedProps, DefaultizedCommonSeriesType {} diff --git a/packages/x-charts/src/models/seriesType/common.ts b/packages/x-charts/src/models/seriesType/common.ts index e9b83e60935a2..45206855294ed 100644 --- a/packages/x-charts/src/models/seriesType/common.ts +++ b/packages/x-charts/src/models/seriesType/common.ts @@ -1,8 +1,9 @@ export type CommonSeriesType = { - id: string; + id?: string; }; export type DefaultizedCommonSeriesType = { + id: string; color: string; /** * Formatter used to render values in tooltip or other data display. diff --git a/packages/x-charts/src/models/seriesType/config.ts b/packages/x-charts/src/models/seriesType/config.ts index fd02092ae7332..ed213ead5a64e 100644 --- a/packages/x-charts/src/models/seriesType/config.ts +++ b/packages/x-charts/src/models/seriesType/config.ts @@ -2,22 +2,23 @@ import { ScatterSeriesType, DefaultizedScatterSeriesType, ScatterItemIdentifier import { LineSeriesType, DefaultizedLineSeriesType, LineItemIdentifier } from './line'; import { BarItemIdentifier, BarSeriesType, DefaultizedBarSeriesType } from './bar'; import { AxisConfig } from '../axis'; +import { DefaultizedProps } from '../helpers'; interface ChartsSeriesConfig { bar: { - seriesInput: BarSeriesType & { color: string }; + seriesInput: DefaultizedProps & { color: string }; series: DefaultizedBarSeriesType; canBeStacked: true; itemIdentifier: BarItemIdentifier; }; line: { - seriesInput: LineSeriesType & { color: string }; + seriesInput: DefaultizedProps & { color: string }; series: DefaultizedLineSeriesType; canBeStacked: true; itemIdentifier: LineItemIdentifier; }; scatter: { - seriesInput: ScatterSeriesType & { color: string }; + seriesInput: DefaultizedProps & { color: string }; series: DefaultizedScatterSeriesType; itemIdentifier: ScatterItemIdentifier; }; @@ -40,6 +41,7 @@ export type ChartItemIdentifier = type ExtremumGetterParams = { series: { [id: string]: ChartSeries }; axis: AxisConfig; + isDefaultAxis: boolean; }; export type ExtremumGetterResult = [number, number] | [null, null]; diff --git a/packages/x-charts/src/models/seriesType/index.ts b/packages/x-charts/src/models/seriesType/index.ts index 1fe1285f29362..a38fb768d172b 100644 --- a/packages/x-charts/src/models/seriesType/index.ts +++ b/packages/x-charts/src/models/seriesType/index.ts @@ -13,7 +13,7 @@ type DefaultizedCartesianSeriesType = | DefaultizedLineSeriesType | DefaultizedScatterSeriesType; // | PieSeriesType -type StackableSeriesType = BarSeriesType | LineSeriesType; +type StackableSeriesType = DefaultizedBarSeriesType | DefaultizedLineSeriesType; export type { BarSeriesType, diff --git a/packages/x-charts/src/models/seriesType/line.ts b/packages/x-charts/src/models/seriesType/line.ts index d66d510f6cbba..26ac463af899e 100644 --- a/packages/x-charts/src/models/seriesType/line.ts +++ b/packages/x-charts/src/models/seriesType/line.ts @@ -17,7 +17,7 @@ export interface LineSeriesType extends CommonSeriesType, CartesianSeriesType { */ export type LineItemIdentifier = { type: 'line'; - seriesId: LineSeriesType['id']; + seriesId: DefaultizedLineSeriesType['id']; /** * `dataIndex` can be `undefined` if the mouse is over the area and not a specific item. */ @@ -25,5 +25,5 @@ export type LineItemIdentifier = { }; export interface DefaultizedLineSeriesType - extends DefaultizedProps, + extends DefaultizedProps, DefaultizedCommonSeriesType {} diff --git a/packages/x-charts/src/models/seriesType/scatter.ts b/packages/x-charts/src/models/seriesType/scatter.ts index e3c8553a75268..b0115627c0ad9 100644 --- a/packages/x-charts/src/models/seriesType/scatter.ts +++ b/packages/x-charts/src/models/seriesType/scatter.ts @@ -16,10 +16,10 @@ export interface ScatterSeriesType extends CommonSeriesType, CartesianSeriesType */ export type ScatterItemIdentifier = { type: 'scatter'; - seriesId: ScatterSeriesType['id']; + seriesId: DefaultizedScatterSeriesType['id']; dataIndex: number; }; export interface DefaultizedScatterSeriesType - extends DefaultizedProps, + extends DefaultizedProps, DefaultizedCommonSeriesType {} From 92641bddc23cf421058db8d8ced27cbf8b26cbc6 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Thu, 4 May 2023 17:00:14 +0200 Subject: [PATCH 66/80] [DataGridPremium] Fix expanded groups being collapsed after calling `updateRows` (#8823) --- .../rowGrouping.DataGridPremium.test.tsx | 20 +++++++++++++++++++ .../x-data-grid-pro/src/utils/tree/utils.ts | 4 +++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx index ee3463d14c6b5..7cc9bd9912331 100644 --- a/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx @@ -2634,4 +2634,24 @@ describe(' - Row Grouping', () => { await waitFor(() => expect(getCell(1, 3).textContent).to.equal('username 2')); }); + + // See https://github.com/mui/mui-x/issues/8580 + it('should not collapse expanded groups after `updateRows`', async () => { + render( + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'see children' })); + + act(() => apiRef.current.updateRows([{ id: 1, group: 'A', username: 'username 2' }])); + + await waitFor(() => { + expect(screen.getByRole('button', { name: 'hide children' })).toBeVisible(); + }); + await waitFor(() => expect(getCell(1, 3).textContent).to.equal('username 2')); + }); }); diff --git a/packages/grid/x-data-grid-pro/src/utils/tree/utils.ts b/packages/grid/x-data-grid-pro/src/utils/tree/utils.ts index cf01d96e52e8a..3ac79ae56b863 100644 --- a/packages/grid/x-data-grid-pro/src/utils/tree/utils.ts +++ b/packages/grid/x-data-grid-pro/src/utils/tree/utils.ts @@ -35,13 +35,15 @@ export const getNodePathInTree = ({ while (node.id !== GRID_ROOT_GROUP_ID) { path.push({ - field: (node as GridGroupNode).groupingField, + field: node.type === 'leaf' ? null : node.groupingField, key: node.groupingKey, }); node = tree[node.parent!] as GridGroupNode | GridLeafNode; } + path.reverse(); + return path; }; From 99345610727480f4c40ca9437cbfb24255ad7239 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 4 May 2023 18:40:19 -0400 Subject: [PATCH 67/80] [DataGrid] Remove unwarranted warning log (#8847) --- packages/grid/x-data-grid/src/components/GridPagination.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/grid/x-data-grid/src/components/GridPagination.tsx b/packages/grid/x-data-grid/src/components/GridPagination.tsx index 2271ca3d582bb..3dfaa26a3135e 100644 --- a/packages/grid/x-data-grid/src/components/GridPagination.tsx +++ b/packages/grid/x-data-grid/src/components/GridPagination.tsx @@ -62,10 +62,11 @@ export const GridPagination = React.forwardRef Date: Fri, 5 May 2023 11:27:12 +0200 Subject: [PATCH 68/80] v6.3.1 (#8866) --- CHANGELOG.md | 50 ++++++++++++++++++- package.json | 4 +- .../grid/x-data-grid-generator/package.json | 4 +- .../grid/x-data-grid-premium/package.json | 6 +-- packages/grid/x-data-grid-pro/package.json | 4 +- packages/grid/x-data-grid/package.json | 2 +- packages/x-date-pickers-pro/package.json | 4 +- packages/x-date-pickers/package.json | 2 +- scripts/README.md | 1 - 9 files changed, 61 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61da091068b6c..2e1abbc531063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,52 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 6.3.1 + +_May 5, 2023_ + +We'd like to offer a big thanks to the 7 contributors who made this release possible. Here are some highlights ✨: + +- 🐞 Bugfixes +- 📚 Documentation improvements + +### `@mui/x-data-grid@v6.3.1` / `@mui/x-data-grid-pro@v6.3.1` / `@mui/x-data-grid-premium@v6.3.1` + +#### Changes + +- [DataGrid] Fix broken filtering in the value formatter demo (#8621) @cherniavskii +- [DataGrid] Fix falsy filter values not showing in filter button tooltip (#8550) @ithrforu +- [DataGrid] Fix missing watermark in Pro and Premium packages (#8797) @cherniavskii +- [DataGrid] Remove unwarranted warning log (#8847) @romgrk +- [DataGrid] Add Joy UI slots (`Select`, `SelectOption`, `InputLabel`, `FormControl`) (#8747) @cherniavskii +- [DataGridPremium] Fix expanded groups being collapsed after calling `updateRows` (#8823) @cherniavskii + +### `@mui/x-date-pickers@v6.3.1` / `@mui/x-date-pickers-pro@v6.3.1` + +#### Changes + +- [pickers] Fix `minutesStep` validation prop behavior (#8794) @LukasTy +- [pickers] Fix time picker `viewRenderers` overriding (#8830) @LukasTy +- [pickers] Remove last additional character when using LTR (#8848) @alexfauquette + +### Docs + +- [docs] Fix controlled mode demo on Editing page (#8800) @yaredtsy +- [docs] Fix scrolling demo not working with React 18 (#6489) @cherniavskii +- [docs] Update demo to support agregation on popular feature cell (#8617) @BalaM314 +- [docs] Clarify what `` is (#8764) @alexfauquette + +### Core + +- [core] Do not include playground pages in `yarn typescript` script (#8822) @cherniavskii +- [core] Limit `typescript:ci` step memory limit (#8796) @LukasTy +- [core] Upgrade monorepo (#8835) @cherniavskii +- [test] Use `fake` clock on `MobileDateRangePicker` (#8861) @LukasTy +- [charts] Clean some styling (#8778) @alexfauquette +- [charts] Improve tooltip (#8792) @alexfauquette +- [charts] Improvement and docs on axis (#8654) @alexfauquette +- [charts] Defaultize attributes (#8788) @alexfauquette + ## 6.3.0 _Apr 28, 2023_ @@ -264,9 +310,9 @@ We'd like to offer a big thanks to the 9 contributors who made this release poss - [fields] Add missing tokens to `AdapterDateFnsJalali` (#8402) @flaviendelangle - [fields] Clean the active date manager (#8370) @flaviendelangle -- [fields] Cleanup `useFieldState` (#8292) @flaviendelangle +- [fields] Cleanup `useFieldState` (#8292) @flaviendelangle - [fields] Only add RTL characters when needed (#8325) @flaviendelangle -- [pickers] Add support for single input fields in range pickers (#7927) @flaviendelangle +- [pickers] Add support for single input fields in range pickers (#7927) @flaviendelangle - [pickers] Allows non token characters in format (#8256) @alexfauquette - [pickers] Avoid root imports and move public models to the models folder (#8337) @flaviendelangle - [pickers] Update `view` when `views` or `openTo` changes (#8361) @LukasTy diff --git a/package.json b/package.json index cfd0092e90c2c..81fcc0d25ad65 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "6.3.0", + "version": "6.3.1", "private": true, "scripts": { "start": "yarn && yarn docs:dev", @@ -59,7 +59,7 @@ "build:codesandbox": "yarn release:build", "install:codesandbox": "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 yarn install --ignore-engines", "release:changelog": "node scripts/releaseChangelog.mjs", - "release:version": "lerna version --exact --no-changelog --no-push --no-git-tag-version", + "release:version": "lerna version --exact --no-changelog --no-push --no-git-tag-version --no-private", "release:build": "lerna run --parallel --no-private --scope \"@mui/*\" build", "release:publish": "lerna publish from-package --no-private --dist-tag latest --contents build", "release:publish:dry-run": "lerna publish from-package --dist-tag latest --contents build --registry=\"http://localhost:4873/\"", diff --git a/packages/grid/x-data-grid-generator/package.json b/packages/grid/x-data-grid-generator/package.json index d7a5be80a6c93..eb136242ad645 100644 --- a/packages/grid/x-data-grid-generator/package.json +++ b/packages/grid/x-data-grid-generator/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-generator", - "version": "6.3.0", + "version": "6.3.1", "description": "Generate fake data for demo purposes only.", "author": "MUI Team", "main": "src/index.ts", @@ -32,7 +32,7 @@ "dependencies": { "@babel/runtime": "^7.21.0", "@mui/base": "^5.0.0-alpha.128", - "@mui/x-data-grid-premium": "6.3.0", + "@mui/x-data-grid-premium": "6.3.1", "chance": "^1.1.11", "clsx": "^1.2.1", "lru-cache": "^7.18.3" diff --git a/packages/grid/x-data-grid-premium/package.json b/packages/grid/x-data-grid-premium/package.json index b4111ec02335d..a61b9a90228e7 100644 --- a/packages/grid/x-data-grid-premium/package.json +++ b/packages/grid/x-data-grid-premium/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-premium", - "version": "6.3.0", + "version": "6.3.1", "description": "The Premium plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -44,8 +44,8 @@ "dependencies": { "@babel/runtime": "^7.21.0", "@mui/utils": "^5.12.3", - "@mui/x-data-grid": "6.3.0", - "@mui/x-data-grid-pro": "6.3.0", + "@mui/x-data-grid": "6.3.1", + "@mui/x-data-grid-pro": "6.3.1", "@mui/x-license-pro": "6.0.4", "@types/format-util": "^1.0.2", "clsx": "^1.2.1", diff --git a/packages/grid/x-data-grid-pro/package.json b/packages/grid/x-data-grid-pro/package.json index 2bf4223acbf6d..dbc05ac66f715 100644 --- a/packages/grid/x-data-grid-pro/package.json +++ b/packages/grid/x-data-grid-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-pro", - "version": "6.3.0", + "version": "6.3.1", "description": "The Pro plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -44,7 +44,7 @@ "dependencies": { "@babel/runtime": "^7.21.0", "@mui/utils": "^5.12.3", - "@mui/x-data-grid": "6.3.0", + "@mui/x-data-grid": "6.3.1", "@mui/x-license-pro": "6.0.4", "@types/format-util": "^1.0.2", "clsx": "^1.2.1", diff --git a/packages/grid/x-data-grid/package.json b/packages/grid/x-data-grid/package.json index 3b8c3a484a71a..95a54954dba9e 100644 --- a/packages/grid/x-data-grid/package.json +++ b/packages/grid/x-data-grid/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid", - "version": "6.3.0", + "version": "6.3.1", "description": "The community edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index 225a0999b5ed5..40a7669b86f7e 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers-pro", - "version": "6.3.0", + "version": "6.3.1", "description": "The commercial edition of the date picker components (MUI X).", "author": "MUI Team", "main": "./src/index.js", @@ -43,7 +43,7 @@ "dependencies": { "@babel/runtime": "^7.21.0", "@mui/utils": "^5.12.3", - "@mui/x-date-pickers": "6.3.0", + "@mui/x-date-pickers": "6.3.1", "@mui/x-license-pro": "6.0.4", "clsx": "^1.2.1", "prop-types": "^15.8.1", diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 791d47c9d9620..5808c0b1d0697 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers", - "version": "6.3.0", + "version": "6.3.1", "description": "The community edition of the date picker components (MUI X).", "author": "MUI Team", "main": "./src/index.js", diff --git a/scripts/README.md b/scripts/README.md index 2f6fa73efd2fc..28df9f9ffd84c 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -27,7 +27,6 @@ The following steps must be proposed as a pull request. - [ ] Clean the generated changelog, to match the format of [https://github.com/mui/mui-x/releases](https://github.com/mui/mui-x/releases). - [ ] Update the root `package.json`'s version - [ ] Update the versions of the other `package.json` files and of the dependencies with `yarn release:version` (`yarn release:version prerelease` for alpha / beta releases). -- [ ] Manually fix any package version if it should not be bumped (i.e. `benchmark`, `eslint-plugin-material-ui`). - [ ] Open PR with changes and wait for review and green CI. - [ ] Merge PR once CI is green, and it has been approved. From 44571c4c2ed6662deb3efa252d282de2bfb14616 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 8 May 2023 13:47:20 +0300 Subject: [PATCH 69/80] [core] Update license key for tests (#8917) --- test/regressions/index.js | 2 +- test/utils/mochaHooks.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/regressions/index.js b/test/regressions/index.js index fbae8d78814cf..52be197c53419 100644 --- a/test/regressions/index.js +++ b/test/regressions/index.js @@ -8,7 +8,7 @@ import { useFakeTimers } from 'sinon'; // This license key is only valid for use with Material UI SAS's projects // See the terms: https://mui.com/r/x-license-eula LicenseInfo.setLicenseKey( - '61628ce74db2c1b62783a6d438593bc5Tz1NVUktRG9jLEU9MTY4MzQ0NzgyMTI4NCxTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLEtWPTI=', + 'd483a722e0dc68f4d483487da0ccac45Tz1NVUktRG9jLEU9MTcxNTE2MzgwOTMwNyxTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLEtWPTI=', ); const blacklist = [ diff --git a/test/utils/mochaHooks.js b/test/utils/mochaHooks.js index 41f385d6f7390..78e9ea7838953 100644 --- a/test/utils/mochaHooks.js +++ b/test/utils/mochaHooks.js @@ -15,7 +15,7 @@ export function createXMochaHooks(coreMochaHooks = {}) { // This license key is only valid for use with Material UI SAS's projects // See the terms: https://mui.com/r/x-license-eula LicenseInfo.setLicenseKey( - '61628ce74db2c1b62783a6d438593bc5Tz1NVUktRG9jLEU9MTY4MzQ0NzgyMTI4NCxTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLEtWPTI=', + 'd483a722e0dc68f4d483487da0ccac45Tz1NVUktRG9jLEU9MTcxNTE2MzgwOTMwNyxTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLEtWPTI=', ); }); From 15bc0f79a54ca3fb5f7677903d826e9daad65052 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Tue, 9 May 2023 10:26:08 +0200 Subject: [PATCH 70/80] [pickers] Stop using deprecated adapter methods (#8735) --- codecov.yml | 2 +- .../DateRangeCalendar/DateRangeCalendar.tsx | 4 +-- .../src/AdapterDayjs/AdapterDayjs.ts | 2 +- .../src/AdapterLuxon/AdapterLuxon.ts | 2 +- .../src/DateCalendar/DateCalendar.tsx | 8 +++-- .../src/DateCalendar/DayCalendar.tsx | 35 ++++++------------- .../DateCalendar/PickersCalendarHeader.tsx | 4 +-- .../DateCalendar/tests/DateCalendar.test.tsx | 11 ++---- .../src/MonthCalendar/MonthCalendar.tsx | 4 +-- .../hooks/useField/useField.utils.ts | 9 ++--- .../src/internals/utils/date-utils.ts | 25 +++++++++++++ .../x-date-pickers/src/models/adapters.ts | 2 +- 12 files changed, 60 insertions(+), 48 deletions(-) diff --git a/codecov.yml b/codecov.yml index 13e576e5e4dbd..5918408fd7f45 100644 --- a/codecov.yml +++ b/codecov.yml @@ -5,7 +5,7 @@ coverage: target: auto threshold: 1% adapters: - target: 95% + target: 94% paths: - 'packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts' - 'packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts' diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx index 18ab686a93ff1..1f45a5af7b1e8 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx @@ -348,11 +348,11 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar( }, [rangePosition, value]); // eslint-disable-line const selectNextMonth = React.useCallback(() => { - changeMonth(utils.getNextMonth(calendarState.currentMonth)); + changeMonth(utils.addMonths(calendarState.currentMonth, 1)); }, [changeMonth, calendarState.currentMonth, utils]); const selectPreviousMonth = React.useCallback(() => { - changeMonth(utils.getPreviousMonth(calendarState.currentMonth)); + changeMonth(utils.addMonths(calendarState.currentMonth, -1)); }, [changeMonth, calendarState.currentMonth, utils]); const isNextMonthDisabled = useNextMonthDisabled(calendarState.currentMonth, { diff --git a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts index 4a667ad4e5e79..665cdb2a122a3 100644 --- a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts +++ b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts @@ -432,7 +432,7 @@ export class AdapterDayjs implements MuiPickersAdapter { while (monthArray.length < 12) { const prevMonth = monthArray[monthArray.length - 1]; - monthArray.push(this.getNextMonth(prevMonth)); + monthArray.push(this.addMonths(prevMonth, 1)); } return monthArray; diff --git a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts index f8bb1638e9c85..c96e283e1999b 100644 --- a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts +++ b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts @@ -444,7 +444,7 @@ export class AdapterLuxon implements MuiPickersAdapter { while (monthArray.length < 12) { const prevMonth = monthArray[monthArray.length - 1]; - monthArray.push(this.getNextMonth(prevMonth)); + monthArray.push(this.addMonths(prevMonth, 1)); } return monthArray; diff --git a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx index 10285289ee999..49091644462c0 100644 --- a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx +++ b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx @@ -17,7 +17,11 @@ import { MonthCalendar } from '../MonthCalendar'; import { YearCalendar } from '../YearCalendar'; import { useViews } from '../internals/hooks/useViews'; import { PickersCalendarHeader } from './PickersCalendarHeader'; -import { findClosestEnabledDate, applyDefaultDate } from '../internals/utils/date-utils'; +import { + findClosestEnabledDate, + applyDefaultDate, + mergeDateAndTime, +} from '../internals/utils/date-utils'; import { PickerViewRoot } from '../internals/components/PickerViewRoot'; import { defaultReduceAnimations } from '../internals/utils/defaultReduceAnimations'; import { getDateCalendarUtilityClass } from './dateCalendarClasses'; @@ -239,7 +243,7 @@ export const DateCalendar = React.forwardRef(function DateCalendar( const handleSelectedDayChange = useEventCallback((day: TDate | null) => { if (value && day) { // If there is a date already selected, then we want to keep its time - return setValueAndGoToNextView(utils.mergeDateAndTime(day, value), 'finish'); + return setValueAndGoToNextView(mergeDateAndTime(utils, day, value), 'finish'); } return setValueAndGoToNextView(day, 'finish'); diff --git a/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx b/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx index 44f872741266d..db983231f0ae0 100644 --- a/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx +++ b/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx @@ -322,6 +322,7 @@ export function DayCalendar(inProps: DayCalendarProps) { const props = useThemeProps({ props: inProps, name: 'MuiDayCalendar' }); const classes = useUtilityClasses(props); const theme = useTheme(); + const isRTL = theme.direction === 'rtl'; const { onFocusedDayChange, @@ -406,21 +407,14 @@ export function DayCalendar(inProps: DayCalendarProps) { event.preventDefault(); break; case 'ArrowLeft': { - const newFocusedDayDefault = utils.addDays(day, theme.direction === 'ltr' ? -1 : 1); - const nextAvailableMonth = - theme.direction === 'ltr' ? utils.getPreviousMonth(day) : utils.getNextMonth(day); + const newFocusedDayDefault = utils.addDays(day, isRTL ? 1 : -1); + const nextAvailableMonth = utils.addMonths(day, isRTL ? 1 : -1); const closestDayToFocus = findClosestEnabledDate({ utils, date: newFocusedDayDefault, - minDate: - theme.direction === 'ltr' - ? utils.startOfMonth(nextAvailableMonth) - : newFocusedDayDefault, - maxDate: - theme.direction === 'ltr' - ? newFocusedDayDefault - : utils.endOfMonth(nextAvailableMonth), + minDate: isRTL ? newFocusedDayDefault : utils.startOfMonth(nextAvailableMonth), + maxDate: isRTL ? utils.endOfMonth(nextAvailableMonth) : newFocusedDayDefault, isDateDisabled, }); focusDay(closestDayToFocus || newFocusedDayDefault); @@ -428,21 +422,14 @@ export function DayCalendar(inProps: DayCalendarProps) { break; } case 'ArrowRight': { - const newFocusedDayDefault = utils.addDays(day, theme.direction === 'ltr' ? 1 : -1); - const nextAvailableMonth = - theme.direction === 'ltr' ? utils.getNextMonth(day) : utils.getPreviousMonth(day); + const newFocusedDayDefault = utils.addDays(day, isRTL ? -1 : 1); + const nextAvailableMonth = utils.addMonths(day, isRTL ? -1 : 1); const closestDayToFocus = findClosestEnabledDate({ utils, date: newFocusedDayDefault, - minDate: - theme.direction === 'ltr' - ? newFocusedDayDefault - : utils.startOfMonth(nextAvailableMonth), - maxDate: - theme.direction === 'ltr' - ? utils.endOfMonth(nextAvailableMonth) - : newFocusedDayDefault, + minDate: isRTL ? utils.startOfMonth(nextAvailableMonth) : newFocusedDayDefault, + maxDate: isRTL ? newFocusedDayDefault : utils.endOfMonth(nextAvailableMonth), isDateDisabled, }); focusDay(closestDayToFocus || newFocusedDayDefault); @@ -458,11 +445,11 @@ export function DayCalendar(inProps: DayCalendarProps) { event.preventDefault(); break; case 'PageUp': - focusDay(utils.getNextMonth(day)); + focusDay(utils.addMonths(day, 1)); event.preventDefault(); break; case 'PageDown': - focusDay(utils.getPreviousMonth(day)); + focusDay(utils.addMonths(day, -1)); event.preventDefault(); break; default: diff --git a/packages/x-date-pickers/src/DateCalendar/PickersCalendarHeader.tsx b/packages/x-date-pickers/src/DateCalendar/PickersCalendarHeader.tsx index 09133b9ebdb3a..49bceecd9dc37 100644 --- a/packages/x-date-pickers/src/DateCalendar/PickersCalendarHeader.tsx +++ b/packages/x-date-pickers/src/DateCalendar/PickersCalendarHeader.tsx @@ -219,8 +219,8 @@ export function PickersCalendarHeader(inProps: PickersCalendarHeaderProps className: classes.switchViewIcon, }); - const selectNextMonth = () => onMonthChange(utils.getNextMonth(month), 'left'); - const selectPreviousMonth = () => onMonthChange(utils.getPreviousMonth(month), 'right'); + const selectNextMonth = () => onMonthChange(utils.addMonths(month, 1), 'left'); + const selectPreviousMonth = () => onMonthChange(utils.addMonths(month, -1), 'right'); const isNextMonthDisabled = useNextMonthDisabled(month, { disableFuture, diff --git a/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx b/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx index 50ac5dd7b585b..55172e380e327 100644 --- a/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx +++ b/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx @@ -242,14 +242,9 @@ describe('', () => { { - // Missing `getDate` in adapters - // The following disable from Apr 1st to Apr 5th - return ( - adapterToUse.getMonth(date) === 3 && - adapterToUse.getDiff(date, adapterToUse.startOfMonth(date), 'days') < 5 - ); - }} + shouldDisableDate={(date) => + adapterToUse.getMonth(date) === 3 && adapterToUse.getDate(date) < 6 + } views={['month', 'day']} openTo="month" />, diff --git a/packages/x-date-pickers/src/MonthCalendar/MonthCalendar.tsx b/packages/x-date-pickers/src/MonthCalendar/MonthCalendar.tsx index 3189241923b6d..4604d8ce75c80 100644 --- a/packages/x-date-pickers/src/MonthCalendar/MonthCalendar.tsx +++ b/packages/x-date-pickers/src/MonthCalendar/MonthCalendar.tsx @@ -11,7 +11,7 @@ import { import { PickersMonth } from './PickersMonth'; import { useUtils, useNow, useDefaultDates } from '../internals/hooks/useUtils'; import { getMonthCalendarUtilityClass } from './monthCalendarClasses'; -import { applyDefaultDate } from '../internals/utils/date-utils'; +import { applyDefaultDate, getMonthsInYear } from '../internals/utils/date-utils'; import { DefaultizedProps } from '../internals/models/helpers'; import { MonthCalendarProps } from './MonthCalendar.types'; @@ -237,7 +237,7 @@ export const MonthCalendar = React.forwardRef(function MonthCalendar( ownerState={ownerState} {...other} > - {utils.getMonthArray(selectedDateOrStartOfMonth).map((month) => { + {getMonthsInYear(utils, selectedDateOrStartOfMonth).map((month) => { const monthNumber = utils.getMonth(month); const monthText = utils.format(month, 'monthShort'); const isSelected = monthNumber === selectedMonth; diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts index b531e0358179d..9eba5318572cd 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts @@ -14,6 +14,7 @@ import { FieldSectionContentType, } from '../../../models'; import { PickersLocaleText } from '../../../locales/utils/pickersLocaleTextApi'; +import { getMonthsInYear } from '../../utils/date-utils'; export const getDateSectionConfigFromFormatToken = ( utils: MuiPickersAdapter, @@ -83,9 +84,9 @@ export const getLetterEditingOptions = ( ) => { switch (sectionType) { case 'month': { - return utils - .getMonthArray(utils.date()!) - .map((month) => utils.formatByString(month, format!)); + return getMonthsInYear(utils, utils.date()!).map((month) => + utils.formatByString(month, format!), + ); } case 'weekDay': { @@ -663,7 +664,7 @@ export const getSectionsBoundaries = ( const endOfYear = utils.endOfYear(today); - const { maxDaysInMonth, longestMonth } = utils.getMonthArray(today).reduce( + const { maxDaysInMonth, longestMonth } = getMonthsInYear(utils, today).reduce( (acc, month) => { const daysInMonth = utils.getDaysInMonth(month); diff --git a/packages/x-date-pickers/src/internals/utils/date-utils.ts b/packages/x-date-pickers/src/internals/utils/date-utils.ts index 61bbfcbec2929..2ea3e7562a8e7 100644 --- a/packages/x-date-pickers/src/internals/utils/date-utils.ts +++ b/packages/x-date-pickers/src/internals/utils/date-utils.ts @@ -109,3 +109,28 @@ export const areDatesEqual = (utils: MuiPickersAdapter, a: TDate, return utils.isEqual(a, b); }; + +export const getMonthsInYear = (utils: MuiPickersAdapter, year: TDate) => { + const firstMonth = utils.startOfYear(year); + const months = [firstMonth]; + + while (months.length < 12) { + const prevMonth = months[months.length - 1]; + months.push(utils.addMonths(prevMonth, 1)); + } + + return months; +}; + +export const mergeDateAndTime = ( + utils: MuiPickersAdapter, + dateParam: TDate, + timeParam: TDate, +) => { + let mergedDate = dateParam; + mergedDate = utils.setHours(mergedDate, utils.getHours(timeParam)); + mergedDate = utils.setMinutes(mergedDate, utils.getMinutes(timeParam)); + mergedDate = utils.setSeconds(mergedDate, utils.getSeconds(timeParam)); + + return mergedDate; +}; diff --git a/packages/x-date-pickers/src/models/adapters.ts b/packages/x-date-pickers/src/models/adapters.ts index 0616387e0ff58..6a2b11acd4f3a 100644 --- a/packages/x-date-pickers/src/models/adapters.ts +++ b/packages/x-date-pickers/src/models/adapters.ts @@ -112,7 +112,6 @@ export interface MuiPickersAdapter { date(value?: any): TDate | null; /** * Convert a date in the library format into a JavaScript `Date` object. - * @deprecate Will be removed in v7. * @template TDate * @param {TDate} value The value to convert. * @returns {Date} the JavaScript date. @@ -550,6 +549,7 @@ export interface MuiPickersAdapter { getMonthArray(value: TDate): TDate[]; /** * Create a date with the date of the first param and the time of the second param. + * @deprecated Use `adapter.setHours`, `adapter.setMinutes` and `adapter.setSeconds`. * @template TDate * @param {TDate} dateParam Param from which we want to get the date. * @param {TDate} timeParam Param from which we want to get the time. From c098a8007fe11e14450da1e883ed792e1f31dbe0 Mon Sep 17 00:00:00 2001 From: Gabriel Vallereau Date: Tue, 9 May 2023 14:46:55 +0200 Subject: [PATCH 71/80] [l10n] Improve French (fr-FR) locale (#8825) --- packages/grid/x-data-grid/src/locales/frFR.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/grid/x-data-grid/src/locales/frFR.ts b/packages/grid/x-data-grid/src/locales/frFR.ts index 27ed9f349f128..06da45ba2b8eb 100644 --- a/packages/grid/x-data-grid/src/locales/frFR.ts +++ b/packages/grid/x-data-grid/src/locales/frFR.ts @@ -10,7 +10,7 @@ const frFRGrid: Partial = { // Density selector toolbar button text toolbarDensity: 'Densité', toolbarDensityLabel: 'Densité', - toolbarDensityCompact: 'Compact', + toolbarDensityCompact: 'Compacte', toolbarDensityStandard: 'Standard', toolbarDensityComfortable: 'Confortable', @@ -27,7 +27,7 @@ const frFRGrid: Partial = { count > 1 ? `${count} filtres actifs` : `${count} filtre actif`, // Quick filter toolbar field - toolbarQuickFilterPlaceholder: 'Recherche…', + toolbarQuickFilterPlaceholder: 'Rechercher…', toolbarQuickFilterLabel: 'Recherche', toolbarQuickFilterDeleteIconLabel: 'Supprimer', @@ -39,7 +39,7 @@ const frFRGrid: Partial = { toolbarExportExcel: 'Télécharger pour Excel', // Columns panel text - columnsPanelTextFieldLabel: 'Chercher colonne', + columnsPanelTextFieldLabel: 'Chercher une colonne', columnsPanelTextFieldPlaceholder: 'Titre de la colonne', columnsPanelDragIconLabel: 'Réorganiser la colonne', columnsPanelShowAllButton: 'Tout afficher', @@ -53,13 +53,13 @@ const frFRGrid: Partial = { filterPanelOperator: 'Opérateur', filterPanelOperatorAnd: 'Et', filterPanelOperatorOr: 'Ou', - filterPanelColumns: 'Colonnes', + filterPanelColumns: 'Colonne', filterPanelInputLabel: 'Valeur', filterPanelInputPlaceholder: 'Filtrer la valeur', // Filter operators text filterOperatorContains: 'contient', - filterOperatorEquals: 'égal à', + filterOperatorEquals: 'est égal à', filterOperatorStartsWith: 'commence par', filterOperatorEndsWith: 'se termine par', filterOperatorIs: 'est', @@ -100,7 +100,7 @@ const frFRGrid: Partial = { : `${count.toLocaleString()} ligne sélectionnée`, // Total row amount footer text - footerTotalRows: 'Lignes totales :', + footerTotalRows: 'Total de lignes :', // Total visible row amount footer text footerTotalVisibleRows: (visibleCount, totalCount) => From 3f4bf65bc6f6f1efbc7c4332d09e7f43f98f3b0b Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Tue, 9 May 2023 13:37:23 -0300 Subject: [PATCH 72/80] [docs] Fix master detail demo (#8894) --- docs/data/data-grid/master-detail/FullWidthDetailPanel.js | 5 +++++ docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx | 5 +++++ .../data-grid/master-detail/FullWidthDetailPanel.tsx.preview | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/docs/data/data-grid/master-detail/FullWidthDetailPanel.js b/docs/data/data-grid/master-detail/FullWidthDetailPanel.js index 13035fb9d8482..2f20ce066a065 100644 --- a/docs/data/data-grid/master-detail/FullWidthDetailPanel.js +++ b/docs/data/data-grid/master-detail/FullWidthDetailPanel.js @@ -211,6 +211,11 @@ export default function FullWidthDetailPanel() { pinnedColumns={{ left: [GRID_DETAIL_PANEL_TOGGLE_FIELD] }} getDetailPanelHeight={getDetailPanelHeight} getDetailPanelContent={getDetailPanelContent} + sx={{ + '& .MuiDataGrid-detailPanel': { + overflow: 'visible', + }, + }} /> ); diff --git a/docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx b/docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx index 202e6650158e5..0c2a103ca5e4b 100644 --- a/docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx +++ b/docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx @@ -209,6 +209,11 @@ export default function FullWidthDetailPanel() { pinnedColumns={{ left: [GRID_DETAIL_PANEL_TOGGLE_FIELD] }} getDetailPanelHeight={getDetailPanelHeight} getDetailPanelContent={getDetailPanelContent} + sx={{ + '& .MuiDataGrid-detailPanel': { + overflow: 'visible', + }, + }} /> ); diff --git a/docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx.preview b/docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx.preview index d9e51d18e5865..cfdbb78bc2dde 100644 --- a/docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx.preview +++ b/docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx.preview @@ -5,4 +5,9 @@ pinnedColumns={{ left: [GRID_DETAIL_PANEL_TOGGLE_FIELD] }} getDetailPanelHeight={getDetailPanelHeight} getDetailPanelContent={getDetailPanelContent} + sx={{ + '& .MuiDataGrid-detailPanel': { + overflow: 'visible', + }, + }} /> \ No newline at end of file From f2df7ba178e876ea36c0be9b8b9014c1417867b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fa=C4=8Dkovec?= <76104834+MatejFacko@users.noreply.github.com> Date: Wed, 10 May 2023 10:52:20 +0200 Subject: [PATCH 73/80] [l10n][pickers] Add Slovak (sk-SK) locale (#8875) --- docs/data/date-pickers/localization/data.json | 8 ++ packages/x-date-pickers/src/locales/csCZ.ts | 6 +- packages/x-date-pickers/src/locales/index.ts | 1 + packages/x-date-pickers/src/locales/skSK.ts | 85 +++++++++++++++++++ scripts/x-date-pickers-pro.exports.json | 1 + scripts/x-date-pickers.exports.json | 1 + 6 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 packages/x-date-pickers/src/locales/skSK.ts diff --git a/docs/data/date-pickers/localization/data.json b/docs/data/date-pickers/localization/data.json index 6133e9f06bc6a..516a295dfb01a 100644 --- a/docs/data/date-pickers/localization/data.json +++ b/docs/data/date-pickers/localization/data.json @@ -167,6 +167,14 @@ "totalKeysCount": 36, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/ruRU.ts" }, + { + "languageTag": "sk-SK", + "importName": "skSK", + "localeName": "Slovak", + "missingKeysCount": 1, + "totalKeysCount": 36, + "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/skSK.ts" + }, { "languageTag": "es-ES", "importName": "esES", diff --git a/packages/x-date-pickers/src/locales/csCZ.ts b/packages/x-date-pickers/src/locales/csCZ.ts index 35b44e4c8b025..df72ea2744674 100644 --- a/packages/x-date-pickers/src/locales/csCZ.ts +++ b/packages/x-date-pickers/src/locales/csCZ.ts @@ -13,7 +13,7 @@ const timeViews: Record = { const csCZPickers: Partial> = { // Calendar navigation previousMonth: 'Další měsíc', - nextMonth: 'Předchozí month', + nextMonth: 'Předchozí měsíc', // View navigation openPreviousView: 'otevřít předchozí zobrazení', @@ -60,11 +60,11 @@ const csCZPickers: Partial> = { // Open picker labels openDatePickerDialogue: (value, utils) => value !== null && utils.isValid(value) - ? `Vybrané datum, vybrané datum je ${utils.format(value, 'fullDate')}` + ? `Vyberte datum, vybrané datum je ${utils.format(value, 'fullDate')}` : 'Vyberte datum', openTimePickerDialogue: (value, utils) => value !== null && utils.isValid(value) - ? `Vybrané čas, vybraný čas je ${utils.format(value, 'fullTime')}` + ? `Vyberte čas, vybraný čas je ${utils.format(value, 'fullTime')}` : 'Vyberte čas', // Table labels diff --git a/packages/x-date-pickers/src/locales/index.ts b/packages/x-date-pickers/src/locales/index.ts index c5ff10acad258..df89fca1128f1 100644 --- a/packages/x-date-pickers/src/locales/index.ts +++ b/packages/x-date-pickers/src/locales/index.ts @@ -24,4 +24,5 @@ export * from './urPK'; export * from './beBY'; export * from './ruRU'; export * from './heIL'; +export * from './skSK'; export * from './utils/pickersLocaleTextApi'; diff --git a/packages/x-date-pickers/src/locales/skSK.ts b/packages/x-date-pickers/src/locales/skSK.ts new file mode 100644 index 0000000000000..22ab6650219ec --- /dev/null +++ b/packages/x-date-pickers/src/locales/skSK.ts @@ -0,0 +1,85 @@ +import { PickersLocaleText } from './utils/pickersLocaleTextApi'; +import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +// maps TimeView to its translation +const timeViews: Record = { + hours: 'Hodiny', + minutes: 'Minúty', + seconds: 'Sekundy', + meridiem: 'Popoludnie', +}; + +const skSKPickers: Partial> = { + // Calendar navigation + previousMonth: 'Ďalší mesiac', + nextMonth: 'Predchádzajúci mesiac', + + // View navigation + openPreviousView: 'otvoriť predchádzajúce zobrazenie', + openNextView: 'otvoriť ďalšie zobrazenie', + calendarViewSwitchingButtonAriaLabel: (view) => + view === 'year' + ? 'ročné zobrazenie otvorené, prepnite do zobrazenia kalendára' + : 'zobrazenie kalendára otvorené, prepnite do zobrazenia roka', + + // DateRange placeholders + start: 'Začiatok', + end: 'Koniec', + + // Action bar + cancelButtonLabel: 'Zrušiť', + clearButtonLabel: 'Vymazať', + okButtonLabel: 'Potvrdiť', + todayButtonLabel: 'Dnes', + + // Toolbar titles + datePickerToolbarTitle: 'Vyberte dátum', + dateTimePickerToolbarTitle: 'Vyberte dátum a čas', + timePickerToolbarTitle: 'Vyberte čas', + dateRangePickerToolbarTitle: 'Vyberete rozmedzie dátumov', + + // Clock labels + clockLabelText: (view, time, adapter) => + `${timeViews[view] ?? view} vybraný. ${ + time === null ? 'Nie je vybraný čas' : `Vybraný čas je ${adapter.format(time, 'fullTime')}` + }`, + hoursClockNumberText: (hours) => `${hours} hodín`, + minutesClockNumberText: (minutes) => `${minutes} minút`, + secondsClockNumberText: (seconds) => `${seconds} sekúnd`, + + // Digital clock labels + selectViewText: (view) => `Vyberte ${timeViews[view]}`, + + // Calendar labels + calendarWeekNumberHeaderLabel: 'Týždeň v roku', + calendarWeekNumberHeaderText: '#', + calendarWeekNumberAriaLabelText: (weekNumber) => `${weekNumber} týždeň v roku`, + calendarWeekNumberText: (weekNumber) => `${weekNumber}`, + + // Open picker labels + openDatePickerDialogue: (value, utils) => + value !== null && utils.isValid(value) + ? `Vyberte dátum, vybraný dátum je ${utils.format(value, 'fullDate')}` + : 'Vyberte dátum', + openTimePickerDialogue: (value, utils) => + value !== null && utils.isValid(value) + ? `Vyberte čas, vybraný čas je ${utils.format(value, 'fullTime')}` + : 'Vyberte čas', + + // Table labels + timeTableLabel: 'vyberte čas', + dateTableLabel: 'vyberte dátum', + + // Field section placeholders + fieldYearPlaceholder: (params) => 'Y'.repeat(params.digitAmount), + fieldMonthPlaceholder: (params) => (params.contentType === 'letter' ? 'MMMM' : 'MM'), + fieldDayPlaceholder: () => 'DD', + // fieldWeekDayPlaceholder: params => params.contentType === 'letter' ? 'EEEE' : 'EE', + fieldHoursPlaceholder: () => 'hh', + fieldMinutesPlaceholder: () => 'mm', + fieldSecondsPlaceholder: () => 'ss', + fieldMeridiemPlaceholder: () => 'aa', +}; + +export const skSK = getPickersLocalization(skSKPickers); diff --git a/scripts/x-date-pickers-pro.exports.json b/scripts/x-date-pickers-pro.exports.json index 51d12ba955e83..97a4bb6bee9b7 100644 --- a/scripts/x-date-pickers-pro.exports.json +++ b/scripts/x-date-pickers-pro.exports.json @@ -257,6 +257,7 @@ { "name": "SingleInputDateTimeRangeFieldProps", "kind": "Interface" }, { "name": "SingleInputTimeRangeField", "kind": "Variable" }, { "name": "SingleInputTimeRangeFieldProps", "kind": "Interface" }, + { "name": "skSK", "kind": "Variable" }, { "name": "StaticDatePicker", "kind": "Variable" }, { "name": "StaticDatePickerProps", "kind": "Interface" }, { "name": "StaticDatePickerSlotsComponent", "kind": "Interface" }, diff --git a/scripts/x-date-pickers.exports.json b/scripts/x-date-pickers.exports.json index c21243e7c87f4..f5352a871a428 100644 --- a/scripts/x-date-pickers.exports.json +++ b/scripts/x-date-pickers.exports.json @@ -202,6 +202,7 @@ { "name": "renderMultiSectionDigitalClockTimeView", "kind": "Variable" }, { "name": "renderTimeViewClock", "kind": "Variable" }, { "name": "ruRU", "kind": "Variable" }, + { "name": "skSK", "kind": "Variable" }, { "name": "StaticDatePicker", "kind": "Variable" }, { "name": "StaticDatePickerProps", "kind": "Interface" }, { "name": "StaticDatePickerSlotsComponent", "kind": "Interface" }, From 0f70a86ba1816e9a3c2dc6383c275bc49d964fec Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Wed, 10 May 2023 10:54:47 +0200 Subject: [PATCH 74/80] [l10n][data grid] Improve German (de-DE) locale (#8898) --- docs/data/data-grid/localization/data.json | 2 +- packages/grid/x-data-grid/src/locales/deDE.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/data/data-grid/localization/data.json b/docs/data/data-grid/localization/data.json index 7b9450c270052..426b8fa1a6df1 100644 --- a/docs/data/data-grid/localization/data.json +++ b/docs/data/data-grid/localization/data.json @@ -83,7 +83,7 @@ "languageTag": "de-DE", "importName": "deDE", "localeName": "German", - "missingKeysCount": 2, + "missingKeysCount": 0, "totalKeysCount": 94, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/grid/x-data-grid/src/locales/deDE.ts" }, diff --git a/packages/grid/x-data-grid/src/locales/deDE.ts b/packages/grid/x-data-grid/src/locales/deDE.ts index 5597f4e142b16..fe95a8e0d6041 100644 --- a/packages/grid/x-data-grid/src/locales/deDE.ts +++ b/packages/grid/x-data-grid/src/locales/deDE.ts @@ -47,7 +47,7 @@ const deDEGrid: Partial = { // Filter panel text filterPanelAddFilter: 'Filter hinzufügen', - // filterPanelRemoveAll: 'Remove all', + filterPanelRemoveAll: 'Alle entfernen', filterPanelDeleteIconLabel: 'Löschen', filterPanelLogicOperator: 'Logische Operatoren', filterPanelOperator: 'Operatoren', @@ -80,7 +80,7 @@ const deDEGrid: Partial = { // Column menu text columnMenuLabel: 'Menü', columnMenuShowColumns: 'Zeige alle Spalten', - // columnMenuManageColumns: 'Manage columns', + columnMenuManageColumns: 'Spalten verwalten', columnMenuFilter: 'Filter', columnMenuHideColumn: 'Verbergen', columnMenuUnsort: 'Sortierung deaktivieren', From 5f85b3f38273006122504b2b24af66026c84706b Mon Sep 17 00:00:00 2001 From: Siriwat K Date: Thu, 11 May 2023 00:09:24 +0700 Subject: [PATCH 75/80] [DataGrid] Add Joy UI icon slots (#8940) Co-authored-by: Andrew Cherniavskyi --- packages/grid/x-data-grid/src/joy/icons.tsx | 341 ++++++++++++++++++ .../grid/x-data-grid/src/joy/joySlots.tsx | 2 + 2 files changed, 343 insertions(+) create mode 100644 packages/grid/x-data-grid/src/joy/icons.tsx diff --git a/packages/grid/x-data-grid/src/joy/icons.tsx b/packages/grid/x-data-grid/src/joy/icons.tsx new file mode 100644 index 0000000000000..9ab27fb4f333c --- /dev/null +++ b/packages/grid/x-data-grid/src/joy/icons.tsx @@ -0,0 +1,341 @@ +/** + * Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT). + * All other copyright (c) for Lucide are held by Lucide Contributors 2022. + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +import * as React from 'react'; +import SvgIcon, { SvgIconProps } from '@mui/joy/SvgIcon'; +import { useGridRootProps } from '../hooks/utils/useGridRootProps'; +import { GridSortDirection } from '../models/gridSortModel'; +import { GridIconSlotsComponent } from '../models/gridIconSlotsComponent'; +import { UncapitalizeObjectKeys } from '../internals/utils'; + +function createSvgIcon(path: React.ReactNode, displayName: string): typeof SvgIcon { + const fontSizeMap = { small: 'lg', medium: 'xl', large: 'xl2', inherit: undefined } as const; + // @ts-ignore internal component + function Component({ sx, fontSize, ...props }, ref) { + return ( + + {path} + + ); + } + + if (process.env.NODE_ENV !== 'production') { + // Need to set `displayName` on the inner component for React.memo. + // React prior to 16.14 ignores `displayName` on the wrapper. + Component.displayName = `${displayName}Icon`; + } + + // @ts-ignore internal component + Component.muiName = SvgIcon.muiName; + + // @ts-ignore internal component + return React.memo(React.forwardRef(Component)); +} + +export const GridArrowUpwardIcon = createSvgIcon( + + + + , + 'ArrowUpward', +); + +export const GridArrowDownwardIcon = createSvgIcon( + + + + , + 'ArrowDownward', +); + +export const GridKeyboardArrowRight = createSvgIcon( + , + 'KeyboardArrowRight', +); + +export const GridExpandMoreIcon = createSvgIcon( + + + + , + 'ExpandMore', +); + +export const GridFilterListIcon = createSvgIcon( + , + 'FilterList', +); +export const GridFilterAltIcon = createSvgIcon( + + + + + , + 'FilterAlt', +); + +export const GridSearchIcon = createSvgIcon( + + + + , + 'Search', +); + +export const GridMenuIcon = createSvgIcon( + + + + + + , + 'Menu', +); + +export const GridCheckCircleIcon = createSvgIcon( + + + + , + 'CheckCircle', +); + +export const GridColumnIcon = createSvgIcon( + + + + , + 'ColumnIcon', +); + +export const GridSeparatorIcon = createSvgIcon(, 'Separator'); + +export const GridViewHeadlineIcon = createSvgIcon( + , + 'ViewHeadline', +); + +export const GridTableRowsIcon = createSvgIcon( + , + 'TableRows', +); + +export const GridViewStreamIcon = createSvgIcon( + , + 'ViewStream', +); + +export const GridTripleDotsVerticalIcon = createSvgIcon( + + + + + , + 'TripleDotsVertical', +); + +export const GridCloseIcon = createSvgIcon( + + + + + , + 'Close', +); + +export const GridAddIcon = createSvgIcon( + + + + + , + 'Add', +); + +export const GridRemoveIcon = createSvgIcon( + + + + + , + 'Remove', +); + +export const GridLoadIcon = createSvgIcon( + + + + + , + 'Load', +); + +export const GridDragIcon = createSvgIcon( + + + + + + + + , + 'Drag', +); + +export const GridSaveAltIcon = createSvgIcon( + + + + + + , + 'SaveAlt', +); + +export const GridCheckIcon = createSvgIcon(, 'Check'); + +export const GridMoreVertIcon = createSvgIcon( + + + + + , + 'MoreVert', +); + +export const GridVisibilityOffIcon = createSvgIcon( + + + + + + , + 'VisibilityOff', +); + +export const GridViewColumnIcon = createSvgIcon( + + + + , + 'ViewColumn', +); + +export const GridClearIcon = createSvgIcon( + + + + + , + 'Clear', +); + +export const GridDeleteIcon = createSvgIcon( + + + + + + + , + 'Delete', +); + +export const GridDeleteForeverIcon = createSvgIcon( + + + + + + + , + 'Delete', +); + +interface GridColumnUnsortedIconProps extends SvgIconProps { + sortingOrder: GridSortDirection[]; +} +const GridColumnUnsortedIcon = React.memo(function GridColumnHeaderSortIcon( + props: GridColumnUnsortedIconProps, +) { + const { sortingOrder, ...other } = props; + const rootProps = useGridRootProps(); + const [nextSortDirection] = sortingOrder; + + const Icon = + nextSortDirection === 'asc' + ? rootProps.slots.columnSortedAscendingIcon + : rootProps.slots.columnSortedDescendingIcon; + + return Icon ? : null; +}); + +const joyIconSlots: UncapitalizeObjectKeys = { + booleanCellTrueIcon: GridCheckIcon, + booleanCellFalseIcon: GridCloseIcon, + columnMenuIcon: GridTripleDotsVerticalIcon, + openFilterButtonIcon: GridFilterListIcon, + filterPanelDeleteIcon: GridCloseIcon, + columnFilteredIcon: GridFilterAltIcon, + columnSelectorIcon: GridColumnIcon, + columnUnsortedIcon: GridColumnUnsortedIcon, + columnSortedAscendingIcon: GridArrowUpwardIcon, + columnSortedDescendingIcon: GridArrowDownwardIcon, + columnResizeIcon: GridSeparatorIcon, + densityCompactIcon: GridViewHeadlineIcon, + densityStandardIcon: GridTableRowsIcon, + densityComfortableIcon: GridViewStreamIcon, + exportIcon: GridSaveAltIcon, + moreActionsIcon: GridMoreVertIcon, + treeDataCollapseIcon: GridExpandMoreIcon, + treeDataExpandIcon: GridKeyboardArrowRight, + groupingCriteriaCollapseIcon: GridExpandMoreIcon, + groupingCriteriaExpandIcon: GridKeyboardArrowRight, + detailPanelExpandIcon: GridAddIcon, + detailPanelCollapseIcon: GridRemoveIcon, + rowReorderIcon: GridDragIcon, + quickFilterIcon: GridSearchIcon, + quickFilterClearIcon: GridCloseIcon, + columnMenuHideIcon: GridVisibilityOffIcon, + columnMenuSortAscendingIcon: GridArrowUpwardIcon, + columnMenuSortDescendingIcon: GridArrowDownwardIcon, + columnMenuFilterIcon: GridFilterAltIcon, + columnMenuManageColumnsIcon: GridViewColumnIcon, + columnMenuClearIcon: GridClearIcon, + loadIcon: GridLoadIcon, + filterPanelAddIcon: GridAddIcon, + filterPanelRemoveAllIcon: GridDeleteForeverIcon, + columnReorderIcon: GridDragIcon, +}; + +export default joyIconSlots; diff --git a/packages/grid/x-data-grid/src/joy/joySlots.tsx b/packages/grid/x-data-grid/src/joy/joySlots.tsx index 34d5e8e9cee6b..f8fd0385cedd4 100644 --- a/packages/grid/x-data-grid/src/joy/joySlots.tsx +++ b/packages/grid/x-data-grid/src/joy/joySlots.tsx @@ -11,6 +11,7 @@ import JoySwitch, { SwitchProps as JoySwitchProps } from '@mui/joy/Switch'; import JoySelect, { SelectProps as JoySelectProps } from '@mui/joy/Select'; import JoyOption, { OptionProps as JoyOptionProps } from '@mui/joy/Option'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import joyIconSlots from './icons'; import type { UncapitalizeObjectKeys } from '../internals/utils'; import type { GridSlotsComponent, GridSlotsComponentsProps } from '../models'; @@ -289,6 +290,7 @@ const InputLabel = React.forwardRef< }); const joySlots: UncapitalizeObjectKeys> = { + ...joyIconSlots, baseCheckbox: Checkbox, baseTextField: TextField, baseButton: Button, From 039c3cb9e35d35443831092bc8112e031be791dd Mon Sep 17 00:00:00 2001 From: Rich Bustos <92274722+richbustos@users.noreply.github.com> Date: Wed, 10 May 2023 11:20:40 -0700 Subject: [PATCH 76/80] [docs] Fix date pickers typo in the docs (#8939) Signed-off-by: Rich Bustos <92274722+richbustos@users.noreply.github.com> Co-authored-by: Rich Bustos Co-authored-by: Sam Sycamore <71297412+samuelsycamore@users.noreply.github.com> --- docs/data/date-pickers/localization/localization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data/date-pickers/localization/localization.md b/docs/data/date-pickers/localization/localization.md index 618adbd024d8e..f618bd67c2065 100644 --- a/docs/data/date-pickers/localization/localization.md +++ b/docs/data/date-pickers/localization/localization.md @@ -26,7 +26,7 @@ If you need to change the formatting of the text to conform to a given locale, v ### Using the theme To translate all your components from `@mui/x-date-pickers` and `@mui/x-date-pickers-pro`, -you just have to import the locale from `@mui/x-date-pikers` (see the [list of supported locales below](#supported-locales)). +import the locale from `@mui/x-date-pickers` (see the [list of supported locales below](#supported-locales)). ```jsx import { createTheme, ThemeProvider } from '@mui/material/styles'; From a527ba462f5684df26da35cdd0575ec8d0ba974a Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Wed, 10 May 2023 22:05:19 +0200 Subject: [PATCH 77/80] [DataGrid] Add Joy UI pagination slot (#8871) Co-authored-by: siriwatknp --- docs/data/data-grid/joy-ui/GridJoyUISlots.js | 43 +++++-- docs/data/data-grid/joy-ui/GridJoyUISlots.tsx | 51 ++++++-- packages/grid/x-data-grid/src/joy/icons.tsx | 5 + .../grid/x-data-grid/src/joy/joySlots.tsx | 119 +++++++++++++++++- 4 files changed, 191 insertions(+), 27 deletions(-) diff --git a/docs/data/data-grid/joy-ui/GridJoyUISlots.js b/docs/data/data-grid/joy-ui/GridJoyUISlots.js index 8f11ba1a3c779..be5b71b98f56b 100644 --- a/docs/data/data-grid/joy-ui/GridJoyUISlots.js +++ b/docs/data/data-grid/joy-ui/GridJoyUISlots.js @@ -10,6 +10,14 @@ import { import { CssVarsProvider as JoyCssVarsProvider } from '@mui/joy/styles'; import DeleteIcon from '@mui/icons-material/Delete'; +import { + randomCompanyName, + randomRating, + randomCreatedDate, + randomBoolean, + randomArrayItem, +} from '@mui/x-data-grid-generator'; + const materialTheme = materialExtendTheme({ components: { MuiSvgIcon: { @@ -29,6 +37,8 @@ const materialTheme = materialExtendTheme({ }, }); +const singleSelectValueOptions = ['Item1', 'Item2', 'Item3']; + const columns = [ { field: 'name', editable: true, type: 'string', minWidth: 140 }, { field: 'number', editable: true, type: 'number', flex: 1 }, @@ -39,7 +49,7 @@ const columns = [ field: 'singleSelect', editable: true, type: 'singleSelect', - valueOptions: ['Item1', 'Item2', 'Item3'], + valueOptions: singleSelectValueOptions, minWidth: 100, flex: 1, }, @@ -60,23 +70,24 @@ const columns = [ }, ]; -const rows = [ - { - id: 1, - name: 'Data Grid x Joy UI', - number: 1, - date: new Date(), - dateTime: new Date(), - boolean: true, - singleSelect: 'Item1', - }, -]; +const rows = []; +for (let i = 0; i < 20; i += 1) { + rows.push({ + id: i, + name: randomCompanyName(), + number: randomRating(), + date: randomCreatedDate(), + dateTime: randomCreatedDate(), + boolean: randomBoolean(), + singleSelect: randomArrayItem(singleSelectValueOptions), + }); +} export default function GridJoyUISlots() { return ( - + - + , + 'KeyboardArrowLeft', +); + export const GridKeyboardArrowRight = createSvgIcon( , 'KeyboardArrowRight', diff --git a/packages/grid/x-data-grid/src/joy/joySlots.tsx b/packages/grid/x-data-grid/src/joy/joySlots.tsx index f8fd0385cedd4..3b673e065064b 100644 --- a/packages/grid/x-data-grid/src/joy/joySlots.tsx +++ b/packages/grid/x-data-grid/src/joy/joySlots.tsx @@ -10,10 +10,15 @@ import JoyIconButton from '@mui/joy/IconButton'; import JoySwitch, { SwitchProps as JoySwitchProps } from '@mui/joy/Switch'; import JoySelect, { SelectProps as JoySelectProps } from '@mui/joy/Select'; import JoyOption, { OptionProps as JoyOptionProps } from '@mui/joy/Option'; +import JoyBox from '@mui/joy/Box'; +import JoyTypography from '@mui/joy/Typography'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; -import joyIconSlots from './icons'; +import joyIconSlots, { GridKeyboardArrowRight, GridKeyboardArrowLeft } from './icons'; import type { UncapitalizeObjectKeys } from '../internals/utils'; import type { GridSlotsComponent, GridSlotsComponentsProps } from '../models'; +import { useGridApiContext } from '../hooks/utils/useGridApiContext'; +import { useGridRootProps } from '../hooks/utils/useGridRootProps'; +import { gridFilteredTopLevelRowCountSelector, gridPaginationModelSelector } from '../hooks'; function convertColor< T extends @@ -289,6 +294,117 @@ const InputLabel = React.forwardRef< return } />; }); +function labelDisplayedRows({ from, to, count }: { from: number; to: number; count: number }) { + return `${from}–${to} of ${count !== -1 ? count : `more than ${to}`}`; +} +const getLabelDisplayedRowsTo = ({ + page, + pageSize, + rowCount, +}: { + page: number; + pageSize: number; + rowCount: number; +}) => { + if (rowCount === -1) { + return (page + 1) * pageSize; + } + return pageSize === -1 ? rowCount : Math.min(rowCount, (page + 1) * pageSize); +}; + +const Pagination = React.forwardRef< + HTMLDivElement, + NonNullable +>((props, ref) => { + const apiRef = useGridApiContext(); + const rootProps = useGridRootProps(); + const paginationModel = gridPaginationModelSelector(apiRef); + const visibleTopLevelRowCount = gridFilteredTopLevelRowCountSelector(apiRef); + + const rowCount = React.useMemo( + () => rootProps.rowCount ?? visibleTopLevelRowCount ?? 0, + [rootProps.rowCount, visibleTopLevelRowCount], + ); + + const lastPage = React.useMemo( + () => Math.floor(rowCount / (paginationModel.pageSize || 1)), + [rowCount, paginationModel.pageSize], + ); + + const handlePageChange = React.useCallback( + (page: number) => { + apiRef.current.setPage(page); + }, + [apiRef], + ); + + const page = paginationModel.page <= lastPage ? paginationModel.page : lastPage; + const pageSize = paginationModel.pageSize; + + const pageSizeOptions = rootProps.pageSizeOptions?.includes(pageSize) + ? rootProps.pageSizeOptions + : []; + + const handleChangeRowsPerPage: JoySelectProps['onChange'] = (event, newValue) => { + const newPageSize = Number(newValue); + apiRef.current.setPageSize(newPageSize); + }; + return ( + + + Rows per page: + onChange={handleChangeRowsPerPage} value={pageSize}> + {pageSizeOptions.map((option) => { + return ( + + ); + })} + + + + {labelDisplayedRows({ + from: rowCount === 0 ? 0 : page * pageSize + 1, + to: getLabelDisplayedRowsTo({ page, pageSize, rowCount }), + count: rowCount === -1 ? -1 : rowCount, + })} + + + handlePageChange(page - 1)} + sx={{ bgcolor: 'background.surface' }} + > + + + = Math.ceil(rowCount / pageSize) - 1 : false} + onClick={() => handlePageChange(page + 1)} + sx={{ bgcolor: 'background.surface' }} + > + + + + + ); +}); + const joySlots: UncapitalizeObjectKeys> = { ...joyIconSlots, baseCheckbox: Checkbox, @@ -302,6 +418,7 @@ const joySlots: UncapitalizeObjectKeys> = { baseFormControl: JoyFormControl, // BaseTooltip: MUITooltip, // BasePopper: MUIPopper, + pagination: Pagination, }; export default joySlots; From 56ed9ce2738607763b7117bde8fe30e7b91f5395 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Thu, 11 May 2023 12:46:55 +0200 Subject: [PATCH 78/80] [test] Fix flaky regression tests (#8954) --- .../data-grid/column-visibility/ColumnSelectorGridSnap.js | 6 +++++- .../data-grid/column-visibility/ColumnSelectorGridSnap.tsx | 6 +++++- .../column-visibility/ColumnSelectorGridSnap.tsx.preview | 6 +++++- test/regressions/index.test.js | 1 - 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.js b/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.js index 2c671c626fa84..e55aee07b0b24 100644 --- a/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.js +++ b/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.js @@ -22,7 +22,11 @@ export default function ColumnSelectorGridSnap() { return (

- +
); } diff --git a/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.tsx b/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.tsx index 2c671c626fa84..e55aee07b0b24 100644 --- a/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.tsx +++ b/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.tsx @@ -22,7 +22,11 @@ export default function ColumnSelectorGridSnap() { return (
- +
); } diff --git a/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.tsx.preview b/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.tsx.preview index 58ffe10e4cd8f..67314af005d28 100644 --- a/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.tsx.preview +++ b/docs/data/data-grid/column-visibility/ColumnSelectorGridSnap.tsx.preview @@ -1 +1,5 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/test/regressions/index.test.js b/test/regressions/index.test.js index dd80215844de4..dcf347f6cc884 100644 --- a/test/regressions/index.test.js +++ b/test/regressions/index.test.js @@ -105,7 +105,6 @@ async function main() { '/docs-data-grid-filtering/ServerFilterGrid', // No content rendered '/docs-data-grid-filtering/CustomMultiValueOperator', // No content rendered '/docs-data-grid-filtering/QuickFilteringInitialize', // No content rendered - '/docs-data-grid-sorting/ExtendedSortComparator', // No flag column '/docs-data-grid-sorting/FullyCustomSortComparator', // No flag column '/docs-data-grid-sorting/ServerSortingGrid', // No flag column ]; From 1386e9a40ad52198d53cbeab8267f65dea601928 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Thu, 11 May 2023 13:58:00 +0200 Subject: [PATCH 79/80] [DataGrid] Fix layout when rendered inside a parent with `display: grid` (#8577) --- .../components/containers/GridRootStyles.ts | 1 + .../src/tests/layout.DataGrid.test.tsx | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts b/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts index afdf105b587ae..ef394e1fb72e2 100644 --- a/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts +++ b/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts @@ -115,6 +115,7 @@ export const GridRootStyles = styled('div', { outline: 'none', height: '100%', display: 'flex', + overflow: 'hidden', flexDirection: 'column', overflowAnchor: 'none', // Keep the same scrolling position [`&.${gridClasses.autoHeight}`]: { diff --git a/packages/grid/x-data-grid/src/tests/layout.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/layout.DataGrid.test.tsx index 54722958b94ce..f3d0e32044737 100644 --- a/packages/grid/x-data-grid/src/tests/layout.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/layout.DataGrid.test.tsx @@ -1040,4 +1040,46 @@ describe(' - Layout & Warnings', () => { setProps({ loading: false }); expect(NoRowsOverlay.callCount).not.to.equal(0); }); + + describe('sould not overflow parent', () => { + before(function beforeHook() { + if (/jsdom/.test(window.navigator.userAgent)) { + this.skip(); // Doesn't work with mocked window.getComputedStyle + } + }); + + const rows = [{ id: 1, username: '@MUI', age: 20 }]; + const columns = [ + { field: 'id', width: 300 }, + { field: 'username', width: 300 }, + ]; + + it('grid container', async () => { + render( +
+
+ +
+
, + ); + + await waitFor(() => { + expect(screen.getByRole('grid')).toHaveComputedStyle({ width: '400px' }); + }); + }); + + it('flex container', async () => { + render( +
+
+ +
+
, + ); + + await waitFor(() => { + expect(screen.getByRole('grid')).toHaveComputedStyle({ width: '400px' }); + }); + }); + }); }); From 1f0f06e4697a991388d06d4bdf16aa9da4800780 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Thu, 11 May 2023 14:10:08 +0200 Subject: [PATCH 80/80] [pickers] Fix trailing zeros inconsistency in `LuxonAdapter` (#8955) --- packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts index c96e283e1999b..622ab7356e22a 100644 --- a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts +++ b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts @@ -11,7 +11,7 @@ const formatTokenMap: FieldFormatTokenMap = { // Year y: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, yy: 'year', - yyyy: 'year', + yyyy: { sectionType: 'year', contentType: 'digit', maxLength: 4 }, // Month L: { sectionType: 'month', contentType: 'digit', maxLength: 2 },