From 18d92600c72cb40a81c374d2b97cb510fb67b6d4 Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Wed, 20 Sep 2023 12:36:30 +0800 Subject: [PATCH] [base-ui][useSelect][useOption] Align external props handling (#39038) --- docs/pages/base-ui/api/use-option.json | 4 +- docs/pages/base-ui/api/use-select.json | 12 ++-- .../api-docs/use-option/use-option.json | 11 +++- packages/mui-base/src/useOption/useOption.ts | 23 +++++--- .../mui-base/src/useOption/useOption.types.ts | 27 ++++++--- packages/mui-base/src/useSelect/useSelect.ts | 56 ++++++++++--------- .../mui-base/src/useSelect/useSelect.types.ts | 24 ++++---- 7 files changed, 95 insertions(+), 62 deletions(-) diff --git a/docs/pages/base-ui/api/use-option.json b/docs/pages/base-ui/api/use-option.json index e71313c454812e..0db56f9c19f981 100644 --- a/docs/pages/base-ui/api/use-option.json +++ b/docs/pages/base-ui/api/use-option.json @@ -14,8 +14,8 @@ "returnValue": { "getRootProps": { "type": { - "name": "<Other extends EventHandlers>(otherHandlers?: Other) => UseOptionRootSlotProps<Other>", - "description": "<Other extends EventHandlers>(otherHandlers?: Other) => UseOptionRootSlotProps<Other>" + "name": "<ExternalProps extends Record<string, unknown>>(externalProps?: ExternalProps) => UseOptionRootSlotProps<ExternalProps>", + "description": "<ExternalProps extends Record<string, unknown>>(externalProps?: ExternalProps) => UseOptionRootSlotProps<ExternalProps>" }, "required": true }, diff --git a/docs/pages/base-ui/api/use-select.json b/docs/pages/base-ui/api/use-select.json index e930c7a6758041..20af4c0a2f7de0 100644 --- a/docs/pages/base-ui/api/use-select.json +++ b/docs/pages/base-ui/api/use-select.json @@ -96,22 +96,22 @@ }, "getButtonProps": { "type": { - "name": "<OtherHandlers extends EventHandlers = {}>(otherHandlers?: OtherHandlers) => UseSelectButtonSlotProps<OtherHandlers>", - "description": "<OtherHandlers extends EventHandlers = {}>(otherHandlers?: OtherHandlers) => UseSelectButtonSlotProps<OtherHandlers>" + "name": "<ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseSelectButtonSlotProps<ExternalProps>", + "description": "<ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseSelectButtonSlotProps<ExternalProps>" }, "required": true }, "getHiddenInputProps": { "type": { - "name": "<OtherHandlers extends EventHandlers = {}>(otherHandlers?: OtherHandlers) => UseSelectHiddenInputSlotProps<OtherHandlers>", - "description": "<OtherHandlers extends EventHandlers = {}>(otherHandlers?: OtherHandlers) => UseSelectHiddenInputSlotProps<OtherHandlers>" + "name": "<ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseSelectHiddenInputSlotProps<ExternalProps>", + "description": "<ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseSelectHiddenInputSlotProps<ExternalProps>" }, "required": true }, "getListboxProps": { "type": { - "name": "<OtherHandlers extends EventHandlers = {}>(otherHandlers?: OtherHandlers) => UseSelectListboxSlotProps<OtherHandlers>", - "description": "<OtherHandlers extends EventHandlers = {}>(otherHandlers?: OtherHandlers) => UseSelectListboxSlotProps<OtherHandlers>" + "name": "<ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseSelectListboxSlotProps<ExternalProps>", + "description": "<ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseSelectListboxSlotProps<ExternalProps>" }, "required": true }, diff --git a/docs/translations/api-docs/use-option/use-option.json b/docs/translations/api-docs/use-option/use-option.json index e3eb65c6e43006..07b5b62753ad2c 100644 --- a/docs/translations/api-docs/use-option/use-option.json +++ b/docs/translations/api-docs/use-option/use-option.json @@ -1 +1,10 @@ -{ "hookDescription": "", "parametersDescriptions": {}, "returnValueDescriptions": {} } +{ + "hookDescription": "", + "parametersDescriptions": {}, + "returnValueDescriptions": { + "getRootProps": { "description": "Resolver for the root slot's props." }, + "highlighted": { "description": "If true, the option is highlighted." }, + "rootRef": { "description": "Ref to the root slot DOM node." }, + "selected": { "description": "If true, the option is selected." } + } +} diff --git a/packages/mui-base/src/useOption/useOption.ts b/packages/mui-base/src/useOption/useOption.ts index 556e82506e3344..87f9f02865ff88 100644 --- a/packages/mui-base/src/useOption/useOption.ts +++ b/packages/mui-base/src/useOption/useOption.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { unstable_useForkRef as useForkRef, unstable_useId as useId } from '@mui/utils'; import { SelectOption, UseOptionParameters, UseOptionReturnValue } from './useOption.types'; -import { EventHandlers } from '../utils'; +import { extractEventHandlers } from '../utils/extractEventHandlers'; import { useListItem } from '../useList'; import { useCompoundItem } from '../utils/useCompoundItem'; @@ -48,14 +48,19 @@ export function useOption(params: UseOptionParameters): UseOptionR const handleRef = useForkRef(optionRefParam, optionRef, listItemRefHandler)!; return { - getRootProps: (otherHandlers: Other = {} as Other) => ({ - ...otherHandlers, - ...getListItemProps(otherHandlers), - id, - ref: handleRef, - role: 'option', - 'aria-selected': selected, - }), + getRootProps: = {}>( + externalProps: ExternalProps = {} as ExternalProps, + ) => { + const externalEventHandlers = extractEventHandlers(externalProps); + return { + ...externalProps, + ...getListItemProps(externalEventHandlers), + id, + ref: handleRef, + role: 'option', + 'aria-selected': selected, + }; + }, highlighted, index, selected, diff --git a/packages/mui-base/src/useOption/useOption.types.ts b/packages/mui-base/src/useOption/useOption.types.ts index 7693a3c9fa40ef..32225dc0035728 100644 --- a/packages/mui-base/src/useOption/useOption.types.ts +++ b/packages/mui-base/src/useOption/useOption.types.ts @@ -1,5 +1,4 @@ import { UseListItemRootSlotProps } from '../useList'; -import { EventHandlers } from '../utils'; export interface SelectOption { value: Value; @@ -18,16 +17,30 @@ export interface UseOptionParameters { } export interface UseOptionReturnValue { + /** + * If `true`, the option is selected. + */ selected: boolean; + /** + * If `true`, the option is highlighted. + */ highlighted: boolean; index: number; - getRootProps: ( - otherHandlers?: Other, - ) => UseOptionRootSlotProps; + /** + * Resolver for the root slot's props. + * @param externalProps props for the root slot + * @returns props that should be spread on the root slot + */ + getRootProps: >( + externalProps?: ExternalProps, + ) => UseOptionRootSlotProps; + /** + * Ref to the root slot DOM node. + */ rootRef: React.RefCallback | null; } -export type UseOptionRootSlotProps = - UseListItemRootSlotProps & { +export type UseOptionRootSlotProps = {}> = + UseListItemRootSlotProps & { ref?: React.RefCallback | null; - } & Other; + } & ExternalProps; diff --git a/packages/mui-base/src/useSelect/useSelect.ts b/packages/mui-base/src/useSelect/useSelect.ts index 03fb06affc6a99..265b70099168fb 100644 --- a/packages/mui-base/src/useSelect/useSelect.ts +++ b/packages/mui-base/src/useSelect/useSelect.ts @@ -23,6 +23,7 @@ import { EventHandlers } from '../utils/types'; import { defaultOptionStringifier } from './defaultOptionStringifier'; import { SelectProviderValue } from './SelectProvider'; import { useCompoundParent } from '../utils/useCompound'; +import { extractEventHandlers } from '../utils/extractEventHandlers'; import { SelectOption } from '../useOption/useOption.types'; import { selectReducer } from './selectReducer'; import { combineHooksSlotProps } from '../utils/combineHooksSlotProps'; @@ -299,9 +300,8 @@ function useSelect( } = useList(useListParameters); const createHandleButtonMouseDown = - (otherHandlers?: Record>) => - (event: React.MouseEvent & MuiCancellableEvent) => { - otherHandlers?.onMouseDown?.(event); + (externalEventHandlers?: EventHandlers) => (event: React.MouseEvent & MuiCancellableEvent) => { + externalEventHandlers?.onMouseDown?.(event); if (!event.defaultMuiPrevented) { const action: ButtonClickAction = { type: SelectActionTypes.buttonClick, @@ -336,8 +336,8 @@ function useSelect( [getOptionByValue], ); - const getSelectTriggerProps = ( - otherHandlers: TOther = {} as TOther, + const getSelectTriggerProps = ( + otherHandlers: OtherHandlers = {} as OtherHandlers, ) => { return { ...otherHandlers, @@ -349,19 +349,23 @@ function useSelect( }; }; - const getButtonProps = ( - otherHandlers: TOther = {} as TOther, - ): UseSelectButtonSlotProps => { + const getButtonProps = >( + externalProps: ExternalProps = {} as ExternalProps, + ): UseSelectButtonSlotProps => { + const externalEventHandlers = extractEventHandlers(externalProps); const listboxAndButtonProps = combineHooksSlotProps(getButtonRootProps, getListboxRootProps); const combinedProps = combineHooksSlotProps(listboxAndButtonProps, getSelectTriggerProps); - return combinedProps(otherHandlers); + return { + ...externalProps, + ...combinedProps(externalEventHandlers), + }; }; - const getListboxProps = ( - otherHandlers: TOther = {} as TOther, - ): UseSelectListboxSlotProps => { + const getListboxProps = >( + externalProps: ExternalProps = {} as ExternalProps, + ): UseSelectListboxSlotProps => { return { - ...otherHandlers, + ...externalProps, id: listboxId, role: 'listbox' as const, 'aria-multiselectable': multiple ? 'true' : undefined, @@ -404,18 +408,20 @@ function useSelect( null) as SelectValue, Multiple>; } - const getHiddenInputProps = ( - otherHandlers: TOther = {} as TOther, - ): UseSelectHiddenInputSlotProps => ({ - name, - tabIndex: -1, - 'aria-hidden': true, - required: required ? true : undefined, - value: getSerializedValue(selectedOptionsMetadata), - onChange: noop, - style: visuallyHiddenStyle, - ...otherHandlers, - }); + const getHiddenInputProps = >( + externalProps: ExternalProps = {} as ExternalProps, + ): UseSelectHiddenInputSlotProps => { + return { + name, + tabIndex: -1, + 'aria-hidden': true, + required: required ? true : undefined, + value: getSerializedValue(selectedOptionsMetadata), + onChange: noop, + style: visuallyHiddenStyle, + ...externalProps, + }; + }; return { buttonActive, diff --git a/packages/mui-base/src/useSelect/useSelect.types.ts b/packages/mui-base/src/useSelect/useSelect.types.ts index 762b922a13df84..33c7653608ae7f 100644 --- a/packages/mui-base/src/useSelect/useSelect.types.ts +++ b/packages/mui-base/src/useSelect/useSelect.types.ts @@ -1,7 +1,6 @@ import * as React from 'react'; import { ListAction, ListState, UseListRootSlotProps } from '../useList'; import { SelectOption } from '../useOption/useOption.types'; -import { EventHandlers } from '../utils/types'; import { SelectProviderValue } from './SelectProvider'; import { MuiCancellableEventHandler } from '../utils/MuiCancellableEvent'; @@ -182,27 +181,28 @@ export interface UseSelectReturnValue { dispatch: (action: ListAction | SelectAction) => void; /** * Resolver for the button slot's props. - * @param otherHandlers event handlers for the button slot + * @param externalProps event handlers for the button slot * @returns props that should be spread on the button slot */ - getButtonProps: ( - otherHandlers?: OtherHandlers, - ) => UseSelectButtonSlotProps; + getButtonProps: = {}>( + externalProps?: ExternalProps, + ) => UseSelectButtonSlotProps; /** * Resolver for the hidden input slot's props. + * @param externalProps event handlers for the hidden input slot * @returns HTML input attributes that should be spread on the hidden input slot */ - getHiddenInputProps: ( - otherHandlers?: OtherHandlers, - ) => UseSelectHiddenInputSlotProps; + getHiddenInputProps: = {}>( + externalProps?: ExternalProps, + ) => UseSelectHiddenInputSlotProps; /** * Resolver for the listbox slot's props. - * @param otherHandlers event handlers for the listbox slot + * @param externalProps event handlers for the listbox slot * @returns props that should be spread on the listbox slot */ - getListboxProps: ( - otherHandlers?: OtherHandlers, - ) => UseSelectListboxSlotProps; + getListboxProps: = {}>( + externalProps?: ExternalProps, + ) => UseSelectListboxSlotProps; /** * A function that returns the metadata of an option with a given value. *