@simplysm/solid 13.0.70 → 13.0.71
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.
- package/README.md +1 -1
- package/dist/components/disclosure/Dropdown.d.ts +6 -4
- package/dist/components/disclosure/Dropdown.d.ts.map +1 -1
- package/dist/components/disclosure/Dropdown.js +24 -8
- package/dist/components/disclosure/Dropdown.js.map +2 -2
- package/dist/components/disclosure/dialogZIndex.d.ts +2 -0
- package/dist/components/disclosure/dialogZIndex.d.ts.map +1 -1
- package/dist/components/disclosure/dialogZIndex.js +4 -0
- package/dist/components/disclosure/dialogZIndex.js.map +1 -1
- package/dist/components/features/crud-detail/CrudDetail.d.ts.map +1 -1
- package/dist/components/features/crud-detail/CrudDetail.js +16 -7
- package/dist/components/features/crud-detail/CrudDetail.js.map +2 -2
- package/dist/components/features/crud-sheet/CrudSheet.d.ts.map +1 -1
- package/dist/components/features/crud-sheet/CrudSheet.js +14 -5
- package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
- package/dist/components/features/crudRegistry.d.ts +16 -0
- package/dist/components/features/crudRegistry.d.ts.map +1 -0
- package/dist/components/features/crudRegistry.js +37 -0
- package/dist/components/features/crudRegistry.js.map +6 -0
- package/dist/components/features/permission-table/PermissionTable.d.ts.map +1 -1
- package/dist/components/features/permission-table/PermissionTable.js +71 -86
- package/dist/components/features/permission-table/PermissionTable.js.map +2 -2
- package/dist/components/features/shared-data/SharedDataSelect.js +2 -4
- package/dist/components/features/shared-data/SharedDataSelect.js.map +2 -2
- package/dist/components/features/shared-data/SharedDataSelectList.d.ts +2 -4
- package/dist/components/features/shared-data/SharedDataSelectList.d.ts.map +1 -1
- package/dist/components/features/shared-data/SharedDataSelectList.js +11 -46
- package/dist/components/features/shared-data/SharedDataSelectList.js.map +2 -2
- package/dist/components/form-control/select/Select.d.ts.map +1 -1
- package/dist/components/form-control/select/Select.js +1 -1
- package/dist/components/form-control/select/Select.js.map +1 -1
- package/dist/helpers/createAppStructure.d.ts.map +1 -1
- package/dist/helpers/createAppStructure.js +3 -2
- package/dist/helpers/createAppStructure.js.map +1 -1
- package/dist/helpers/createHmrSafeContext.d.ts +3 -0
- package/dist/helpers/createHmrSafeContext.d.ts.map +1 -0
- package/dist/helpers/createHmrSafeContext.js +10 -0
- package/dist/helpers/createHmrSafeContext.js.map +6 -0
- package/dist/hooks/createSelectionGroup.d.ts.map +1 -1
- package/dist/hooks/createSelectionGroup.js +3 -2
- package/dist/hooks/createSelectionGroup.js.map +2 -2
- package/package.json +6 -5
- package/src/components/disclosure/Dropdown.tsx +31 -17
- package/src/components/disclosure/dialogZIndex.ts +5 -0
- package/src/components/features/crud-detail/CrudDetail.tsx +16 -5
- package/src/components/features/crud-sheet/CrudSheet.tsx +13 -3
- package/src/components/features/crudRegistry.ts +60 -0
- package/src/components/features/permission-table/PermissionTable.tsx +49 -46
- package/src/components/features/shared-data/SharedDataSelect.tsx +2 -2
- package/src/components/features/shared-data/SharedDataSelectList.tsx +11 -36
- package/src/components/form-control/select/Select.tsx +1 -5
- package/src/helpers/createAppStructure.ts +3 -2
- package/src/helpers/createHmrSafeContext.ts +8 -0
- package/src/hooks/createSelectionGroup.tsx +4 -2
- package/tests/components/data/List.spec.tsx +52 -52
- package/tests/components/data/Pagination.spec.tsx +43 -43
- package/tests/components/data/Table.spec.tsx +4 -4
- package/tests/components/data/kanban/Kanban.selection.spec.tsx +21 -21
- package/tests/components/data/sheet/DataSheet.spec.tsx +50 -50
- package/tests/components/disclosure/Collapse.spec.tsx +24 -24
- package/tests/components/disclosure/Dialog.spec.tsx +33 -33
- package/tests/components/disclosure/DialogProvider.spec.tsx +9 -9
- package/tests/components/disclosure/Dropdown.spec.tsx +134 -14
- package/tests/components/disclosure/Tabs.spec.tsx +21 -21
- package/tests/components/disclosure/dialogZIndex.spec.ts +45 -0
- package/tests/components/display/Alert.spec.tsx +4 -4
- package/tests/components/display/Barcode.spec.tsx +7 -7
- package/tests/components/display/Card.spec.tsx +3 -3
- package/tests/components/display/Link.spec.tsx +5 -5
- package/tests/components/display/Tag.spec.tsx +4 -4
- package/tests/components/features/address/AddressSearch.spec.tsx +3 -3
- package/tests/components/features/crudRegistry.spec.ts +119 -0
- package/tests/components/features/data-select-button/DataSelectButton.spec.tsx +8 -8
- package/tests/components/features/permission-table/PermissionTable.spec.tsx +43 -43
- package/tests/components/features/shared-data/SharedDataSelectList.spec.tsx +2 -17
- package/tests/components/feedback/busy/BusyContainer.spec.tsx +7 -7
- package/tests/components/feedback/notification/NotificationBell.spec.tsx +9 -9
- package/tests/components/feedback/print/Print.spec.tsx +4 -4
- package/tests/components/form-control/Button.spec.tsx +18 -18
- package/tests/components/form-control/checkbox/Checkbox.spec.tsx +20 -20
- package/tests/components/form-control/checkbox/CheckboxGroup.spec.tsx +12 -12
- package/tests/components/form-control/checkbox/Radio.spec.tsx +21 -21
- package/tests/components/form-control/checkbox/RadioGroup.spec.tsx +12 -12
- package/tests/components/form-control/color-picker/ColorPicker.spec.tsx +10 -10
- package/tests/components/form-control/combobox/Combobox.spec.tsx +16 -16
- package/tests/components/form-control/combobox/ComboboxItem.spec.tsx +7 -7
- package/tests/components/form-control/date-range-picker/DateRangePicker.spec.tsx +24 -24
- package/tests/components/form-control/field/DatePicker.spec.tsx +50 -50
- package/tests/components/form-control/field/DateTimePicker.spec.tsx +47 -47
- package/tests/components/form-control/field/NumberInput.spec.tsx +54 -54
- package/tests/components/form-control/field/TextInput.spec.tsx +49 -49
- package/tests/components/form-control/field/Textarea.spec.tsx +33 -33
- package/tests/components/form-control/field/TimePicker.spec.tsx +42 -42
- package/tests/components/form-control/numpad/Numpad.spec.tsx +40 -40
- package/tests/components/form-control/select/Select.spec.tsx +9 -9
- package/tests/components/form-control/select/SelectItem.spec.tsx +10 -10
- package/tests/helpers/createAppStructure.spec.tsx +57 -57
- package/tests/helpers/mergeStyles.spec.ts +31 -31
|
@@ -3,21 +3,21 @@ import { describe, it, expect, vi } from "vitest";
|
|
|
3
3
|
import { createSignal } from "solid-js";
|
|
4
4
|
import { Pagination } from "../../../src/components/data/Pagination";
|
|
5
5
|
|
|
6
|
-
describe("Pagination
|
|
6
|
+
describe("Pagination component", () => {
|
|
7
7
|
describe("basic rendering", () => {
|
|
8
|
-
it("nav
|
|
8
|
+
it("renders as nav element", () => {
|
|
9
9
|
const { container } = render(() => <Pagination page={1} totalPageCount={5} />);
|
|
10
10
|
|
|
11
11
|
expect(container.querySelector("nav")).toBeTruthy();
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
it("data-pagination
|
|
14
|
+
it("applies data-pagination attribute", () => {
|
|
15
15
|
const { container } = render(() => <Pagination page={1} totalPageCount={5} />);
|
|
16
16
|
|
|
17
17
|
expect(container.querySelector("[data-pagination]")).toBeTruthy();
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
it("4
|
|
20
|
+
it("renders 4 navigation buttons and page buttons", () => {
|
|
21
21
|
const { container } = render(() => <Pagination page={1} totalPageCount={5} />);
|
|
22
22
|
|
|
23
23
|
const buttons = container.querySelectorAll("[data-button]");
|
|
@@ -25,7 +25,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
25
25
|
expect(buttons.length).toBe(9);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
it("
|
|
28
|
+
it("displays page numbers as 1-based", () => {
|
|
29
29
|
const { getByText } = render(() => <Pagination page={1} totalPageCount={3} />);
|
|
30
30
|
|
|
31
31
|
expect(getByText("1")).toBeTruthy();
|
|
@@ -33,7 +33,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
33
33
|
expect(getByText("3")).toBeTruthy();
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
it("custom class
|
|
36
|
+
it("passes custom class to nav element", () => {
|
|
37
37
|
const { container } = render(() => (
|
|
38
38
|
// eslint-disable-next-line tailwindcss/no-custom-classname
|
|
39
39
|
<Pagination page={1} totalPageCount={5} class="my-custom" />
|
|
@@ -44,8 +44,8 @@ describe("Pagination 컴포넌트", () => {
|
|
|
44
44
|
});
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
describe("displayPageCount
|
|
48
|
-
it("displayPageCount
|
|
47
|
+
describe("displayPageCount default", () => {
|
|
48
|
+
it("default displayPageCount is 10", () => {
|
|
49
49
|
const { container } = render(() => <Pagination page={1} totalPageCount={25} />);
|
|
50
50
|
|
|
51
51
|
const buttons = container.querySelectorAll("[data-button]");
|
|
@@ -53,7 +53,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
53
53
|
expect(buttons.length).toBe(14);
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
it("
|
|
56
|
+
it("displays 5 page buttons when displayPageCount=5", () => {
|
|
57
57
|
const { container } = render(() => (
|
|
58
58
|
<Pagination page={1} totalPageCount={25} displayPageCount={5} />
|
|
59
59
|
));
|
|
@@ -64,12 +64,12 @@ describe("Pagination 컴포넌트", () => {
|
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
describe("
|
|
68
|
-
it("
|
|
67
|
+
describe("current page display", () => {
|
|
68
|
+
it("current page button has a different variant", () => {
|
|
69
69
|
const { container } = render(() => <Pagination page={3} totalPageCount={5} />);
|
|
70
70
|
|
|
71
71
|
const buttons = container.querySelectorAll("[data-button]");
|
|
72
|
-
// buttons[0]=<<, [1]=<, [2]=page1, [3]=page2, [4]=page3(
|
|
72
|
+
// buttons[0]=<<, [1]=<, [2]=page1, [3]=page2, [4]=page3(current), [5]=page4, [6]=page5, [7]=>, [8]=>>
|
|
73
73
|
const currentButton = buttons[4]; // page 3 (1-based page=3)
|
|
74
74
|
const otherButton = buttons[2]; // page 1
|
|
75
75
|
|
|
@@ -77,8 +77,8 @@ describe("Pagination 컴포넌트", () => {
|
|
|
77
77
|
});
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
-
describe("
|
|
81
|
-
it("page=1, displayPageCount=5
|
|
80
|
+
describe("page group calculation", () => {
|
|
81
|
+
it("displays pages 1-5 when page=1, displayPageCount=5", () => {
|
|
82
82
|
const { getByText, queryByText } = render(() => (
|
|
83
83
|
<Pagination page={1} totalPageCount={20} displayPageCount={5} />
|
|
84
84
|
));
|
|
@@ -91,7 +91,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
91
91
|
expect(queryByText("6")).toBeFalsy();
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
it("page=6, displayPageCount=5
|
|
94
|
+
it("displays pages 6-10 when page=6, displayPageCount=5", () => {
|
|
95
95
|
const { getByText, queryByText } = render(() => (
|
|
96
96
|
<Pagination page={6} totalPageCount={20} displayPageCount={5} />
|
|
97
97
|
));
|
|
@@ -105,7 +105,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
105
105
|
expect(queryByText("11")).toBeFalsy();
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
it("page=13, displayPageCount=10
|
|
108
|
+
it("displays pages 11-20 when page=13, displayPageCount=10", () => {
|
|
109
109
|
const { getByText, queryByText } = render(() => (
|
|
110
110
|
<Pagination page={13} totalPageCount={25} displayPageCount={10} />
|
|
111
111
|
));
|
|
@@ -116,7 +116,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
116
116
|
expect(queryByText("21")).toBeFalsy();
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
-
it("
|
|
119
|
+
it("displays only remaining pages in the last group below totalPageCount", () => {
|
|
120
120
|
const { container, getByText, queryByText } = render(() => (
|
|
121
121
|
<Pagination page={8} totalPageCount={8} displayPageCount={5} />
|
|
122
122
|
));
|
|
@@ -133,8 +133,8 @@ describe("Pagination 컴포넌트", () => {
|
|
|
133
133
|
});
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
describe("
|
|
137
|
-
it("
|
|
136
|
+
describe("navigation button disabled state", () => {
|
|
137
|
+
it("disables first/prev buttons in the first group", () => {
|
|
138
138
|
const { container } = render(() => (
|
|
139
139
|
<Pagination page={1} totalPageCount={20} displayPageCount={5} />
|
|
140
140
|
));
|
|
@@ -147,7 +147,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
147
147
|
expect(prevBtn.hasAttribute("disabled")).toBe(true);
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
it("
|
|
150
|
+
it("disables next/last buttons in the last group", () => {
|
|
151
151
|
const { container } = render(() => (
|
|
152
152
|
<Pagination page={19} totalPageCount={20} displayPageCount={5} />
|
|
153
153
|
));
|
|
@@ -160,7 +160,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
160
160
|
expect(lastBtn.hasAttribute("disabled")).toBe(true);
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
it("
|
|
163
|
+
it("all navigation buttons are enabled in a middle group", () => {
|
|
164
164
|
const { container } = render(() => (
|
|
165
165
|
<Pagination page={6} totalPageCount={20} displayPageCount={5} />
|
|
166
166
|
));
|
|
@@ -178,8 +178,8 @@ describe("Pagination 컴포넌트", () => {
|
|
|
178
178
|
});
|
|
179
179
|
});
|
|
180
180
|
|
|
181
|
-
describe("
|
|
182
|
-
it("
|
|
181
|
+
describe("page click", () => {
|
|
182
|
+
it("calls onPageChange when a page button is clicked", () => {
|
|
183
183
|
const onPageChange = vi.fn();
|
|
184
184
|
const { getByText } = render(() => (
|
|
185
185
|
<Pagination page={1} totalPageCount={5} onPageChange={onPageChange} />
|
|
@@ -190,7 +190,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
190
190
|
expect(onPageChange).toHaveBeenCalledWith(3); // 1-based
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
-
it("
|
|
193
|
+
it("calls onPageChange even when clicking the current page", () => {
|
|
194
194
|
const onPageChange = vi.fn();
|
|
195
195
|
const { getByText } = render(() => (
|
|
196
196
|
<Pagination page={1} totalPageCount={5} onPageChange={onPageChange} />
|
|
@@ -202,8 +202,8 @@ describe("Pagination 컴포넌트", () => {
|
|
|
202
202
|
});
|
|
203
203
|
});
|
|
204
204
|
|
|
205
|
-
describe("first
|
|
206
|
-
it("
|
|
205
|
+
describe("first button (<<)", () => {
|
|
206
|
+
it("navigates to page 1 when first button is clicked", () => {
|
|
207
207
|
const onPageChange = vi.fn();
|
|
208
208
|
const { container } = render(() => (
|
|
209
209
|
<Pagination
|
|
@@ -221,8 +221,8 @@ describe("Pagination 컴포넌트", () => {
|
|
|
221
221
|
});
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
-
describe("last
|
|
225
|
-
it("last
|
|
224
|
+
describe("last button (>>)", () => {
|
|
225
|
+
it("navigates to last page when last button is clicked", () => {
|
|
226
226
|
const onPageChange = vi.fn();
|
|
227
227
|
const { container } = render(() => (
|
|
228
228
|
<Pagination page={1} totalPageCount={20} displayPageCount={5} onPageChange={onPageChange} />
|
|
@@ -235,8 +235,8 @@ describe("Pagination 컴포넌트", () => {
|
|
|
235
235
|
});
|
|
236
236
|
});
|
|
237
237
|
|
|
238
|
-
describe("prev
|
|
239
|
-
it("
|
|
238
|
+
describe("prev group button (<)", () => {
|
|
239
|
+
it("navigates to last page of previous group when prev button is clicked", () => {
|
|
240
240
|
const onPageChange = vi.fn();
|
|
241
241
|
const { container } = render(() => (
|
|
242
242
|
<Pagination page={8} totalPageCount={20} displayPageCount={5} onPageChange={onPageChange} />
|
|
@@ -251,8 +251,8 @@ describe("Pagination 컴포넌트", () => {
|
|
|
251
251
|
});
|
|
252
252
|
});
|
|
253
253
|
|
|
254
|
-
describe("next
|
|
255
|
-
it("
|
|
254
|
+
describe("next group button (>)", () => {
|
|
255
|
+
it("navigates to first page of next group when next button is clicked", () => {
|
|
256
256
|
const onPageChange = vi.fn();
|
|
257
257
|
const { container } = render(() => (
|
|
258
258
|
<Pagination page={4} totalPageCount={20} displayPageCount={5} onPageChange={onPageChange} />
|
|
@@ -268,7 +268,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
268
268
|
});
|
|
269
269
|
|
|
270
270
|
describe("size prop", () => {
|
|
271
|
-
it("size prop
|
|
271
|
+
it("applies different styles when size prop is provided", () => {
|
|
272
272
|
const { container: defaultContainer } = render(() => (
|
|
273
273
|
<Pagination page={1} totalPageCount={5} />
|
|
274
274
|
));
|
|
@@ -283,34 +283,34 @@ describe("Pagination 컴포넌트", () => {
|
|
|
283
283
|
const smNav = smContainer.querySelector("nav");
|
|
284
284
|
const lgNav = lgContainer.querySelector("nav");
|
|
285
285
|
|
|
286
|
-
// sm
|
|
286
|
+
// sm and lg should have different classes from default
|
|
287
287
|
expect(smNav?.className).not.toBe(defaultNav?.className);
|
|
288
288
|
expect(lgNav?.className).not.toBe(defaultNav?.className);
|
|
289
289
|
});
|
|
290
290
|
});
|
|
291
291
|
|
|
292
|
-
describe("
|
|
293
|
-
it("
|
|
292
|
+
describe("reactive behavior", () => {
|
|
293
|
+
it("updates UI when page signal changes", () => {
|
|
294
294
|
const [page, setPage] = createSignal(1);
|
|
295
295
|
|
|
296
296
|
const { container, getByText } = render(() => (
|
|
297
297
|
<Pagination page={page()} totalPageCount={20} displayPageCount={5} onPageChange={setPage} />
|
|
298
298
|
));
|
|
299
299
|
|
|
300
|
-
//
|
|
300
|
+
// initial state: pages 1-5 are displayed
|
|
301
301
|
expect(getByText("1")).toBeTruthy();
|
|
302
302
|
|
|
303
|
-
// next
|
|
303
|
+
// navigate to next group (> click → page 6)
|
|
304
304
|
const buttons = container.querySelectorAll("[data-button]");
|
|
305
|
-
fireEvent.click(buttons[buttons.length - 2]); // >
|
|
305
|
+
fireEvent.click(buttons[buttons.length - 2]); // > click → page 6
|
|
306
306
|
|
|
307
|
-
//
|
|
307
|
+
// after page change, group 6-10 should be displayed
|
|
308
308
|
expect(getByText("6")).toBeTruthy();
|
|
309
309
|
});
|
|
310
310
|
});
|
|
311
311
|
|
|
312
|
-
describe("
|
|
313
|
-
it("
|
|
312
|
+
describe("edge cases", () => {
|
|
313
|
+
it("all navigation buttons are disabled when totalPageCount=1", () => {
|
|
314
314
|
const { container } = render(() => <Pagination page={1} totalPageCount={1} />);
|
|
315
315
|
|
|
316
316
|
const buttons = container.querySelectorAll("[data-button]");
|
|
@@ -325,7 +325,7 @@ describe("Pagination 컴포넌트", () => {
|
|
|
325
325
|
expect(lastBtn.hasAttribute("disabled")).toBe(true);
|
|
326
326
|
});
|
|
327
327
|
|
|
328
|
-
it("
|
|
328
|
+
it("no page buttons displayed when totalPageCount=0", () => {
|
|
329
329
|
const { container } = render(() => <Pagination page={1} totalPageCount={0} />);
|
|
330
330
|
|
|
331
331
|
const buttons = container.querySelectorAll("[data-button]");
|
|
@@ -2,9 +2,9 @@ import { render } from "@solidjs/testing-library";
|
|
|
2
2
|
import { describe, it, expect } from "vitest";
|
|
3
3
|
import { Table } from "../../../src/components/data/Table";
|
|
4
4
|
|
|
5
|
-
describe("Table
|
|
5
|
+
describe("Table component", () => {
|
|
6
6
|
describe("basic rendering", () => {
|
|
7
|
-
it("children
|
|
7
|
+
it("renders children inside Table", () => {
|
|
8
8
|
const { container } = render(() => (
|
|
9
9
|
<Table>
|
|
10
10
|
<tbody>
|
|
@@ -24,8 +24,8 @@ describe("Table 컴포넌트", () => {
|
|
|
24
24
|
});
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
describe("inset
|
|
28
|
-
it("
|
|
27
|
+
describe("inset prop", () => {
|
|
28
|
+
it("applies different styles based on inset prop", () => {
|
|
29
29
|
const { container: defaultContainer } = render(() => <Table>Content</Table>);
|
|
30
30
|
const { container: insetContainer } = render(() => <Table inset>Content</Table>);
|
|
31
31
|
|
|
@@ -2,7 +2,7 @@ import { render, fireEvent } from "@solidjs/testing-library";
|
|
|
2
2
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
3
3
|
import { Kanban } from "../../../../src/components/data/kanban/Kanban";
|
|
4
4
|
|
|
5
|
-
describe("Kanban
|
|
5
|
+
describe("Kanban selection system", () => {
|
|
6
6
|
function renderKanban(options?: {
|
|
7
7
|
selectedValues?: unknown[];
|
|
8
8
|
onSelectedValuesChange?: (v: unknown[]) => void;
|
|
@@ -36,8 +36,8 @@ describe("Kanban 선택 시스템", () => {
|
|
|
36
36
|
));
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
describe("Shift+Click
|
|
40
|
-
it("Shift+Click
|
|
39
|
+
describe("Shift+Click selection", () => {
|
|
40
|
+
it("selects a card with Shift+Click", () => {
|
|
41
41
|
const handleChange = vi.fn();
|
|
42
42
|
const { getByText } = renderKanban({
|
|
43
43
|
selectedValues: [],
|
|
@@ -48,7 +48,7 @@ describe("Kanban 선택 시스템", () => {
|
|
|
48
48
|
expect(handleChange).toHaveBeenCalledWith([1]);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
it("
|
|
51
|
+
it("deselects an already selected card with Shift+Click", () => {
|
|
52
52
|
const handleChange = vi.fn();
|
|
53
53
|
const { getByText } = renderKanban({
|
|
54
54
|
selectedValues: [1],
|
|
@@ -59,7 +59,7 @@ describe("Kanban 선택 시스템", () => {
|
|
|
59
59
|
expect(handleChange).toHaveBeenCalledWith([]);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
it("Shift
|
|
62
|
+
it("click without Shift does not change selection", () => {
|
|
63
63
|
const handleChange = vi.fn();
|
|
64
64
|
const { getByText } = renderKanban({
|
|
65
65
|
selectedValues: [],
|
|
@@ -70,7 +70,7 @@ describe("Kanban 선택 시스템", () => {
|
|
|
70
70
|
expect(handleChange).not.toHaveBeenCalled();
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
it("selectable=false
|
|
73
|
+
it("does not select card with selectable=false on Shift+Click", () => {
|
|
74
74
|
const handleChange = vi.fn();
|
|
75
75
|
const { getByText } = renderKanban({
|
|
76
76
|
selectedValues: [],
|
|
@@ -84,8 +84,8 @@ describe("Kanban 선택 시스템", () => {
|
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
describe("
|
|
88
|
-
it("
|
|
87
|
+
describe("selection visual feedback", () => {
|
|
88
|
+
it("applies ring + shadow classes to selected card", () => {
|
|
89
89
|
const { getByText } = renderKanban({ selectedValues: [1] });
|
|
90
90
|
const card1Content = getByText("Card 1").closest("[data-card]") as HTMLElement;
|
|
91
91
|
expect(card1Content.classList.contains("ring-2")).toBe(true);
|
|
@@ -93,21 +93,21 @@ describe("Kanban 선택 시스템", () => {
|
|
|
93
93
|
expect(card1Content.classList.contains("shadow-md")).toBe(true);
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
it("
|
|
96
|
+
it("unselected card has no ring class", () => {
|
|
97
97
|
const { getByText } = renderKanban({ selectedValues: [1] });
|
|
98
98
|
const card2Content = getByText("Card 2").closest("[data-card]") as HTMLElement;
|
|
99
99
|
expect(card2Content.classList.contains("ring-2")).toBe(false);
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
describe("
|
|
104
|
-
it("
|
|
103
|
+
describe("per-lane select-all checkbox", () => {
|
|
104
|
+
it("displays select-all checkbox in lanes that have selectable cards", () => {
|
|
105
105
|
const { container } = renderKanban({ selectedValues: [] });
|
|
106
106
|
const checkboxes = container.querySelectorAll("[role='checkbox']");
|
|
107
107
|
expect(checkboxes.length).toBe(2);
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
it("
|
|
110
|
+
it("selects all selectable cards in lane when select-all checkbox is clicked", () => {
|
|
111
111
|
const handleChange = vi.fn();
|
|
112
112
|
const { container } = renderKanban({
|
|
113
113
|
selectedValues: [],
|
|
@@ -119,7 +119,7 @@ describe("Kanban 선택 시스템", () => {
|
|
|
119
119
|
expect(handleChange).toHaveBeenCalledWith([1, 2]);
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
it("
|
|
122
|
+
it("deselects only cards in the lane when clicking checkbox in fully-selected state", () => {
|
|
123
123
|
const handleChange = vi.fn();
|
|
124
124
|
const { container } = renderKanban({
|
|
125
125
|
selectedValues: [1, 2, 4],
|
|
@@ -131,14 +131,14 @@ describe("Kanban 선택 시스템", () => {
|
|
|
131
131
|
expect(handleChange).toHaveBeenCalledWith([4]);
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
-
it("
|
|
134
|
+
it("checkbox is checked when all selectable cards are selected", () => {
|
|
135
135
|
const { container } = renderKanban({ selectedValues: [1, 2] });
|
|
136
136
|
const firstLane = container.querySelector("[data-kanban-lane]") as HTMLElement;
|
|
137
137
|
const checkbox = firstLane.querySelector("[role='checkbox']") as HTMLElement;
|
|
138
138
|
expect(checkbox.getAttribute("aria-checked")).toBe("true");
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
-
it("
|
|
141
|
+
it("checkbox is unchecked when only some cards are selected", () => {
|
|
142
142
|
const { container } = renderKanban({ selectedValues: [1] });
|
|
143
143
|
const firstLane = container.querySelector("[data-kanban-lane]") as HTMLElement;
|
|
144
144
|
const checkbox = firstLane.querySelector("[role='checkbox']") as HTMLElement;
|
|
@@ -146,7 +146,7 @@ describe("Kanban 선택 시스템", () => {
|
|
|
146
146
|
});
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
-
describe("
|
|
149
|
+
describe("long press exclusive selection", () => {
|
|
150
150
|
beforeEach(() => {
|
|
151
151
|
vi.useFakeTimers();
|
|
152
152
|
});
|
|
@@ -155,7 +155,7 @@ describe("Kanban 선택 시스템", () => {
|
|
|
155
155
|
vi.useRealTimers();
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
-
it("
|
|
158
|
+
it("exclusively selects that card when pressed for 500ms or more", () => {
|
|
159
159
|
const handleChange = vi.fn();
|
|
160
160
|
const { getByText } = renderKanban({
|
|
161
161
|
selectedValues: [2, 4],
|
|
@@ -169,7 +169,7 @@ describe("Kanban 선택 시스템", () => {
|
|
|
169
169
|
expect(handleChange).toHaveBeenCalledWith([1]);
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
-
it("
|
|
172
|
+
it("does not change selection when pressed for less than 500ms", () => {
|
|
173
173
|
const handleChange = vi.fn();
|
|
174
174
|
const { getByText } = renderKanban({
|
|
175
175
|
selectedValues: [],
|
|
@@ -184,7 +184,7 @@ describe("Kanban 선택 시스템", () => {
|
|
|
184
184
|
expect(handleChange).not.toHaveBeenCalled();
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
-
it("selectable=false
|
|
187
|
+
it("does not select card with selectable=false on long press", () => {
|
|
188
188
|
const handleChange = vi.fn();
|
|
189
189
|
const { getByText } = renderKanban({
|
|
190
190
|
selectedValues: [],
|
|
@@ -201,8 +201,8 @@ describe("Kanban 선택 시스템", () => {
|
|
|
201
201
|
});
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
-
describe("
|
|
205
|
-
it("
|
|
204
|
+
describe("uncontrolled mode", () => {
|
|
205
|
+
it("Shift+Click selection works without onSelectedValuesChange", () => {
|
|
206
206
|
const { getByText } = renderKanban();
|
|
207
207
|
const card1 = getByText("Card 1").closest("[data-kanban-card]") as HTMLElement;
|
|
208
208
|
fireEvent.click(card1, { shiftKey: true });
|