Skip to content

Commit 846a78a

Browse files
committed
Replace custom details toggle with native <details>/<summary>
Removes the showDetails state and custom button toggle in favor of the native HTML <details>/<summary> element, which provides built-in toggle behavior, keyboard accessibility, and proper semantics. Uses key={collection?.id} to auto-reset to closed when switching collections.
1 parent 92283a3 commit 846a78a

3 files changed

Lines changed: 35 additions & 42 deletions

File tree

src/components/CollectionImportButton.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,11 @@ const CollectionImportButton: React.FC<CollectionImportButtonProps> = ({
2626
const [importing, setImporting] = React.useState(false);
2727
const [feedback, setFeedback] = React.useState<string | null>(null);
2828
const [success, setSuccess] = React.useState(false);
29-
const [showDetails, setShowDetails] = React.useState(false);
30-
3129
React.useEffect(() => {
3230
setForce(false);
3331
setImporting(false);
3432
setFeedback(null);
3533
setSuccess(false);
36-
setShowDetails(false);
3734
}, [collection?.id]);
3835

3936
const supportsImport = (): boolean => {
@@ -104,15 +101,8 @@ const CollectionImportButton: React.FC<CollectionImportButtonProps> = ({
104101
Queue Import picks up new and changed items. Check{" "}
105102
<strong>Force full re-import</strong> to re-process everything.
106103
</p>
107-
<button
108-
className="collection-import-details-toggle"
109-
type="button"
110-
onClick={() => setShowDetails(!showDetails)}
111-
aria-expanded={showDetails}
112-
>
113-
{showDetails ? "Less details" : "More details"}
114-
</button>
115-
{showDetails && (
104+
<details className="collection-import-details" key={collection?.id}>
105+
<summary>More details</summary>
116106
<dl className="collection-import-docs">
117107
<dt>Queue Import</dt>
118108
<dd>
@@ -133,7 +123,7 @@ const CollectionImportButton: React.FC<CollectionImportButtonProps> = ({
133123
re-processes all items.
134124
</dd>
135125
</dl>
136-
)}
126+
</details>
137127
</div>
138128
);
139129

src/stylesheets/collection.scss

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,25 @@
2424
}
2525
}
2626

27-
.collection-import-details-toggle {
28-
background: none;
29-
border: none;
30-
padding: 0;
31-
color: $dark-gray;
32-
cursor: pointer;
33-
font-size: 0.8rem;
27+
details.collection-import-details {
3428
margin-bottom: 1em;
3529

36-
&:hover,
37-
&:focus-visible {
38-
color: $blue-dark;
30+
summary {
31+
list-style: none;
32+
color: $dark-gray;
33+
cursor: pointer;
34+
font-size: 0.8rem;
3935
text-decoration: underline;
36+
37+
&::-webkit-details-marker {
38+
display: none;
39+
}
40+
41+
&:hover,
42+
&:focus-visible {
43+
color: $blue-dark;
44+
text-decoration: underline;
45+
}
4046
}
4147
}
4248

tests/jest/components/CollectionImportButton.test.tsx

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,48 +90,45 @@ describe("CollectionImportButton", () => {
9090
screen.getByText(/queue import picks up new and changed items/i)
9191
).toBeInTheDocument();
9292
expect(
93-
screen.queryByText(/schedules a background import job/i)
94-
).not.toBeInTheDocument();
93+
screen.getByText(/schedules a background import job/i)
94+
).not.toBeVisible();
9595
expect(
96-
screen.queryByText(/the import job re-processes every item/i)
97-
).not.toBeInTheDocument();
96+
screen.getByText(/the import job re-processes every item/i)
97+
).not.toBeVisible();
9898
});
9999

100100
it("clicking 'More details' reveals the detailed docs", async () => {
101101
const user = userEvent.setup();
102102
renderButton();
103103
await expandPanel(user);
104104

105-
const toggle = screen.getByRole("button", { name: "More details" });
106-
expect(toggle).toHaveAttribute("aria-expanded", "false");
105+
const details = screen.getByText("More details").closest("details");
106+
expect(details).not.toHaveAttribute("open");
107107

108-
await user.click(toggle);
108+
await user.click(screen.getByText("More details"));
109109

110+
expect(details).toHaveAttribute("open");
110111
expect(
111112
screen.getByText(/schedules a background import job/i)
112-
).toBeInTheDocument();
113+
).toBeVisible();
113114
expect(
114115
screen.getByText(/the import job re-processes every item/i)
115-
).toBeInTheDocument();
116-
expect(
117-
screen.getByRole("button", { name: "Less details" })
118-
).toHaveAttribute("aria-expanded", "true");
116+
).toBeVisible();
119117
});
120118

121-
it("clicking 'Less details' hides the detailed docs again", async () => {
119+
it("clicking 'More details' again hides the detailed docs", async () => {
122120
const user = userEvent.setup();
123121
renderButton();
124122
await expandPanel(user);
125123

126-
await user.click(screen.getByRole("button", { name: "More details" }));
124+
await user.click(screen.getByText("More details"));
127125
expect(
128126
screen.getByText(/schedules a background import job/i)
129-
).toBeInTheDocument();
127+
).toBeVisible();
130128

131-
await user.click(screen.getByRole("button", { name: "Less details" }));
132-
expect(
133-
screen.queryByText(/schedules a background import job/i)
134-
).not.toBeInTheDocument();
129+
await user.click(screen.getByText("More details"));
130+
const details = screen.getByText("More details").closest("details");
131+
expect(details).not.toHaveAttribute("open");
135132
});
136133

137134
it("checkbox toggles force state", async () => {

0 commit comments

Comments
 (0)