Skip to content

Commit 8de87b3

Browse files
committed
Improve collection import panel UX
Collapse detailed docs behind a "More details" toggle, show a compact description summary below the controls using the standard description class, change the button label/style to "Queue Full Re-import" with btn-warning when force is checked, and provide context-aware success messages for regular vs force imports.
1 parent 92e18a5 commit 8de87b3

3 files changed

Lines changed: 209 additions & 32 deletions

File tree

src/components/CollectionImportButton.tsx

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ 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);
2930

3031
React.useEffect(() => {
3132
setForce(false);
3233
setImporting(false);
3334
setFeedback(null);
3435
setSuccess(false);
36+
setShowDetails(false);
3537
}, [collection?.id]);
3638

3739
const supportsImport = (): boolean => {
@@ -48,7 +50,11 @@ const CollectionImportButton: React.FC<CollectionImportButtonProps> = ({
4850
try {
4951
await importCollection(collection.id, force);
5052
setImporting(false);
51-
setFeedback("Import task queued.");
53+
setFeedback(
54+
force
55+
? "Full re-import task queued. All items will be re-processed — this may take longer than a regular import. Changes will appear in the catalog once processing completes."
56+
: "Import task queued. New and updated items will appear in the catalog once processing completes."
57+
);
5258
setSuccess(true);
5359
} catch (e) {
5460
const message =
@@ -67,37 +73,25 @@ const CollectionImportButton: React.FC<CollectionImportButtonProps> = ({
6773

6874
const feedbackClass = success ? "alert alert-success" : "alert alert-danger";
6975

76+
const buttonLabel = force
77+
? importing
78+
? "Queuing Full Re-import..."
79+
: "Queue Full Re-import"
80+
: importing
81+
? "Queuing..."
82+
: "Queue Import";
83+
84+
const buttonClass = force ? "btn btn-warning" : "btn btn-default";
85+
7086
const panelContent = (
7187
<div className="collection-import">
72-
<dl className="collection-import-docs">
73-
<dt>Queue Import</dt>
74-
<dd>
75-
Schedules a background import job that checks for new or updated items
76-
from the collection source and adds them to the catalog. Only items
77-
that have changed since the last import are processed. Use this when
78-
new titles have been added to a collection but do not yet appear in
79-
the catalog, or when you want to pick up recent changes from the
80-
source.
81-
</dd>
82-
<dt>Force full re-import</dt>
83-
<dd>
84-
When checked, the import job re-processes every item in the
85-
collection, regardless of whether it appears to have changed since the
86-
last import. Use this to correct metadata that is out of date, or to
87-
resolve issues caused by a previously incomplete import. A forced
88-
re-import will take longer than a regular import because it
89-
re-processes all items. Check this box <em>before</em> clicking Queue
90-
Import.
91-
</dd>
92-
</dl>
93-
{feedback && <div className={feedbackClass}>{feedback}</div>}
9488
<div className="collection-import-controls">
9589
<button
96-
className="btn btn-default"
90+
className={buttonClass}
9791
disabled={disabled || importing}
9892
onClick={handleImport}
9993
>
100-
{importing ? "Queuing..." : "Queue Import"}
94+
{buttonLabel}
10195
</button>
10296
<label>
10397
<input
@@ -109,6 +103,42 @@ const CollectionImportButton: React.FC<CollectionImportButtonProps> = ({
109103
Force full re-import
110104
</label>
111105
</div>
106+
{feedback && <div className={feedbackClass}>{feedback}</div>}
107+
<p className="description">
108+
Queue Import picks up new and changed items. Check{" "}
109+
<strong>Force full re-import</strong> to re-process everything.
110+
</p>
111+
<button
112+
className="collection-import-details-toggle"
113+
type="button"
114+
onClick={() => setShowDetails(!showDetails)}
115+
aria-expanded={showDetails}
116+
>
117+
{showDetails ? "Less details" : "More details"}
118+
</button>
119+
{showDetails && (
120+
<dl className="collection-import-docs">
121+
<dt>Queue Import</dt>
122+
<dd>
123+
Schedules a background import job that checks for new or updated
124+
items from the collection source and adds them to the catalog. Only
125+
items that have changed since the last import are processed. Use
126+
this when new titles have been added to a collection but do not yet
127+
appear in the catalog, or when you want to pick up recent changes
128+
from the source.
129+
</dd>
130+
<dt>Force full re-import</dt>
131+
<dd>
132+
When checked, the import job re-processes every item in the
133+
collection, regardless of whether it appears to have changed since
134+
the last import. Use this to correct metadata that is out of date,
135+
or to resolve issues caused by a previously incomplete import. A
136+
forced re-import will take longer than a regular import because it
137+
re-processes all items. Check this box <em>before</em> clicking
138+
Queue Import.
139+
</dd>
140+
</dl>
141+
)}
112142
</div>
113143
);
114144

src/stylesheets/collection.scss

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,23 @@
1111
}
1212

1313
.collection-import {
14+
.collection-import-details-toggle {
15+
background: none;
16+
border: none;
17+
padding: 0;
18+
color: #777;
19+
cursor: pointer;
20+
font-size: 0.8rem;
21+
margin-bottom: 1em;
22+
23+
&:hover {
24+
color: #333;
25+
text-decoration: underline;
26+
}
27+
}
28+
1429
.collection-import-docs {
30+
font-size: 0.8rem;
1531
margin-bottom: 1em;
1632

1733
dt {

tests/jest/components/CollectionImportButton.test.tsx

Lines changed: 138 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,56 @@ describe("CollectionImportButton", () => {
8282
expect(screen.getByLabelText("Force full re-import")).toBeInTheDocument();
8383
});
8484

85-
it("renders usage documentation for both import options", async () => {
85+
it("shows compact summary by default; detailed docs are hidden", async () => {
8686
const user = userEvent.setup();
8787
renderButton();
8888
await expandPanel(user);
89+
expect(
90+
screen.getByText(/queue import picks up new and changed items/i)
91+
).toBeInTheDocument();
92+
expect(
93+
screen.queryByText(/schedules a background import job/i)
94+
).not.toBeInTheDocument();
95+
expect(
96+
screen.queryByText(/the import job re-processes every item/i)
97+
).not.toBeInTheDocument();
98+
});
99+
100+
it("clicking 'More details' reveals the detailed docs", async () => {
101+
const user = userEvent.setup();
102+
renderButton();
103+
await expandPanel(user);
104+
105+
const toggle = screen.getByRole("button", { name: "More details" });
106+
expect(toggle).toHaveAttribute("aria-expanded", "false");
107+
108+
await user.click(toggle);
109+
89110
expect(
90111
screen.getByText(/schedules a background import job/i)
91112
).toBeInTheDocument();
92113
expect(
93114
screen.getByText(/the import job re-processes every item/i)
94115
).toBeInTheDocument();
116+
expect(
117+
screen.getByRole("button", { name: "Less details" })
118+
).toHaveAttribute("aria-expanded", "true");
119+
});
120+
121+
it("clicking 'Less details' hides the detailed docs again", async () => {
122+
const user = userEvent.setup();
123+
renderButton();
124+
await expandPanel(user);
125+
126+
await user.click(screen.getByRole("button", { name: "More details" }));
127+
expect(
128+
screen.getByText(/schedules a background import job/i)
129+
).toBeInTheDocument();
130+
131+
await user.click(screen.getByRole("button", { name: "Less details" }));
132+
expect(
133+
screen.queryByText(/schedules a background import job/i)
134+
).not.toBeInTheDocument();
95135
});
96136

97137
it("checkbox toggles force state", async () => {
@@ -106,6 +146,43 @@ describe("CollectionImportButton", () => {
106146
expect(checkbox).not.toBeChecked();
107147
});
108148

149+
it("button text changes to 'Queue Full Re-import' when force is checked", async () => {
150+
const user = userEvent.setup();
151+
renderButton();
152+
await expandPanel(user);
153+
154+
expect(
155+
screen.getByRole("button", { name: "Queue Import" })
156+
).toBeInTheDocument();
157+
158+
await user.click(screen.getByRole("checkbox"));
159+
160+
expect(
161+
screen.getByRole("button", { name: "Queue Full Re-import" })
162+
).toBeInTheDocument();
163+
expect(
164+
screen.queryByRole("button", { name: "Queue Import" })
165+
).not.toBeInTheDocument();
166+
});
167+
168+
it("button uses btn-warning class when force is checked", async () => {
169+
const user = userEvent.setup();
170+
renderButton();
171+
await expandPanel(user);
172+
173+
const button = screen.getByRole("button", { name: "Queue Import" });
174+
expect(button).toHaveClass("btn-default");
175+
expect(button).not.toHaveClass("btn-warning");
176+
177+
await user.click(screen.getByRole("checkbox"));
178+
179+
const forceButton = screen.getByRole("button", {
180+
name: "Queue Full Re-import",
181+
});
182+
expect(forceButton).toHaveClass("btn-warning");
183+
expect(forceButton).not.toHaveClass("btn-default");
184+
});
185+
109186
it("button triggers import with correct args (force=false)", async () => {
110187
const user = userEvent.setup();
111188
const { importCollection } = renderButton();
@@ -121,18 +198,39 @@ describe("CollectionImportButton", () => {
121198
await expandPanel(user);
122199
const checkbox = screen.getByRole("checkbox");
123200
await user.click(checkbox);
124-
const button = screen.getByRole("button", { name: "Queue Import" });
201+
const button = screen.getByRole("button", {
202+
name: "Queue Full Re-import",
203+
});
125204
await user.click(button);
126205
expect(importCollection).toHaveBeenCalledWith(42, true);
127206
});
128207

129-
it("shows success feedback with alert-success styling after import", async () => {
208+
it("shows success feedback for regular import", async () => {
130209
const user = userEvent.setup();
131210
renderButton();
132211
await expandPanel(user);
133212
await user.click(screen.getByRole("button", { name: "Queue Import" }));
134213
await waitFor(() => {
135-
const feedback = screen.getByText("Import task queued.");
214+
const feedback = screen.getByText(
215+
/import task queued\. new and updated items will appear/i
216+
);
217+
expect(feedback).toBeInTheDocument();
218+
expect(feedback).toHaveClass("alert", "alert-success");
219+
});
220+
});
221+
222+
it("shows success feedback for force re-import", async () => {
223+
const user = userEvent.setup();
224+
renderButton();
225+
await expandPanel(user);
226+
await user.click(screen.getByRole("checkbox"));
227+
await user.click(
228+
screen.getByRole("button", { name: "Queue Full Re-import" })
229+
);
230+
await waitFor(() => {
231+
const feedback = screen.getByText(
232+
/full re-import task queued\. all items will be re-processed/i
233+
);
136234
expect(feedback).toBeInTheDocument();
137235
expect(feedback).toHaveClass("alert", "alert-success");
138236
});
@@ -162,9 +260,13 @@ describe("CollectionImportButton", () => {
162260
await user.click(checkbox);
163261
expect(checkbox).toBeChecked();
164262

165-
await user.click(screen.getByRole("button", { name: "Queue Import" }));
263+
await user.click(
264+
screen.getByRole("button", { name: "Queue Full Re-import" })
265+
);
166266
await waitFor(() => {
167-
expect(screen.getByText("Import task queued.")).toBeInTheDocument();
267+
expect(
268+
screen.getByText(/full re-import task queued/i)
269+
).toBeInTheDocument();
168270
});
169271

170272
const nextCollection: CollectionData = {
@@ -183,7 +285,9 @@ describe("CollectionImportButton", () => {
183285

184286
await waitFor(() => {
185287
expect(screen.getByRole("checkbox")).not.toBeChecked();
186-
expect(screen.queryByText("Import task queued.")).not.toBeInTheDocument();
288+
expect(
289+
screen.queryByText(/full re-import task queued/i)
290+
).not.toBeInTheDocument();
187291
});
188292
});
189293

@@ -216,4 +320,31 @@ describe("CollectionImportButton", () => {
216320
).toBeEnabled();
217321
});
218322
});
323+
324+
it("shows 'Queuing Full Re-import...' while importing with force", async () => {
325+
const user = userEvent.setup();
326+
let resolveImport: () => void;
327+
const pendingImport = new Promise<void>((resolve) => {
328+
resolveImport = resolve;
329+
});
330+
const mockImport = jest.fn().mockReturnValue(pendingImport);
331+
renderButton({ importCollection: mockImport });
332+
await expandPanel(user);
333+
334+
await user.click(screen.getByRole("checkbox"));
335+
await user.click(
336+
screen.getByRole("button", { name: "Queue Full Re-import" })
337+
);
338+
339+
expect(
340+
screen.getByRole("button", { name: "Queuing Full Re-import..." })
341+
).toBeDisabled();
342+
343+
resolveImport();
344+
await waitFor(() => {
345+
expect(
346+
screen.getByRole("button", { name: "Queue Full Re-import" })
347+
).toBeEnabled();
348+
});
349+
});
219350
});

0 commit comments

Comments
 (0)