Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[base-ui][useSelect][useOption] Align external props handling #39038

Merged
merged 1 commit into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Update types
  • Loading branch information
mj12albert committed Sep 18, 2023
commit d313bf5c5ee230f16ed4e1cbcd40ecdcc2a0ad51
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