diff --git a/packages/code-studio/src/styleguide/Pickers.tsx b/packages/code-studio/src/styleguide/Pickers.tsx index 739ff8d51..bef43fbf9 100644 --- a/packages/code-studio/src/styleguide/Pickers.tsx +++ b/packages/code-studio/src/styleguide/Pickers.tsx @@ -9,6 +9,7 @@ import { PickerNormalized, Checkbox, ComboBox, + ComboBoxNormalized, } from '@deephaven/components'; import { vsPerson } from '@deephaven/icons'; import { Icon } from '@adobe/react-spectrum'; @@ -61,10 +62,24 @@ function PersonIcon(): JSX.Element { } export function Pickers(): JSX.Element { - const [selectedKey, setSelectedKey] = useState(null); + const [selectedKey, setSelectedKey] = useState(200); const [showIcons, setShowIcons] = useState(true); + const [filteredItems, setFilteredItems] = useState(itemsWithIcons); + + const onSearch = useCallback( + (searchText: string) => + setFilteredItems( + searchText === '' + ? itemsWithIcons + : itemsWithIcons.filter( + ({ item }) => item?.textValue?.includes(searchText) + ) + ), + [] + ); + const getInitialScrollPosition = useCallback( async () => getPositionOfSelectedItem({ @@ -163,6 +178,17 @@ export function Pickers(): JSX.Element { showItemIcons={showIcons} onChange={onChange} /> + diff --git a/packages/components/src/SearchableCombobox.tsx b/packages/components/src/SearchableCombobox.tsx index 584831541..33af6ab35 100644 --- a/packages/components/src/SearchableCombobox.tsx +++ b/packages/components/src/SearchableCombobox.tsx @@ -1,8 +1,7 @@ /* eslint-disable react/jsx-props-no-spreading */ import { Key, useCallback } from 'react'; import { ComboBox, Item, SpectrumComboBoxProps } from '@adobe/react-spectrum'; -import type { FocusableRef } from '@react-types/shared'; -import type { ReactSpectrumComponent } from '@deephaven/react-hooks'; +import type { DOMRefValue, FocusableRef } from '@react-types/shared'; import TextWithTooltip from './TextWithTooltip'; export interface SearchableComboboxProps @@ -12,7 +11,7 @@ export interface SearchableComboboxProps > { getItemDisplayText: (item: TItem | null | undefined) => string | null; getKey: (item: TItem | null | undefined) => TKey | null; - scrollRef: React.RefObject>; + scrollRef: React.RefObject; onSelectionChange: (key: TKey | null) => void; } diff --git a/packages/components/src/spectrum/comboBox/ComboBox.tsx b/packages/components/src/spectrum/comboBox/ComboBox.tsx index 6b4647fb4..75baff7de 100644 --- a/packages/components/src/spectrum/comboBox/ComboBox.tsx +++ b/packages/components/src/spectrum/comboBox/ComboBox.tsx @@ -16,8 +16,8 @@ export function ComboBox({ const { defaultSelectedKey, disabledKeys, + ref, selectedKey, - scrollRef, ...comboBoxProps } = usePickerProps(props); @@ -25,8 +25,8 @@ export function ComboBox({ } UNSAFE_className={cl('dh-combobox', UNSAFE_className)} + ref={ref as FocusableRef} // Type assertions are necessary here since Spectrum types don't account // for number and boolean key values even though they are valid runtime // values. diff --git a/packages/components/src/spectrum/comboBox/ComboBoxNormalized.tsx b/packages/components/src/spectrum/comboBox/ComboBoxNormalized.tsx new file mode 100644 index 000000000..b963803a4 --- /dev/null +++ b/packages/components/src/spectrum/comboBox/ComboBoxNormalized.tsx @@ -0,0 +1,39 @@ +import { ComboBox as SpectrumComboBox } from '@adobe/react-spectrum'; +import { FocusableRef } from '@react-types/shared'; +import cl from 'classnames'; +import { PickerNormalizedPropsT, usePickerNormalizedProps } from '../picker'; +import { ComboBoxProps } from './ComboBox'; + +export type ComboBoxNormalizedProps = PickerNormalizedPropsT; + +/** + * ComboBox that takes an array of `NormalizedItem` or `NormalizedSection` items + * as children and uses a render item function to render the items. `NormalizedItem` + * and `NormalizedSection` datums always provide a `key` property but have an + * optional `item` property that can be lazy loaded. This is necessary to support + * windowed data since we need a representative key for every item in the + * collection. + */ +export function ComboBoxNormalized({ + UNSAFE_className, + ...props +}: ComboBoxNormalizedProps): JSX.Element { + const { forceRerenderKey, ref, ...pickerProps } = + usePickerNormalizedProps(props); + + return ( + } + UNSAFE_className={cl( + 'dh-combobox', + 'dh-combobox-normalized', + UNSAFE_className + )} + /> + ); +} + +export default ComboBoxNormalized; diff --git a/packages/components/src/spectrum/comboBox/index.ts b/packages/components/src/spectrum/comboBox/index.ts index d122f03ba..e184e7b6b 100644 --- a/packages/components/src/spectrum/comboBox/index.ts +++ b/packages/components/src/spectrum/comboBox/index.ts @@ -1 +1,2 @@ export * from './ComboBox'; +export * from './ComboBoxNormalized'; diff --git a/packages/components/src/spectrum/listView/ListViewWrapper.tsx b/packages/components/src/spectrum/listView/ListViewWrapper.tsx index 7aff7f988..b08f98e87 100644 --- a/packages/components/src/spectrum/listView/ListViewWrapper.tsx +++ b/packages/components/src/spectrum/listView/ListViewWrapper.tsx @@ -46,7 +46,7 @@ export function ListViewWrapper( // of the parent container and only render the ListView when it has a non-zero // height. See https://github.com/adobe/react-spectrum/issues/6213 const { ref: contentRectRef, contentRect } = useContentRect( - extractSpectrumHTMLElement + extractSpectrumHTMLElement ); return ( diff --git a/packages/components/src/spectrum/picker/Picker.tsx b/packages/components/src/spectrum/picker/Picker.tsx index d6a85ce87..543f6888a 100644 --- a/packages/components/src/spectrum/picker/Picker.tsx +++ b/packages/components/src/spectrum/picker/Picker.tsx @@ -2,7 +2,6 @@ import { Picker as SpectrumPicker, SpectrumPickerProps, } from '@adobe/react-spectrum'; -import type { DOMRef } from '@react-types/shared'; import cl from 'classnames'; import type { NormalizedItem } from '../utils'; import type { PickerProps } from './PickerProps'; @@ -19,19 +18,13 @@ export function Picker({ UNSAFE_className, ...props }: PickerProps): JSX.Element { - const { - defaultSelectedKey, - disabledKeys, - selectedKey, - scrollRef, - ...pickerProps - } = usePickerProps(props); + const { defaultSelectedKey, disabledKeys, selectedKey, ...pickerProps } = + usePickerProps(props); return ( } UNSAFE_className={cl('dh-picker', UNSAFE_className)} // Type assertions are necessary here since Spectrum types don't account // for number and boolean key values even though they are valid runtime diff --git a/packages/components/src/spectrum/picker/PickerNormalized.tsx b/packages/components/src/spectrum/picker/PickerNormalized.tsx index 4e4409b92..ffe18b594 100644 --- a/packages/components/src/spectrum/picker/PickerNormalized.tsx +++ b/packages/components/src/spectrum/picker/PickerNormalized.tsx @@ -1,19 +1,8 @@ -import { useMemo } from 'react'; import { Picker as SpectrumPicker } from '@adobe/react-spectrum'; -import type { DOMRef } from '@react-types/shared'; import cl from 'classnames'; -import { EMPTY_FUNCTION } from '@deephaven/utils'; -import { Section } from '../shared'; import type { PickerNormalizedProps } from './PickerProps'; -import { - getItemKey, - isNormalizedSection, - normalizeTooltipOptions, - useRenderNormalizedItem, - useStringifiedSelection, -} from '../utils'; -import usePickerScrollOnOpen from './usePickerScrollOnOpen'; +import usePickerNormalizedProps from './usePickerNormalizedProps'; /** * Picker that takes an array of `NormalizedItem` or `NormalizedSection` items @@ -21,98 +10,25 @@ import usePickerScrollOnOpen from './usePickerScrollOnOpen'; * necessary to support windowed data. */ export function PickerNormalized({ - normalizedItems, - tooltip = true, - selectedKey, - defaultSelectedKey, - disabledKeys, - showItemIcons, UNSAFE_className, - getInitialScrollPosition, - onChange, - onOpenChange, - onScroll = EMPTY_FUNCTION, - onSelectionChange, ...props }: PickerNormalizedProps): JSX.Element { - const tooltipOptions = useMemo( - () => normalizeTooltipOptions(tooltip), - [tooltip] - ); - - const renderNormalizedItem = useRenderNormalizedItem({ - itemIconSlot: 'icon', - // Descriptions introduce variable item heights which throws off calculation - // of initial scroll position and setting viewport on windowed data. For now - // not going to implement description support in Picker. - // https://github.com/deephaven/web-client-ui/issues/1958 - showItemDescriptions: false, - showItemIcons, - tooltipOptions, - }); - - // Spectrum doesn't re-render if only the `renderNormalizedItems` function - // changes, so we create a key from its dependencies that can be used to force - // re-render. - const forceRerenderKey = `${showItemIcons}-${tooltipOptions?.placement}`; - - const { ref: scrollRef, onOpenChange: onOpenChangeInternal } = - usePickerScrollOnOpen({ - getInitialScrollPosition, - onScroll, - onOpenChange, - }); - - // Spectrum Picker treats keys as strings if the `key` prop is explicitly - // set on `Item` elements. Since we do this in `renderItem`, we need to - // map original key types to and from strings so that selection works. - const { - selectedStringKey, - defaultSelectedStringKey, - disabledStringKeys, - onStringSelectionChange, - } = useStringifiedSelection({ - normalizedItems, - selectedKey, - defaultSelectedKey, - disabledKeys, - onChange: onChange ?? onSelectionChange, - }); + const { forceRerenderKey, ...pickerProps } = usePickerNormalizedProps< + PickerNormalizedProps, + HTMLDivElement + >(props); return ( } UNSAFE_className={cl( 'dh-picker', 'dh-picker-normalized', UNSAFE_className )} - items={normalizedItems} - selectedKey={selectedStringKey} - defaultSelectedKey={defaultSelectedStringKey} - disabledKeys={disabledStringKeys} - onSelectionChange={onStringSelectionChange} - onOpenChange={onOpenChangeInternal} - > - {itemOrSection => { - if (isNormalizedSection(itemOrSection)) { - return ( -
- {renderNormalizedItem} -
- ); - } - - return renderNormalizedItem(itemOrSection); - }} -
+ /> ); } diff --git a/packages/components/src/spectrum/picker/index.ts b/packages/components/src/spectrum/picker/index.ts index f1180f521..1582bec05 100644 --- a/packages/components/src/spectrum/picker/index.ts +++ b/packages/components/src/spectrum/picker/index.ts @@ -2,5 +2,6 @@ export * from './Picker'; export * from './PickerNormalized'; export * from './PickerProps'; export * from './usePickerItemScale'; +export * from './usePickerNormalizedProps'; export * from './usePickerProps'; export * from './usePickerScrollOnOpen'; diff --git a/packages/components/src/spectrum/picker/usePickerNormalizedProps.tsx b/packages/components/src/spectrum/picker/usePickerNormalizedProps.tsx new file mode 100644 index 000000000..01c2d7938 --- /dev/null +++ b/packages/components/src/spectrum/picker/usePickerNormalizedProps.tsx @@ -0,0 +1,151 @@ +import { Key, useCallback, useMemo } from 'react'; +import { EMPTY_FUNCTION } from '@deephaven/utils'; +import type { DOMRef } from '@react-types/shared'; +import { + getItemKey, + isNormalizedSection, + NormalizedItem, + NormalizedSection, + normalizeTooltipOptions, + useRenderNormalizedItem, + useStringifiedSelection, +} from '../utils'; +import { PickerNormalizedPropsT } from './PickerProps'; +import { usePickerScrollOnOpen } from './usePickerScrollOnOpen'; +import { Section } from '../shared'; + +/** Props that are derived by `usePickerNormalizedProps` */ +export type UsePickerNormalizedDerivedProps = { + children: (itemOrSection: NormalizedItem | NormalizedSection) => JSX.Element; + forceRerenderKey: Key; + items: (NormalizedItem | NormalizedSection)[]; + defaultSelectedKey?: Key; + disabledKeys?: Iterable; + ref: DOMRef; + selectedKey?: Key | null; + onSelectionChange: (key: Key | null) => void; + onOpenChange: (isOpen: boolean) => void; +}; + +/** + * Props that are passed through untouched. (should exclude all of the + * destructured props passed into `usePickerNormalizedProps` that are not in the + * spread ...props) + */ +export type UsePickerNormalizedPassthroughProps = Omit< + PickerNormalizedPropsT, + | 'defaultSelectedKey' + | 'disabledKeys' + | 'getInitialScrollPosition' + | 'normalizedItems' + | 'onChange' + | 'onOpenChange' + | 'onScroll' + | 'onSelectionChange' + | 'selectedKey' + | 'showItemIcons' + | 'tooltip' +>; + +/** Props returned from `usePickerNormalizedProps` hook. */ +export type UsePickerNormalizedProps< + TProps, + THtml extends HTMLElement, +> = UsePickerNormalizedDerivedProps & + UsePickerNormalizedPassthroughProps; + +export function usePickerNormalizedProps< + TProps, + THtml extends HTMLElement = HTMLElement, +>({ + defaultSelectedKey, + disabledKeys, + getInitialScrollPosition, + normalizedItems, + onChange, + onOpenChange, + onScroll = EMPTY_FUNCTION, + onSelectionChange, + selectedKey, + showItemIcons, + tooltip = true, + ...props +}: PickerNormalizedPropsT): UsePickerNormalizedProps { + const tooltipOptions = useMemo( + () => normalizeTooltipOptions(tooltip), + [tooltip] + ); + + const renderNormalizedItem = useRenderNormalizedItem({ + itemIconSlot: 'icon', + // Descriptions introduce variable item heights which throws off calculation + // of initial scroll position and setting viewport on windowed data. For now + // not going to implement description support in Picker. + // https://github.com/deephaven/web-client-ui/issues/1958 + showItemDescriptions: false, + showItemIcons, + tooltipOptions, + }); + + // Spectrum doesn't re-render if only the `renderNormalizedItems` function + // changes, so we create a key from its dependencies that can be used to force + // re-render. + const forceRerenderKey = `${showItemIcons}-${tooltipOptions?.placement}`; + + const { ref, onOpenChange: onOpenChangeInternal } = + usePickerScrollOnOpen({ + getInitialScrollPosition, + onScroll, + onOpenChange, + }); + + // Spectrum Picker treats keys as strings if the `key` prop is explicitly + // set on `Item` elements. Since we do this in `renderItem`, we need to + // map original key types to and from strings so that selection works. + const { + selectedStringKey, + defaultSelectedStringKey, + disabledStringKeys, + onStringSelectionChange, + } = useStringifiedSelection({ + normalizedItems, + selectedKey, + defaultSelectedKey, + disabledKeys, + onChange: onChange ?? onSelectionChange, + }); + + const children = useCallback( + (itemOrSection: NormalizedItem | NormalizedSection) => { + if (isNormalizedSection(itemOrSection)) { + return ( +
+ {renderNormalizedItem} +
+ ); + } + + return renderNormalizedItem(itemOrSection); + }, + [renderNormalizedItem] + ); + + return { + ...props, + children, + forceRerenderKey, + ref, + items: normalizedItems, + selectedKey: selectedStringKey, + defaultSelectedKey: defaultSelectedStringKey, + disabledKeys: disabledStringKeys, + onSelectionChange: onStringSelectionChange, + onOpenChange: onOpenChangeInternal, + }; +} + +export default usePickerNormalizedProps; diff --git a/packages/components/src/spectrum/picker/usePickerProps.ts b/packages/components/src/spectrum/picker/usePickerProps.ts index 2d7535d1c..e0a86a80f 100644 --- a/packages/components/src/spectrum/picker/usePickerProps.ts +++ b/packages/components/src/spectrum/picker/usePickerProps.ts @@ -18,17 +18,21 @@ import type { PickerPropsT } from './PickerProps'; import usePickerItemScale from './usePickerItemScale'; import usePickerScrollOnOpen from './usePickerScrollOnOpen'; -/** Props that are derived. */ -export type UsePickerDerivedProps = { +/** Props that are derived by `usePickerProps`. */ +export type UsePickerDerivedProps = { children: (SectionElement | ItemElement)[]; defaultSelectedKey?: ItemKey | undefined; + ref: DOMRef; selectedKey?: ItemKey | null | undefined; - scrollRef: DOMRef; onOpenChange: (isOpen: boolean) => void; onSelectionChange: ((key: ItemKey | null) => void) | undefined; }; -/** Props that are passed through untouched. */ +/** + * Props that are passed through untouched. (should exclude all of the + * destructured props passed into `usePickerProps` that are not in the spread + * ...props) +) */ export type UsePickerPassthroughProps = Omit< PickerPropsT, | 'children' @@ -41,15 +45,21 @@ export type UsePickerPassthroughProps = Omit< | 'onSelectionChange' >; -export type UsePickerProps = UsePickerDerivedProps & - UsePickerPassthroughProps; +/** Props returned from `usePickerProps` hook. */ +export type UsePickerProps< + TProps, + THtml extends HTMLElement, +> = UsePickerDerivedProps & UsePickerPassthroughProps; /** * Derive props for Picker components (e.g. Picker and ComboBox). Specifically * handles wrapping children items and initial scroll position when the picker * is opened. */ -export function usePickerProps({ +export function usePickerProps< + TProps, + THtml extends HTMLElement = HTMLElement, +>({ children, defaultSelectedKey, selectedKey, @@ -59,7 +69,7 @@ export function usePickerProps({ onScroll = EMPTY_FUNCTION, onSelectionChange: onSelectionChangeHandler, ...props -}: PickerPropsT): UsePickerProps { +}: PickerPropsT): UsePickerProps { const { itemHeight } = usePickerItemScale(); const tooltipOptions = useMemo( @@ -86,7 +96,7 @@ export function usePickerProps({ topOffset: PICKER_TOP_OFFSET, }); - const { ref: scrollRef, onOpenChange } = usePickerScrollOnOpen({ + const { ref, onOpenChange } = usePickerScrollOnOpen({ getInitialScrollPosition, onScroll, onOpenChange: onOpenChangeHandler, @@ -95,9 +105,9 @@ export function usePickerProps({ return { ...props, defaultSelectedKey, + ref, selectedKey, children: items, - scrollRef, onOpenChange, onSelectionChange: onChangeMaybeUncontrolled, }; diff --git a/packages/components/src/spectrum/picker/usePickerScrollOnOpen.ts b/packages/components/src/spectrum/picker/usePickerScrollOnOpen.ts index d9ad3114f..6e0c5bc0e 100644 --- a/packages/components/src/spectrum/picker/usePickerScrollOnOpen.ts +++ b/packages/components/src/spectrum/picker/usePickerScrollOnOpen.ts @@ -11,8 +11,8 @@ export interface UsePickerScrollOnOpenOptions { onOpenChange?: (isOpen: boolean) => void; } -export interface UsePickerScrollOnOpenResult { - ref: DOMRef; +export interface UsePickerScrollOnOpenResult { + ref: DOMRef; onOpenChange: (isOpen: boolean) => void; } @@ -25,13 +25,13 @@ export interface UsePickerScrollOnOpenResult { * @return A ref to attach to the Picker and a callback to handle open change * events for the Picker. */ -export function usePickerScrollOnOpen({ +export function usePickerScrollOnOpen({ getInitialScrollPosition, onScroll, onOpenChange, -}: UsePickerScrollOnOpenOptions): UsePickerScrollOnOpenResult { +}: UsePickerScrollOnOpenOptions): UsePickerScrollOnOpenResult { const { ref, onOpenChange: popoverOnOpenChange } = usePopoverOnScrollRef( - findSpectrumPickerScrollArea, + findSpectrumPickerScrollArea, onScroll, getInitialScrollPosition ); diff --git a/packages/components/src/spectrum/utils/useStringifiedSelection.ts b/packages/components/src/spectrum/utils/useStringifiedSelection.ts index ffea625f5..d954760c7 100644 --- a/packages/components/src/spectrum/utils/useStringifiedSelection.ts +++ b/packages/components/src/spectrum/utils/useStringifiedSelection.ts @@ -12,14 +12,14 @@ export interface UseStringifiedSelectionOptions { selectedKey: ItemKey | null | undefined; defaultSelectedKey: ItemKey | undefined; disabledKeys: Iterable | undefined; - onChange: ((key: ItemKey) => void) | undefined; + onChange: ((key: ItemKey | null) => void) | undefined; } export interface UseStringifiedSelectionResult { defaultSelectedStringKey?: Key; selectedStringKey?: Key | null; disabledStringKeys?: Set; - onStringSelectionChange: (key: Key) => void; + onStringSelectionChange: (key: Key | null) => void; } /** @@ -63,7 +63,7 @@ export function useStringifiedSelection({ ); const onStringSelectionChange = useCallback( - (key: Key): void => { + (key: Key | null): void => { if (onChange == null) { return; } diff --git a/packages/react-hooks/src/SpectrumUtils.test.ts b/packages/react-hooks/src/SpectrumUtils.test.ts index 5e7dc6ad2..9fb99afe3 100644 --- a/packages/react-hooks/src/SpectrumUtils.test.ts +++ b/packages/react-hooks/src/SpectrumUtils.test.ts @@ -1,4 +1,5 @@ import { KeyedItem, TestUtils } from '@deephaven/utils'; +import type { DOMRefValue } from '@react-types/shared'; import { createValidationProps, extractSpectrumHTMLElement, @@ -8,7 +9,6 @@ import { findSpectrumPopoverScrollArea, getPositionOfSelectedItem, identityExtractHTMLElement, - ReactSpectrumComponent, } from './SpectrumUtils'; const { asMock, createMockProxy } = TestUtils; @@ -31,7 +31,7 @@ describe('createValidationProps', () => { describe('extractSpectrumHTMLElement', () => { const mock = { element: createMockProxy(), - ref: createMockProxy(), + ref: createMockProxy(), }; beforeEach(() => { @@ -49,7 +49,7 @@ describe('extractSpectrumHTMLElement', () => { describe('extractSpectrumLastChildHTMLElement', () => { const mock = { - ref: createMockProxy(), + ref: createMockProxy(), }; it('should return null if ref is null', () => { @@ -86,8 +86,8 @@ describe('extractSpectrumLastChildHTMLElement', () => { describe.each([ // General popover function - ['i', findSpectrumPopoverScrollArea], - ['span', findSpectrumPopoverScrollArea], + ['i', findSpectrumPopoverScrollArea], + ['span', findSpectrumPopoverScrollArea], // Specific consumer functions ['input', findSpectrumComboBoxScrollArea], ['button', findSpectrumPickerScrollArea], @@ -128,7 +128,7 @@ describe.each([ 'should find `aria-controls` element of trigger element', (hasRef, hasTrigger, hasPopup, shouldFind) => { const ref = hasRef - ? createMockProxy({ + ? createMockProxy({ UNSAFE_getDOMNode: () => el.component, }) : null; diff --git a/packages/react-hooks/src/SpectrumUtils.ts b/packages/react-hooks/src/SpectrumUtils.ts index 2f48b318c..41e2ab365 100644 --- a/packages/react-hooks/src/SpectrumUtils.ts +++ b/packages/react-hooks/src/SpectrumUtils.ts @@ -2,9 +2,6 @@ import type { SpectrumTextFieldProps } from '@adobe/react-spectrum'; import { KeyedItem } from '@deephaven/utils'; import type { DOMRefValue } from '@react-types/shared'; -export type ReactSpectrumComponent = - DOMRefValue; - /** * Creates validation props for a Spectrum field. If `isValid` is true, returns * empty props. If false, returns { errorMessage, validationState: 'invalid' } @@ -29,9 +26,9 @@ export function createValidationProps( * Extract DOM node from React Spectrum component ref. * @param ref */ -export function extractSpectrumHTMLElement( - ref: ReactSpectrumComponent | null -): HTMLElement | null { +export function extractSpectrumHTMLElement< + THtml extends HTMLElement = HTMLElement, +>(ref: DOMRefValue | null): HTMLElement | null { return ref?.UNSAFE_getDOMNode() ?? null; } @@ -40,9 +37,9 @@ export function extractSpectrumHTMLElement( * ref. * @param ref */ -export function extractSpectrumLastChildHTMLElement( - ref: ReactSpectrumComponent | null -): HTMLElement | null { +export function extractSpectrumLastChildHTMLElement< + THtml extends HTMLElement = HTMLElement, +>(ref: DOMRefValue | null): HTMLElement | null { const maybeHTMLElement = ref?.UNSAFE_getDOMNode().lastElementChild; return identityExtractHTMLElement(maybeHTMLElement); } @@ -51,9 +48,9 @@ export function extractSpectrumLastChildHTMLElement( * Find the popover associated with a given Spectrum ComboBox ref. * @param ref The ref to the Spectrum ComboBox component */ -export function findSpectrumComboBoxScrollArea( - ref: ReactSpectrumComponent | null -): HTMLElement | null { +export function findSpectrumComboBoxScrollArea< + THtml extends HTMLElement = HTMLElement, +>(ref: DOMRefValue | null): HTMLElement | null { return findSpectrumPopoverScrollArea(ref, 'input'); } @@ -61,9 +58,9 @@ export function findSpectrumComboBoxScrollArea( * Find the popover associated with a given Spectrum Picker ref. * @param ref The ref to the Spectrum Picker component */ -export function findSpectrumPickerScrollArea( - ref: ReactSpectrumComponent | null -): HTMLElement | null { +export function findSpectrumPickerScrollArea< + THtml extends HTMLElement = HTMLElement, +>(ref: DOMRefValue | null): HTMLElement | null { return findSpectrumPopoverScrollArea(ref, 'button'); } @@ -74,10 +71,8 @@ export function findSpectrumPickerScrollArea( */ export function findSpectrumPopoverScrollArea< K extends keyof HTMLElementTagNameMap, ->( - ref: ReactSpectrumComponent | null, - triggerElementType: K -): HTMLElement | null { + THtml extends HTMLElement = HTMLElement, +>(ref: DOMRefValue | null, triggerElementType: K): HTMLElement | null { const maybeHTMLElement = ref?.UNSAFE_getDOMNode(); const trigger = maybeHTMLElement?.querySelector(triggerElementType); const popupId = trigger?.getAttribute('aria-controls'); diff --git a/packages/react-hooks/src/useSpectrumDisableSpellcheckRef.test.ts b/packages/react-hooks/src/useSpectrumDisableSpellcheckRef.test.ts index 1185f2751..e02a4172c 100644 --- a/packages/react-hooks/src/useSpectrumDisableSpellcheckRef.test.ts +++ b/packages/react-hooks/src/useSpectrumDisableSpellcheckRef.test.ts @@ -1,9 +1,6 @@ import { renderHook } from '@testing-library/react-hooks'; -import { - ReactSpectrumComponent, - SPELLCHECK_FALSE_ATTRIBUTE, - TestUtils, -} from '@deephaven/utils'; +import { DOMRefValue } from '@react-types/shared'; +import { SPELLCHECK_FALSE_ATTRIBUTE, TestUtils } from '@deephaven/utils'; import useSetAttributesCallback from './useSetAttributesCallback'; import useSpectrumDisableSpellcheckRef from './useSpectrumDisableSpellcheckRef'; @@ -20,7 +17,7 @@ describe('useSpectrumDisableSpellcheckRef', () => { const mock = { useSetAttributesCallbackResult: jest.fn(), selectors: 'mock.selectors', - ref: createMockProxy(), + ref: createMockProxy(), rootEl: createMockProxy(), }; diff --git a/packages/react-hooks/src/useSpectrumDisableSpellcheckRef.ts b/packages/react-hooks/src/useSpectrumDisableSpellcheckRef.ts index 4016e792b..33fb7ab26 100644 --- a/packages/react-hooks/src/useSpectrumDisableSpellcheckRef.ts +++ b/packages/react-hooks/src/useSpectrumDisableSpellcheckRef.ts @@ -1,8 +1,6 @@ import { SPELLCHECK_FALSE_ATTRIBUTE } from '@deephaven/utils'; -import { - extractSpectrumHTMLElement, - ReactSpectrumComponent, -} from './SpectrumUtils'; +import type { DOMRefValue } from '@react-types/shared'; +import { extractSpectrumHTMLElement } from './SpectrumUtils'; import useMappedRef from './useMappedRef'; import useSetAttributesCallback from './useSetAttributesCallback'; @@ -15,7 +13,7 @@ import useSetAttributesCallback from './useSetAttributesCallback'; */ export function useSpectrumDisableSpellcheckRef( selectors?: string -): (ref: ReactSpectrumComponent | null) => void { +): (ref: DOMRefValue | null) => void { const disableSpellcheck = useSetAttributesCallback( SPELLCHECK_FALSE_ATTRIBUTE, selectors diff --git a/tests/styleguide.spec.ts-snapshots/pickers-chromium-linux.png b/tests/styleguide.spec.ts-snapshots/pickers-chromium-linux.png index 10724bfe6..b4dfbf984 100644 Binary files a/tests/styleguide.spec.ts-snapshots/pickers-chromium-linux.png and b/tests/styleguide.spec.ts-snapshots/pickers-chromium-linux.png differ diff --git a/tests/styleguide.spec.ts-snapshots/pickers-firefox-linux.png b/tests/styleguide.spec.ts-snapshots/pickers-firefox-linux.png index c2d689247..c6542e6f6 100644 Binary files a/tests/styleguide.spec.ts-snapshots/pickers-firefox-linux.png and b/tests/styleguide.spec.ts-snapshots/pickers-firefox-linux.png differ diff --git a/tests/styleguide.spec.ts-snapshots/pickers-webkit-linux.png b/tests/styleguide.spec.ts-snapshots/pickers-webkit-linux.png index 984f2d4dd..23f3da751 100644 Binary files a/tests/styleguide.spec.ts-snapshots/pickers-webkit-linux.png and b/tests/styleguide.spec.ts-snapshots/pickers-webkit-linux.png differ