@simplysm/solid 13.0.70 → 13.0.72
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/data/sheet/DataSheet.d.ts.map +1 -1
- package/dist/components/data/sheet/DataSheet.js +3 -6
- package/dist/components/data/sheet/DataSheet.js.map +1 -1
- package/dist/components/data/sheet/DataSheet.styles.d.ts.map +1 -1
- package/dist/components/data/sheet/DataSheet.styles.js +1 -1
- package/dist/components/data/sheet/DataSheet.styles.js.map +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/data/sheet/DataSheet.styles.ts +1 -1
- package/src/components/data/sheet/DataSheet.tsx +3 -4
- 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
|
@@ -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 });
|
|
@@ -30,7 +30,7 @@ const testData: TestItem[] = [
|
|
|
30
30
|
];
|
|
31
31
|
|
|
32
32
|
describe("DataSheet", () => {
|
|
33
|
-
it("
|
|
33
|
+
it("basic rendering: column headers and data rows are displayed", () => {
|
|
34
34
|
const { container } = render(() => (
|
|
35
35
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
36
36
|
<TestWrapper>
|
|
@@ -59,7 +59,7 @@ describe("DataSheet", () => {
|
|
|
59
59
|
expect(rows.length).toBe(3);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
it("
|
|
62
|
+
it("multi-level header: colspan and rowspan are applied correctly", () => {
|
|
63
63
|
const { container } = render(() => (
|
|
64
64
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
65
65
|
<TestWrapper>
|
|
@@ -79,22 +79,22 @@ describe("DataSheet", () => {
|
|
|
79
79
|
));
|
|
80
80
|
|
|
81
81
|
const headerRows = container.querySelectorAll("thead tr");
|
|
82
|
-
// 2
|
|
82
|
+
// 2 rows: first row has "기본정보"(colspan=2) + "이메일"(rowspan=2), second row has "이름" + "나이"
|
|
83
83
|
expect(headerRows.length).toBeGreaterThanOrEqual(2);
|
|
84
84
|
|
|
85
85
|
const firstRowThs = headerRows[0].querySelectorAll("th");
|
|
86
|
-
// "기본정보" th
|
|
86
|
+
// "기본정보" th has colspan=2
|
|
87
87
|
const groupTh = Array.from(firstRowThs).find((th) => th.textContent.includes("기본정보"));
|
|
88
88
|
expect(groupTh).toBeTruthy();
|
|
89
89
|
expect(groupTh!.getAttribute("colspan")).toBe("2");
|
|
90
90
|
|
|
91
|
-
// "이메일" th
|
|
91
|
+
// "이메일" th has rowspan=2
|
|
92
92
|
const emailTh = Array.from(firstRowThs).find((th) => th.textContent.includes("이메일"));
|
|
93
93
|
expect(emailTh).toBeTruthy();
|
|
94
94
|
expect(emailTh!.getAttribute("rowspan")).toBe("2");
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
it("
|
|
97
|
+
it("summary row: summary column displays a summary row in thead", () => {
|
|
98
98
|
const { container } = render(() => (
|
|
99
99
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
100
100
|
<TestWrapper>
|
|
@@ -111,14 +111,14 @@ describe("DataSheet", () => {
|
|
|
111
111
|
));
|
|
112
112
|
|
|
113
113
|
const theadRows = container.querySelectorAll("thead tr");
|
|
114
|
-
//
|
|
114
|
+
// 1 header row + 1 summary row = 2 rows
|
|
115
115
|
expect(theadRows.length).toBe(2);
|
|
116
116
|
|
|
117
117
|
const summaryRow = theadRows[theadRows.length - 1];
|
|
118
118
|
expect(summaryRow.textContent).toContain("합계: 83");
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
-
it("
|
|
121
|
+
it("empty data: tbody is empty", () => {
|
|
122
122
|
const { container } = render(() => (
|
|
123
123
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
124
124
|
<TestWrapper>
|
|
@@ -134,12 +134,12 @@ describe("DataSheet", () => {
|
|
|
134
134
|
const rows = container.querySelectorAll("tbody tr");
|
|
135
135
|
expect(rows.length).toBe(0);
|
|
136
136
|
|
|
137
|
-
//
|
|
137
|
+
// header is still displayed
|
|
138
138
|
const ths = container.querySelectorAll("thead th");
|
|
139
139
|
expect(ths.length).toBe(1);
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
-
it("hidden
|
|
142
|
+
it("hidden columns are not rendered", () => {
|
|
143
143
|
const { container } = render(() => (
|
|
144
144
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
145
145
|
<TestWrapper>
|
|
@@ -160,7 +160,7 @@ describe("DataSheet", () => {
|
|
|
160
160
|
expect(ths[0].textContent).toContain("이름");
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
it("
|
|
163
|
+
it("sort: clicking header calls onSortsChange", () => {
|
|
164
164
|
let capturedSorts: SortingDef[] = [];
|
|
165
165
|
const { container } = render(() => (
|
|
166
166
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
@@ -184,13 +184,13 @@ describe("DataSheet", () => {
|
|
|
184
184
|
</I18nProvider></ConfigProvider>
|
|
185
185
|
));
|
|
186
186
|
|
|
187
|
-
// "이름"
|
|
187
|
+
// click "이름" header
|
|
188
188
|
const ths = container.querySelectorAll("thead th");
|
|
189
189
|
(ths[0] as HTMLElement).click();
|
|
190
190
|
expect(capturedSorts).toEqual([{ key: "name", desc: false }]);
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
-
it("
|
|
193
|
+
it("sort: sortable={false} column does not sort on click", () => {
|
|
194
194
|
let capturedSorts: SortingDef[] = [];
|
|
195
195
|
const { container } = render(() => (
|
|
196
196
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
@@ -216,7 +216,7 @@ describe("DataSheet", () => {
|
|
|
216
216
|
expect(capturedSorts).toEqual([]);
|
|
217
217
|
});
|
|
218
218
|
|
|
219
|
-
it("
|
|
219
|
+
it("auto sort: data is sorted when autoSort is true", () => {
|
|
220
220
|
const { container } = render(() => (
|
|
221
221
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
222
222
|
<TestWrapper>
|
|
@@ -239,7 +239,7 @@ describe("DataSheet", () => {
|
|
|
239
239
|
expect(names).toEqual(["김철수", "이영희", "홍길동"]);
|
|
240
240
|
});
|
|
241
241
|
|
|
242
|
-
it("
|
|
242
|
+
it("pagination: data is sliced by itemsPerPage", () => {
|
|
243
243
|
const { container } = render(() => (
|
|
244
244
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
245
245
|
<TestWrapper>
|
|
@@ -256,7 +256,7 @@ describe("DataSheet", () => {
|
|
|
256
256
|
expect(rows.length).toBe(2);
|
|
257
257
|
});
|
|
258
258
|
|
|
259
|
-
it("
|
|
259
|
+
it("pagination: Pagination is displayed when there are 2 or more pages", () => {
|
|
260
260
|
const { container } = render(() => (
|
|
261
261
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
262
262
|
<TestWrapper>
|
|
@@ -273,7 +273,7 @@ describe("DataSheet", () => {
|
|
|
273
273
|
expect(pagination).toBeTruthy();
|
|
274
274
|
});
|
|
275
275
|
|
|
276
|
-
it("
|
|
276
|
+
it("pagination: Pagination is not displayed when there is only 1 page", () => {
|
|
277
277
|
const { container } = render(() => (
|
|
278
278
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
279
279
|
<TestWrapper>
|
|
@@ -290,7 +290,7 @@ describe("DataSheet", () => {
|
|
|
290
290
|
expect(pagination).toBeFalsy();
|
|
291
291
|
});
|
|
292
292
|
|
|
293
|
-
it("
|
|
293
|
+
it("fixed column: sticky class is applied to td of fixed column", () => {
|
|
294
294
|
const { container } = render(() => (
|
|
295
295
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
296
296
|
<TestWrapper>
|
|
@@ -311,7 +311,7 @@ describe("DataSheet", () => {
|
|
|
311
311
|
expect(tds[1].classList.contains("sticky")).toBe(false);
|
|
312
312
|
});
|
|
313
313
|
|
|
314
|
-
it("
|
|
314
|
+
it("fixed column: border class is applied to the last fixed column", () => {
|
|
315
315
|
const { container } = render(() => (
|
|
316
316
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
317
317
|
<TestWrapper>
|
|
@@ -328,11 +328,11 @@ describe("DataSheet", () => {
|
|
|
328
328
|
));
|
|
329
329
|
|
|
330
330
|
const tds = container.querySelectorAll("tbody tr:first-child td");
|
|
331
|
-
//
|
|
331
|
+
// verify class included in fixedLastClass
|
|
332
332
|
expect(tds[0].classList.contains("border-r")).toBe(true);
|
|
333
333
|
});
|
|
334
334
|
|
|
335
|
-
it("
|
|
335
|
+
it("resizer: resizable column has a resizer handle", () => {
|
|
336
336
|
const { container } = render(() => (
|
|
337
337
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
338
338
|
<TestWrapper>
|
|
@@ -349,7 +349,7 @@ describe("DataSheet", () => {
|
|
|
349
349
|
));
|
|
350
350
|
|
|
351
351
|
const resizers = container.querySelectorAll(".cursor-ew-resize");
|
|
352
|
-
//
|
|
352
|
+
// only the first column should have a resizer
|
|
353
353
|
expect(resizers.length).toBe(1);
|
|
354
354
|
});
|
|
355
355
|
});
|
|
@@ -366,22 +366,22 @@ describe("applySorting", () => {
|
|
|
366
366
|
{ name: "나", age: 28 },
|
|
367
367
|
];
|
|
368
368
|
|
|
369
|
-
it("
|
|
369
|
+
it("preserves original order when sorts is empty", () => {
|
|
370
370
|
const result = applySorting(items, []);
|
|
371
371
|
expect(result.map((i) => i.name)).toEqual(["다", "가", "나"]);
|
|
372
372
|
});
|
|
373
373
|
|
|
374
|
-
it("
|
|
374
|
+
it("single ascending sort", () => {
|
|
375
375
|
const result = applySorting(items, [{ key: "name", desc: false }]);
|
|
376
376
|
expect(result.map((i) => i.name)).toEqual(["가", "나", "다"]);
|
|
377
377
|
});
|
|
378
378
|
|
|
379
|
-
it("
|
|
379
|
+
it("single descending sort", () => {
|
|
380
380
|
const result = applySorting(items, [{ key: "age", desc: true }]);
|
|
381
381
|
expect(result.map((i) => i.age)).toEqual([30, 28, 25]);
|
|
382
382
|
});
|
|
383
383
|
|
|
384
|
-
it("
|
|
384
|
+
it("multi-sort: first key takes priority, ties broken by second key", () => {
|
|
385
385
|
const data: Item[] = [
|
|
386
386
|
{ name: "가", age: 30 },
|
|
387
387
|
{ name: "나", age: 25 },
|
|
@@ -398,7 +398,7 @@ describe("applySorting", () => {
|
|
|
398
398
|
]);
|
|
399
399
|
});
|
|
400
400
|
|
|
401
|
-
it("
|
|
401
|
+
it("does not mutate the original array", () => {
|
|
402
402
|
const original = [...items];
|
|
403
403
|
applySorting(items, [{ key: "name", desc: false }]);
|
|
404
404
|
expect(items).toEqual(original);
|
|
@@ -421,21 +421,21 @@ describe("flattenTree", () => {
|
|
|
421
421
|
{ id: "b" },
|
|
422
422
|
];
|
|
423
423
|
|
|
424
|
-
it("getChildren
|
|
424
|
+
it("returns flat list when getChildren is not provided", () => {
|
|
425
425
|
const result = flattenTree(tree, []);
|
|
426
426
|
expect(result.map((r) => r.item.id)).toEqual(["a", "b"]);
|
|
427
427
|
expect(result.every((r) => r.depth === 0)).toBe(true);
|
|
428
428
|
expect(result.every((r) => !r.hasChildren)).toBe(true);
|
|
429
429
|
});
|
|
430
430
|
|
|
431
|
-
it("
|
|
431
|
+
it("returns only roots when all are collapsed", () => {
|
|
432
432
|
const result = flattenTree(tree, [], getChildren);
|
|
433
433
|
expect(result.map((r) => r.item.id)).toEqual(["a", "b"]);
|
|
434
434
|
expect(result[0].hasChildren).toBe(true);
|
|
435
435
|
expect(result[1].hasChildren).toBe(false);
|
|
436
436
|
});
|
|
437
437
|
|
|
438
|
-
it("
|
|
438
|
+
it("includes 1-level children when root is expanded", () => {
|
|
439
439
|
const result = flattenTree(tree, [tree[0]], getChildren);
|
|
440
440
|
expect(result.map((r) => r.item.id)).toEqual(["a", "a1", "a2", "b"]);
|
|
441
441
|
expect(result[1].depth).toBe(1);
|
|
@@ -443,7 +443,7 @@ describe("flattenTree", () => {
|
|
|
443
443
|
expect(result[2].hasChildren).toBe(true);
|
|
444
444
|
});
|
|
445
445
|
|
|
446
|
-
it("
|
|
446
|
+
it("includes up to 2-level children when nested expanded", () => {
|
|
447
447
|
const a2 = tree[0].children![1];
|
|
448
448
|
const result = flattenTree(tree, [tree[0], a2], getChildren);
|
|
449
449
|
expect(result.map((r) => r.item.id)).toEqual(["a", "a1", "a2", "a2x", "b"]);
|
|
@@ -451,24 +451,24 @@ describe("flattenTree", () => {
|
|
|
451
451
|
expect(result[3].parent).toBe(a2);
|
|
452
452
|
});
|
|
453
453
|
|
|
454
|
-
it("
|
|
455
|
-
// a2
|
|
454
|
+
it("children of collapsed nodes are not included", () => {
|
|
455
|
+
// only a2 is expanded but a is collapsed → a2 itself is not visible, so its children are also hidden
|
|
456
456
|
const a2 = tree[0].children![1];
|
|
457
457
|
const result = flattenTree(tree, [a2], getChildren);
|
|
458
458
|
expect(result.map((r) => r.item.id)).toEqual(["a", "b"]);
|
|
459
459
|
});
|
|
460
460
|
|
|
461
|
-
it("
|
|
461
|
+
it("returns empty result for empty array", () => {
|
|
462
462
|
const result = flattenTree([], [], getChildren);
|
|
463
463
|
expect(result).toEqual([]);
|
|
464
464
|
});
|
|
465
465
|
|
|
466
|
-
it("row
|
|
466
|
+
it("row increments in order", () => {
|
|
467
467
|
const result = flattenTree(tree, [tree[0]], getChildren);
|
|
468
468
|
expect(result.map((r) => r.row)).toEqual([0, 1, 2, 3]);
|
|
469
469
|
});
|
|
470
470
|
|
|
471
|
-
it("index
|
|
471
|
+
it("index returns position within containing array", () => {
|
|
472
472
|
const result = flattenTree(tree, [tree[0]], getChildren);
|
|
473
473
|
// tree[0]="a" → index 0 (root items[0])
|
|
474
474
|
// tree[0].children[0]="a1" → index 0 (children[0])
|
|
@@ -477,7 +477,7 @@ describe("flattenTree", () => {
|
|
|
477
477
|
expect(result.map((r) => r.index)).toEqual([0, 0, 1, 1]);
|
|
478
478
|
});
|
|
479
479
|
|
|
480
|
-
it("
|
|
480
|
+
it("uses original index for root when getOriginalIndex is provided", () => {
|
|
481
481
|
const items = [tree[1], tree[0]]; // reversed
|
|
482
482
|
const originalMap = new Map<TreeNode, number>();
|
|
483
483
|
tree.forEach((item, i) => originalMap.set(item, i));
|
|
@@ -505,7 +505,7 @@ describe("collectAllExpandable", () => {
|
|
|
505
505
|
|
|
506
506
|
const getChildren = (item: TreeNode) => item.children;
|
|
507
507
|
|
|
508
|
-
it("
|
|
508
|
+
it("recursively collects all nodes that have children", () => {
|
|
509
509
|
const tree: TreeNode[] = [
|
|
510
510
|
{
|
|
511
511
|
id: "a",
|
|
@@ -517,14 +517,14 @@ describe("collectAllExpandable", () => {
|
|
|
517
517
|
expect(result.map((r) => r.id)).toEqual(["a", "a2"]);
|
|
518
518
|
});
|
|
519
519
|
|
|
520
|
-
it("
|
|
520
|
+
it("returns empty array when no nodes have children", () => {
|
|
521
521
|
const tree: TreeNode[] = [{ id: "a" }, { id: "b" }];
|
|
522
522
|
const result = collectAllExpandable(tree, getChildren);
|
|
523
523
|
expect(result).toEqual([]);
|
|
524
524
|
});
|
|
525
525
|
});
|
|
526
526
|
|
|
527
|
-
describe("DataSheet
|
|
527
|
+
describe("DataSheet tree expansion", () => {
|
|
528
528
|
interface TreeItem {
|
|
529
529
|
name: string;
|
|
530
530
|
children?: TreeItem[];
|
|
@@ -538,7 +538,7 @@ describe("DataSheet 트리 확장", () => {
|
|
|
538
538
|
{ name: "폴더B" },
|
|
539
539
|
];
|
|
540
540
|
|
|
541
|
-
it("
|
|
541
|
+
it("renders expand column when getChildren is set", () => {
|
|
542
542
|
const { container } = render(() => (
|
|
543
543
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
544
544
|
<TestWrapper>
|
|
@@ -551,16 +551,16 @@ describe("DataSheet 트리 확장", () => {
|
|
|
551
551
|
</I18nProvider></ConfigProvider>
|
|
552
552
|
));
|
|
553
553
|
|
|
554
|
-
//
|
|
554
|
+
// expand column col is added to colgroup
|
|
555
555
|
const cols = container.querySelectorAll("colgroup col");
|
|
556
|
-
expect(cols.length).toBe(2); //
|
|
556
|
+
expect(cols.length).toBe(2); // expand column + name column
|
|
557
557
|
|
|
558
|
-
//
|
|
558
|
+
// expand toggle button exists in header
|
|
559
559
|
const expandBtn = container.querySelector("thead button");
|
|
560
560
|
expect(expandBtn).toBeTruthy();
|
|
561
561
|
});
|
|
562
562
|
|
|
563
|
-
it("
|
|
563
|
+
it("displays only root items in collapsed state", () => {
|
|
564
564
|
const { container } = render(() => (
|
|
565
565
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
566
566
|
<TestWrapper>
|
|
@@ -585,7 +585,7 @@ describe("DataSheet 트리 확장", () => {
|
|
|
585
585
|
expect(Array.from(names).map((n) => n.textContent)).toEqual(["폴더A", "폴더B"]);
|
|
586
586
|
});
|
|
587
587
|
|
|
588
|
-
it("
|
|
588
|
+
it("displays children of expanded items", () => {
|
|
589
589
|
const { container } = render(() => (
|
|
590
590
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
591
591
|
<TestWrapper>
|
|
@@ -604,7 +604,7 @@ describe("DataSheet 트리 확장", () => {
|
|
|
604
604
|
));
|
|
605
605
|
|
|
606
606
|
const rows = container.querySelectorAll("tbody tr");
|
|
607
|
-
expect(rows.length).toBe(4); // 폴더A, 파일A1, 파일A2, 폴더B
|
|
607
|
+
expect(rows.length).toBe(4); // 폴더A, 파일A1, 파일A2, 폴더B (test data kept as-is)
|
|
608
608
|
|
|
609
609
|
const names = container.querySelectorAll("tbody [data-testid='name']");
|
|
610
610
|
expect(Array.from(names).map((n) => n.textContent)).toEqual([
|
|
@@ -615,7 +615,7 @@ describe("DataSheet 트리 확장", () => {
|
|
|
615
615
|
]);
|
|
616
616
|
});
|
|
617
617
|
|
|
618
|
-
it("
|
|
618
|
+
it("hides expand icon for items with no children", () => {
|
|
619
619
|
const { container } = render(() => (
|
|
620
620
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
621
621
|
<TestWrapper>
|
|
@@ -634,11 +634,11 @@ describe("DataSheet 트리 확장", () => {
|
|
|
634
634
|
));
|
|
635
635
|
|
|
636
636
|
const tbodyRows = container.querySelectorAll("tbody tr");
|
|
637
|
-
// 폴더B(
|
|
637
|
+
// 폴더B (index 1) has no children → no expand button
|
|
638
638
|
const secondRowBtns = tbodyRows[1].querySelectorAll("button");
|
|
639
639
|
expect(secondRowBtns.length).toBe(0);
|
|
640
640
|
|
|
641
|
-
// 폴더A(
|
|
641
|
+
// 폴더A (index 0) has children → expand button exists
|
|
642
642
|
const firstRowBtns = tbodyRows[0].querySelectorAll("button");
|
|
643
643
|
expect(firstRowBtns.length).toBe(1);
|
|
644
644
|
});
|