Skip to content

Commit

Permalink
separateSpectrumProps util (deephaven#1890)
Browse files Browse the repository at this point in the history
  • Loading branch information
bmingles committed Apr 29, 2024
1 parent 48341f5 commit a360e1f
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 49 deletions.
67 changes: 18 additions & 49 deletions packages/components/src/spectrum/listView/ListViewWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,22 @@ import { EMPTY_FUNCTION } from '@deephaven/utils';
import cl from 'classnames';
import { useSpectrumThemeProvider } from '../../theme';
import { Flex } from '../layout';
import { separateSpectrumProps } from '../utils';
import './ListViewWrapper.scss';

export interface ListViewWrapperProps<T> extends SpectrumListViewProps<T> {
/** Handler that is called when the picker is scrolled. */
onScroll?: (event: Event) => void;
}

export function ListViewWrapper<T>({
// List view specific props are passed to the ListView,
children,
defaultSelectedKeys,
density,
disabledBehavior,
disabledKeys,
disallowEmptySelection,
dragAndDropHooks,
isQuiet,
items,
loadingState,
overflowMode,
renderEmptyState,
selectedKeys,
selectionMode,
selectionStyle,
onAction,
onLoadMore,
onSelectionChange,
onScroll = EMPTY_FUNCTION,
// Layout specific props to be applied to the Flex container
UNSAFE_className,
...layoutProps
}: ListViewWrapperProps<T>): JSX.Element {
export function ListViewWrapper<T>(
props: ListViewWrapperProps<T>
): JSX.Element {
const { ariaLabelProps, componentProps, styleProps } =
separateSpectrumProps(props);

const { onScroll = EMPTY_FUNCTION, ...listViewProps } = componentProps;

const { scale } = useSpectrumThemeProvider();

// Spectrum ListView crashes when it has zero height. Track the contentRect
Expand All @@ -58,14 +42,14 @@ export function ListViewWrapper<T>({
<Flex
ref={contentRectRef}
direction="column"
// eslint-disable-next-line react/jsx-props-no-spreading
{...styleProps}
UNSAFE_className={cl(
'dh-list-view-wrapper',
`dh-list-view-wrapper-density-${density ?? 'regular'}`,
`dh-list-view-wrapper-density-${listViewProps.density ?? 'regular'}`,
`dh-list-view-wrapper-scale-${scale}`,
UNSAFE_className
styleProps.UNSAFE_className
)}
// eslint-disable-next-line react/jsx-props-no-spreading
{...layoutProps}
>
{contentRect.height === 0 ? (
// Use &nbsp; to ensure content has a non-zero height. This ensures the
Expand All @@ -83,26 +67,11 @@ export function ListViewWrapper<T>({
) : (
<SpectrumListView
ref={scrollRef}
defaultSelectedKeys={defaultSelectedKeys}
density={density}
disabledBehavior={disabledBehavior}
disabledKeys={disabledKeys}
disallowEmptySelection={disallowEmptySelection}
dragAndDropHooks={dragAndDropHooks}
isQuiet={isQuiet}
items={items}
loadingState={loadingState}
overflowMode={overflowMode}
renderEmptyState={renderEmptyState}
selectedKeys={selectedKeys}
selectionMode={selectionMode}
selectionStyle={selectionStyle}
onAction={onAction}
onLoadMore={onLoadMore}
onSelectionChange={onSelectionChange}
>
{children}
</SpectrumListView>
// eslint-disable-next-line react/jsx-props-no-spreading
{...ariaLabelProps}
// eslint-disable-next-line react/jsx-props-no-spreading
{...listViewProps}
/>
)}
</Flex>
);
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/spectrum/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './itemUtils';
export * from './itemWrapperUtils';
export * from './propsUtils';
export * from './themeUtils';
export * from './useRenderNormalizedItem';
export * from './useStringifiedMultiSelection';
Expand Down
74 changes: 74 additions & 0 deletions packages/components/src/spectrum/utils/propUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { AriaLabelingProps, StyleProps } from '@react-types/shared';
import { separateSpectrumProps } from './propsUtils';

describe('separateSpectrumProps', () => {
const mockAriaLabelProps: AriaLabelingProps = {
'aria-label': 'test',
'aria-labelledby': 'testId',
'aria-describedby': 'testDesc',
'aria-details': 'testDetails',
};

const mockStyleProps: StyleProps = {
marginX: '10px',
marginY: '20px',
width: '100px',
height: '200px',
minWidth: '50px',
minHeight: '100px',
maxWidth: '150px',
maxHeight: '200px',
flex: '1 1 auto',
flexGrow: 1,
flexShrink: 1,
flexBasis: 'auto',
justifySelf: 'center',
alignSelf: 'center',
order: 1,
gridArea: '1 / 1 / 2 / 2',
gridColumn: '1 / span 2',
gridRow: '1 / span 2',
gridColumnStart: '1',
gridColumnEnd: '3',
gridRowStart: '1',
gridRowEnd: '3',
position: 'relative',
zIndex: 1,
top: '10px',
bottom: '10px',
start: '10px',
end: '10px',
left: '10px',
right: '10px',
isHidden: false,
};

const mockComponentProps = {
otherPropA: 'otherValue',
otherPropB: 999,
} as const;

it('should separate aria, style, and component properties', () => {
const props = {
...mockAriaLabelProps,
...mockStyleProps,
...mockComponentProps,
};

const { ariaLabelProps, styleProps, componentProps } =
separateSpectrumProps(props);

expect(ariaLabelProps).toEqual(mockAriaLabelProps);
expect(styleProps).toEqual(mockStyleProps);
expect(componentProps).toEqual(mockComponentProps);
});

it('should return empty objects if no props are passed', () => {
const { ariaLabelProps, styleProps, componentProps } =
separateSpectrumProps({});

expect(ariaLabelProps).toEqual({});
expect(styleProps).toEqual({});
expect(componentProps).toEqual({});
});
});
103 changes: 103 additions & 0 deletions packages/components/src/spectrum/utils/propsUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import type { AriaLabelingProps, StyleProps } from '@react-types/shared';

/**
* Separate props for Spectrum components into AriaLabelingProps, StyleProps, and
* any remaining props.
* @param props The props to separate
* @returns The separated props
*/
export function separateSpectrumProps<T extends AriaLabelingProps & StyleProps>(
props: T
): {
ariaLabelProps: AriaLabelingProps;
styleProps: StyleProps;
componentProps: Omit<T, keyof (AriaLabelingProps & StyleProps)>;
} {
const {
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledby,
'aria-describedby': ariaDescribedby,
'aria-details': ariaHidden,

marginX,
marginY,
width,
height,
minWidth,
minHeight,
maxWidth,
maxHeight,
flex,
flexGrow,
flexShrink,
flexBasis,
justifySelf,
alignSelf,
order,
gridArea,
gridColumn,
gridRow,
gridColumnStart,
gridColumnEnd,
gridRowStart,
gridRowEnd,
position,
zIndex,
top,
bottom,
start,
end,
left,
right,
isHidden,
...restProps
} = props;

return {
ariaLabelProps: {
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledby,
'aria-describedby': ariaDescribedby,
'aria-details': ariaHidden,
},
styleProps: {
marginX,
marginY,
width,
height,
minWidth,
minHeight,
maxWidth,
maxHeight,
flex,
flexGrow,
flexShrink,
flexBasis,
justifySelf,
alignSelf,
order,
gridArea,
gridColumn,
gridRow,
gridColumnStart,
gridColumnEnd,
gridRowStart,
gridRowEnd,
position,
zIndex,
top,
bottom,
start,
end,
left,
right,
isHidden,
},
componentProps: restProps as Omit<
T,
keyof (AriaLabelingProps & StyleProps)
>,
};
}

export default separateSpectrumProps;

0 comments on commit a360e1f

Please sign in to comment.