Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ecf9415
feat: s2 ListView
snowystinger Jul 3, 2025
a5b53c0
Merge branch 'main' into s2-listview
snowystinger Sep 17, 2025
379dac3
Explore highlight selection
snowystinger Sep 17, 2025
5ee48f9
fix docs type check
snowystinger Sep 17, 2025
1dc83e0
Add highlight selection option b to table
snowystinger Sep 18, 2025
1d8e8a1
Add highlight selection option b to ListView
snowystinger Sep 18, 2025
e62eb1d
change selection behaviour of table for highlight mode
snowystinger Sep 18, 2025
020541a
add option D as highlightMode
snowystinger Sep 19, 2025
22545f2
change font weight to bold for highlight selection
snowystinger Oct 2, 2025
49beedf
update background color
snowystinger Oct 2, 2025
c07d593
Start new highlight selection prototypes, ListView and part of TreeView
snowystinger Oct 21, 2025
e5fc83d
fix lint
snowystinger Oct 21, 2025
f75c75a
update styles for listview and treeview
snowystinger Oct 22, 2025
3fb6b67
tableview
snowystinger Oct 22, 2025
637f244
design updates
snowystinger Oct 23, 2025
ccc682e
fix colors and add disabled items
snowystinger Oct 23, 2025
d0ae356
fix lint
snowystinger Oct 23, 2025
725dcd6
Merge branch 'main' into s2-listview
snowystinger Nov 5, 2025
8014718
Merge branch 'main' into s2-listview
snowystinger Nov 13, 2025
de65230
Merge remote-tracking branch 'origin/main' into s2-listview
reidbarber Feb 19, 2026
d112dfe
initialize docs
reidbarber Feb 19, 2026
68ff3d0
fix edgeToText import
reidbarber Feb 19, 2026
ee99c77
update ListView (loadingMore, actionmenu, empty state, checkbox)
reidbarber Feb 19, 2026
c6edcb2
match selection styles to treeview/tableview
reidbarber Feb 19, 2026
d336314
docs improvements
reidbarber Feb 19, 2026
cfbcd66
add jsdoc
reidbarber Feb 19, 2026
bdc5d35
support hasChildItems chevron
reidbarber Feb 20, 2026
2bdc780
add stories
reidbarber Feb 20, 2026
e3f372b
add hasChildItems example to docs
reidbarber Feb 20, 2026
e788526
fix overflow in empty state
reidbarber Feb 20, 2026
7196465
use objectFit: 'cover' for images
reidbarber Feb 20, 2026
2d8f2f2
vertically align icons
reidbarber Feb 20, 2026
acff11b
add overflowMode
reidbarber Feb 20, 2026
c318334
audit props
reidbarber Feb 20, 2026
eb13258
Merge remote-tracking branch 'origin/main' into s2-listview
reidbarber Feb 20, 2026
478e2a5
codemods
reidbarber Feb 20, 2026
4f1c4ab
add chromatic stories
reidbarber Feb 20, 2026
a0458ca
lint
reidbarber Feb 20, 2026
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
7 changes: 7 additions & 0 deletions .storybook-s2/docs/Migrating.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ export function Migrating() {
<li className={style({font: 'body', marginY: 8})}>If within <Code>Picker</Code>: Update <Code>Item</Code> to be a <Code>PickerItem</Code></li>
<li className={style({font: 'body', marginY: 8})}>If within <Code>ComboBox</Code>: Update <Code>Item</Code> to be a <Code>ComboBoxItem</Code></li>
<li className={style({font: 'body', marginY: 8})}>If within <Code>ListBox</Code>: Update <Code>Item</Code> to be a <Code>ListBoxItem</Code></li>
<li className={style({font: 'body', marginY: 8})}>If within <Code>ListView</Code>: Update <Code>Item</Code> to be a <Code>ListViewItem</Code></li>
<li className={style({font: 'body', marginY: 8})}>If within <Code>TabList</Code>: Update <Code>Item</Code> to be a <Code>Tab</Code></li>
<li className={style({font: 'body', marginY: 8})}>If within <Code>TabPanels</Code>: Update <Code>Item</Code> to be a <Code>TabPanel</Code> and remove surrounding <Code>TabPanels</Code></li>
<li className={style({font: 'body', marginY: 8})}>Update <Code>key</Code> to be <Code>id</Code> (and keep <Code>key</Code> if rendered inside <Code>array.map</Code>)</li>
Expand All @@ -275,6 +276,12 @@ export function Migrating() {
<li className={style({font: 'body', marginY: 8})}>Update <Code>Item</Code> to be a <Code>ListBoxItem</Code></li>
</ul>

<H3>ListView</H3>
<ul className="sb-unstyled">
<li className={style({font: 'body', marginY: 8})}>[PENDING] Comment out <Code>density</Code> (it has not been implemented yet)</li>
<li className={style({font: 'body', marginY: 8})}>[PENDING] Comment out <Code>dragAndDropHooks</Code> (it has not been implemented yet)</li>
</ul>

<H3>Menu</H3>
<ul className="sb-unstyled">
<li className={style({font: 'body', marginY: 8})}>Update <Code>Item</Code> to be a <Code>MenuItem</Code></li>
Expand Down
192 changes: 192 additions & 0 deletions packages/@react-spectrum/s2/chromatic/ListView.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {ActionButton, ActionButtonGroup, ActionMenu, Content, Heading, IllustratedMessage, ListView, ListViewItem, MenuItem, Text} from '../src';
import Delete from '../s2wf-icons/S2_Icon_Delete_20_N.svg';
import Edit from '../s2wf-icons/S2_Icon_Edit_20_N.svg';
import File from '../s2wf-icons/S2_Icon_File_20_N.svg';
import Folder from '../s2wf-icons/S2_Icon_Folder_20_N.svg';
import FolderOpen from '../spectrum-illustrations/linear/FolderOpen';
import type {Meta, StoryObj} from '@storybook/react';
import {style} from '../style/spectrum-theme' with {type: 'macro'};

const meta: Meta<typeof ListView> = {
component: ListView,
parameters: {
chromaticProvider: {disableAnimations: true}
},
title: 'S2 Chromatic/ListView'
};

export default meta;
type Story = StoryObj<typeof ListView>;

let listViewStyles = style({width: 320, height: 320});

const items = [
{id: 'utilities', name: 'Utilities', type: 'folder', description: '16 items'},
{id: 'photoshop', name: 'Adobe Photoshop', type: 'file', description: 'Application'},
{id: 'illustrator', name: 'Adobe Illustrator', type: 'file', description: 'Application'},
{id: 'xd', name: 'Adobe XD', type: 'file', description: 'Application'}
];

export const Example: Story = {
render: (args) => (
<ListView {...args} aria-label="Files" styles={listViewStyles}>
<ListViewItem id="utilities" textValue="Utilities" hasChildItems>
<Folder />
<Text>Utilities</Text>
<Text slot="description">16 items</Text>
</ListViewItem>
<ListViewItem id="photoshop">Adobe Photoshop</ListViewItem>
<ListViewItem id="illustrator">Adobe Illustrator</ListViewItem>
<ListViewItem id="xd">Adobe XD</ListViewItem>
</ListView>
),
args: {
selectionMode: 'multiple',
onLoadMore: undefined
}
};

export const HighlightSelection: Story = {
...Example,
args: {
...Example.args,
selectionStyle: 'highlight',
selectedKeys: ['photoshop', 'illustrator']
}
};

export const Quiet: Story = {
...Example,
args: {
...Example.args,
isQuiet: true
}
};

export const OverflowTruncate: Story = {
render: (args) => (
<ListView {...args} aria-label="Long text examples" styles={listViewStyles}>
<ListViewItem id="a">
This is a very very very very very very very very long title.
</ListViewItem>
<ListViewItem id="b" textValue="Short title, long description">
<Text>Short title</Text>
<Text slot="description">This is a very very very very very very very very long description.</Text>
</ListViewItem>
<ListViewItem id="c" textValue="Long title, long description">
<Text>This is a very very very very very very very very long title.</Text>
<Text slot="description">This is a very very very very very very very very long description.</Text>
</ListViewItem>
</ListView>
),
args: {
...Example.args,
overflowMode: 'truncate'
}
};

export const OverflowWrap: Story = {
render: (args) => (
<ListView {...args} aria-label="Long text examples" styles={listViewStyles}>
<ListViewItem id="a">
This is a very very very very very very very very long title.
</ListViewItem>
<ListViewItem id="b" textValue="Short title, long description">
<Text>Short title</Text>
<Text slot="description">This is a very very very very very very very very long description.</Text>
</ListViewItem>
<ListViewItem id="c" textValue="Long title, long description">
<Text>This is a very very very very very very very very long title.</Text>
<Text slot="description">This is a very very very very very very very very long description.</Text>
</ListViewItem>
</ListView>
),
args: {
...Example.args,
overflowMode: 'wrap'
}
};

export const DisabledItems: Story = {
render: (args) => (
<ListView {...args} aria-label="Files" items={items} styles={listViewStyles} selectionMode="multiple" disabledKeys={['utilities', 'illustrator']}>
{(item) => (
<ListViewItem textValue={item.name} hasChildItems={item.type === 'folder'}>
{item.type === 'folder' ? <Folder /> : <File />}
<Text>{item.name}</Text>
<Text slot="description">{item.description}</Text>
</ListViewItem>
)}
</ListView>
)
};

export const DisabledBehaviorSelection: Story = {
...DisabledItems,
args: {
disabledBehavior: 'selection'
}
};

export const WithActions: Story = {
render: (args) => (
<ListView {...args} aria-label="Files" styles={listViewStyles}>
<ListViewItem id="utilities" textValue="Utilities" hasChildItems>
<Folder />
<Text>Utilities</Text>
<Text slot="description">16 items</Text>
<ActionButtonGroup>
<ActionButton aria-label="Edit"><Edit /></ActionButton>
</ActionButtonGroup>
<ActionMenu>
<MenuItem id="edit"><Edit /><Text>Edit</Text></MenuItem>
<MenuItem id="delete"><Delete /><Text>Delete</Text></MenuItem>
</ActionMenu>
</ListViewItem>
<ListViewItem id="photoshop" textValue="Adobe Photoshop">
<Text>Adobe Photoshop</Text>
<Text slot="description">Application</Text>
<ActionButtonGroup>
<ActionButton aria-label="Edit"><Edit /></ActionButton>
</ActionButtonGroup>
<ActionMenu>
<MenuItem id="edit"><Edit /><Text>Edit</Text></MenuItem>
<MenuItem id="delete"><Delete /><Text>Delete</Text></MenuItem>
</ActionMenu>
</ListViewItem>
</ListView>
),
args: {
selectionMode: 'single'
}
};

export const EmptyState: Story = {
render: (args) => (
<ListView
{...args}
aria-label="Empty list"
styles={listViewStyles}
renderEmptyState={() => (
<IllustratedMessage>
<FolderOpen />
<Heading>No results</Heading>
<Content>No results found.</Content>
</IllustratedMessage>
)}>
{[]}
</ListView>
)
};
Loading