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

New Panels component #2001

Merged
merged 146 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 141 commits
Commits
Show all changes
146 commits
Select commit Hold shift + click to select a range
7736226
Starter exploration
r100-stack Apr 15, 2024
7060cca
Working demo
r100-stack Apr 23, 2024
ef1e1e1
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Apr 25, 2024
d248d21
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack May 10, 2024
3fd8c0b
Revert all changes
r100-stack May 10, 2024
ff277dd
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack May 10, 2024
46eea38
WIP from last week
r100-stack May 13, 2024
94230bd
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Jun 14, 2024
33c571c
Added PanelHeader, PanelBackButton.
r100-stack Jun 17, 2024
2190432
Remove fallbackId since it's not a DOM node
r100-stack Jun 17, 2024
a6953e2
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Jun 20, 2024
6a8f5af
Logic improvements/additions.
r100-stack Jun 21, 2024
0d11d42
WIP sandbox with animations
r100-stack Jun 24, 2024
078acdc
WIP
r100-stack Jun 24, 2024
00e98ac
WIP
r100-stack Jun 28, 2024
619e2c0
WIP state reducer
r100-stack Jul 1, 2024
f39e15c
WIP: Fix short flickering when changing pages
r100-stack Jul 2, 2024
45ee0d0
Animation working in Panels and playground
r100-stack Jul 22, 2024
e99c2f6
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Jul 22, 2024
019f0f4
Cleanup
r100-stack Jul 22, 2024
a93d8a6
Focus new panel after animation completes.
r100-stack Jul 22, 2024
8fac8c3
Prevent scroll on focus
r100-stack Jul 29, 2024
571acc8
Controlled state without storing history
r100-stack Jul 30, 2024
68d1691
Don't change activePanel if panel already there
r100-stack Jul 30, 2024
f9984f8
inert, reduced-motion, JSDocs, testing
r100-stack Jul 30, 2024
8f23e20
instance, animationOptions, stories
r100-stack Jul 31, 2024
ca6e854
Fix focus
r100-stack Jul 31, 2024
2f9fa72
Tests
r100-stack Aug 1, 2024
4aa3bd5
More tests, PanelInstanceProvider
r100-stack Aug 5, 2024
782224a
Rename
r100-stack Aug 5, 2024
ceba173
Fix failing build
r100-stack Aug 6, 2024
c28041f
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Aug 6, 2024
4ab3281
Undo playgrounds
r100-stack Aug 6, 2024
ad35667
Fix install, leftover improvements
r100-stack Aug 6, 2024
3debf85
Removed unused class
r100-stack Aug 6, 2024
a90b292
Better inert
r100-stack Aug 7, 2024
bd27069
Remove unused code, small cleanups
r100-stack Aug 7, 2024
5adb857
Remove animationOptions
r100-stack Aug 7, 2024
a62ec7e
Remove controlled mode
r100-stack Aug 7, 2024
650d958
Remove no longer needed tests
r100-stack Aug 7, 2024
ac15a37
Keep focus on trigger during animation
r100-stack Aug 7, 2024
c7b4f3f
Fix e2e test
r100-stack Aug 7, 2024
8617961
No `position: absolute`
r100-stack Aug 8, 2024
4e51ac0
Better names
r100-stack Aug 8, 2024
e93d3c0
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Aug 8, 2024
c60c296
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Aug 19, 2024
77f2829
Move animations out
r100-stack Aug 19, 2024
729ba7c
Merge remote-tracking branch 'origin/rohan/layered-dropdown-menu' int…
r100-stack Aug 20, 2024
4eb5efb
goBack in PanelInstanceProvider
r100-stack Aug 21, 2024
c573b9e
Remove options in goBack for simplification
r100-stack Aug 21, 2024
14e28ef
No ref.current in rendering path
r100-stack Aug 21, 2024
9079808
Added examples and corrected stories
r100-stack Aug 22, 2024
9b3a202
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Aug 22, 2024
d16d7b6
nit
r100-stack Aug 23, 2024
898c806
Step towards removing animations.
r100-stack Aug 23, 2024
5f52104
Working front and back.
r100-stack Aug 23, 2024
2c21cb5
Working smooth scroll.
r100-stack Aug 23, 2024
1e7d0a8
TODOs from planning
r100-stack Aug 23, 2024
ec1b2ae
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Aug 23, 2024
268d0fd
WIP improvements based on TODOs and regressions
r100-stack Aug 27, 2024
4431970
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Aug 27, 2024
a0e535c
More WIP improvements. overflow-anchor not working.
r100-stack Aug 28, 2024
5c6be6a
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Aug 28, 2024
c94ee12
Cleanup PR a bit for overflow-anchor testing
r100-stack Sep 3, 2024
4936b63
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Sep 3, 2024
3905400
Leftover
r100-stack Sep 3, 2024
18af31d
Diff from Mayank's PR comment
r100-stack Sep 4, 2024
e0c8590
Prevent changing panels when transitioning
r100-stack Sep 4, 2024
66fe6ad
Call onActivePanelChange
r100-stack Sep 4, 2024
a410dbe
Working focus
r100-stack Sep 4, 2024
1d844a8
- Removed `compareDocumentPosition`
r100-stack Sep 4, 2024
f52e3d8
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Sep 4, 2024
a891dcd
No scroll when reduced motion preferred
r100-stack Sep 5, 2024
f1e5552
e2e tests improvements
r100-stack Sep 5, 2024
9bdde7c
Rename vars
r100-stack Sep 5, 2024
0cc4c41
JSDocs, tests, etc.
r100-stack Sep 5, 2024
d1a711c
Fix test failures
r100-stack Sep 5, 2024
c296e05
Undo a891dcd. Use useMediaQuery instead.
r100-stack Sep 6, 2024
756f8a3
Added missing useInertPolyfill
r100-stack Sep 6, 2024
49c0983
changeset
r100-stack Sep 6, 2024
5da8fdc
nit
r100-stack Sep 6, 2024
6c2c2cf
Revert testing leftover
r100-stack Sep 6, 2024
345e494
useDelayed can return undefined
r100-stack Sep 6, 2024
760391f
Leftover
r100-stack Sep 6, 2024
88c4bea
Merge remote-tracking branch 'origin/r/add-missing-inert-polyfills' i…
r100-stack Sep 6, 2024
cd202e9
Remove panelElements
r100-stack Sep 6, 2024
974c6f6
Focus management using refs.
r100-stack Sep 6, 2024
0ab1fde
Code rearrangements
r100-stack Sep 6, 2024
ee06164
Remove unnecessary ref
r100-stack Sep 6, 2024
d3490e4
Show warning when trigger points to dne panel
r100-stack Sep 9, 2024
24516bb
Better var names
r100-stack Sep 9, 2024
4ea8b6a
Leftover
r100-stack Sep 9, 2024
89230b8
Remove unnecessary variables
r100-stack Sep 9, 2024
225b3a8
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Oct 3, 2024
9ae4ee7
Remove outdated inert polyfill code
r100-stack Oct 3, 2024
79b3d7e
Fix a11y errors
r100-stack Oct 3, 2024
0aa8092
Leftover
r100-stack Oct 3, 2024
3b9626a
Remove unnecessary tabIndex
r100-stack Oct 4, 2024
cff00ce
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Oct 4, 2024
1ed6a4e
Fix focus and e2e tests
r100-stack Oct 4, 2024
1449bfe
Removed `initialActiveId`
r100-stack Oct 7, 2024
72b19c5
Review, touchup, cleanup
r100-stack Oct 7, 2024
2674ba3
Show JSDocs on hover
r100-stack Oct 7, 2024
6abb22f
aria-label instead of label
r100-stack Oct 7, 2024
71d4c44
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Oct 7, 2024
c25ce72
Merge remote-tracking branch 'origin/rohan/layered-dropdown-menu' int…
r100-stack Oct 7, 2024
4929239
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Oct 10, 2024
74b071a
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Oct 22, 2024
3de27af
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Oct 23, 2024
e5016b1
Leftover
r100-stack Oct 23, 2024
9dc9aed
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Oct 23, 2024
5313c88
Use `useControlledState()` & `useLatestRef()`
r100-stack Oct 25, 2024
a14645d
Revert "Use `useControlledState()` & `useLatestRef()`"
r100-stack Oct 25, 2024
e43d454
Call `onActiveIdChange` only when needed
r100-stack Oct 25, 2024
8d193d2
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Oct 30, 2024
4834366
useLatestRef
r100-stack Nov 5, 2024
d5cddce
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Nov 5, 2024
dd90359
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Nov 7, 2024
e38ab9c
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Nov 7, 2024
2551790
`unstable_Panels`
r100-stack Nov 7, 2024
3c70287
Fix imports
r100-stack Nov 8, 2024
7d81e13
Merge remote-tracking branch 'origin/main' into rohan/layered-dropdow…
r100-stack Nov 8, 2024
36ff42b
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Nov 15, 2024
dc9ba17
Docs
r100-stack Nov 15, 2024
87efaae
Merge remote-tracking branch 'origin/rohan/layered-dropdown-menu' int…
r100-stack Nov 15, 2024
59ac7d0
Remaining docs
r100-stack Nov 18, 2024
a0ce156
🛫
r100-stack Nov 18, 2024
a9057e4
Merge branch 'r/unstable-docs-starter' into rohan/layered-dropdown-menu
r100-stack Nov 18, 2024
69978a6
Leftover
r100-stack Nov 18, 2024
0e78a5c
PR comments
r100-stack Nov 18, 2024
f24250e
Merge branch 'r/unstable-docs-starter' into rohan/layered-dropdown-menu
r100-stack Nov 18, 2024
72c9690
PR comments
r100-stack Nov 18, 2024
d5ad00d
Add `componentName` prop
r100-stack Nov 18, 2024
c67a45a
Feedback link
r100-stack Nov 18, 2024
f26dadb
Merge branch 'r/unstable-docs-starter' into rohan/layered-dropdown-menu
r100-stack Nov 18, 2024
e19e57a
Add componentName prop
r100-stack Nov 18, 2024
e163abf
Added role=group
r100-stack Nov 18, 2024
d39bd4e
Remove nested Surfaces
r100-stack Nov 18, 2024
acab7f6
Leftover
r100-stack Nov 18, 2024
3568640
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Nov 19, 2024
601f513
Leftover
r100-stack Nov 19, 2024
a3aba6e
Changeset clarity
r100-stack Nov 19, 2024
b787a8e
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Nov 19, 2024
4a7abbc
Fix tests
r100-stack Nov 19, 2024
9564c8f
Missing import
r100-stack Nov 19, 2024
74991e7
Merge branch 'main' into rohan/layered-dropdown-menu
r100-stack Nov 19, 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
6 changes: 6 additions & 0 deletions .changeset/sour-parrots-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@itwin/itwinui-react': minor
---

Added a new generic `Panels` component (**unstable**) for easy setup of nested screens/panels. Example uses: multi-layered menus, wizards, settings screens, etc.
- This API is temporarily marked as **unstable** to collect [feedback](https://github.com/iTwin/iTwinUI/discussions/2348) for some time to tailor the generic component specifically to users' needs.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
303 changes: 303 additions & 0 deletions apps/react-workshop/src/Panels.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import * as React from 'react';
import {
Button,
Divider,
Flex,
List,
ListItem,
unstable_Panels as Panels,
Surface,
Text,
ToggleSwitch,
} from '@itwin/itwinui-react';

export default {
component: Panels,
title: 'Panels',
};

export const Basic = () => {
const panelIdRoot = 'root';
const panelIdMoreInfo = 'more-info';

return (
<Panels.Wrapper
as={Surface}
style={{
inlineSize: 'min(300px, 30vw)',
blockSize: 'min(500px, 50vh)',
}}
>
<Panels.Panel id={panelIdRoot}>
<Surface.Header as={Panels.Header}>Root</Surface.Header>
<Surface.Body as={List}>
<ListItem>
<Panels.Trigger for={panelIdMoreInfo}>
<ListItem.Action>More details</ListItem.Action>
</Panels.Trigger>
</ListItem>
</Surface.Body>
</Panels.Panel>

<Panels.Panel id={panelIdMoreInfo}>
<Surface.Header as={Panels.Header}>More details</Surface.Header>
<Surface.Body isPadded>
<Text>Content</Text>
</Surface.Body>
</Panels.Panel>
</Panels.Wrapper>
);
};

export const MultiPanelInformationPanel = () => {
const initialActiveId = 'root';

const panels = Array.from(Array(20).keys()).map((i) => ({
id: `panel-${i}`,
label: `Panel ${i}`,
}));

return (
<Panels.Wrapper
as={Surface}
style={{
inlineSize: 'min(300px, 30vw)',
blockSize: 'min(500px, 50vh)',
}}
>
<Panels.Panel id={initialActiveId}>
<Surface.Header as={Panels.Header}>Root</Surface.Header>
<Surface.Body as={List} style={{ height: '100%' }}>
{panels.map((panel) => (
<ListItem key={panel.id}>
<ListItem.Content>
<Panels.Trigger for={`${panel.id}`}>
<ListItem.Action>{panel.label}</ListItem.Action>
</Panels.Trigger>
</ListItem.Content>
</ListItem>
))}
</Surface.Body>
</Panels.Panel>

{panels.map((panel) => (
<Panels.Panel
as={Flex}
key={panel.id}
id={panel.id}
flexDirection='column'
alignItems='stretch'
>
<Surface.Header as={Panels.Header}>{panel.label}</Surface.Header>
<Surface.Body as={Flex} flexDirection='column'>
<Text>{`Content for ${panel.id}`}</Text>
<Flex.Spacer />
<Divider />
<Text>{`Footer for ${panel.id}`}</Text>
</Surface.Body>
</Panels.Panel>
))}
</Panels.Wrapper>
);
};

export const MultiLevelList = () => {
const initialActiveId = React.useId();
const qualityPanelId = React.useId();
const speedPanelId = React.useId();
const accessibilityPanelId = React.useId();

const [repeat, setRepeat] = React.useState(false);
const [quality, setQuality] = React.useState('240p');
const [speed, setSpeed] = React.useState('1.0x');
const [accessibilityOptions, setAccessibilityOptions] = React.useState<
string[]
>([]);

const panels = Panels.useInstance();

const _Item = React.useCallback(
({
content,
state,
setState,
}: {
content: string;
state: string;
setState: React.Dispatch<React.SetStateAction<string>>;
}) => {
const selected = state === content;

return (
<ListItem
active={selected}
aria-selected={selected}
onClick={() => {
panels.goBack();
}}
>
<ListItem.Action onClick={() => setState(content)}>
{content}
</ListItem.Action>
<ListItem.Icon />
</ListItem>
);
},
[panels],
);

const _ItemQuality = React.useCallback(
({ content }: { content: string }) => (
<_Item content={content} state={quality} setState={setQuality} />
),
[_Item, quality],
);

const _ItemSpeed = React.useCallback(
({ content }: { content: string }) => (
<_Item content={content} state={speed} setState={setSpeed} />
),
[_Item, speed],
);

const _ItemAccessibility = React.useCallback(
({ content }: { content: string }) => (
<_Item
content={content}
state={accessibilityOptions.includes(content) ? content : ''}
setState={() => {
setAccessibilityOptions((prev) =>
prev.includes(content)
? prev.filter((item) => item !== content)
: [...prev, content],
);
}}
/>
),
[_Item, accessibilityOptions],
);

const qualities = React.useMemo(
() => ['240p', '360p', '480p', '720p', '1080p'],
[],
);

const speeds = React.useMemo(
() => Array.from({ length: 21 }, (_, i) => (i * 0.1).toFixed(1) + 'x'),
[],
);

const toggleSwitchId = React.useId();

return (
<>
<Panels.Wrapper
instance={panels}
as={Surface}
style={{
inlineSize: 'min(200px, 30vw)',
blockSize: 'min(250px, 50vh)',
}}
>
<Panels.Panel as={List} id={initialActiveId}>
<ListItem>
<ListItem.Content as='label' htmlFor={toggleSwitchId}>
Repeat
</ListItem.Content>
<ToggleSwitch
id={toggleSwitchId}
onChange={(e) => setRepeat(e.target.checked)}
checked={repeat}
/>
</ListItem>
<ListItem>
<Panels.Trigger for={qualityPanelId}>
<ListItem.Action>Quality</ListItem.Action>
</Panels.Trigger>
</ListItem>
<ListItem>
<Panels.Trigger for={speedPanelId}>
<ListItem.Action>Speed</ListItem.Action>
</Panels.Trigger>
</ListItem>
<ListItem>
<Panels.Trigger for={accessibilityPanelId}>
<ListItem.Action>Accessibility</ListItem.Action>
</Panels.Trigger>
</ListItem>
</Panels.Panel>

<Panels.Panel id={qualityPanelId}>
<Surface.Header as={Panels.Header}>Quality</Surface.Header>
<Surface.Body as={List}>
{qualities.map((quality) => (
<_ItemQuality key={quality} content={quality} />
))}
</Surface.Body>
</Panels.Panel>

<Panels.Panel id={speedPanelId}>
<Surface.Header as={Panels.Header}>Speed</Surface.Header>
<Surface.Body as={List}>
{speeds.map((speed) => (
<_ItemSpeed key={speed} content={speed} />
))}
</Surface.Body>
</Panels.Panel>

<Panels.Panel id={accessibilityPanelId}>
<Surface.Header as={Panels.Header}>Accessibility</Surface.Header>
<Surface.Body as={List}>
<_ItemAccessibility content='High contrast' />
<_ItemAccessibility content='Large text' />
<_ItemAccessibility content='Screen reader' />
</Surface.Body>
</Panels.Panel>
</Panels.Wrapper>
</>
);
};

export const NestedPanels = () => {
const panels = Panels.useInstance();

const initialActiveId = 'root';
const panel1Id = 'panel-1';
const panel1_1Id = 'panel-1-1';
const panel1_1_1Id = 'panel-1-1-1';

const panelIds = [initialActiveId, panel1Id, panel1_1Id, panel1_1_1Id];

return (
<Flex flexDirection='column' alignItems='flex-start'>
<Button id='instance-go-back' onClick={() => panels.goBack()}>
Go Back
</Button>
<Panels.Wrapper
instance={panels}
as={Surface}
style={{
width: 'min(300px, 30vw)',
height: 'min(500px, 50vh)',
}}
>
{panelIds.map((id, index) => (
<Panels.Panel key={id} id={id}>
<Surface.Header as={Panels.Header}>{id}</Surface.Header>
<Surface.Body isPadded>
<Panels.Trigger for={panelIds[index + 1]}>
<Button>
Go to {panelIds[index + 1] ?? "panel that doesn't exist"}
</Button>
</Panels.Trigger>
</Surface.Body>
</Panels.Panel>
))}
</Panels.Wrapper>
</Flex>
);
};
21 changes: 21 additions & 0 deletions apps/react-workshop/src/Panels.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
describe('Panels', () => {
const storyPath = 'Panels';
const tests = [
'Basic',
'Multi Panel Information Panel',
'Multi Level List',
'Nested Panels',
];

tests.forEach((testName) => {
it(testName, function () {
const id = Cypress.storyId(storyPath, testName);
cy.visit('/', { qs: { mode: 'preview', story: id } });
cy.compareSnapshot(testName);
});
});
});
Loading
Loading