@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
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
type SelectContextValue,
|
|
8
8
|
} from "../../../../src/components/form-control/select/SelectContext";
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Test provider
|
|
11
11
|
function TestProvider(props: { children: JSX.Element; value: SelectContextValue }) {
|
|
12
12
|
return <SelectContext.Provider value={props.value}>{props.children}</SelectContext.Provider>;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
describe("SelectItem
|
|
15
|
+
describe("SelectItem component", () => {
|
|
16
16
|
describe("basic rendering", () => {
|
|
17
17
|
it("renders children", () => {
|
|
18
18
|
const mockContext: SelectContextValue = {
|
|
@@ -34,7 +34,7 @@ describe("SelectItem 컴포넌트", () => {
|
|
|
34
34
|
expect(getByText("사과")).not.toBeNull();
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
it("data-select-item
|
|
37
|
+
it("sets data-select-item attribute", () => {
|
|
38
38
|
const mockContext: SelectContextValue = {
|
|
39
39
|
multiple: () => false,
|
|
40
40
|
isSelected: () => false,
|
|
@@ -55,8 +55,8 @@ describe("SelectItem 컴포넌트", () => {
|
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
describe("
|
|
59
|
-
it("
|
|
58
|
+
describe("selection behavior", () => {
|
|
59
|
+
it("calls toggleValue on click", () => {
|
|
60
60
|
const toggleValue = vi.fn();
|
|
61
61
|
const mockContext: SelectContextValue = {
|
|
62
62
|
multiple: () => false,
|
|
@@ -78,7 +78,7 @@ describe("SelectItem 컴포넌트", () => {
|
|
|
78
78
|
expect(toggleValue).toHaveBeenCalledWith("apple");
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
it("
|
|
81
|
+
it("calls closeDropdown on click in single selection mode", () => {
|
|
82
82
|
const closeDropdown = vi.fn();
|
|
83
83
|
const mockContext: SelectContextValue = {
|
|
84
84
|
multiple: () => false,
|
|
@@ -100,7 +100,7 @@ describe("SelectItem 컴포넌트", () => {
|
|
|
100
100
|
expect(closeDropdown).toHaveBeenCalled();
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
it("
|
|
103
|
+
it("does not call closeDropdown on click in multiple selection mode", () => {
|
|
104
104
|
const closeDropdown = vi.fn();
|
|
105
105
|
const mockContext: SelectContextValue = {
|
|
106
106
|
multiple: () => true,
|
|
@@ -123,8 +123,8 @@ describe("SelectItem 컴포넌트", () => {
|
|
|
123
123
|
});
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
describe("
|
|
127
|
-
it("
|
|
126
|
+
describe("selected state", () => {
|
|
127
|
+
it("sets aria-selected=true on selected item", () => {
|
|
128
128
|
const mockContext: SelectContextValue = {
|
|
129
129
|
multiple: () => false,
|
|
130
130
|
isSelected: (v) => v === "apple",
|
|
@@ -147,7 +147,7 @@ describe("SelectItem 컴포넌트", () => {
|
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
describe("disabled state", () => {
|
|
150
|
-
it("
|
|
150
|
+
it("does not call toggleValue on click when disabled", () => {
|
|
151
151
|
const toggleValue = vi.fn();
|
|
152
152
|
const mockContext: SelectContextValue = {
|
|
153
153
|
multiple: () => false,
|
|
@@ -3,13 +3,13 @@ import { type Accessor, createRoot, createSignal } from "solid-js";
|
|
|
3
3
|
import type { Component } from "solid-js";
|
|
4
4
|
import { createAppStructure, type AppStructureItem } from "../../src/helpers/createAppStructure";
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// dummy components for tests
|
|
7
7
|
const DummyA: Component = () => null;
|
|
8
8
|
const DummyB: Component = () => null;
|
|
9
9
|
const DummyC: Component = () => null;
|
|
10
10
|
const DummyD: Component = () => null;
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// common test structure
|
|
13
13
|
function createTestItems(): AppStructureItem<string>[] {
|
|
14
14
|
return [
|
|
15
15
|
{
|
|
@@ -77,7 +77,7 @@ function buildTestStructure<const TItems extends AppStructureItem<string>[]>(opt
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
describe("createAppStructure", () => {
|
|
80
|
-
it("AppStructureProvider
|
|
80
|
+
it("returns AppStructureProvider and useAppStructure", () => {
|
|
81
81
|
createRoot((dispose) => {
|
|
82
82
|
const { AppStructureProvider, useAppStructure } = createAppStructure(() => ({
|
|
83
83
|
items: [
|
|
@@ -104,7 +104,7 @@ describe("createAppStructure", () => {
|
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
describe("usableRoutes", () => {
|
|
107
|
-
it("
|
|
107
|
+
it("includes paths and components for all leaf items", () => {
|
|
108
108
|
createRoot((dispose) => {
|
|
109
109
|
const result = buildTestStructure({ items: createTestItems() });
|
|
110
110
|
|
|
@@ -119,7 +119,7 @@ describe("createAppStructure", () => {
|
|
|
119
119
|
});
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
it("
|
|
122
|
+
it("excludes items without a component from usableRoutes", () => {
|
|
123
123
|
createRoot((dispose) => {
|
|
124
124
|
const items: AppStructureItem<string>[] = [
|
|
125
125
|
{
|
|
@@ -136,7 +136,7 @@ describe("createAppStructure", () => {
|
|
|
136
136
|
});
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
-
it("
|
|
139
|
+
it("filters out modules not matching usableModules", () => {
|
|
140
140
|
createRoot((dispose) => {
|
|
141
141
|
const [modules] = createSignal<string[]>(["erp"]);
|
|
142
142
|
|
|
@@ -145,8 +145,8 @@ describe("createAppStructure", () => {
|
|
|
145
145
|
usableModules: modules,
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
// sales
|
|
149
|
-
// admin
|
|
148
|
+
// sales group has modules: ["sales"] — filtered out when only "erp" is present
|
|
149
|
+
// admin has no modules — always included
|
|
150
150
|
const routes = result.usableRoutes();
|
|
151
151
|
expect(routes.map((r) => r.path)).toEqual(["/admin/users", "/admin/hidden"]);
|
|
152
152
|
|
|
@@ -154,7 +154,7 @@ describe("createAppStructure", () => {
|
|
|
154
154
|
});
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
-
it("requiredModules
|
|
157
|
+
it("filters out items when all requiredModules are missing", () => {
|
|
158
158
|
createRoot((dispose) => {
|
|
159
159
|
const [modules] = createSignal<string[]>(["sales"]);
|
|
160
160
|
|
|
@@ -163,8 +163,8 @@ describe("createAppStructure", () => {
|
|
|
163
163
|
usableModules: modules,
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
-
// order
|
|
167
|
-
// invoice
|
|
166
|
+
// order has requiredModules: ["sales", "erp"] — filtered because "erp" is missing
|
|
167
|
+
// invoice has no requiredModules — included
|
|
168
168
|
const routes = result.usableRoutes();
|
|
169
169
|
expect(routes.map((r) => r.path)).toEqual([
|
|
170
170
|
"/sales/invoice",
|
|
@@ -176,7 +176,7 @@ describe("createAppStructure", () => {
|
|
|
176
176
|
});
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
-
it("
|
|
179
|
+
it("filters out items without use permission in permRecord", () => {
|
|
180
180
|
createRoot((dispose) => {
|
|
181
181
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
182
182
|
"/home/sales/invoice/use": false,
|
|
@@ -199,7 +199,7 @@ describe("createAppStructure", () => {
|
|
|
199
199
|
});
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
-
it("module
|
|
202
|
+
it("includes only items satisfying both module and perm requirements", () => {
|
|
203
203
|
createRoot((dispose) => {
|
|
204
204
|
const [modules] = createSignal<string[]>(["sales", "erp"]);
|
|
205
205
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
@@ -224,7 +224,7 @@ describe("createAppStructure", () => {
|
|
|
224
224
|
});
|
|
225
225
|
});
|
|
226
226
|
|
|
227
|
-
it("isNotMenu
|
|
227
|
+
it("includes isNotMenu items in routes", () => {
|
|
228
228
|
createRoot((dispose) => {
|
|
229
229
|
const result = buildTestStructure({ items: createTestItems() });
|
|
230
230
|
|
|
@@ -237,18 +237,18 @@ describe("createAppStructure", () => {
|
|
|
237
237
|
});
|
|
238
238
|
|
|
239
239
|
describe("allFlatPerms", () => {
|
|
240
|
-
it("requiredModulesChain
|
|
240
|
+
it("collects requiredModulesChain per hierarchy level", () => {
|
|
241
241
|
createRoot((dispose) => {
|
|
242
242
|
const result = buildTestStructure({ items: createTestItems() });
|
|
243
243
|
|
|
244
|
-
// order
|
|
245
|
-
//
|
|
244
|
+
// order has requiredModules: ["sales", "erp"]
|
|
245
|
+
// parent sales has modules: ["sales"] (not requiredModules)
|
|
246
246
|
const orderUsePerm = result.allFlatPerms.find((p) => p.code === "/home/sales/order/use");
|
|
247
247
|
expect(orderUsePerm).toBeDefined();
|
|
248
248
|
expect(orderUsePerm!.modulesChain).toEqual([["sales"]]);
|
|
249
249
|
expect(orderUsePerm!.requiredModulesChain).toEqual([["sales", "erp"]]);
|
|
250
250
|
|
|
251
|
-
// invoice
|
|
251
|
+
// invoice has no requiredModules
|
|
252
252
|
const invoiceUsePerm = result.allFlatPerms.find(
|
|
253
253
|
(p) => p.code === "/home/sales/invoice/use",
|
|
254
254
|
);
|
|
@@ -262,16 +262,16 @@ describe("createAppStructure", () => {
|
|
|
262
262
|
});
|
|
263
263
|
|
|
264
264
|
describe("usableMenus", () => {
|
|
265
|
-
it("
|
|
266
|
-
// permRecord
|
|
267
|
-
// permRecord[href + "/use"]
|
|
265
|
+
it("hides leaves with perms and shows only perms-less leaves when permRecord is absent", () => {
|
|
266
|
+
// permRecord defaults to {}, so items with "use" in perms are
|
|
267
|
+
// filtered because permRecord[href + "/use"] is undefined (falsy)
|
|
268
268
|
createRoot((dispose) => {
|
|
269
269
|
const result = buildTestStructure({ items: createTestItems() });
|
|
270
270
|
const menus = result.usableMenus();
|
|
271
271
|
|
|
272
|
-
//
|
|
273
|
-
// →
|
|
274
|
-
//
|
|
272
|
+
// sales sub-items (invoice, order) both have "use" in perms → hidden
|
|
273
|
+
// → sales group itself is also hidden because no children remain
|
|
274
|
+
// admin sub-items: only 사용자 (no perms) shown, 숨김 (isNotMenu) excluded
|
|
275
275
|
expect(menus).toHaveLength(1);
|
|
276
276
|
expect(menus[0].title).toBe("관리");
|
|
277
277
|
expect(menus[0].children).toHaveLength(1);
|
|
@@ -281,7 +281,7 @@ describe("createAppStructure", () => {
|
|
|
281
281
|
});
|
|
282
282
|
});
|
|
283
283
|
|
|
284
|
-
it("
|
|
284
|
+
it("displays all menus except isNotMenu when all permissions are granted", () => {
|
|
285
285
|
createRoot((dispose) => {
|
|
286
286
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
287
287
|
"/home/sales/invoice/use": true,
|
|
@@ -309,7 +309,7 @@ describe("createAppStructure", () => {
|
|
|
309
309
|
});
|
|
310
310
|
});
|
|
311
311
|
|
|
312
|
-
it("href
|
|
312
|
+
it("generates href as the correct full path", () => {
|
|
313
313
|
createRoot((dispose) => {
|
|
314
314
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
315
315
|
"/home/sales/invoice/use": true,
|
|
@@ -333,7 +333,7 @@ describe("createAppStructure", () => {
|
|
|
333
333
|
});
|
|
334
334
|
});
|
|
335
335
|
|
|
336
|
-
it("modules OR
|
|
336
|
+
it("modules OR filter: group is hidden when its module is not in usableModules", () => {
|
|
337
337
|
createRoot((dispose) => {
|
|
338
338
|
const [modules] = createSignal<string[]>(["erp"]);
|
|
339
339
|
|
|
@@ -343,7 +343,7 @@ describe("createAppStructure", () => {
|
|
|
343
343
|
});
|
|
344
344
|
|
|
345
345
|
const menus = result.usableMenus();
|
|
346
|
-
// sales
|
|
346
|
+
// sales group has modules: ["sales"] but "sales" is not in usableModules → hidden
|
|
347
347
|
expect(menus).toHaveLength(1);
|
|
348
348
|
expect(menus[0].title).toBe("관리");
|
|
349
349
|
|
|
@@ -351,7 +351,7 @@ describe("createAppStructure", () => {
|
|
|
351
351
|
});
|
|
352
352
|
});
|
|
353
353
|
|
|
354
|
-
it("requiredModules AND
|
|
354
|
+
it("requiredModules AND filter: displayed only when all required modules are present", () => {
|
|
355
355
|
createRoot((dispose) => {
|
|
356
356
|
const [modules] = createSignal<string[]>(["sales"]);
|
|
357
357
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
@@ -366,9 +366,9 @@ describe("createAppStructure", () => {
|
|
|
366
366
|
});
|
|
367
367
|
|
|
368
368
|
const menus = result.usableMenus();
|
|
369
|
-
// sales
|
|
370
|
-
// order
|
|
371
|
-
// invoice
|
|
369
|
+
// sales group matches modules: ["sales"] → shown
|
|
370
|
+
// order has requiredModules: ["sales", "erp"] but "erp" is missing → hidden
|
|
371
|
+
// invoice has no requiredModules → shown
|
|
372
372
|
expect(menus[0].children).toHaveLength(1);
|
|
373
373
|
expect(menus[0].children![0].title).toBe("송장");
|
|
374
374
|
|
|
@@ -376,7 +376,7 @@ describe("createAppStructure", () => {
|
|
|
376
376
|
});
|
|
377
377
|
});
|
|
378
378
|
|
|
379
|
-
it("permRecord
|
|
379
|
+
it("permRecord filter: hidden when use permission is absent", () => {
|
|
380
380
|
createRoot((dispose) => {
|
|
381
381
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
382
382
|
"/home/sales/invoice/use": false,
|
|
@@ -399,7 +399,7 @@ describe("createAppStructure", () => {
|
|
|
399
399
|
});
|
|
400
400
|
});
|
|
401
401
|
|
|
402
|
-
it("perms
|
|
402
|
+
it("leaves without perms are always shown without permission check", () => {
|
|
403
403
|
createRoot((dispose) => {
|
|
404
404
|
const [perms] = createSignal<Record<string, boolean>>({});
|
|
405
405
|
|
|
@@ -409,9 +409,9 @@ describe("createAppStructure", () => {
|
|
|
409
409
|
});
|
|
410
410
|
|
|
411
411
|
const menus = result.usableMenus();
|
|
412
|
-
// permRecord
|
|
413
|
-
//
|
|
414
|
-
// 관리
|
|
412
|
+
// permRecord is empty → all items with perms: ["use"] are hidden
|
|
413
|
+
// only 사용자 (DummyC, no perms) is shown
|
|
414
|
+
// only 관리 group remains (index 0)
|
|
415
415
|
expect(menus).toHaveLength(1);
|
|
416
416
|
expect(menus[0].title).toBe("관리");
|
|
417
417
|
expect(menus[0].children![0].title).toBe("사용자");
|
|
@@ -420,7 +420,7 @@ describe("createAppStructure", () => {
|
|
|
420
420
|
});
|
|
421
421
|
});
|
|
422
422
|
|
|
423
|
-
it("
|
|
423
|
+
it("hides group when all children are filtered out", () => {
|
|
424
424
|
createRoot((dispose) => {
|
|
425
425
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
426
426
|
"/home/sales/invoice/use": false,
|
|
@@ -436,7 +436,7 @@ describe("createAppStructure", () => {
|
|
|
436
436
|
});
|
|
437
437
|
|
|
438
438
|
const menus = result.usableMenus();
|
|
439
|
-
//
|
|
439
|
+
// all sales sub-items filtered out by missing permissions → sales group is also hidden
|
|
440
440
|
expect(menus).toHaveLength(1);
|
|
441
441
|
expect(menus[0].title).toBe("관리");
|
|
442
442
|
|
|
@@ -444,7 +444,7 @@ describe("createAppStructure", () => {
|
|
|
444
444
|
});
|
|
445
445
|
});
|
|
446
446
|
|
|
447
|
-
it("
|
|
447
|
+
it("menus are recalculated when usableModules changes", () => {
|
|
448
448
|
createRoot((dispose) => {
|
|
449
449
|
const [modules, setModules] = createSignal<string[] | undefined>(undefined);
|
|
450
450
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
@@ -458,10 +458,10 @@ describe("createAppStructure", () => {
|
|
|
458
458
|
permRecord: perms,
|
|
459
459
|
});
|
|
460
460
|
|
|
461
|
-
// usableModules
|
|
461
|
+
// usableModules is undefined → no module filtering → all shown
|
|
462
462
|
expect(result.usableMenus()).toHaveLength(2);
|
|
463
463
|
|
|
464
|
-
// usableModules
|
|
464
|
+
// setting usableModules to ["sales"] fails order's requiredModules
|
|
465
465
|
setModules(["sales"]);
|
|
466
466
|
expect(result.usableMenus()[0].title).toBe("영업");
|
|
467
467
|
expect(result.usableMenus()[0].children).toHaveLength(1);
|
|
@@ -473,7 +473,7 @@ describe("createAppStructure", () => {
|
|
|
473
473
|
});
|
|
474
474
|
|
|
475
475
|
describe("usableFlatMenus", () => {
|
|
476
|
-
it("
|
|
476
|
+
it("flattens the tree and returns titleChain and href", () => {
|
|
477
477
|
createRoot((dispose) => {
|
|
478
478
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
479
479
|
"/home/sales/invoice/use": true,
|
|
@@ -505,7 +505,7 @@ describe("createAppStructure", () => {
|
|
|
505
505
|
});
|
|
506
506
|
|
|
507
507
|
describe("getTitleChainByHref", () => {
|
|
508
|
-
it("
|
|
508
|
+
it("returns title chain from href", () => {
|
|
509
509
|
createRoot((dispose) => {
|
|
510
510
|
const result = buildTestStructure({ items: createTestItems() });
|
|
511
511
|
|
|
@@ -516,7 +516,7 @@ describe("createAppStructure", () => {
|
|
|
516
516
|
});
|
|
517
517
|
});
|
|
518
518
|
|
|
519
|
-
it("isNotMenu
|
|
519
|
+
it("also finds isNotMenu items", () => {
|
|
520
520
|
createRoot((dispose) => {
|
|
521
521
|
const result = buildTestStructure({ items: createTestItems() });
|
|
522
522
|
|
|
@@ -526,7 +526,7 @@ describe("createAppStructure", () => {
|
|
|
526
526
|
});
|
|
527
527
|
});
|
|
528
528
|
|
|
529
|
-
it("
|
|
529
|
+
it("returns empty array for nonexistent href", () => {
|
|
530
530
|
createRoot((dispose) => {
|
|
531
531
|
const result = buildTestStructure({ items: createTestItems() });
|
|
532
532
|
|
|
@@ -539,7 +539,7 @@ describe("createAppStructure", () => {
|
|
|
539
539
|
});
|
|
540
540
|
|
|
541
541
|
describe("usablePerms", () => {
|
|
542
|
-
it("perms
|
|
542
|
+
it("excludes leaves without perms from usablePerms", () => {
|
|
543
543
|
createRoot((dispose) => {
|
|
544
544
|
const result = buildTestStructure({
|
|
545
545
|
items: [
|
|
@@ -559,7 +559,7 @@ describe("createAppStructure", () => {
|
|
|
559
559
|
});
|
|
560
560
|
|
|
561
561
|
const perms = result.usablePerms();
|
|
562
|
-
// home
|
|
562
|
+
// home group's children should not include "메인"
|
|
563
563
|
const homeGroup = perms[0];
|
|
564
564
|
expect(homeGroup.children).toHaveLength(1);
|
|
565
565
|
expect(homeGroup.children![0].title).toBe("사용자");
|
|
@@ -568,7 +568,7 @@ describe("createAppStructure", () => {
|
|
|
568
568
|
});
|
|
569
569
|
});
|
|
570
570
|
|
|
571
|
-
it("
|
|
571
|
+
it("excludes leaves with empty perms array", () => {
|
|
572
572
|
createRoot((dispose) => {
|
|
573
573
|
const result = buildTestStructure({
|
|
574
574
|
items: [
|
|
@@ -600,7 +600,7 @@ describe("createAppStructure", () => {
|
|
|
600
600
|
});
|
|
601
601
|
});
|
|
602
602
|
|
|
603
|
-
it("children
|
|
603
|
+
it("excludes group when all children are filtered out", () => {
|
|
604
604
|
createRoot((dispose) => {
|
|
605
605
|
const result = buildTestStructure({
|
|
606
606
|
items: [
|
|
@@ -634,7 +634,7 @@ describe("createAppStructure", () => {
|
|
|
634
634
|
});
|
|
635
635
|
|
|
636
636
|
describe("perms", () => {
|
|
637
|
-
it("
|
|
637
|
+
it("returns true for perms that are true in permRecord", () => {
|
|
638
638
|
createRoot((dispose) => {
|
|
639
639
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
640
640
|
"/home/sales/invoice/use": true,
|
|
@@ -671,7 +671,7 @@ describe("createAppStructure", () => {
|
|
|
671
671
|
});
|
|
672
672
|
});
|
|
673
673
|
|
|
674
|
-
it("
|
|
674
|
+
it("returns false for perms absent from permRecord", () => {
|
|
675
675
|
createRoot((dispose) => {
|
|
676
676
|
const [perms] = createSignal<Record<string, boolean>>({});
|
|
677
677
|
|
|
@@ -698,7 +698,7 @@ describe("createAppStructure", () => {
|
|
|
698
698
|
});
|
|
699
699
|
});
|
|
700
700
|
|
|
701
|
-
it("
|
|
701
|
+
it("all perms are false when permRecord is absent", () => {
|
|
702
702
|
createRoot((dispose) => {
|
|
703
703
|
const result = buildTestStructure({
|
|
704
704
|
items: [
|
|
@@ -722,7 +722,7 @@ describe("createAppStructure", () => {
|
|
|
722
722
|
});
|
|
723
723
|
});
|
|
724
724
|
|
|
725
|
-
it("subPerms
|
|
725
|
+
it("subPerms are accessible in the same way", () => {
|
|
726
726
|
createRoot((dispose) => {
|
|
727
727
|
const [perms] = createSignal<Record<string, boolean>>({
|
|
728
728
|
"/home/user/auth/use": true,
|
|
@@ -752,7 +752,7 @@ describe("createAppStructure", () => {
|
|
|
752
752
|
});
|
|
753
753
|
});
|
|
754
754
|
|
|
755
|
-
it("
|
|
755
|
+
it("perm values update reactively when permRecord changes", () => {
|
|
756
756
|
createRoot((dispose) => {
|
|
757
757
|
const [perms, setPerms] = createSignal<Record<string, boolean>>({
|
|
758
758
|
"/home/user/use": false,
|
|
@@ -784,7 +784,7 @@ describe("createAppStructure", () => {
|
|
|
784
784
|
});
|
|
785
785
|
});
|
|
786
786
|
|
|
787
|
-
it("perms
|
|
787
|
+
it("excludes leaves without perms from the perms tree", () => {
|
|
788
788
|
createRoot((dispose) => {
|
|
789
789
|
const result = buildTestStructure({
|
|
790
790
|
items: [
|
|
@@ -810,7 +810,7 @@ describe("createAppStructure", () => {
|
|
|
810
810
|
});
|
|
811
811
|
});
|
|
812
812
|
|
|
813
|
-
it("
|
|
813
|
+
it("excludes groups without any perm from the perms tree", () => {
|
|
814
814
|
createRoot((dispose) => {
|
|
815
815
|
const result = buildTestStructure({
|
|
816
816
|
items: [
|