fix(ui): a picker whose list failed says so, and takes no pick while one is being written

- Combobox gains `error` (said in the list, not "No matches") and a soft `disabled` (keeps the
  focus, announced, opens and changes nothing).
- UserPicker passes both: the share dialog's recipient picker is off while a share is pending, and
  the share and quota pickers say when the user list failed to load.
- ChannelPicker and the Plex playlist/collection pickers say a failed load too.
- The channel menu's Tags row says loading / failed instead of "no tags yet".
This commit is contained in:
2026-09-27 14:47:46 +02:00
parent 70f98b352f
commit 17bb14fb18
10 changed files with 102 additions and 6 deletions
@@ -40,6 +40,7 @@ afterEach(() => {
h.role = "user";
h.canWrite = true;
tags = [];
tagsState = "ready";
onToggleTag.mockReset();
onPickPriority.mockReset();
});
@@ -70,6 +71,7 @@ const onView = vi.fn();
const onToggleTag = vi.fn();
const onPickPriority = vi.fn();
let tags: Tag[] = [];
let tagsState: "loading" | "error" | "ready" = "ready";
const tag = (id: number, name: string) => ({ id, name }) as Tag;
function mount() {
const qc = new QueryClient({ defaultOptions: { mutations: { retry: false } } });
@@ -77,7 +79,15 @@ function mount() {
<QueryClientProvider client={qc}>{children}</QueryClientProvider>
);
return renderHook(
() => useChannelActions({ onView, onOpenWizard: () => {}, tags, onToggleTag, onPickPriority }),
() =>
useChannelActions({
onView,
onOpenWizard: () => {},
tags,
tagsState,
onToggleTag,
onPickPriority,
}),
{ wrapper },
);
}
@@ -220,6 +230,14 @@ describe("useChannelActions — the Tags submenu", () => {
expect(onToggleTag).toHaveBeenCalledWith(expect.objectContaining({ id: "UC1" }), 2);
});
// E28 review J3: a failed /tags read said "no tags yet" — an error dressed as an empty list.
it("says the tags are loading, or failed to load, instead of 'no tags yet'", () => {
tagsState = "loading";
expect(row(ch(), "tags")).toMatchObject({ disabled: true, note: "common.loading" });
tagsState = "error";
expect(row(ch(), "tags")).toMatchObject({ disabled: true, note: "common.loadError" });
});
it("stays in place, off, saying where tags are made, with no tag yet", () => {
expect(row(ch(), "tags")).toMatchObject({ disabled: true, note: "channels.row.noTagsYet" });
expect(row(ch(), "tags").items).toBeUndefined();