Skip to content

Commit

Permalink
[base-ui][useSelect][useOption] Align external props handling (#39038)
Browse files Browse the repository at this point in the history
  • Loading branch information
mj12albert authored Sep 20, 2023
1 parent 9124a83 commit 18d9260
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 62 deletions.
4 changes: 2 additions & 2 deletions docs/pages/base-ui/api/use-option.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down
12 changes: 6 additions & 6 deletions docs/pages/base-ui/api/use-select.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down
11 changes: 10 additions & 1 deletion docs/translations/api-docs/use-option/use-option.json
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
{ "hookDescription": "", "parametersDescriptions": {}, "returnValueDescriptions": {} }
{
"hookDescription": "",
"parametersDescriptions": {},
"returnValueDescriptions": {
"getRootProps": { "description": "Resolver for the root slot's props." },
"highlighted": { "description": "If <code>true</code>, the option is highlighted." },
"rootRef": { "description": "Ref to the root slot DOM node." },
"selected": { "description": "If <code>true</code>, the option is selected." }
}
}
23 changes: 14 additions & 9 deletions packages/mui-base/src/useOption/useOption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -48,14 +48,19 @@ export function useOption<Value>(params: UseOptionParameters<Value>): UseOptionR
const handleRef = useForkRef(optionRefParam, optionRef, listItemRefHandler)!;

return {
getRootProps: <Other extends EventHandlers = {}>(otherHandlers: Other = {} as Other) => ({
...otherHandlers,
...getListItemProps(otherHandlers),
id,
ref: handleRef,
role: 'option',
'aria-selected': selected,
}),
getRootProps: <ExternalProps extends Record<string, unknown> = {}>(
externalProps: ExternalProps = {} as ExternalProps,
) => {
const externalEventHandlers = extractEventHandlers(externalProps);
return {
...externalProps,
...getListItemProps(externalEventHandlers),
id,
ref: handleRef,
role: 'option',
'aria-selected': selected,
};
},
highlighted,
index,
selected,
Expand Down
27 changes: 20 additions & 7 deletions packages/mui-base/src/useOption/useOption.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { UseListItemRootSlotProps } from '../useList';
import { EventHandlers } from '../utils';

export interface SelectOption<Value> {
value: Value;
Expand All @@ -18,16 +17,30 @@ export interface UseOptionParameters<Value> {
}

export interface UseOptionReturnValue {
/**
* If `true`, the option is selected.
*/
selected: boolean;
/**
* If `true`, the option is highlighted.
*/
highlighted: boolean;
index: number;
getRootProps: <Other extends EventHandlers>(
otherHandlers?: Other,
) => UseOptionRootSlotProps<Other>;
/**
* 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 extends Record<string, unknown>>(
externalProps?: ExternalProps,
) => UseOptionRootSlotProps<ExternalProps>;
/**
* Ref to the root slot DOM node.
*/
rootRef: React.RefCallback<Element> | null;
}

export type UseOptionRootSlotProps<Other extends EventHandlers = {}> =
UseListItemRootSlotProps<Other> & {
export type UseOptionRootSlotProps<ExternalProps extends Record<string, unknown> = {}> =
UseListItemRootSlotProps<ExternalProps> & {
ref?: React.RefCallback<Element> | null;
} & Other;
} & ExternalProps;
56 changes: 31 additions & 25 deletions packages/mui-base/src/useSelect/useSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -299,9 +300,8 @@ function useSelect<OptionValue, Multiple extends boolean = false>(
} = useList(useListParameters);

const createHandleButtonMouseDown =
(otherHandlers?: Record<string, React.EventHandler<any>>) =>
(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,
Expand Down Expand Up @@ -336,8 +336,8 @@ function useSelect<OptionValue, Multiple extends boolean = false>(
[getOptionByValue],
);

const getSelectTriggerProps = <TOther extends EventHandlers>(
otherHandlers: TOther = {} as TOther,
const getSelectTriggerProps = <OtherHandlers extends EventHandlers>(
otherHandlers: OtherHandlers = {} as OtherHandlers,
) => {
return {
...otherHandlers,
Expand All @@ -349,19 +349,23 @@ function useSelect<OptionValue, Multiple extends boolean = false>(
};
};

const getButtonProps = <TOther extends EventHandlers>(
otherHandlers: TOther = {} as TOther,
): UseSelectButtonSlotProps<TOther> => {
const getButtonProps = <ExternalProps extends Record<string, unknown>>(
externalProps: ExternalProps = {} as ExternalProps,
): UseSelectButtonSlotProps<ExternalProps> => {
const externalEventHandlers = extractEventHandlers(externalProps);
const listboxAndButtonProps = combineHooksSlotProps(getButtonRootProps, getListboxRootProps);
const combinedProps = combineHooksSlotProps(listboxAndButtonProps, getSelectTriggerProps);
return combinedProps(otherHandlers);
return {
...externalProps,
...combinedProps(externalEventHandlers),
};
};

const getListboxProps = <TOther extends EventHandlers>(
otherHandlers: TOther = {} as TOther,
): UseSelectListboxSlotProps<TOther> => {
const getListboxProps = <ExternalProps extends Record<string, unknown>>(
externalProps: ExternalProps = {} as ExternalProps,
): UseSelectListboxSlotProps<ExternalProps> => {
return {
...otherHandlers,
...externalProps,
id: listboxId,
role: 'listbox' as const,
'aria-multiselectable': multiple ? 'true' : undefined,
Expand Down Expand Up @@ -404,18 +408,20 @@ function useSelect<OptionValue, Multiple extends boolean = false>(
null) as SelectValue<SelectOption<OptionValue>, Multiple>;
}

const getHiddenInputProps = <TOther extends EventHandlers>(
otherHandlers: TOther = {} as TOther,
): UseSelectHiddenInputSlotProps<TOther> => ({
name,
tabIndex: -1,
'aria-hidden': true,
required: required ? true : undefined,
value: getSerializedValue(selectedOptionsMetadata),
onChange: noop,
style: visuallyHiddenStyle,
...otherHandlers,
});
const getHiddenInputProps = <ExternalProps extends Record<string, unknown>>(
externalProps: ExternalProps = {} as ExternalProps,
): UseSelectHiddenInputSlotProps<ExternalProps> => {
return {
name,
tabIndex: -1,
'aria-hidden': true,
required: required ? true : undefined,
value: getSerializedValue(selectedOptionsMetadata),
onChange: noop,
style: visuallyHiddenStyle,
...externalProps,
};
};

return {
buttonActive,
Expand Down
24 changes: 12 additions & 12 deletions packages/mui-base/src/useSelect/useSelect.types.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -182,27 +181,28 @@ export interface UseSelectReturnValue<Value, Multiple> {
dispatch: (action: ListAction<Value> | 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 extends EventHandlers = {}>(
otherHandlers?: OtherHandlers,
) => UseSelectButtonSlotProps<OtherHandlers>;
getButtonProps: <ExternalProps extends Record<string, unknown> = {}>(
externalProps?: ExternalProps,
) => UseSelectButtonSlotProps<ExternalProps>;
/**
* 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 extends EventHandlers = {}>(
otherHandlers?: OtherHandlers,
) => UseSelectHiddenInputSlotProps<OtherHandlers>;
getHiddenInputProps: <ExternalProps extends Record<string, unknown> = {}>(
externalProps?: ExternalProps,
) => UseSelectHiddenInputSlotProps<ExternalProps>;
/**
* 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 extends EventHandlers = {}>(
otherHandlers?: OtherHandlers,
) => UseSelectListboxSlotProps<OtherHandlers>;
getListboxProps: <ExternalProps extends Record<string, unknown> = {}>(
externalProps?: ExternalProps,
) => UseSelectListboxSlotProps<ExternalProps>;
/**
* A function that returns the metadata of an option with a given value.
*
Expand Down

0 comments on commit 18d9260

Please sign in to comment.