@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.
Files changed (98) hide show
  1. package/README.md +1 -1
  2. package/dist/components/disclosure/Dropdown.d.ts +6 -4
  3. package/dist/components/disclosure/Dropdown.d.ts.map +1 -1
  4. package/dist/components/disclosure/Dropdown.js +24 -8
  5. package/dist/components/disclosure/Dropdown.js.map +2 -2
  6. package/dist/components/disclosure/dialogZIndex.d.ts +2 -0
  7. package/dist/components/disclosure/dialogZIndex.d.ts.map +1 -1
  8. package/dist/components/disclosure/dialogZIndex.js +4 -0
  9. package/dist/components/disclosure/dialogZIndex.js.map +1 -1
  10. package/dist/components/features/crud-detail/CrudDetail.d.ts.map +1 -1
  11. package/dist/components/features/crud-detail/CrudDetail.js +16 -7
  12. package/dist/components/features/crud-detail/CrudDetail.js.map +2 -2
  13. package/dist/components/features/crud-sheet/CrudSheet.d.ts.map +1 -1
  14. package/dist/components/features/crud-sheet/CrudSheet.js +14 -5
  15. package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
  16. package/dist/components/features/crudRegistry.d.ts +16 -0
  17. package/dist/components/features/crudRegistry.d.ts.map +1 -0
  18. package/dist/components/features/crudRegistry.js +37 -0
  19. package/dist/components/features/crudRegistry.js.map +6 -0
  20. package/dist/components/features/permission-table/PermissionTable.d.ts.map +1 -1
  21. package/dist/components/features/permission-table/PermissionTable.js +71 -86
  22. package/dist/components/features/permission-table/PermissionTable.js.map +2 -2
  23. package/dist/components/features/shared-data/SharedDataSelect.js +2 -4
  24. package/dist/components/features/shared-data/SharedDataSelect.js.map +2 -2
  25. package/dist/components/features/shared-data/SharedDataSelectList.d.ts +2 -4
  26. package/dist/components/features/shared-data/SharedDataSelectList.d.ts.map +1 -1
  27. package/dist/components/features/shared-data/SharedDataSelectList.js +11 -46
  28. package/dist/components/features/shared-data/SharedDataSelectList.js.map +2 -2
  29. package/dist/components/form-control/select/Select.d.ts.map +1 -1
  30. package/dist/components/form-control/select/Select.js +1 -1
  31. package/dist/components/form-control/select/Select.js.map +1 -1
  32. package/dist/helpers/createAppStructure.d.ts.map +1 -1
  33. package/dist/helpers/createAppStructure.js +3 -2
  34. package/dist/helpers/createAppStructure.js.map +1 -1
  35. package/dist/helpers/createHmrSafeContext.d.ts +3 -0
  36. package/dist/helpers/createHmrSafeContext.d.ts.map +1 -0
  37. package/dist/helpers/createHmrSafeContext.js +10 -0
  38. package/dist/helpers/createHmrSafeContext.js.map +6 -0
  39. package/dist/hooks/createSelectionGroup.d.ts.map +1 -1
  40. package/dist/hooks/createSelectionGroup.js +3 -2
  41. package/dist/hooks/createSelectionGroup.js.map +2 -2
  42. package/package.json +6 -5
  43. package/src/components/disclosure/Dropdown.tsx +31 -17
  44. package/src/components/disclosure/dialogZIndex.ts +5 -0
  45. package/src/components/features/crud-detail/CrudDetail.tsx +16 -5
  46. package/src/components/features/crud-sheet/CrudSheet.tsx +13 -3
  47. package/src/components/features/crudRegistry.ts +60 -0
  48. package/src/components/features/permission-table/PermissionTable.tsx +49 -46
  49. package/src/components/features/shared-data/SharedDataSelect.tsx +2 -2
  50. package/src/components/features/shared-data/SharedDataSelectList.tsx +11 -36
  51. package/src/components/form-control/select/Select.tsx +1 -5
  52. package/src/helpers/createAppStructure.ts +3 -2
  53. package/src/helpers/createHmrSafeContext.ts +8 -0
  54. package/src/hooks/createSelectionGroup.tsx +4 -2
  55. package/tests/components/data/List.spec.tsx +52 -52
  56. package/tests/components/data/Pagination.spec.tsx +43 -43
  57. package/tests/components/data/Table.spec.tsx +4 -4
  58. package/tests/components/data/kanban/Kanban.selection.spec.tsx +21 -21
  59. package/tests/components/data/sheet/DataSheet.spec.tsx +50 -50
  60. package/tests/components/disclosure/Collapse.spec.tsx +24 -24
  61. package/tests/components/disclosure/Dialog.spec.tsx +33 -33
  62. package/tests/components/disclosure/DialogProvider.spec.tsx +9 -9
  63. package/tests/components/disclosure/Dropdown.spec.tsx +134 -14
  64. package/tests/components/disclosure/Tabs.spec.tsx +21 -21
  65. package/tests/components/disclosure/dialogZIndex.spec.ts +45 -0
  66. package/tests/components/display/Alert.spec.tsx +4 -4
  67. package/tests/components/display/Barcode.spec.tsx +7 -7
  68. package/tests/components/display/Card.spec.tsx +3 -3
  69. package/tests/components/display/Link.spec.tsx +5 -5
  70. package/tests/components/display/Tag.spec.tsx +4 -4
  71. package/tests/components/features/address/AddressSearch.spec.tsx +3 -3
  72. package/tests/components/features/crudRegistry.spec.ts +119 -0
  73. package/tests/components/features/data-select-button/DataSelectButton.spec.tsx +8 -8
  74. package/tests/components/features/permission-table/PermissionTable.spec.tsx +43 -43
  75. package/tests/components/features/shared-data/SharedDataSelectList.spec.tsx +2 -17
  76. package/tests/components/feedback/busy/BusyContainer.spec.tsx +7 -7
  77. package/tests/components/feedback/notification/NotificationBell.spec.tsx +9 -9
  78. package/tests/components/feedback/print/Print.spec.tsx +4 -4
  79. package/tests/components/form-control/Button.spec.tsx +18 -18
  80. package/tests/components/form-control/checkbox/Checkbox.spec.tsx +20 -20
  81. package/tests/components/form-control/checkbox/CheckboxGroup.spec.tsx +12 -12
  82. package/tests/components/form-control/checkbox/Radio.spec.tsx +21 -21
  83. package/tests/components/form-control/checkbox/RadioGroup.spec.tsx +12 -12
  84. package/tests/components/form-control/color-picker/ColorPicker.spec.tsx +10 -10
  85. package/tests/components/form-control/combobox/Combobox.spec.tsx +16 -16
  86. package/tests/components/form-control/combobox/ComboboxItem.spec.tsx +7 -7
  87. package/tests/components/form-control/date-range-picker/DateRangePicker.spec.tsx +24 -24
  88. package/tests/components/form-control/field/DatePicker.spec.tsx +50 -50
  89. package/tests/components/form-control/field/DateTimePicker.spec.tsx +47 -47
  90. package/tests/components/form-control/field/NumberInput.spec.tsx +54 -54
  91. package/tests/components/form-control/field/TextInput.spec.tsx +49 -49
  92. package/tests/components/form-control/field/Textarea.spec.tsx +33 -33
  93. package/tests/components/form-control/field/TimePicker.spec.tsx +42 -42
  94. package/tests/components/form-control/numpad/Numpad.spec.tsx +40 -40
  95. package/tests/components/form-control/select/Select.spec.tsx +9 -9
  96. package/tests/components/form-control/select/SelectItem.spec.tsx +10 -10
  97. package/tests/helpers/createAppStructure.spec.tsx +57 -57
  98. package/tests/helpers/mergeStyles.spec.ts +31 -31
@@ -1,14 +1,10 @@
1
1
  import { createEffect, createMemo, createSignal, For, type JSX, Show, splitProps } from "solid-js";
2
- import { IconExternalLink } from "@tabler/icons-solidjs";
3
2
  import clsx from "clsx";
4
3
  import { twMerge } from "tailwind-merge";
5
4
  import { type SharedDataAccessor } from "../../../providers/shared-data/SharedDataContext";
6
5
  import { List } from "../../data/list/List";
7
6
  import { Pagination } from "../../data/Pagination";
8
- import { Button } from "../../form-control/Button";
9
- import { Icon } from "../../display/Icon";
10
7
  import { TextInput } from "../../form-control/field/TextInput";
11
- import { useDialog } from "../../disclosure/DialogContext";
12
8
  import { useI18nOptional } from "../../../providers/i18n/I18nContext";
13
9
  import { textMuted } from "../../../styles/tokens.styles";
14
10
  import { createSlotSignal } from "../../../hooks/createSlotSignal";
@@ -39,10 +35,8 @@ export interface SharedDataSelectListProps<TItem> {
39
35
  canChange?: (item: TItem | undefined) => boolean | Promise<boolean>;
40
36
  /** Page size (shows Pagination if provided) */
41
37
  pageSize?: number;
42
- /** Header text */
43
- header?: string;
44
- /** Management modal component factory */
45
- modal?: () => JSX.Element;
38
+ /** Header content */
39
+ header?: JSX.Element;
46
40
 
47
41
  /** Compound sub-components (ItemTemplate, Filter) */
48
42
  children?: JSX.Element;
@@ -57,8 +51,6 @@ export interface SharedDataSelectListProps<TItem> {
57
51
 
58
52
  const containerClass = clsx("flex-col gap-1");
59
53
 
60
- const headerClass = clsx("flex items-center gap-1 px-2 py-1 text-sm font-semibold");
61
-
62
54
  // ─── Component ───────────────────────────────────────────
63
55
 
64
56
  export interface SharedDataSelectListComponent {
@@ -83,10 +75,8 @@ export const SharedDataSelectList: SharedDataSelectListComponent = (<TItem,>(
83
75
  "canChange",
84
76
  "pageSize",
85
77
  "header",
86
- "modal",
87
78
  ]);
88
79
 
89
- const dialog = useDialog();
90
80
  const i18n = useI18nOptional();
91
81
 
92
82
  // ─── Slot signals ──────────────────────────────────────
@@ -192,13 +182,6 @@ export const SharedDataSelectList: SharedDataSelectListComponent = (<TItem,>(
192
182
  }
193
183
  };
194
184
 
195
- // ─── Open modal ────────────────────────────────────────
196
-
197
- const handleOpenModal = async () => {
198
- if (!local.modal) return;
199
- await dialog.show(local.modal, {});
200
- };
201
-
202
185
  // ─── Render ────────────────────────────────────────────
203
186
 
204
187
  return (
@@ -212,26 +195,18 @@ export const SharedDataSelectList: SharedDataSelectListComponent = (<TItem,>(
212
195
  style={local.style}
213
196
  >
214
197
  {/* Header */}
215
- <Show when={local.header != null || local.modal != null}>
216
- <div class={headerClass}>
217
- <Show when={local.header != null}>{local.header}</Show>
218
- <Show when={local.modal != null}>
219
- <Button size="sm" onClick={() => void handleOpenModal()}>
220
- <Icon icon={IconExternalLink} />
221
- </Button>
222
- </Show>
223
- </div>
224
- </Show>
198
+ <Show when={local.header != null}>{local.header}</Show>
225
199
 
226
200
  {/* Search input: when Filter compound is absent and getSearchText exists */}
227
201
  <Show when={!filter() && local.data.getSearchText}>
228
- <TextInput
229
- value={searchText()}
230
- onValueChange={setSearchText}
231
- placeholder={i18n?.t("sharedDataSelectList.searchPlaceholder") ?? "Search..."}
232
- size="sm"
233
- inset
234
- />
202
+ <div class={"p-1"}>
203
+ <TextInput
204
+ value={searchText()}
205
+ onValueChange={setSearchText}
206
+ placeholder={i18n?.t("sharedDataSelectList.searchPlaceholder") ?? "Search..."}
207
+ class={"w-full"}
208
+ />
209
+ </div>
235
210
  </Show>
236
211
 
237
212
  {/* Custom Filter */}
@@ -44,11 +44,7 @@ const searchInputClass = clsx(
44
44
  "w-full",
45
45
  "rounded-none",
46
46
  "border-0 border-b",
47
- borderSubtle,
48
- "bg-transparent dark:bg-transparent",
49
- "h-auto",
50
- "py-1.5",
51
- "text-sm",
47
+ borderSubtle
52
48
  );
53
49
 
54
50
  // Select all/deselect all button area styles
@@ -1,5 +1,6 @@
1
1
  import type { Component, ParentComponent } from "solid-js";
2
- import { type Accessor, createContext, createMemo, createRoot, useContext } from "solid-js";
2
+ import { type Accessor, createMemo, createRoot, useContext } from "solid-js";
3
+ import { createHmrSafeContext } from "./createHmrSafeContext";
3
4
  import type { IconProps } from "@tabler/icons-solidjs";
4
5
 
5
6
  // ── Input Types ──
@@ -497,7 +498,7 @@ export function createAppStructure<TModule, const TItems extends AppStructureIte
497
498
  } {
498
499
  type TRet = AppStructure<TModule> & { perms: InferPerms<TItems> };
499
500
 
500
- const Ctx = createContext<TRet>();
501
+ const Ctx = createHmrSafeContext<TRet>("AppStructure");
501
502
 
502
503
  const AppStructureProvider: ParentComponent = (props) => {
503
504
  const structure = buildAppStructure(getOpts());
@@ -0,0 +1,8 @@
1
+ import { type Context, createContext } from "solid-js";
2
+
3
+ const CACHE_KEY = "__simplysm_ctx__";
4
+ const cache = ((globalThis as unknown as Record<string, Record<string, unknown>>)[CACHE_KEY] ??= {});
5
+
6
+ export function createHmrSafeContext<TValue>(key: string): Context<TValue | undefined> {
7
+ return (cache[key] ??= createContext<TValue>()) as Context<TValue | undefined>;
8
+ }
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  type JSX,
3
3
  type ParentComponent,
4
- createContext,
5
4
  createMemo,
6
5
  splitProps,
7
6
  useContext,
8
7
  } from "solid-js";
8
+ import { createHmrSafeContext } from "../helpers/createHmrSafeContext";
9
9
  import { twMerge } from "tailwind-merge";
10
10
  import { createControllableSignal } from "./createControllableSignal";
11
11
  import { Invalid } from "../components/form-control/Invalid";
@@ -106,7 +106,9 @@ export function createSelectionGroup(config: MultiGroupConfig | SingleGroupConfi
106
106
  Item: <TValue = unknown>(props: SelectionGroupItemProps<TValue>) => JSX.Element;
107
107
  };
108
108
  } {
109
- const Context = createContext<MultiSelectContext | SingleSelectContext>();
109
+ const Context = createHmrSafeContext<MultiSelectContext | SingleSelectContext>(
110
+ `SelectionGroup_${config.contextName}`,
111
+ );
110
112
  const ItemComponent = config.ItemComponent;
111
113
 
112
114
  function ItemInner<TValue>(props: SelectionGroupItemProps<TValue>) {
@@ -4,9 +4,9 @@ import { createSignal } from "solid-js";
4
4
  import { IconCheck } from "@tabler/icons-solidjs";
5
5
  import { List } from "../../../src/components/data/list/List";
6
6
 
7
- describe("List 컴포넌트", () => {
7
+ describe("List component", () => {
8
8
  describe("basic rendering", () => {
9
- it("children List 내부에 표시된다", () => {
9
+ it("renders children inside List", () => {
10
10
  const { getByText } = render(() => (
11
11
  <List>
12
12
  <List.Item>Item 1</List.Item>
@@ -16,7 +16,7 @@ describe("List 컴포넌트", () => {
16
16
  expect(getByText("Item 1")).toBeTruthy();
17
17
  });
18
18
 
19
- it("role=tree 속성이 적용된다", () => {
19
+ it("applies role=tree", () => {
20
20
  const { getByRole } = render(() => (
21
21
  <List>
22
22
  <List.Item>Item</List.Item>
@@ -26,7 +26,7 @@ describe("List 컴포넌트", () => {
26
26
  expect(getByRole("tree")).toBeTruthy();
27
27
  });
28
28
 
29
- it("data-list 속성이 적용된다", () => {
29
+ it("applies data-list attribute", () => {
30
30
  const { container } = render(() => (
31
31
  <List>
32
32
  <List.Item>Item</List.Item>
@@ -37,8 +37,8 @@ describe("List 컴포넌트", () => {
37
37
  });
38
38
  });
39
39
 
40
- describe("inset 속성", () => {
41
- it("inset prop에 따라 스타일이 달라진다", () => {
40
+ describe("inset prop", () => {
41
+ it("applies different styles based on inset prop", () => {
42
42
  const { container: defaultContainer } = render(() => (
43
43
  <List>
44
44
  <List.Item>Item</List.Item>
@@ -57,8 +57,8 @@ describe("List 컴포넌트", () => {
57
57
  });
58
58
  });
59
59
 
60
- describe("키보드 네비게이션", () => {
61
- it("ArrowDown 키로 다음 항목으로 포커스 이동", () => {
60
+ describe("keyboard navigation", () => {
61
+ it("ArrowDown moves focus to next item", () => {
62
62
  const { getAllByRole } = render(() => (
63
63
  <List>
64
64
  <List.Item>Item 1</List.Item>
@@ -75,7 +75,7 @@ describe("List 컴포넌트", () => {
75
75
  expect(document.activeElement).toBe(items[1]);
76
76
  });
77
77
 
78
- it("ArrowUp 키로 이전 항목으로 포커스 이동", () => {
78
+ it("ArrowUp moves focus to previous item", () => {
79
79
  const { getAllByRole } = render(() => (
80
80
  <List>
81
81
  <List.Item>Item 1</List.Item>
@@ -92,7 +92,7 @@ describe("List 컴포넌트", () => {
92
92
  expect(document.activeElement).toBe(items[0]);
93
93
  });
94
94
 
95
- it("Home 키로 번째 항목으로 포커스 이동", () => {
95
+ it("Home moves focus to first item", () => {
96
96
  const { getAllByRole } = render(() => (
97
97
  <List>
98
98
  <List.Item>Item 1</List.Item>
@@ -109,7 +109,7 @@ describe("List 컴포넌트", () => {
109
109
  expect(document.activeElement).toBe(items[0]);
110
110
  });
111
111
 
112
- it("End 키로 마지막 항목으로 포커스 이동", () => {
112
+ it("End moves focus to last item", () => {
113
113
  const { getAllByRole } = render(() => (
114
114
  <List>
115
115
  <List.Item>Item 1</List.Item>
@@ -126,7 +126,7 @@ describe("List 컴포넌트", () => {
126
126
  expect(document.activeElement).toBe(items[2]);
127
127
  });
128
128
 
129
- it("Space 키로 항목 토글", () => {
129
+ it("Space toggles item", () => {
130
130
  const { getAllByRole } = render(() => (
131
131
  <List>
132
132
  <List.Item>
@@ -148,7 +148,7 @@ describe("List 컴포넌트", () => {
148
148
  expect(items[0].getAttribute("aria-expanded")).toBe("true");
149
149
  });
150
150
 
151
- it("Enter 키로 항목 토글", () => {
151
+ it("Enter toggles item", () => {
152
152
  const { getAllByRole } = render(() => (
153
153
  <List>
154
154
  <List.Item>
@@ -168,7 +168,7 @@ describe("List 컴포넌트", () => {
168
168
  expect(items[0].getAttribute("aria-expanded")).toBe("true");
169
169
  });
170
170
 
171
- it("ArrowRight 키로 닫힌 항목 열기", () => {
171
+ it("ArrowRight opens a closed item", () => {
172
172
  const { getAllByRole } = render(() => (
173
173
  <List>
174
174
  <List.Item>
@@ -190,7 +190,7 @@ describe("List 컴포넌트", () => {
190
190
  expect(items[0].getAttribute("aria-expanded")).toBe("true");
191
191
  });
192
192
 
193
- it("ArrowRight 키로 열린 항목에서 자식으로 포커스 이동", () => {
193
+ it("ArrowRight moves focus to first child when item is open", () => {
194
194
  const { getAllByRole } = render(() => (
195
195
  <List>
196
196
  <List.Item open>
@@ -210,7 +210,7 @@ describe("List 컴포넌트", () => {
210
210
  expect(document.activeElement).toBe(items[1]);
211
211
  });
212
212
 
213
- it("ArrowLeft 키로 열린 항목 닫기", () => {
213
+ it("ArrowLeft closes an open item", () => {
214
214
  const { getAllByRole } = render(() => (
215
215
  <List>
216
216
  <List.Item open>
@@ -232,7 +232,7 @@ describe("List 컴포넌트", () => {
232
232
  expect(items[0].getAttribute("aria-expanded")).toBe("false");
233
233
  });
234
234
 
235
- it("ArrowLeft 키로 닫힌 항목에서 부모로 포커스 이동", () => {
235
+ it("ArrowLeft moves focus to parent from a closed item", () => {
236
236
  const { getAllByRole } = render(() => (
237
237
  <List>
238
238
  <List.Item open>
@@ -254,9 +254,9 @@ describe("List 컴포넌트", () => {
254
254
  });
255
255
  });
256
256
 
257
- describe("List.Item 컴포넌트", () => {
257
+ describe("List.Item component", () => {
258
258
  describe("basic rendering", () => {
259
- it("children List.Item 내부에 표시된다", () => {
259
+ it("renders children inside List.Item", () => {
260
260
  const { getByText } = render(() => (
261
261
  <List>
262
262
  <List.Item>Test Item</List.Item>
@@ -266,7 +266,7 @@ describe("List.Item 컴포넌트", () => {
266
266
  expect(getByText("Test Item")).toBeTruthy();
267
267
  });
268
268
 
269
- it("role=treeitem 속성이 적용된다", () => {
269
+ it("applies role=treeitem", () => {
270
270
  const { getByRole } = render(() => (
271
271
  <List>
272
272
  <List.Item>Item</List.Item>
@@ -276,7 +276,7 @@ describe("List.Item 컴포넌트", () => {
276
276
  expect(getByRole("treeitem")).toBeTruthy();
277
277
  });
278
278
 
279
- it("data-list-item 속성이 적용된다", () => {
279
+ it("applies data-list-item attribute", () => {
280
280
  const { container } = render(() => (
281
281
  <List>
282
282
  <List.Item>Item</List.Item>
@@ -287,8 +287,8 @@ describe("List.Item 컴포넌트", () => {
287
287
  });
288
288
  });
289
289
 
290
- describe("중첩 리스트", () => {
291
- it("List.Item.Children 있을 때 클릭 시 collapse 토글", () => {
290
+ describe("nested list", () => {
291
+ it("toggles collapse on click when List.Item.Children is present", () => {
292
292
  const { getByRole } = render(() => (
293
293
  <List>
294
294
  <List.Item>
@@ -313,7 +313,7 @@ describe("List.Item 컴포넌트", () => {
313
313
  expect(item.getAttribute("aria-expanded")).toBe("false");
314
314
  });
315
315
 
316
- it("List.Item.Children 있을 때 chevron 아이콘이 표시된다", () => {
316
+ it("shows chevron icon when List.Item.Children is present", () => {
317
317
  const { container } = render(() => (
318
318
  <List>
319
319
  <List.Item>
@@ -329,7 +329,7 @@ describe("List.Item 컴포넌트", () => {
329
329
  expect(svg).toBeTruthy();
330
330
  });
331
331
 
332
- it("List.Item.Children 없을 때 chevron 아이콘이 숨겨진다", () => {
332
+ it("hides chevron icon when List.Item.Children is absent", () => {
333
333
  const { container } = render(() => (
334
334
  <List>
335
335
  <List.Item>Simple Item</List.Item>
@@ -341,7 +341,7 @@ describe("List.Item 컴포넌트", () => {
341
341
  expect(svg).toBeFalsy();
342
342
  });
343
343
 
344
- it("aria-expanded 없으면 List.Item.Children이 없는 것으로 간주", () => {
344
+ it("no aria-expanded means no List.Item.Children", () => {
345
345
  const { getByRole } = render(() => (
346
346
  <List>
347
347
  <List.Item>Simple Item</List.Item>
@@ -352,7 +352,7 @@ describe("List.Item 컴포넌트", () => {
352
352
  expect(item.hasAttribute("aria-expanded")).toBe(false);
353
353
  });
354
354
 
355
- it("List.Item.Children 내부에 role=group이 적용된다", () => {
355
+ it("applies role=group inside List.Item.Children", () => {
356
356
  const { getByRole } = render(() => (
357
357
  <List>
358
358
  <List.Item open>
@@ -368,8 +368,8 @@ describe("List.Item 컴포넌트", () => {
368
368
  });
369
369
  });
370
370
 
371
- describe("selected 상태", () => {
372
- it("selected prop에 따라 스타일이 달라진다", () => {
371
+ describe("selected state", () => {
372
+ it("applies different styles based on selected prop", () => {
373
373
  const { container: defaultContainer } = render(() => (
374
374
  <List>
375
375
  <List.Item>Item</List.Item>
@@ -389,7 +389,7 @@ describe("List.Item 컴포넌트", () => {
389
389
  expect(defaultClass).not.toBe(selectedClass);
390
390
  });
391
391
 
392
- it("aria-selected가 설정된다", () => {
392
+ it("sets aria-selected", () => {
393
393
  const { getByRole } = render(() => (
394
394
  <List>
395
395
  <List.Item selected>Selected Item</List.Item>
@@ -401,8 +401,8 @@ describe("List.Item 컴포넌트", () => {
401
401
  });
402
402
  });
403
403
 
404
- describe("readonly 상태", () => {
405
- it("readonly=true일 클릭해도 onClick 호출되지 않음", () => {
404
+ describe("readonly state", () => {
405
+ it("does not call onClick when readonly=true", () => {
406
406
  const onClick = vi.fn();
407
407
  const { getByRole } = render(() => (
408
408
  <List>
@@ -418,7 +418,7 @@ describe("List.Item 컴포넌트", () => {
418
418
  expect(onClick).not.toHaveBeenCalled();
419
419
  });
420
420
 
421
- it("readonly prop에 따라 스타일이 달라진다", () => {
421
+ it("applies different styles based on readonly prop", () => {
422
422
  const { container: defaultContainer } = render(() => (
423
423
  <List>
424
424
  <List.Item>Item</List.Item>
@@ -440,7 +440,7 @@ describe("List.Item 컴포넌트", () => {
440
440
  });
441
441
 
442
442
  describe("disabled state", () => {
443
- it("disabled prop에 따라 스타일이 달라진다", () => {
443
+ it("applies different styles based on disabled prop", () => {
444
444
  const { container: defaultContainer } = render(() => (
445
445
  <List>
446
446
  <List.Item>Item</List.Item>
@@ -460,7 +460,7 @@ describe("List.Item 컴포넌트", () => {
460
460
  expect(defaultClass).not.toBe(disabledClass);
461
461
  });
462
462
 
463
- it("disabled=true일 때 클릭 불가", () => {
463
+ it("not clickable when disabled=true", () => {
464
464
  const onClick = vi.fn();
465
465
  const { getByRole } = render(() => (
466
466
  <List>
@@ -476,7 +476,7 @@ describe("List.Item 컴포넌트", () => {
476
476
  expect(onClick).not.toHaveBeenCalled();
477
477
  });
478
478
 
479
- it("disabled=true일 tabindex=-1 적용된다", () => {
479
+ it("applies tabindex=-1 when disabled=true", () => {
480
480
  const { getByRole } = render(() => (
481
481
  <List>
482
482
  <List.Item disabled>Disabled Item</List.Item>
@@ -487,7 +487,7 @@ describe("List.Item 컴포넌트", () => {
487
487
  expect(item.getAttribute("tabindex")).toBe("-1");
488
488
  });
489
489
 
490
- it("aria-disabled가 설정된다", () => {
490
+ it("sets aria-disabled", () => {
491
491
  const { getByRole } = render(() => (
492
492
  <List>
493
493
  <List.Item disabled>Disabled Item</List.Item>
@@ -500,7 +500,7 @@ describe("List.Item 컴포넌트", () => {
500
500
  });
501
501
 
502
502
  describe("selectedIcon", () => {
503
- it("selectedIcon 제공되고 List.Item.Children이 없을 때 아이콘이 표시된다", () => {
503
+ it("shows icon when selectedIcon is provided and no List.Item.Children", () => {
504
504
  const { container } = render(() => (
505
505
  <List>
506
506
  <List.Item selectedIcon={IconCheck}>Item</List.Item>
@@ -511,7 +511,7 @@ describe("List.Item 컴포넌트", () => {
511
511
  expect(svg).toBeTruthy();
512
512
  });
513
513
 
514
- it("selectedIcon과 selected 상태에 따라 아이콘 스타일이 달라진다", () => {
514
+ it("applies different icon styles based on selectedIcon and selected state", () => {
515
515
  const { container: unselectedContainer } = render(() => (
516
516
  <List>
517
517
  <List.Item selectedIcon={IconCheck}>Item</List.Item>
@@ -531,7 +531,7 @@ describe("List.Item 컴포넌트", () => {
531
531
  expect(unselectedSvg?.className).not.toBe(selectedSvg?.className);
532
532
  });
533
533
 
534
- it("selectedIcon 제공되고 List.Item.Children 있을 때 아이콘이 숨겨진다", () => {
534
+ it("hides selectedIcon when List.Item.Children is present", () => {
535
535
  const { container } = render(() => (
536
536
  <List>
537
537
  <List.Item selectedIcon={IconCheck}>
@@ -546,13 +546,13 @@ describe("List.Item 컴포넌트", () => {
546
546
  const button = container.querySelector("[data-list-item]") as HTMLElement;
547
547
  const svgs = button.querySelectorAll("svg");
548
548
 
549
- // chevron 있어야 (selectedIcon 숨겨짐)
549
+ // only chevron should be present (selectedIcon is hidden)
550
550
  expect(svgs.length).toBe(1);
551
551
  });
552
552
  });
553
553
 
554
554
  describe("onClick", () => {
555
- it("List.Item.Children이 없고 onClick 제공될 클릭 호출된다", () => {
555
+ it("calls onClick on click when no List.Item.Children", () => {
556
556
  const onClick = vi.fn();
557
557
  const { getByRole } = render(() => (
558
558
  <List>
@@ -566,7 +566,7 @@ describe("List.Item 컴포넌트", () => {
566
566
  expect(onClick).toHaveBeenCalledTimes(1);
567
567
  });
568
568
 
569
- it("List.Item.Children이 있을 때는 onClick 대신 collapse 토글이 발생한다", () => {
569
+ it("toggles collapse instead of onClick when List.Item.Children is present", () => {
570
570
  const onClick = vi.fn();
571
571
  const { getByRole } = render(() => (
572
572
  <List>
@@ -587,8 +587,8 @@ describe("List.Item 컴포넌트", () => {
587
587
  });
588
588
  });
589
589
 
590
- describe("controlled 모드", () => {
591
- it("open onOpenChange 제공되면 controlled 모드로 동작", () => {
590
+ describe("controlled mode", () => {
591
+ it("operates in controlled mode when open and onOpenChange are provided", () => {
592
592
  const onOpenChange = vi.fn();
593
593
 
594
594
  const { getByRole } = render(() => (
@@ -606,11 +606,11 @@ describe("List.Item 컴포넌트", () => {
606
606
  fireEvent.click(item);
607
607
 
608
608
  expect(onOpenChange).toHaveBeenCalledWith(true);
609
- // controlled 모드이므로 실제 상태는 변경되지 않음
609
+ // controlled mode: actual state does not change
610
610
  expect(item.getAttribute("aria-expanded")).toBe("false");
611
611
  });
612
612
 
613
- it("open prop 변경 상태가 반영된다", () => {
613
+ it("reflects state change when open prop changes", () => {
614
614
  const [open, setOpen] = createSignal(false);
615
615
 
616
616
  const { getByRole } = render(() => (
@@ -635,7 +635,7 @@ describe("List.Item 컴포넌트", () => {
635
635
  });
636
636
 
637
637
  describe("aria-level", () => {
638
- it("aria-level 중첩 수준에 따라 설정된다", () => {
638
+ it("sets aria-level based on nesting depth", () => {
639
639
  const { getAllByRole } = render(() => (
640
640
  <List>
641
641
  <List.Item open>
@@ -661,7 +661,7 @@ describe("List.Item 컴포넌트", () => {
661
661
  });
662
662
 
663
663
  describe("Roving tabindex", () => {
664
- it("포커스 현재 항목만 tabindex=0이 된다", () => {
664
+ it("only focused item gets tabindex=0", () => {
665
665
  const { getAllByRole } = render(() => (
666
666
  <List>
667
667
  <List.Item>Item 1</List.Item>
@@ -672,15 +672,15 @@ describe("List.Item 컴포넌트", () => {
672
672
 
673
673
  const items = getAllByRole("treeitem");
674
674
 
675
- // 초기 상태: 모든 항목이 tabindex=0
675
+ // initial state: all items have tabindex=0
676
676
  items.forEach((item) => {
677
677
  expect(item.getAttribute("tabindex")).toBe("0");
678
678
  });
679
679
 
680
- // 번째 항목 포커스
680
+ // focus second item
681
681
  fireEvent.focus(items[1]);
682
682
 
683
- // 포커스된 항목만 tabindex=0, 나머지는 -1
683
+ // only focused item has tabindex=0, others are -1
684
684
  expect(items[0].getAttribute("tabindex")).toBe("-1");
685
685
  expect(items[1].getAttribute("tabindex")).toBe("0");
686
686
  expect(items[2].getAttribute("tabindex")).toBe("-1");