Skip to content

Commit febcd41

Browse files
committed
fix(stories): keep the panel stories' opener mounted
The panel stories showed "Open thread" only while the panel was closed, so the button left the page in the same commit that mounted the panel. The panel found no opener to record, and closing it sent focus to the body instead of back to the button. Keep a toggle in the main area in every panel story, with aria-expanded and aria-controls, so the panel records it and returns focus to it.
1 parent 2bc344f commit febcd41

1 file changed

Lines changed: 50 additions & 15 deletions

File tree

‎stories/chat/panel.stories.tsx‎

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from "react";
1+
import { useId, useState } from "react";
22
import type { ReactNode } from "react";
33

44
import { Button } from "../../src/ui/button.js";
@@ -23,14 +23,22 @@ const MESSAGES = Array.from({ length: 24 }, (_, index) => ({
2323
: `Reply ${index + 1}: keep going, and flag anything over the usual range.`,
2424
}));
2525

26-
/** The main area beside the panel, with a button that reopens it. */
26+
/**
27+
* The main area beside the panel, with the button that opens and closes it.
28+
* The button stays mounted while the panel is open, so the panel records it
29+
* as the opener and gives it focus back on close.
30+
*/
2731
function Main({
2832
open,
29-
onOpen,
33+
onToggle,
34+
panelId,
35+
label,
3036
children,
3137
}: {
3238
readonly open: boolean;
33-
readonly onOpen: () => void;
39+
readonly onToggle: () => void;
40+
readonly panelId: string;
41+
readonly label: string;
3442
readonly children?: ReactNode;
3543
}) {
3644
return (
@@ -40,11 +48,15 @@ function Main({
4048
The conversation keeps at least 440px however wide the panel is dragged.
4149
</p>
4250
{children}
43-
{open ? null : (
44-
<Button variant="outline" className="self-start" onClick={onOpen}>
45-
Open thread
46-
</Button>
47-
)}
51+
<Button
52+
variant="outline"
53+
className="self-start"
54+
aria-expanded={open}
55+
aria-controls={open ? panelId : undefined}
56+
onClick={onToggle}
57+
>
58+
{label}
59+
</Button>
4860
</main>
4961
);
5062
}
@@ -68,11 +80,17 @@ function Thread() {
6880

6981
/** A wide screen: drag the edge, use the arrow keys, or double-click to reset. */
7082
export const Wide = () => {
83+
const panelId = useId();
7184
const [open, setOpen] = useState(true);
7285
const [width, setWidth] = useState<number | null>(null);
7386
return (
7487
<div className="@container relative flex h-[560px] overflow-hidden rounded-lg border border-border">
75-
<Main open={open} onOpen={() => setOpen(true)}>
88+
<Main
89+
open={open}
90+
onToggle={() => setOpen(!open)}
91+
panelId={panelId}
92+
label="Thread"
93+
>
7694
<p className="text-sm">
7795
Chosen width:{" "}
7896
<output aria-label="Panel width">
@@ -81,7 +99,12 @@ export const Wide = () => {
8199
</p>
82100
</Main>
83101
{open ? (
84-
<Panel aria-label="Thread" width={width} onWidthChange={setWidth}>
102+
<Panel
103+
id={panelId}
104+
aria-label="Thread"
105+
width={width}
106+
onWidthChange={setWidth}
107+
>
85108
<PanelHandle label="Resize thread panel" />
86109
<PanelHeader>
87110
<h2 className="text-sm font-semibold">Thread</h2>
@@ -102,12 +125,18 @@ export const Wide = () => {
102125

103126
/** A row too narrow for both: the panel covers it and hides its handle. */
104127
export const NarrowScreenOverlay = () => {
128+
const panelId = useId();
105129
const [open, setOpen] = useState(true);
106130
return (
107131
<div className="@container relative flex h-[560px] max-w-sm overflow-hidden rounded-lg border border-border">
108-
<Main open={open} onOpen={() => setOpen(true)} />
132+
<Main
133+
open={open}
134+
onToggle={() => setOpen(!open)}
135+
panelId={panelId}
136+
label="Thread"
137+
/>
109138
{open ? (
110-
<Panel aria-label="Thread">
139+
<Panel id={panelId} aria-label="Thread">
111140
<PanelHandle label="Resize thread panel" />
112141
<PanelHeader>
113142
<h2 className="text-sm font-semibold">Thread</h2>
@@ -139,13 +168,19 @@ type PaneId = (typeof PANES)[number]["id"];
139168
* back, and both are where you left them.
140169
*/
141170
export const SeveralPanes = () => {
171+
const panelId = useId();
142172
const [open, setOpen] = useState(true);
143173
const [shown, setShown] = useState<PaneId>("thread");
144174
return (
145175
<div className="@container relative flex h-[560px] overflow-hidden rounded-lg border border-border">
146-
<Main open={open} onOpen={() => setOpen(true)} />
176+
<Main
177+
open={open}
178+
onToggle={() => setOpen(!open)}
179+
panelId={panelId}
180+
label="Side panel"
181+
/>
147182
{open ? (
148-
<Panel aria-label="Side panel">
183+
<Panel id={panelId} aria-label="Side panel">
149184
<PanelHandle />
150185
<PanelHeader>
151186
<div

0 commit comments

Comments
 (0)