Skip to content

Commit 847881d

Browse files
LukasTyJCQuintas
authored andcommitted
[pickers] Refactor slots and slotProps propagation strategy (mui#18867)
1 parent b388600 commit 847881d

File tree

13 files changed

+184
-116
lines changed

13 files changed

+184
-116
lines changed

packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import PropTypes from 'prop-types';
44
import { useThemeProps } from '@mui/material/styles';
55
import refType from '@mui/utils/refType';
66
import { DateRangeIcon } from '@mui/x-date-pickers/icons';
7-
import { PickerFieldUI, useFieldTextFieldProps } from '@mui/x-date-pickers/internals';
7+
import {
8+
PickerFieldUI,
9+
PickerFieldUIContextProvider,
10+
useFieldTextFieldProps,
11+
} from '@mui/x-date-pickers/internals';
812
import { SingleInputDateRangeFieldProps } from './SingleInputDateRangeField.types';
913
import { useSingleInputDateRangeField } from './useSingleInputDateRangeField';
1014
import { FieldType } from '../models';
@@ -51,12 +55,9 @@ const SingleInputDateRangeField = React.forwardRef(function SingleInputDateRange
5155
>(textFieldProps);
5256

5357
return (
54-
<PickerFieldUI
55-
slots={slots}
56-
slotProps={slotProps}
57-
fieldResponse={fieldResponse}
58-
defaultOpenPickerIcon={DateRangeIcon}
59-
/>
58+
<PickerFieldUIContextProvider slots={slots} slotProps={slotProps} inputRef={other.inputRef}>
59+
<PickerFieldUI fieldResponse={fieldResponse} defaultOpenPickerIcon={DateRangeIcon} />
60+
</PickerFieldUIContextProvider>
6061
);
6162
}) as DateRangeFieldComponent;
6263

packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
import * as React from 'react';
33
import PropTypes from 'prop-types';
44
import { DateRangeIcon } from '@mui/x-date-pickers/icons';
5-
import { PickerFieldUI, useFieldTextFieldProps } from '@mui/x-date-pickers/internals';
5+
import {
6+
PickerFieldUI,
7+
PickerFieldUIContextProvider,
8+
useFieldTextFieldProps,
9+
} from '@mui/x-date-pickers/internals';
610
import { useThemeProps } from '@mui/material/styles';
711
import refType from '@mui/utils/refType';
812
import { SingleInputDateTimeRangeFieldProps } from './SingleInputDateTimeRangeField.types';
@@ -51,12 +55,9 @@ const SingleInputDateTimeRangeField = React.forwardRef(function SingleInputDateT
5155
>(textFieldProps);
5256

5357
return (
54-
<PickerFieldUI
55-
slots={slots}
56-
slotProps={slotProps}
57-
fieldResponse={fieldResponse}
58-
defaultOpenPickerIcon={DateRangeIcon}
59-
/>
58+
<PickerFieldUIContextProvider slots={slots} slotProps={slotProps} inputRef={other.inputRef}>
59+
<PickerFieldUI fieldResponse={fieldResponse} defaultOpenPickerIcon={DateRangeIcon} />
60+
</PickerFieldUIContextProvider>
6061
);
6162
}) as DateRangeFieldComponent;
6263

packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
import * as React from 'react';
33
import PropTypes from 'prop-types';
44
import { ClockIcon } from '@mui/x-date-pickers/icons';
5-
import { PickerFieldUI, useFieldTextFieldProps } from '@mui/x-date-pickers/internals';
5+
import {
6+
PickerFieldUI,
7+
PickerFieldUIContextProvider,
8+
useFieldTextFieldProps,
9+
} from '@mui/x-date-pickers/internals';
610
import { useThemeProps } from '@mui/material/styles';
711
import refType from '@mui/utils/refType';
812
import { SingleInputTimeRangeFieldProps } from './SingleInputTimeRangeField.types';
@@ -51,12 +55,9 @@ const SingleInputTimeRangeField = React.forwardRef(function SingleInputTimeRange
5155
>(textFieldProps);
5256

5357
return (
54-
<PickerFieldUI
55-
slots={slots}
56-
slotProps={slotProps}
57-
fieldResponse={fieldResponse}
58-
defaultOpenPickerIcon={ClockIcon}
59-
/>
58+
<PickerFieldUIContextProvider slots={slots} slotProps={slotProps} inputRef={other.inputRef}>
59+
<PickerFieldUI fieldResponse={fieldResponse} defaultOpenPickerIcon={ClockIcon} />
60+
</PickerFieldUIContextProvider>
6061
);
6162
}) as DateRangeFieldComponent;
6263

packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
DateOrTimeViewWithMeridiem,
1010
PickerProvider,
1111
PickerRangeValue,
12-
PickerFieldUIContextProvider,
1312
} from '@mui/x-date-pickers/internals';
1413
import {
1514
UseDesktopRangePickerParams,
@@ -39,7 +38,8 @@ export const useDesktopRangePicker = <
3938
const { slots, slotProps, inputRef, localeText } = props;
4039

4140
const fieldType = getRangeFieldType(slots.field);
42-
const viewContainerRole = fieldType === 'single-input' ? 'dialog' : 'tooltip';
41+
const isSingleInput = fieldType === 'single-input';
42+
const viewContainerRole = isSingleInput ? 'dialog' : 'tooltip';
4343
const rangePositionResponse = useRangePosition(props);
4444

4545
const getStepNavigation = createRangePickerStepNavigation({
@@ -81,16 +81,21 @@ export const useDesktopRangePicker = <
8181

8282
const renderPicker = () => (
8383
<PickerProvider {...providerProps}>
84-
<PickerFieldUIContextProvider slots={slots} slotProps={slotProps} inputRef={inputRef}>
85-
<PickerRangePositionContext.Provider value={rangePositionResponse}>
86-
<Field {...fieldProps} />
87-
<PickerPopper slots={slots} slotProps={slotProps}>
88-
<Layout {...slotProps?.layout} slots={slots} slotProps={slotProps}>
89-
{renderCurrentView()}
90-
</Layout>
91-
</PickerPopper>
92-
</PickerRangePositionContext.Provider>
93-
</PickerFieldUIContextProvider>
84+
<PickerRangePositionContext.Provider value={rangePositionResponse}>
85+
<Field
86+
{...fieldProps}
87+
slots={slots}
88+
slotProps={slotProps}
89+
{...(isSingleInput && {
90+
inputRef,
91+
})}
92+
/>
93+
<PickerPopper slots={slots} slotProps={slotProps}>
94+
<Layout {...slotProps?.layout} slots={slots} slotProps={slotProps}>
95+
{renderCurrentView()}
96+
</Layout>
97+
</PickerPopper>
98+
</PickerRangePositionContext.Provider>
9499
</PickerProvider>
95100
);
96101

packages/x-date-pickers-pro/src/internals/hooks/useMobileRangePicker/useMobileRangePicker.tsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
DateOrTimeViewWithMeridiem,
1212
PickerProvider,
1313
PickerRangeValue,
14-
PickerFieldUIContextProvider,
1514
} from '@mui/x-date-pickers/internals';
1615
import { usePickerTranslations } from '@mui/x-date-pickers/hooks';
1716
import { FieldOwnerState } from '@mui/x-date-pickers/models';
@@ -43,6 +42,7 @@ export const useMobileRangePicker = <
4342
const { slots, slotProps: innerSlotProps, label, inputRef, localeText } = props;
4443

4544
const fieldType = getRangeFieldType(slots.field);
45+
const isSingleInput = fieldType === 'single-input';
4646
const rangePositionResponse = useRangePosition(props);
4747
const contextTranslations = usePickerTranslations();
4848

@@ -76,7 +76,7 @@ export const useMobileRangePicker = <
7676
elementType: Field,
7777
externalSlotProps: innerSlotProps?.field,
7878
additionalProps: {
79-
...(fieldType === 'single-input' &&
79+
...(isSingleInput &&
8080
isToolbarHidden && {
8181
id: labelId,
8282
}),
@@ -131,16 +131,21 @@ export const useMobileRangePicker = <
131131

132132
const renderPicker = () => (
133133
<PickerProvider {...providerProps}>
134-
<PickerFieldUIContextProvider slots={slots} slotProps={slotProps} inputRef={inputRef}>
135-
<PickerRangePositionContext.Provider value={rangePositionResponse}>
136-
<Field {...fieldProps} />
137-
<PickersModalDialog slots={slots} slotProps={slotProps}>
138-
<Layout {...slotProps?.layout} slots={slots} slotProps={slotProps}>
139-
{renderCurrentView()}
140-
</Layout>
141-
</PickersModalDialog>
142-
</PickerRangePositionContext.Provider>
143-
</PickerFieldUIContextProvider>
134+
<PickerRangePositionContext.Provider value={rangePositionResponse}>
135+
<Field
136+
{...fieldProps}
137+
slots={slots}
138+
slotProps={slotProps}
139+
{...(isSingleInput && {
140+
inputRef,
141+
})}
142+
/>
143+
<PickersModalDialog slots={slots} slotProps={slotProps}>
144+
<Layout {...slotProps?.layout} slots={slots} slotProps={slotProps}>
145+
{renderCurrentView()}
146+
</Layout>
147+
</PickersModalDialog>
148+
</PickerRangePositionContext.Provider>
144149
</PickerProvider>
145150
);
146151

packages/x-date-pickers/src/DateField/DateField.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { useThemeProps } from '@mui/material/styles';
55
import refType from '@mui/utils/refType';
66
import { DateFieldProps } from './DateField.types';
77
import { useDateField } from './useDateField';
8-
import { PickerFieldUI, useFieldTextFieldProps } from '../internals/components/PickerFieldUI';
8+
import {
9+
PickerFieldUI,
10+
PickerFieldUIContextProvider,
11+
useFieldTextFieldProps,
12+
} from '../internals/components/PickerFieldUI';
913
import { CalendarIcon } from '../icons';
1014

1115
type DateFieldComponent = (<TEnableAccessibleFieldDOMStructure extends boolean = true>(
@@ -45,12 +49,9 @@ const DateField = React.forwardRef(function DateField<
4549
);
4650

4751
return (
48-
<PickerFieldUI
49-
slots={slots}
50-
slotProps={slotProps}
51-
fieldResponse={fieldResponse}
52-
defaultOpenPickerIcon={CalendarIcon}
53-
/>
52+
<PickerFieldUIContextProvider slots={slots} slotProps={slotProps} inputRef={other.inputRef}>
53+
<PickerFieldUI fieldResponse={fieldResponse} defaultOpenPickerIcon={CalendarIcon} />
54+
</PickerFieldUIContextProvider>
5455
);
5556
}) as DateFieldComponent;
5657

packages/x-date-pickers/src/DateField/tests/DateField.test.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react';
2+
import InputAdornment, { InputAdornmentProps } from '@mui/material/InputAdornment';
23
import { DateField } from '@mui/x-date-pickers/DateField';
34
import { screen } from '@mui/internal-test-utils';
45
import { createPickerRenderer } from 'test/utils/pickers';
@@ -45,4 +46,40 @@ describe('<DateField />', () => {
4546
expect(screen.getByRole('textbox', { description: 'field-helper' })).not.to.equal(null);
4647
});
4748
});
49+
50+
describe('slotProps.inputAdornment behavior', () => {
51+
function CustomInputAdornment(props: InputAdornmentProps) {
52+
const { children, ...other } = props;
53+
return (
54+
<InputAdornment {...other}>
55+
<span>x</span>
56+
{children}
57+
</InputAdornment>
58+
);
59+
}
60+
61+
it('should respect the `slots.inputAdornment` on accessible DOM structure', () => {
62+
render(
63+
<DateField
64+
enableAccessibleFieldDOMStructure
65+
slots={{ inputAdornment: CustomInputAdornment }}
66+
slotProps={{ inputAdornment: { 'aria-label': 'test-adornment-icon', role: 'figure' } }}
67+
/>,
68+
);
69+
70+
expect(screen.getByRole('figure', { name: 'test-adornment-icon' })).to.have.text('x');
71+
});
72+
73+
it('should respect the `slots.inputAdornment` on non-accessible DOM structure', () => {
74+
render(
75+
<DateField
76+
enableAccessibleFieldDOMStructure={false}
77+
slots={{ inputAdornment: CustomInputAdornment }}
78+
slotProps={{ inputAdornment: { 'aria-label': 'test-adornment-icon', role: 'figure' } }}
79+
/>,
80+
);
81+
82+
expect(screen.getByRole('figure', { name: 'test-adornment-icon' })).to.have.text('x');
83+
});
84+
});
4885
});

packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { useThemeProps } from '@mui/material/styles';
55
import refType from '@mui/utils/refType';
66
import { DateTimeFieldProps } from './DateTimeField.types';
77
import { useDateTimeField } from './useDateTimeField';
8-
import { PickerFieldUI, useFieldTextFieldProps } from '../internals/components/PickerFieldUI';
8+
import {
9+
PickerFieldUI,
10+
PickerFieldUIContextProvider,
11+
useFieldTextFieldProps,
12+
} from '../internals/components/PickerFieldUI';
913
import { CalendarIcon } from '../icons';
1014

1115
type DateTimeFieldComponent = (<TEnableAccessibleFieldDOMStructure extends boolean = true>(
@@ -49,12 +53,9 @@ const DateTimeField = React.forwardRef(function DateTimeField<
4953
);
5054

5155
return (
52-
<PickerFieldUI
53-
slots={slots}
54-
slotProps={slotProps}
55-
fieldResponse={fieldResponse}
56-
defaultOpenPickerIcon={CalendarIcon}
57-
/>
56+
<PickerFieldUIContextProvider slots={slots} slotProps={slotProps} inputRef={other.inputRef}>
57+
<PickerFieldUI fieldResponse={fieldResponse} defaultOpenPickerIcon={CalendarIcon} />
58+
</PickerFieldUIContextProvider>
5859
);
5960
}) as DateTimeFieldComponent;
6061

packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { createPickerRenderer, adapterToUse, openPickerAsync } from 'test/utils/
88
import { isJSDOM } from 'test/utils/skipIf';
99
import { PickersActionBar, PickersActionBarAction } from '@mui/x-date-pickers/PickersActionBar';
1010
import { PickerValidDate } from '@mui/x-date-pickers/models';
11+
import InputAdornment, { InputAdornmentProps } from '@mui/material/InputAdornment';
1112

1213
describe('<DesktopDatePicker />', () => {
1314
const { render } = createPickerRenderer();
@@ -485,4 +486,40 @@ describe('<DesktopDatePicker />', () => {
485486
expect(screen.getByRole('textbox')).attribute('name').to.equal('test-field');
486487
});
487488
});
489+
490+
describe('slotProps.inputAdornment behavior', () => {
491+
function CustomInputAdornment(props: InputAdornmentProps) {
492+
const { children, ...other } = props;
493+
return (
494+
<InputAdornment {...other}>
495+
<span>x</span>
496+
{children}
497+
</InputAdornment>
498+
);
499+
}
500+
501+
it('should respect the `slots.inputAdornment` on accessible DOM structure', () => {
502+
render(
503+
<DesktopDatePicker
504+
enableAccessibleFieldDOMStructure
505+
slots={{ inputAdornment: CustomInputAdornment }}
506+
slotProps={{ inputAdornment: { 'aria-label': 'test-adornment-icon', role: 'figure' } }}
507+
/>,
508+
);
509+
510+
expect(screen.getByRole('figure', { name: 'test-adornment-icon' })).to.have.text('x');
511+
});
512+
513+
it('should respect the `slots.inputAdornment` on non-accessible DOM structure', () => {
514+
render(
515+
<DesktopDatePicker
516+
enableAccessibleFieldDOMStructure={false}
517+
slots={{ inputAdornment: CustomInputAdornment }}
518+
slotProps={{ inputAdornment: { 'aria-label': 'test-adornment-icon', role: 'figure' } }}
519+
/>,
520+
);
521+
522+
expect(screen.getByRole('figure', { name: 'test-adornment-icon' })).to.have.text('x');
523+
});
524+
});
488525
});

packages/x-date-pickers/src/TimeField/TimeField.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { useThemeProps } from '@mui/material/styles';
55
import refType from '@mui/utils/refType';
66
import { TimeFieldProps } from './TimeField.types';
77
import { useTimeField } from './useTimeField';
8-
import { PickerFieldUI, useFieldTextFieldProps } from '../internals/components/PickerFieldUI';
8+
import {
9+
PickerFieldUI,
10+
PickerFieldUIContextProvider,
11+
useFieldTextFieldProps,
12+
} from '../internals/components/PickerFieldUI';
913
import { ClockIcon } from '../icons';
1014

1115
type TimeFieldComponent = (<TEnableAccessibleFieldDOMStructure extends boolean = true>(
@@ -45,12 +49,9 @@ const TimeField = React.forwardRef(function TimeField<
4549
);
4650

4751
return (
48-
<PickerFieldUI
49-
slots={slots}
50-
slotProps={slotProps}
51-
fieldResponse={fieldResponse}
52-
defaultOpenPickerIcon={ClockIcon}
53-
/>
52+
<PickerFieldUIContextProvider slots={slots} slotProps={slotProps} inputRef={other.inputRef}>
53+
<PickerFieldUI fieldResponse={fieldResponse} defaultOpenPickerIcon={ClockIcon} />
54+
</PickerFieldUIContextProvider>
5455
);
5556
}) as TimeFieldComponent;
5657

0 commit comments

Comments
 (0)