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

feat: ListView components #1919

Merged
merged 41 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
ed6ed2a
Generics to narrow types to item only when appropriate (#1909)
bmingles Apr 2, 2024
2219115
Tooltip placement override (#1909)
bmingles Apr 2, 2024
5a98665
Rename / move PickerItemContent (#1909)
bmingles Apr 2, 2024
991042f
Smarter item tooltips (#1909)
bmingles Apr 2, 2024
97d7412
forwardedRefs (#1909)
bmingles Apr 2, 2024
b7f4df3
Basic ListView implementation (#1909)
bmingles Apr 2, 2024
9fb7de1
ListView table support (#1909)
bmingles Apr 2, 2024
2a04b31
Fixed import (#1909)
bmingles Apr 3, 2024
f566e72
Mocking virtualizer (#1909)
bmingles Apr 3, 2024
9b5a119
generate normalized items styleguide util (#1909)
bmingles Apr 3, 2024
157e46f
comments (#1909)
bmingles Apr 4, 2024
85f87c3
Renamed callback ref (#1909)
bmingles Apr 4, 2024
9acfc30
useCheckOverflowRef hook (#1909)
bmingles Apr 4, 2024
4b2cd06
renames (#1909)
bmingles Apr 4, 2024
ebf1598
Changed "ref" naming to "callback" (#1909)
bmingles Apr 4, 2024
55d0fc2
cleanup (#1909)
bmingles Apr 4, 2024
9f4c1fb
Cleanup (#1909)
bmingles Apr 4, 2024
c964da7
ItemSelection type (#1909)
bmingles Apr 8, 2024
a111d5a
Item heights (#1909)
bmingles Apr 9, 2024
d488c92
Only render ListView when non-zero height (#1909)
bmingles Apr 9, 2024
dac492f
Removed minHeight (#1909)
bmingles Apr 9, 2024
f2be02b
Removed unused arg (#1909)
bmingles Apr 9, 2024
c7ecd14
useContentRect tests (#1909)
bmingles Apr 9, 2024
663a965
Updated comments (#1909)
bmingles Apr 9, 2024
195c42c
Comments (#1909)
bmingles Apr 9, 2024
055f953
Comments (#1909)
bmingles Apr 9, 2024
8d382b5
useRenderNormalizedItem tests (#1909)
bmingles Apr 9, 2024
91deda8
Fixed useContentRect to update automatically (#1909)
bmingles Apr 9, 2024
962f64c
e2e tests (#1909)
bmingles Apr 9, 2024
599902d
Item key stringify tests (#1909)
bmingles Apr 9, 2024
5fd74dd
Re-use SelectionT (#1909)
bmingles Apr 9, 2024
5b7d5a2
cleanup (#1909)
bmingles Apr 9, 2024
4851669
cleanup (#1909)
bmingles Apr 9, 2024
36d4134
Fixed tests (#1909)
bmingles Apr 10, 2024
ae099ca
useCheckOverflow Tests (#1909)
bmingles Apr 10, 2024
342b24c
Cleanup (#1909)
bmingles Apr 10, 2024
a65e539
Added test case (#1909)
bmingles Apr 10, 2024
95a4744
Addressed review comments (#1909)
bmingles Apr 16, 2024
383b98c
Simplified illustration example (#1909)
bmingles Apr 18, 2024
fb8e4a1
Export spectrum useProvider (#1909)
bmingles Apr 18, 2024
4feb7d9
Removed spectrum dependency (#1909)
bmingles Apr 18, 2024
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
15 changes: 15 additions & 0 deletions jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,21 @@ Object.defineProperty(window, 'ResizeObserver', {
},
});

Object.defineProperty(window, 'DOMRect', {
value: function (x: number = 0, y: number = 0, width = 0, height = 0) {
return TestUtils.createMockProxy<DOMRect>({
x,
y,
width,
height,
top: y,
bottom: y + height,
left: x,
right: x + width,
});
},
});

Object.defineProperty(window, 'TextDecoder', {
value: TextDecoder,
});
Expand Down
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 123 additions & 0 deletions packages/code-studio/src/styleguide/ListViews.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React, { useCallback, useState } from 'react';
import {
Grid,
Icon,
Item,
ListView,
ItemKey,
Text,
} from '@deephaven/components';
import { vsAccount, vsPerson } from '@deephaven/icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { generateNormalizedItems, sampleSectionIdAndClasses } from './utils';

// Generate enough items to require scrolling
const itemsSimple = [...generateNormalizedItems(52)];

function AccountIllustration(): JSX.Element {
return (
// Images in ListView items require a slot of 'image' or 'illustration' to
// be set in order to be positioned correctly:
// https://github.com/adobe/react-spectrum/blob/784737effd44b9d5e2b1316e690da44555eafd7e/packages/%40react-spectrum/list/src/ListViewItem.tsx#L266-L267
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason they don't have a slot provider for icon? We could even add a PR for it... seems odd they support image and illustration but not icon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing @dsmmcken suggestion may be accurate here. Maybe they are trying to not overload the fact that icons can be included in actions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a key difference between listview and listbox, https://react-spectrum.adobe.com/react-spectrum/ListBox.html#complex-items

<Icon slot="illustration">
<FontAwesomeIcon icon={vsAccount} />
</Icon>
);
}

export function ListViews(): JSX.Element {
const [selectedKeys, setSelectedKeys] = useState<'all' | Iterable<ItemKey>>(
[]
);

const onChange = useCallback((keys: 'all' | Iterable<ItemKey>): void => {
setSelectedKeys(keys);
}, []);

return (
// eslint-disable-next-line react/jsx-props-no-spreading
<div {...sampleSectionIdAndClasses('list-views')}>
<h2 className="ui-title">List View</h2>

<Grid columnGap={14} height="size-6000">
<Text>Single Child</Text>
<ListView
density="compact"
gridRow="2"
aria-label="Single Child"
selectionMode="multiple"
>
<Item>Aaa</Item>
</ListView>

<label>Icons</label>
<ListView
gridRow="2"
aria-label="Icon"
density="compact"
selectionMode="multiple"
>
<Item textValue="Item with icon A">
<AccountIllustration />
<Text>Item with icon A</Text>
</Item>
<Item textValue="Item with icon B">
<AccountIllustration />
<Text>Item with icon B</Text>
</Item>
<Item textValue="Item with icon C">
<AccountIllustration />
<Text>Item with icon C</Text>
</Item>
<Item textValue="Item with icon D">
<AccountIllustration />
<Text>Item with icon D with overflowing content</Text>
</Item>
</ListView>

<label>Mixed Children Types</label>
<ListView
gridRow="2"
aria-label="Mixed Children Types"
density="compact"
maxWidth="size-2400"
selectionMode="multiple"
defaultSelectedKeys={[999, 444]}
>
{/* eslint-disable react/jsx-curly-brace-presence */}
{'String 1'}
{'String 2'}
{'String 3'}
{''}
{'Some really long text that should get truncated'}
{/* eslint-enable react/jsx-curly-brace-presence */}
{444}
{999}
{true}
{false}
<Item>Item Aaa</Item>
<Item>Item Bbb</Item>
<Item textValue="Complex Ccc">
<Icon slot="image">
<FontAwesomeIcon icon={vsPerson} />
</Icon>
<Text>Complex Ccc with text that should be truncated</Text>
</Item>
</ListView>

<label>Controlled</label>
<ListView
gridRow="2"
aria-label="Controlled"
selectionMode="multiple"
selectedKeys={selectedKeys}
onChange={onChange}
>
{itemsSimple}
</ListView>
</Grid>
</div>
);
}

export default ListViews;
11 changes: 3 additions & 8 deletions packages/code-studio/src/styleguide/Pickers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,10 @@ import {
import { vsPerson } from '@deephaven/icons';
import { Icon } from '@adobe/react-spectrum';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { sampleSectionIdAndClasses } from './utils';
import { generateNormalizedItems, sampleSectionIdAndClasses } from './utils';

// Generate enough items to require scrolling
const items = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
.split('')
.map((key, i) => ({
key,
item: { key: (i + 1) * 100, content: `${key}${key}${key}` },
}));
const items = [...generateNormalizedItems(52)];

function PersonIcon(): JSX.Element {
return (
Expand All @@ -29,7 +24,7 @@ function PersonIcon(): JSX.Element {
}

export function Pickers(): JSX.Element {
const [selectedKey, setSelectedKey] = useState<ItemKey>();
const [selectedKey, setSelectedKey] = useState<ItemKey | null>(null);

const onChange = useCallback((key: ItemKey): void => {
setSelectedKey(key);
Expand Down
26 changes: 26 additions & 0 deletions packages/code-studio/src/styleguide/StyleGuide.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,37 @@ import StyleGuide from './StyleGuide';
window.HTMLElement.prototype.scroll = jest.fn();
window.HTMLElement.prototype.scrollIntoView = jest.fn();

/**
* Mock a dimension property of a ListView element.
*/
function mockListViewDimension(propName: keyof HTMLElement, value: number) {
jest
.spyOn(window.HTMLElement.prototype, propName, 'get')
.mockImplementation(function getDimension() {
const isSpectrumListView =
this instanceof HTMLElement &&
this.className.includes('_react-spectrum-ListView');

// For non ListView, just return zero which is the default value anyway.
return isSpectrumListView === true ? value : 0;
});
}

describe('<StyleGuide /> mounts', () => {
test('h1 text of StyleGuide renders', () => {
// Provide a non-null array to ThemeProvider to tell it to initialize
const customThemes: ThemeData[] = [];

// React Spectrum `useVirtualizerItem` depends on `scrollWidth` and `scrollHeight`.
// Mocking these to avoid React "Maximum update depth exceeded" errors.
// https://github.com/adobe/react-spectrum/blob/0b2a838b36ad6d86eee13abaf68b7e4d2b4ada6c/packages/%40react-aria/virtualizer/src/useVirtualizerItem.ts#L49C3-L49C60
// From preview docs: https://reactspectrum.blob.core.windows.net/reactspectrum/726a5e8f0ed50fc8d98e39c74bd6dfeb3660fbdf/docs/react-spectrum/testing.html#virtualized-components
// The virtualizer will now think it has a visible area of 1000px x 1000px and that the items within it are 40px x 40px
mockListViewDimension('clientWidth', 1000);
mockListViewDimension('clientHeight', 1000);
mockListViewDimension('scrollHeight', 40);
mockListViewDimension('scrollWidth', 40);

expect(() =>
render(
<ApiContext.Provider value={dh}>
Expand Down
2 changes: 2 additions & 0 deletions packages/code-studio/src/styleguide/StyleGuide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { GoldenLayout } from './GoldenLayout';
import { RandomAreaPlotAnimation } from './RandomAreaPlotAnimation';
import SpectrumComparison from './SpectrumComparison';
import Pickers from './Pickers';
import ListViews from './ListViews';

const stickyProps = {
position: 'sticky',
Expand Down Expand Up @@ -109,6 +110,7 @@ function StyleGuide(): React.ReactElement {
<Buttons />
<Progress />
<Inputs />
<ListViews />
<Pickers />
<ItemListInputs />
<DraggableLists />
Expand Down
Loading
Loading