From 9b758eee271954eb7228916bf822b09a1a715e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chalifour?= Date: Tue, 10 Mar 2020 10:34:46 +0100 Subject: [PATCH] feat(core): introduce `getDropdownProps` The `onMouseLeave` event handler shouldn't be on the menu (a.k.a. list) because there might be some titles or other DOM elements in between. --- .../autocomplete-core/src/autocomplete.ts | 8 ++- packages/autocomplete-core/src/propGetters.ts | 59 +++++++++++-------- .../autocomplete-core/src/types/getters.ts | 39 +++++++----- .../autocomplete-react/src/Autocomplete.tsx | 3 +- packages/autocomplete-react/src/Dropdown.tsx | 7 ++- 5 files changed, 68 insertions(+), 48 deletions(-) diff --git a/packages/autocomplete-core/src/autocomplete.ts b/packages/autocomplete-core/src/autocomplete.ts index c51d630b7..70507f8c5 100644 --- a/packages/autocomplete-core/src/autocomplete.ts +++ b/packages/autocomplete-core/src/autocomplete.ts @@ -29,10 +29,11 @@ function createAutocomplete< getEnvironmentProps, getRootProps, getFormProps, - getInputProps, - getItemProps, getLabelProps, + getInputProps, + getDropdownProps, getMenuProps, + getItemProps, } = getPropGetters({ store, props, @@ -55,9 +56,10 @@ function createAutocomplete< getRootProps, getFormProps, getInputProps, - getItemProps, getLabelProps, + getDropdownProps, getMenuProps, + getItemProps, }; } diff --git a/packages/autocomplete-core/src/propGetters.ts b/packages/autocomplete-core/src/propGetters.ts index 165cae593..eebc534bc 100644 --- a/packages/autocomplete-core/src/propGetters.ts +++ b/packages/autocomplete-core/src/propGetters.ts @@ -6,10 +6,11 @@ import { GetEnvironmentProps, GetRootProps, GetFormProps, - GetInputProps, - GetItemProps, GetLabelProps, + GetInputProps, + GetDropdownProps, GetMenuProps, + GetItemProps, AutocompleteStore, AutocompleteOptions, AutocompleteSetters, @@ -30,8 +31,6 @@ export function getPropGetters({ setStatus, setContext, }: GetPropGettersOptions) { - const isTouchDevice = 'ontouchstart' in props.environment; - const getEnvironmentProps: GetEnvironmentProps = getterProps => { return { // On touch devices, we do not rely on the native `blur` event of the @@ -165,6 +164,7 @@ export function getPropGetters({ store.send('focus', null); } + const isTouchDevice = 'ontouchstart' in props.environment; const { inputElement, ...rest } = providedProps; return { @@ -240,6 +240,32 @@ export function getPropGetters({ }; }; + const getLabelProps: GetLabelProps = rest => { + return { + htmlFor: `${props.id}-input`, + id: `${props.id}-label`, + ...rest, + }; + }; + + const getMenuProps: GetMenuProps = rest => { + return { + role: 'listbox', + 'aria-labelledby': `${props.id}-label`, + id: `${props.id}-menu`, + ...rest, + }; + }; + + const getDropdownProps: GetDropdownProps = rest => { + return { + onMouseLeave() { + store.send('mouseleave', null); + }, + ...rest, + }; + }; + const getItemProps: GetItemProps = providedProps => { const { item, source, ...rest } = providedProps; @@ -329,33 +355,14 @@ export function getPropGetters({ }; }; - const getLabelProps: GetLabelProps = rest => { - return { - htmlFor: `${props.id}-input`, - id: `${props.id}-label`, - ...rest, - }; - }; - - const getMenuProps: GetMenuProps = rest => { - return { - role: 'listbox', - 'aria-labelledby': `${props.id}-label`, - id: `${props.id}-menu`, - onMouseLeave() { - store.send('mouseleave', null); - }, - ...rest, - }; - }; - return { getEnvironmentProps, getRootProps, getFormProps, - getInputProps, - getItemProps, getLabelProps, + getInputProps, + getDropdownProps, getMenuProps, + getItemProps, }; } diff --git a/packages/autocomplete-core/src/types/getters.ts b/packages/autocomplete-core/src/types/getters.ts index d976b39fc..d2ceeb278 100644 --- a/packages/autocomplete-core/src/types/getters.ts +++ b/packages/autocomplete-core/src/types/getters.ts @@ -9,10 +9,11 @@ export interface AutocompleteAccessibilityGetters< getEnvironmentProps: GetEnvironmentProps; getRootProps: GetRootProps; getFormProps: GetFormProps; - getInputProps: GetInputProps; - getItemProps: GetItemProps; getLabelProps: GetLabelProps; + getInputProps: GetInputProps; + getDropdownProps: GetDropdownProps; getMenuProps: GetMenuProps; + getItemProps: GetItemProps; } export type GetEnvironmentProps = (props: { @@ -52,6 +53,13 @@ export type GetFormProps = (props: { onReset(event: TEvent): void; }; +export type GetLabelProps = (props?: { + [key: string]: unknown; +}) => { + htmlFor: string; + id: string; +}; + export type GetInputProps = (props: { [key: string]: unknown; inputElement: HTMLInputElement; @@ -75,30 +83,29 @@ export type GetInputProps = (props: { onClick(event: TMouseEvent): void; }; -export type GetItemProps = (props: { +export type GetDropdownProps = (props?: { [key: string]: unknown; - item: TItem; - source: AutocompleteSource; }) => { - id: string; - role: string; - 'aria-selected': boolean; - onMouseMove(event: TMouseEvent): void; - onMouseDown(event: TMouseEvent): void; - onClick(event: TMouseEvent): void; + onMouseLeave(): void; }; -export type GetLabelProps = (props?: { +export type GetMenuProps = (props?: { [key: string]: unknown; }) => { - htmlFor: string; + role: string; + 'aria-labelledby': string; id: string; }; -export type GetMenuProps = (props?: { +export type GetItemProps = (props: { [key: string]: unknown; + item: TItem; + source: AutocompleteSource; }) => { - role: string; - 'aria-labelledby': string; id: string; + role: string; + 'aria-selected': boolean; + onMouseMove(event: TMouseEvent): void; + onMouseDown(event: TMouseEvent): void; + onClick(event: TMouseEvent): void; }; diff --git a/packages/autocomplete-react/src/Autocomplete.tsx b/packages/autocomplete-react/src/Autocomplete.tsx index 7f42de6ad..fcd796893 100644 --- a/packages/autocomplete-react/src/Autocomplete.tsx +++ b/packages/autocomplete-react/src/Autocomplete.tsx @@ -135,8 +135,9 @@ export function Autocomplete( suggestions={state.suggestions} isOpen={state.isOpen} status={state.status} - getItemProps={autocomplete.getItemProps} getMenuProps={autocomplete.getMenuProps} + getDropdownProps={autocomplete.getDropdownProps} + getItemProps={autocomplete.getItemProps} />, rendererProps.dropdownContainer )} diff --git a/packages/autocomplete-react/src/Dropdown.tsx b/packages/autocomplete-react/src/Dropdown.tsx index 0bbc2b5fa..821b3f8fa 100644 --- a/packages/autocomplete-react/src/Dropdown.tsx +++ b/packages/autocomplete-react/src/Dropdown.tsx @@ -4,16 +4,18 @@ import { reverseHighlightAlgoliaHit } from '@francoischalifour/autocomplete-pres import { AutocompleteState, - GetItemProps, + GetDropdownProps, GetMenuProps, + GetItemProps, } from '@francoischalifour/autocomplete-core'; interface DropdownProps { isOpen: boolean; status: string; suggestions: AutocompleteState['suggestions']; - getItemProps: GetItemProps; + getDropdownProps: GetDropdownProps; getMenuProps: GetMenuProps; + getItemProps: GetItemProps; dropdownRef: React.MutableRefObject; } @@ -29,6 +31,7 @@ export const Dropdown = (props: DropdownProps) => { .join(' ')} ref={props.dropdownRef} hidden={!props.isOpen} + {...props.getDropdownProps()} > {props.isOpen && (