Skip to content

Commit

Permalink
[pickers] Rename ctx.onViewChange to ctx.setView and add it to th…
Browse files Browse the repository at this point in the history
…e actions context (#16044)
  • Loading branch information
flaviendelangle authored Jan 7, 2025
1 parent 011ccd0 commit cad579c
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,9 @@ If the updated values do not fit your use case, you can [override them](/x/react
+const { views } = usePickerContext();

-const { onViewChange } = props;
+const { onViewChange } = usePickerContext();
-onViewChange('month');
+const { setView } = usePickerContext();
+setView('month');
```

- The component passed to the `layout` slot no longer receives the `onClear`, `onSetToday`, `onAccept`, `onCancel`, `onOpen`, `onClose` `onDismiss`, `onChange` and `onSelectShortcut` props.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const DateTimeRangePickerTabs = function DateTimeRangePickerTabs(

const translations = usePickerTranslations();
const { ownerState } = usePickerPrivateContext();
const { view, onViewChange } = usePickerContext();
const { view, setView } = usePickerContext();
const classes = useUtilityClasses(classesProp);
const { rangePosition, onRangePositionChange } = usePickerRangePositionContext();

Expand Down Expand Up @@ -160,13 +160,13 @@ const DateTimeRangePickerTabs = function DateTimeRangePickerTabs(

const changeToPreviousTab = useEventCallback(() => {
const previousTab = value == null ? tabOptions[0] : tabOptions[tabOptions.indexOf(value) - 1];
onViewChange(tabToView(previousTab));
setView(tabToView(previousTab));
handleRangePositionChange(previousTab);
});

const changeToNextTab = useEventCallback(() => {
const nextTab = value == null ? tabOptions[0] : tabOptions[tabOptions.indexOf(value) + 1];
onViewChange(tabToView(nextTab));
setView(tabToView(nextTab));
handleRangePositionChange(nextTab);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePicker
...other
} = props;

const { value, setValue, disabled, readOnly, view, onViewChange, views } = usePickerContext<
const { value, setValue, disabled, readOnly, view, setView, views } = usePickerContext<
PickerRangeValue,
DateTimeRangeViews
>();
Expand Down Expand Up @@ -141,17 +141,17 @@ const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePicker
if (rangePosition !== 'start') {
onRangePositionChange('start');
}
onViewChange(newView);
setView(newView);
};

return {
value: value[0],
setValue: wrappedSetValue,
forceDesktopVariant: true,
onViewChange: handleStartRangeViewChange,
setView: handleStartRangeViewChange,
view: rangePosition === 'start' ? view : null,
};
}, [value, wrappedSetValue, rangePosition, view, onRangePositionChange, onViewChange]);
}, [value, wrappedSetValue, rangePosition, view, onRangePositionChange, setView]);

const endOverrides = React.useMemo(() => {
const handleEndRangeViewChange = (newView: DateOrTimeViewWithMeridiem) => {
Expand All @@ -161,17 +161,17 @@ const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePicker
if (rangePosition !== 'end') {
onRangePositionChange('end');
}
onViewChange(newView);
setView(newView);
};

return {
value: value[1],
setValue: wrappedSetValue,
forceDesktopVariant: true,
onViewChange: handleEndRangeViewChange,
setView: handleEndRangeViewChange,
view: rangePosition === 'end' ? view : null,
};
}, [value, wrappedSetValue, rangePosition, view, onRangePositionChange, onViewChange]);
}, [value, wrappedSetValue, rangePosition, view, onRangePositionChange, setView]);

if (hidden) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ export const useDesktopRangePicker = <
? providerProps.contextValue.view
: undefined,
initialView: initialView.current ?? undefined,
onViewChange: providerProps.contextValue.onViewChange,
...rangePositionResponse,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export interface UseEnrichedRangePickerFieldPropsParams<
anchorRef?: React.Ref<HTMLDivElement>;
currentView?: TView | null;
initialView?: TView;
onViewChange?: (view: TView) => void;
setView?: (view: TView) => void;
startFieldRef: React.RefObject<FieldRef<PickerValue> | null>;
endFieldRef: React.RefObject<FieldRef<PickerValue> | null>;
singleInputFieldRef: React.RefObject<FieldRef<PickerRangeValue> | null>;
Expand All @@ -138,7 +138,7 @@ const useMultiInputFieldSlotProps = <
anchorRef,
currentView,
initialView,
onViewChange,
setView,
startFieldRef,
endFieldRef,
}: UseEnrichedRangePickerFieldPropsParams<
Expand Down Expand Up @@ -198,7 +198,7 @@ const useMultiInputFieldSlotProps = <
if (open) {
onRangePositionChange('start');
if (previousRangePosition.current !== 'start' && initialView) {
onViewChange?.(initialView);
setView?.(initialView);
}
}
};
Expand All @@ -207,7 +207,7 @@ const useMultiInputFieldSlotProps = <
if (open) {
onRangePositionChange('end');
if (previousRangePosition.current !== 'end' && initialView) {
onViewChange?.(initialView);
setView?.(initialView);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ const DateTimePickerTabs = function DateTimePickerTabs(inProps: DateTimePickerTa

const translations = usePickerTranslations();
const { ownerState } = usePickerPrivateContext();
const { view, onViewChange } = usePickerContext();
const { view, setView } = usePickerContext();
const classes = useUtilityClasses(classesProp);

const handleChange = (event: React.SyntheticEvent, value: TabValue) => {
onViewChange(tabToView(value));
setView(tabToView(value));
};

if (hidden) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export const DateTimePickerToolbarOverrideContext = React.createContext<{
value: PickerValue;
setValue: (value: PickerValue, options?: SetValueActionOptions<DateTimeValidationError>) => void;
forceDesktopVariant: boolean;
onViewChange: (view: DateOrTimeViewWithMeridiem) => void;
setView: (view: DateOrTimeViewWithMeridiem) => void;
view: DateOrTimeViewWithMeridiem | null;
} | null>(null);

Expand Down Expand Up @@ -278,7 +278,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToolbarProps) {
variant,
orientation,
view: viewContext,
onViewChange: onViewChangeContext,
setView: setViewContext,
views,
} = usePickerContext();

Expand All @@ -291,7 +291,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToolbarProps) {
const value = overrides ? overrides.value : valueContext;
const setValue = overrides ? overrides.setValue : setValueContext;
const view = overrides ? overrides.view : viewContext;
const onViewChange = overrides ? overrides.onViewChange : onViewChangeContext;
const setView = overrides ? overrides.setView : setViewContext;

const { meridiemMode, handleMeridiemChange } = useMeridiemMode(value, ampm, (newValue) =>
setValue(newValue, { changeImportance: 'set' }),
Expand Down Expand Up @@ -336,7 +336,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToolbarProps) {
tabIndex={-1}
variant="subtitle1"
data-testid="datetimepicker-toolbar-year"
onClick={() => onViewChange('year')}
onClick={() => setView('year')}
selected={view === 'year'}
value={formatSection('year', '–')}
/>
Expand All @@ -347,7 +347,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToolbarProps) {
tabIndex={-1}
variant={isDesktop ? 'h5' : 'h4'}
data-testid="datetimepicker-toolbar-day"
onClick={() => onViewChange('day')}
onClick={() => setView('day')}
selected={view === 'day'}
value={dateText}
/>
Expand All @@ -373,7 +373,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToolbarProps) {
: undefined
}
data-testid="hours"
onClick={() => onViewChange('hours')}
onClick={() => setView('hours')}
selected={view === 'hours'}
value={formatSection(ampm ? 'hours12h' : 'hours24h', '--')}
/>
Expand All @@ -392,7 +392,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToolbarProps) {
: undefined
}
data-testid="minutes"
onClick={() => onViewChange('minutes')}
onClick={() => setView('minutes')}
selected={view === 'minutes' || (!views.includes('minutes') && view === 'hours')}
value={formatSection('minutes', '--')}
disabled={!views.includes('minutes')}
Expand All @@ -417,7 +417,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToolbarProps) {
: undefined
}
data-testid="seconds"
onClick={() => onViewChange('seconds')}
onClick={() => setView('seconds')}
selected={view === 'seconds'}
value={formatSection('seconds', '--')}
/>
Expand Down Expand Up @@ -452,7 +452,7 @@ function DateTimePickerToolbar(inProps: DateTimePickerToolbarProps) {
<PickersToolbarButton
variant="h5"
data-testid="am-pm-view-button"
onClick={() => onViewChange('meridiem')}
onClick={() => setView('meridiem')}
selected={view === 'meridiem'}
value={value && meridiemMode ? formatMeridiem(utils, meridiemMode) : '--'}
width={MULTI_SECTION_CLOCK_SECTION_WIDTH}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('<PickersActionBar />', () => {
const renderWithContext = (element: React.ReactElement) => {
const spys = {
setValue: spy(),
setView: spy(),
setOpen: spy(),
clearValue: spy(),
setValueToToday: spy(),
Expand Down
8 changes: 4 additions & 4 deletions packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ function TimePickerToolbar(inProps: TimePickerToolbarProps) {
const translations = usePickerTranslations();
const ownerState = useToolbarOwnerState();
const classes = useUtilityClasses(classesProp, ownerState);
const { value, setValue, disabled, readOnly, view, onViewChange, views } = usePickerContext<
const { value, setValue, disabled, readOnly, view, setView, views } = usePickerContext<
PickerValue,
TimeViewWithMeridiem
>();
Expand Down Expand Up @@ -199,7 +199,7 @@ function TimePickerToolbar(inProps: TimePickerToolbarProps) {
data-testid="hours"
tabIndex={-1}
variant="h3"
onClick={() => onViewChange('hours')}
onClick={() => setView('hours')}
selected={view === 'hours'}
value={formatSection(ampm ? 'hours12h' : 'hours24h')}
/>
Expand All @@ -211,7 +211,7 @@ function TimePickerToolbar(inProps: TimePickerToolbarProps) {
data-testid="minutes"
tabIndex={-1}
variant="h3"
onClick={() => onViewChange('minutes')}
onClick={() => setView('minutes')}
selected={view === 'minutes'}
value={formatSection('minutes')}
/>
Expand All @@ -222,7 +222,7 @@ function TimePickerToolbar(inProps: TimePickerToolbarProps) {
<PickersToolbarButton
data-testid="seconds"
variant="h3"
onClick={() => onViewChange('seconds')}
onClick={() => setView('seconds')}
selected={view === 'seconds'}
value={formatSection('seconds')}
/>
Expand Down
4 changes: 3 additions & 1 deletion packages/x-date-pickers/src/hooks/usePickerActionsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import {
PickerActionsContext,
PickerActionsContextValue,
} from '../internals/components/PickerProvider';
import { PickerValidValue, PickerValue } from '../internals/models';
import { DateOrTimeViewWithMeridiem, PickerValidValue, PickerValue } from '../internals/models';

/**
* Returns a subset of the context passed by the picker wrapping the current component.
* It only contains the actions and never causes a re-render of the component using it.
*/
export const usePickerActionsContext = <
TValue extends PickerValidValue = PickerValue,
TView extends DateOrTimeViewWithMeridiem = DateOrTimeViewWithMeridiem,
TError = string,
>() => {
const value = React.useContext(PickerActionsContext) as PickerActionsContextValue<
TValue,
TView,
TError
> | null;
if (value == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@ import type {
UsePickerValueContextValue,
UsePickerValuePrivateContextValue,
} from '../hooks/usePicker/usePickerValue.types';
import { UsePickerViewsContextValue } from '../hooks/usePicker/usePickerViews';
import {
UsePickerViewsActionsContextValue,
UsePickerViewsContextValue,
} from '../hooks/usePicker/usePickerViews';
import { IsValidValueContext } from '../../hooks/useIsValidValue';

export const PickerContext = React.createContext<PickerContextValue<any, any, any> | null>(null);

export const PickerActionsContext = React.createContext<PickerActionsContextValue<any, any> | null>(
null,
);
export const PickerActionsContext = React.createContext<PickerActionsContextValue<
any,
any,
any
> | null>(null);

export const PickerPrivateContext = React.createContext<PickerPrivateContextValue>({
ownerState: {
Expand Down Expand Up @@ -68,7 +73,7 @@ export function PickerProvider<TValue extends PickerValidValue>(

export interface PickerProviderProps<TValue extends PickerValidValue> {
contextValue: PickerContextValue<any, any, any>;
actionsContextValue: PickerActionsContextValue<any, any>;
actionsContextValue: PickerActionsContextValue<any, any, any>;
privateContextValue: PickerPrivateContextValue;
isValidContextValue: (value: TValue) => boolean;
localeText: PickersInputLocaleText | undefined;
Expand Down Expand Up @@ -108,8 +113,12 @@ export interface PickerContextValue<
orientation: PickerOrientation;
}

export interface PickerActionsContextValue<TValue extends PickerValidValue, TError = string>
extends UsePickerValueActionsContextValue<TValue, TError> {}
export interface PickerActionsContextValue<
TValue extends PickerValidValue,
TView extends DateOrTimeViewWithMeridiem,
TError = string,
> extends UsePickerValueActionsContextValue<TValue, TError>,
UsePickerViewsActionsContextValue<TView> {}

export interface PickerPrivateContextValue extends UsePickerValuePrivateContextValue {
/**
Expand Down
28 changes: 13 additions & 15 deletions packages/x-date-pickers/src/internals/hooks/useOpenState.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import useEventCallback from '@mui/utils/useEventCallback';

export interface OpenStateProps {
open?: boolean;
Expand All @@ -22,23 +23,20 @@ export const useOpenState = ({ open, onOpen, onClose }: OpenStateProps) => {
}
}, [isControllingOpenProp, open]);

const setOpen = React.useCallback(
(action: React.SetStateAction<boolean>) => {
const newOpen = typeof action === 'function' ? action(openState) : action;
if (!isControllingOpenProp) {
setOpenState(newOpen);
}
const setOpen = useEventCallback((action: React.SetStateAction<boolean>) => {
const newOpen = typeof action === 'function' ? action(openState) : action;
if (!isControllingOpenProp) {
setOpenState(newOpen);
}

if (newOpen && onOpen) {
onOpen();
}
if (newOpen && onOpen) {
onOpen();
}

if (!newOpen && onClose) {
onClose();
}
},
[isControllingOpenProp, onOpen, onClose, openState],
);
if (!newOpen && onClose) {
onClose();
}
});

return { open: openState, setOpen };
};
Loading

0 comments on commit cad579c

Please sign in to comment.