@thepalaceproject/circulation-admin 1.43.0-post.11 → 1.43.0-post.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,220 @@
1
+ import * as React from "react";
2
+ import { render, screen, within, waitFor } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import PairedMenus from "../../../src/components/PairedMenus";
5
+ import { SettingData, LibraryData } from "../../../src/interfaces";
6
+
7
+ describe("PairedMenus", () => {
8
+ const inputListSetting: SettingData = {
9
+ key: "input",
10
+ label: "Input List",
11
+ default: ["a", "b", "c"],
12
+ options: [
13
+ { key: "a", label: "A" },
14
+ { key: "b", label: "B" },
15
+ { key: "c", label: "C" },
16
+ { key: "d", label: "D" },
17
+ { key: "e", label: "E" },
18
+ ],
19
+ paired: "dropdown",
20
+ };
21
+ const dropdownSetting: SettingData = {
22
+ key: "dropdown",
23
+ label: "Dropdown",
24
+ default: "b",
25
+ options: [
26
+ { key: "a", label: "A" },
27
+ { key: "b", label: "B" },
28
+ { key: "c", label: "C" },
29
+ { key: "d", label: "D" },
30
+ { key: "e", label: "E" },
31
+ ],
32
+ type: "select",
33
+ };
34
+
35
+ const renderPairedMenus = (props = {}) =>
36
+ render(
37
+ <PairedMenus
38
+ inputListSetting={inputListSetting}
39
+ dropdownSetting={dropdownSetting}
40
+ disabled={false}
41
+ {...props}
42
+ />
43
+ );
44
+
45
+ // The current InputList items are rendered as read-only text inputs inside the
46
+ // `.input-list-ul` list; return their displayed values.
47
+ const listItemValues = (container: HTMLElement): string[] =>
48
+ within(container.querySelector<HTMLElement>(".input-list-ul")!)
49
+ .getAllByRole("textbox")
50
+ .map((input) => (input as HTMLInputElement).value);
51
+
52
+ const dropdown = (container: HTMLElement): HTMLSelectElement =>
53
+ container.querySelector<HTMLSelectElement>('select[name="dropdown"]')!;
54
+
55
+ it("renders a fieldset with an InputList and a dropdown", () => {
56
+ const { container } = renderPairedMenus();
57
+ expect(
58
+ container.querySelector("fieldset.paired-menus")
59
+ ).toBeInTheDocument();
60
+ expect(container.querySelector(".input-list")).toBeInTheDocument();
61
+ expect(dropdown(container)).toBeInTheDocument();
62
+ });
63
+
64
+ it("renders the InputList items as read-only fields", () => {
65
+ const { container } = renderPairedMenus();
66
+ const listItems = within(
67
+ container.querySelector<HTMLElement>(".input-list-ul")!
68
+ ).getAllByRole("textbox");
69
+ expect(listItems).toHaveLength(3);
70
+ listItems.forEach((input) =>
71
+ expect((input as HTMLInputElement).readOnly).toBe(true)
72
+ );
73
+ expect(listItemValues(container)).toEqual(["A", "B", "C"]);
74
+ });
75
+
76
+ it("renders the dropdown with its default value and available options", () => {
77
+ const { container } = renderPairedMenus();
78
+ const select = dropdown(container);
79
+ expect(select).toHaveValue("b");
80
+ const options = within(select).getAllByRole("option");
81
+ expect(options.map((o) => o.textContent)).toEqual(["A", "B", "C"]);
82
+ expect(options.map((o) => (o as HTMLOptionElement).value)).toEqual([
83
+ "a",
84
+ "b",
85
+ "c",
86
+ ]);
87
+ });
88
+
89
+ it("optionally disables the InputList's button", () => {
90
+ const { rerender } = renderPairedMenus();
91
+ expect(screen.getByRole("button", { name: "Add" })).toBeEnabled();
92
+
93
+ rerender(
94
+ <PairedMenus
95
+ inputListSetting={inputListSetting}
96
+ dropdownSetting={dropdownSetting}
97
+ disabled={false}
98
+ readOnly={true}
99
+ />
100
+ );
101
+ expect(screen.getByRole("button", { name: "Add" })).toBeDisabled();
102
+ });
103
+
104
+ it("reflects the default InputList values and dropdown value", () => {
105
+ const { container } = renderPairedMenus();
106
+ expect(listItemValues(container)).toEqual(["A", "B", "C"]);
107
+ expect(dropdown(container)).toHaveValue("b");
108
+ });
109
+
110
+ it("takes an optional item prop", () => {
111
+ const item: LibraryData = {
112
+ name: "Library",
113
+ short_name: "lib",
114
+ uuid: "123",
115
+ settings: {
116
+ input: ["c", "d", "e"],
117
+ dropdown: "d",
118
+ },
119
+ };
120
+ const { container } = renderPairedMenus({ item });
121
+
122
+ const listItems = within(
123
+ container.querySelector<HTMLElement>(".input-list-ul")!
124
+ ).getAllByRole("textbox");
125
+ expect(listItems.map((i) => (i as HTMLInputElement).value)).toEqual([
126
+ "C",
127
+ "D",
128
+ "E",
129
+ ]);
130
+ expect(listItems.map((i) => (i as HTMLInputElement).name)).toEqual([
131
+ "input_c",
132
+ "input_d",
133
+ "input_e",
134
+ ]);
135
+
136
+ const select = dropdown(container);
137
+ expect(select).toHaveValue("d");
138
+ expect(
139
+ within(select)
140
+ .getAllByRole("option")
141
+ .map((o) => o.textContent)
142
+ ).toEqual(["C", "D", "E"]);
143
+ });
144
+
145
+ it("updates the InputList when an item is removed", async () => {
146
+ const user = userEvent.setup();
147
+ const { container } = renderPairedMenus();
148
+ expect(listItemValues(container)).toEqual(["A", "B", "C"]);
149
+
150
+ // Remove the first list item ("A").
151
+ await user.click(screen.getAllByRole("button", { name: /Delete/ })[0]);
152
+
153
+ await waitFor(() =>
154
+ expect(
155
+ within(
156
+ container.querySelector<HTMLElement>(".input-list-ul")!
157
+ ).getAllByRole("textbox")
158
+ ).toHaveLength(2)
159
+ );
160
+ expect(listItemValues(container)).toEqual(["B", "C"]);
161
+ });
162
+
163
+ it("recalculates the dropdown options based on the InputList values", async () => {
164
+ const user = userEvent.setup();
165
+ const { container } = renderPairedMenus();
166
+ expect(
167
+ within(dropdown(container))
168
+ .getAllByRole("option")
169
+ .map((o) => o.textContent)
170
+ ).toEqual(["A", "B", "C"]);
171
+
172
+ // Removing "A" leaves the InputList holding [b, c], so the dropdown may only
173
+ // offer B and C.
174
+ await user.click(screen.getAllByRole("button", { name: /Delete/ })[0]);
175
+
176
+ await waitFor(() =>
177
+ expect(
178
+ within(dropdown(container))
179
+ .getAllByRole("option")
180
+ .map((o) => o.textContent)
181
+ ).toEqual(["B", "C"])
182
+ );
183
+ });
184
+
185
+ it("updates the dropdown selection", async () => {
186
+ const user = userEvent.setup();
187
+ const { container } = renderPairedMenus();
188
+ const select = dropdown(container);
189
+ expect(select).toHaveValue("b");
190
+
191
+ await user.selectOptions(select, "c");
192
+ expect(select).toHaveValue("c");
193
+ });
194
+
195
+ it("displays a message when the dropdown has no available values", async () => {
196
+ const user = userEvent.setup();
197
+ const { container } = renderPairedMenus();
198
+
199
+ // Remove every item from the InputList.
200
+ for (let remaining = 3; remaining > 0; remaining--) {
201
+ const removeButtons = screen.getAllByRole("button", { name: /Delete/ });
202
+ expect(removeButtons).toHaveLength(remaining);
203
+ await user.click(removeButtons[0]);
204
+ await waitFor(() =>
205
+ expect(
206
+ screen.queryAllByRole("button", { name: /Delete/ })
207
+ ).toHaveLength(remaining - 1)
208
+ );
209
+ }
210
+
211
+ const message =
212
+ "In order to set this value, you must add at least one option from the menu above.";
213
+ expect(screen.getByText(message)).toBeInTheDocument();
214
+ expect(container.querySelector(".bg-warning")).toHaveTextContent(message);
215
+
216
+ // The dropdown select is replaced by the warning; only the add menu remains.
217
+ expect(dropdown(container)).not.toBeInTheDocument();
218
+ expect(container.querySelectorAll("select")).toHaveLength(1);
219
+ });
220
+ });