@simplysm/solid 13.0.71 → 13.0.74

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 (215) hide show
  1. package/README.md +209 -202
  2. package/dist/components/data/calendar/Calendar.d.ts.map +1 -1
  3. package/dist/components/data/calendar/Calendar.js +3 -11
  4. package/dist/components/data/calendar/Calendar.js.map +2 -2
  5. package/dist/components/data/sheet/DataSheet.d.ts.map +1 -1
  6. package/dist/components/data/sheet/DataSheet.js +13 -16
  7. package/dist/components/data/sheet/DataSheet.js.map +2 -2
  8. package/dist/components/data/sheet/DataSheet.styles.d.ts.map +1 -1
  9. package/dist/components/data/sheet/DataSheet.styles.js +1 -1
  10. package/dist/components/data/sheet/DataSheet.styles.js.map +1 -1
  11. package/dist/components/data/sheet/DataSheetConfigDialog.d.ts.map +1 -1
  12. package/dist/components/data/sheet/DataSheetConfigDialog.js +27 -9
  13. package/dist/components/data/sheet/DataSheetConfigDialog.js.map +2 -2
  14. package/dist/components/disclosure/Dialog.d.ts +1 -1
  15. package/dist/components/disclosure/Dialog.d.ts.map +1 -1
  16. package/dist/components/disclosure/Dialog.js +5 -5
  17. package/dist/components/disclosure/Dialog.js.map +2 -2
  18. package/dist/components/disclosure/dialogZIndex.d.ts +1 -1
  19. package/dist/components/features/crud-detail/CrudDetail.js +23 -23
  20. package/dist/components/features/crud-detail/CrudDetail.js.map +2 -2
  21. package/dist/components/features/crud-sheet/CrudSheet.js +49 -49
  22. package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
  23. package/dist/components/features/crud-sheet/types.d.ts +4 -4
  24. package/dist/components/features/crud-sheet/types.d.ts.map +1 -1
  25. package/dist/components/features/data-select-button/DataSelectButton.d.ts +25 -7
  26. package/dist/components/features/data-select-button/DataSelectButton.d.ts.map +1 -1
  27. package/dist/components/features/data-select-button/DataSelectButton.js +27 -12
  28. package/dist/components/features/data-select-button/DataSelectButton.js.map +2 -2
  29. package/dist/components/features/permission-table/PermissionTable.js +4 -4
  30. package/dist/components/features/permission-table/PermissionTable.js.map +2 -2
  31. package/dist/components/features/shared-data/SharedDataSelect.d.ts +22 -10
  32. package/dist/components/features/shared-data/SharedDataSelect.d.ts.map +1 -1
  33. package/dist/components/features/shared-data/SharedDataSelect.js +113 -29
  34. package/dist/components/features/shared-data/SharedDataSelect.js.map +2 -2
  35. package/dist/components/features/shared-data/SharedDataSelectButton.d.ts +3 -3
  36. package/dist/components/features/shared-data/SharedDataSelectButton.d.ts.map +1 -1
  37. package/dist/components/features/shared-data/SharedDataSelectButton.js.map +1 -1
  38. package/dist/components/features/shared-data/SharedDataSelectList.js +5 -4
  39. package/dist/components/features/shared-data/SharedDataSelectList.js.map +2 -2
  40. package/dist/components/feedback/notification/NotificationBanner.js +3 -3
  41. package/dist/components/feedback/notification/NotificationBanner.js.map +2 -2
  42. package/dist/components/feedback/notification/NotificationBell.d.ts.map +1 -1
  43. package/dist/components/feedback/notification/NotificationBell.js +12 -5
  44. package/dist/components/feedback/notification/NotificationBell.js.map +2 -2
  45. package/dist/components/feedback/notification/NotificationProvider.d.ts.map +1 -1
  46. package/dist/components/feedback/notification/NotificationProvider.js +3 -1
  47. package/dist/components/feedback/notification/NotificationProvider.js.map +2 -2
  48. package/dist/components/form-control/ThemeToggle.d.ts.map +1 -1
  49. package/dist/components/form-control/ThemeToggle.js +9 -6
  50. package/dist/components/form-control/ThemeToggle.js.map +2 -2
  51. package/dist/components/form-control/checkbox/Checkbox.d.ts.map +1 -1
  52. package/dist/components/form-control/checkbox/Checkbox.js +3 -1
  53. package/dist/components/form-control/checkbox/Checkbox.js.map +2 -2
  54. package/dist/components/form-control/checkbox/CheckboxGroup.js +1 -1
  55. package/dist/components/form-control/checkbox/CheckboxGroup.js.map +2 -2
  56. package/dist/components/form-control/checkbox/Radio.d.ts.map +1 -1
  57. package/dist/components/form-control/checkbox/Radio.js +3 -1
  58. package/dist/components/form-control/checkbox/Radio.js.map +2 -2
  59. package/dist/components/form-control/checkbox/RadioGroup.js +1 -1
  60. package/dist/components/form-control/checkbox/RadioGroup.js.map +2 -2
  61. package/dist/components/form-control/color-picker/ColorPicker.d.ts.map +1 -1
  62. package/dist/components/form-control/color-picker/ColorPicker.js +3 -1
  63. package/dist/components/form-control/color-picker/ColorPicker.js.map +2 -2
  64. package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
  65. package/dist/components/form-control/combobox/Combobox.js +9 -5
  66. package/dist/components/form-control/combobox/Combobox.js.map +2 -2
  67. package/dist/components/form-control/date-range-picker/DateRangePicker.js +9 -9
  68. package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
  69. package/dist/components/form-control/editor/EditorToolbar.js +3 -3
  70. package/dist/components/form-control/editor/EditorToolbar.js.map +2 -2
  71. package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
  72. package/dist/components/form-control/field/DatePicker.js +9 -3
  73. package/dist/components/form-control/field/DatePicker.js.map +2 -2
  74. package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
  75. package/dist/components/form-control/field/DateTimePicker.js +9 -3
  76. package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
  77. package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
  78. package/dist/components/form-control/field/NumberInput.js +9 -3
  79. package/dist/components/form-control/field/NumberInput.js.map +2 -2
  80. package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
  81. package/dist/components/form-control/field/TextInput.js +10 -4
  82. package/dist/components/form-control/field/TextInput.js.map +2 -2
  83. package/dist/components/form-control/field/Textarea.d.ts.map +1 -1
  84. package/dist/components/form-control/field/Textarea.js +9 -3
  85. package/dist/components/form-control/field/Textarea.js.map +2 -2
  86. package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
  87. package/dist/components/form-control/field/TimePicker.js +9 -3
  88. package/dist/components/form-control/field/TimePicker.js.map +2 -2
  89. package/dist/components/form-control/numpad/Numpad.d.ts.map +1 -1
  90. package/dist/components/form-control/numpad/Numpad.js +5 -1
  91. package/dist/components/form-control/numpad/Numpad.js.map +2 -2
  92. package/dist/components/form-control/select/Select.js +7 -7
  93. package/dist/components/form-control/select/Select.js.map +2 -2
  94. package/dist/components/form-control/state-preset/StatePreset.d.ts.map +1 -1
  95. package/dist/components/form-control/state-preset/StatePreset.js +42 -20
  96. package/dist/components/form-control/state-preset/StatePreset.js.map +2 -2
  97. package/dist/components/layout/sidebar/SidebarContainer.js +3 -3
  98. package/dist/components/layout/sidebar/SidebarContainer.js.map +2 -2
  99. package/dist/components/layout/sidebar/SidebarMenu.d.ts.map +1 -1
  100. package/dist/components/layout/sidebar/SidebarMenu.js +5 -2
  101. package/dist/components/layout/sidebar/SidebarMenu.js.map +2 -2
  102. package/dist/components/layout/topbar/Topbar.js +3 -4
  103. package/dist/components/layout/topbar/Topbar.js.map +2 -2
  104. package/dist/components/layout/topbar/TopbarMenu.js +3 -3
  105. package/dist/components/layout/topbar/TopbarMenu.js.map +2 -2
  106. package/dist/hooks/createSelectionGroup.d.ts +2 -2
  107. package/dist/hooks/createSelectionGroup.d.ts.map +1 -1
  108. package/dist/hooks/createSelectionGroup.js +5 -2
  109. package/dist/hooks/createSelectionGroup.js.map +2 -2
  110. package/dist/providers/i18n/I18nContext.d.ts +0 -4
  111. package/dist/providers/i18n/I18nContext.d.ts.map +1 -1
  112. package/dist/providers/i18n/I18nContext.js +1 -5
  113. package/dist/providers/i18n/I18nContext.js.map +2 -2
  114. package/dist/providers/i18n/locales/en.d.ts +38 -0
  115. package/dist/providers/i18n/locales/en.d.ts.map +1 -1
  116. package/dist/providers/i18n/locales/en.js +39 -1
  117. package/dist/providers/i18n/locales/en.js.map +1 -1
  118. package/dist/providers/i18n/locales/ko.d.ts +38 -0
  119. package/dist/providers/i18n/locales/ko.d.ts.map +1 -1
  120. package/dist/providers/i18n/locales/ko.js +39 -1
  121. package/dist/providers/i18n/locales/ko.js.map +1 -1
  122. package/package.json +6 -6
  123. package/src/components/data/calendar/Calendar.tsx +3 -4
  124. package/src/components/data/sheet/DataSheet.styles.ts +1 -1
  125. package/src/components/data/sheet/DataSheet.tsx +14 -15
  126. package/src/components/data/sheet/DataSheetConfigDialog.tsx +12 -10
  127. package/src/components/data/sheet/types.ts +1 -1
  128. package/src/components/disclosure/Dialog.tsx +10 -10
  129. package/src/components/disclosure/dialogZIndex.ts +1 -1
  130. package/src/components/features/crud-detail/CrudDetail.tsx +25 -25
  131. package/src/components/features/crud-sheet/CrudSheet.tsx +53 -53
  132. package/src/components/features/crud-sheet/types.ts +4 -4
  133. package/src/components/features/data-select-button/DataSelectButton.tsx +51 -21
  134. package/src/components/features/permission-table/PermissionTable.tsx +3 -3
  135. package/src/components/features/shared-data/SharedDataSelect.tsx +172 -33
  136. package/src/components/features/shared-data/SharedDataSelectButton.tsx +3 -2
  137. package/src/components/features/shared-data/SharedDataSelectList.tsx +4 -4
  138. package/src/components/feedback/notification/NotificationBanner.tsx +3 -3
  139. package/src/components/feedback/notification/NotificationBell.tsx +6 -4
  140. package/src/components/feedback/notification/NotificationProvider.tsx +3 -1
  141. package/src/components/form-control/ThemeToggle.tsx +10 -6
  142. package/src/components/form-control/checkbox/Checkbox.tsx +4 -1
  143. package/src/components/form-control/checkbox/CheckboxGroup.tsx +1 -1
  144. package/src/components/form-control/checkbox/Radio.tsx +4 -1
  145. package/src/components/form-control/checkbox/RadioGroup.tsx +1 -1
  146. package/src/components/form-control/color-picker/ColorPicker.tsx +4 -1
  147. package/src/components/form-control/combobox/Combobox.tsx +6 -3
  148. package/src/components/form-control/date-range-picker/DateRangePicker.tsx +8 -8
  149. package/src/components/form-control/editor/EditorToolbar.tsx +23 -23
  150. package/src/components/form-control/field/DatePicker.tsx +6 -3
  151. package/src/components/form-control/field/DateTimePicker.tsx +6 -3
  152. package/src/components/form-control/field/NumberInput.tsx +6 -3
  153. package/src/components/form-control/field/TextInput.tsx +7 -4
  154. package/src/components/form-control/field/Textarea.tsx +6 -3
  155. package/src/components/form-control/field/TimePicker.tsx +6 -3
  156. package/src/components/form-control/numpad/Numpad.tsx +3 -1
  157. package/src/components/form-control/select/Select.tsx +7 -7
  158. package/src/components/form-control/state-preset/StatePreset.tsx +14 -12
  159. package/src/components/layout/sidebar/SidebarContainer.tsx +3 -3
  160. package/src/components/layout/sidebar/SidebarMenu.tsx +3 -1
  161. package/src/components/layout/topbar/Topbar.tsx +3 -3
  162. package/src/components/layout/topbar/TopbarMenu.tsx +3 -3
  163. package/src/hooks/createSelectionGroup.tsx +8 -4
  164. package/src/providers/i18n/I18nContext.tsx +0 -7
  165. package/src/providers/i18n/locales/en.ts +38 -0
  166. package/src/providers/i18n/locales/ko.ts +38 -0
  167. package/tailwind.config.ts +2 -2
  168. package/tests/components/data/kanban/Kanban.selection.spec.tsx +34 -24
  169. package/tests/components/disclosure/Dialog.spec.tsx +28 -28
  170. package/tests/components/disclosure/DialogProvider.spec.tsx +51 -25
  171. package/tests/components/features/address/AddressSearch.spec.tsx +12 -4
  172. package/tests/components/features/crud-detail/CrudDetail.spec.tsx +1 -0
  173. package/tests/components/features/crud-sheet/CrudSheet.spec.tsx +30 -6
  174. package/tests/components/features/data-select-button/DataSelectButton.spec.tsx +77 -56
  175. package/tests/components/features/permission-table/PermissionTable.spec.tsx +12 -8
  176. package/tests/components/features/shared-data/SharedDataSelect.spec.tsx +172 -0
  177. package/tests/components/features/shared-data/SharedDataSelectList.spec.tsx +14 -2
  178. package/tests/components/feedback/notification/LiveRegion.spec.tsx +20 -9
  179. package/tests/components/feedback/notification/NotificationBanner.spec.tsx +64 -46
  180. package/tests/components/feedback/notification/NotificationBell.spec.tsx +70 -51
  181. package/tests/components/feedback/notification/NotificationContext.spec.tsx +105 -78
  182. package/tests/components/form-control/checkbox/Checkbox.spec.tsx +25 -20
  183. package/tests/components/form-control/checkbox/CheckboxGroup.spec.tsx +53 -30
  184. package/tests/components/form-control/checkbox/Radio.spec.tsx +25 -20
  185. package/tests/components/form-control/checkbox/RadioGroup.spec.tsx +53 -30
  186. package/tests/components/form-control/color-picker/ColorPicker.spec.tsx +24 -15
  187. package/tests/components/form-control/combobox/Combobox.spec.tsx +92 -59
  188. package/tests/components/form-control/date-range-picker/DateRangePicker.spec.tsx +2 -2
  189. package/tests/components/form-control/field/DatePicker.spec.tsx +50 -44
  190. package/tests/components/form-control/field/DateTimePicker.spec.tsx +51 -45
  191. package/tests/components/form-control/field/NumberInput.spec.tsx +53 -47
  192. package/tests/components/form-control/field/TextInput.spec.tsx +50 -44
  193. package/tests/components/form-control/field/Textarea.spec.tsx +35 -29
  194. package/tests/components/form-control/field/TimePicker.spec.tsx +43 -37
  195. package/tests/components/form-control/numpad/Numpad.spec.tsx +175 -25
  196. package/tests/components/form-control/select/Select.spec.tsx +5 -0
  197. package/tests/components/form-control/select/SelectItem.spec.tsx +1 -0
  198. package/tests/components/layout/sidebar/Sidebar.spec.tsx +79 -35
  199. package/tests/components/layout/sidebar/SidebarContainer.spec.tsx +1 -0
  200. package/tests/components/layout/sidebar/SidebarMenu.spec.tsx +28 -17
  201. package/tests/components/layout/topbar/TopbarActions.spec.tsx +41 -23
  202. package/tests/components/layout/topbar/createTopbarActions.spec.tsx +1 -0
  203. package/tests/hooks/usePrint.spec.tsx +1 -1
  204. package/tests/hooks/useRouterLink.spec.tsx +2 -0
  205. package/tests/hooks/useSyncConfig.spec.tsx +1 -0
  206. package/tests/providers/ErrorLoggerProvider.spec.tsx +1 -0
  207. package/tests/providers/PwaUpdateProvider.spec.tsx +16 -6
  208. package/tests/providers/ServiceClientContext.spec.tsx +40 -25
  209. package/tests/providers/i18n/I18nContext.spec.tsx +3 -4
  210. package/tests/providers/shared-data/SharedDataProvider.spec.tsx +2 -0
  211. package/dist/hooks/usePrint.d.ts +0 -3
  212. package/dist/hooks/usePrint.d.ts.map +0 -1
  213. package/dist/hooks/usePrint.js +0 -5
  214. package/dist/hooks/usePrint.js.map +0 -6
  215. package/src/hooks/usePrint.ts +0 -2
@@ -1,45 +1,51 @@
1
1
  import { render, fireEvent } from "@solidjs/testing-library";
2
- import { describe, it, expect, vi } from "vitest";
2
+ import { describe, it, expect, vi, beforeEach } from "vitest";
3
3
  import { createSignal } from "solid-js";
4
4
  import { Time } from "@simplysm/core-common";
5
5
  import { TimePicker } from "../../../../src/components/form-control/field/TimePicker";
6
+ import { I18nProvider } from "../../../../src/providers/i18n/I18nContext";
7
+ import { ConfigProvider } from "../../../../src/providers/ConfigContext";
6
8
 
7
9
  describe("TimePicker component", () => {
10
+ beforeEach(() => {
11
+ localStorage.setItem("test.i18n-locale", JSON.stringify("en"));
12
+ });
13
+
8
14
  describe("basic rendering", () => {
9
15
  it("renders input type=time when unit=minute", () => {
10
- const { container } = render(() => <TimePicker unit="minute" />);
16
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="minute" /></I18nProvider></ConfigProvider>);
11
17
  const input = container.querySelector("input") as HTMLInputElement;
12
18
  expect(input).toBeTruthy();
13
19
  expect(input.type).toBe("time");
14
20
  });
15
21
 
16
22
  it("renders input type=time when unit=second", () => {
17
- const { container } = render(() => <TimePicker unit="second" />);
23
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="second" /></I18nProvider></ConfigProvider>);
18
24
  const input = container.querySelector("input") as HTMLInputElement;
19
25
  expect(input).toBeTruthy();
20
26
  expect(input.type).toBe("time");
21
27
  });
22
28
 
23
29
  it("defaults unit to minute", () => {
24
- const { container } = render(() => <TimePicker />);
30
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker /></I18nProvider></ConfigProvider>);
25
31
  const input = container.querySelector("input") as HTMLInputElement;
26
32
  expect(input.type).toBe("time");
27
33
  });
28
34
 
29
35
  it("defaults autocomplete to one-time-code", () => {
30
- const { container } = render(() => <TimePicker />);
36
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker /></I18nProvider></ConfigProvider>);
31
37
  const input = container.querySelector("input") as HTMLInputElement;
32
38
  expect(input.autocomplete).toBe("one-time-code");
33
39
  });
34
40
 
35
41
  it("sets step=1 when unit=second", () => {
36
- const { container } = render(() => <TimePicker unit="second" />);
42
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="second" /></I18nProvider></ConfigProvider>);
37
43
  const input = container.querySelector("input") as HTMLInputElement;
38
44
  expect(input.step).toBe("1");
39
45
  });
40
46
 
41
47
  it("does not set step when unit=minute", () => {
42
- const { container } = render(() => <TimePicker unit="minute" />);
48
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="minute" /></I18nProvider></ConfigProvider>);
43
49
  const input = container.querySelector("input") as HTMLInputElement;
44
50
  expect(input.step).toBe("");
45
51
  });
@@ -48,21 +54,21 @@ describe("TimePicker component", () => {
48
54
  describe("value conversion", () => {
49
55
  it("displays Time in HH:mm format for minute unit", () => {
50
56
  const time = new Time(10, 30, 45);
51
- const { container } = render(() => <TimePicker unit="minute" value={time} />);
57
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="minute" value={time} /></I18nProvider></ConfigProvider>);
52
58
  const input = container.querySelector("input") as HTMLInputElement;
53
59
  expect(input.value).toBe("10:30");
54
60
  });
55
61
 
56
62
  it("displays Time in HH:mm:ss format for second unit", () => {
57
63
  const time = new Time(10, 30, 45);
58
- const { container } = render(() => <TimePicker unit="second" value={time} />);
64
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="second" value={time} /></I18nProvider></ConfigProvider>);
59
65
  const input = container.querySelector("input") as HTMLInputElement;
60
66
  expect(input.value).toBe("10:30:45");
61
67
  });
62
68
 
63
69
  it("passes Time converted from minute input to onValueChange", () => {
64
70
  const handleChange = vi.fn();
65
- const { container } = render(() => <TimePicker unit="minute" onValueChange={handleChange} />);
71
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="minute" onValueChange={handleChange} /></I18nProvider></ConfigProvider>);
66
72
  const input = container.querySelector("input") as HTMLInputElement;
67
73
 
68
74
  fireEvent.change(input, { target: { value: "10:30" } });
@@ -76,7 +82,7 @@ describe("TimePicker component", () => {
76
82
 
77
83
  it("passes Time converted from second input to onValueChange", () => {
78
84
  const handleChange = vi.fn();
79
- const { container } = render(() => <TimePicker unit="second" onValueChange={handleChange} />);
85
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="second" onValueChange={handleChange} /></I18nProvider></ConfigProvider>);
80
86
  const input = container.querySelector("input") as HTMLInputElement;
81
87
 
82
88
  fireEvent.change(input, { target: { value: "10:30:45" } });
@@ -92,7 +98,7 @@ describe("TimePicker component", () => {
92
98
  const handleChange = vi.fn();
93
99
  const time = new Time(10, 30, 45);
94
100
  const { container } = render(() => (
95
- <TimePicker unit="minute" value={time} onValueChange={handleChange} />
101
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="minute" value={time} onValueChange={handleChange} /></I18nProvider></ConfigProvider>
96
102
  ));
97
103
  const input = container.querySelector("input") as HTMLInputElement;
98
104
 
@@ -106,7 +112,7 @@ describe("TimePicker component", () => {
106
112
  it("updates input value when external state changes", () => {
107
113
  const [value, setValue] = createSignal<Time | undefined>(new Time(10, 0, 0));
108
114
  const { container } = render(() => (
109
- <TimePicker unit="minute" value={value()} onValueChange={setValue} />
115
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="minute" value={value()} onValueChange={setValue} /></I18nProvider></ConfigProvider>
110
116
  ));
111
117
  const input = container.querySelector("input") as HTMLInputElement;
112
118
 
@@ -119,7 +125,7 @@ describe("TimePicker component", () => {
119
125
 
120
126
  describe("uncontrolled pattern", () => {
121
127
  it("manages value internally without onValueChange", () => {
122
- const { container } = render(() => <TimePicker unit="minute" value={new Time(10, 0, 0)} />);
128
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="minute" value={new Time(10, 0, 0)} /></I18nProvider></ConfigProvider>);
123
129
  const input = container.querySelector("input") as HTMLInputElement;
124
130
 
125
131
  expect(input.value).toBe("10:00");
@@ -131,7 +137,7 @@ describe("TimePicker component", () => {
131
137
 
132
138
  describe("disabled state", () => {
133
139
  it("renders as div when disabled=true", () => {
134
- const { container } = render(() => <TimePicker disabled value={new Time(10, 30, 0)} />);
140
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker disabled value={new Time(10, 30, 0)} /></I18nProvider></ConfigProvider>);
135
141
  const input = container.querySelector("input:not([aria-hidden])");
136
142
  const div = container.querySelector("div.sd-time-field");
137
143
 
@@ -141,13 +147,13 @@ describe("TimePicker component", () => {
141
147
 
142
148
  it("displays value when disabled", () => {
143
149
  const { getByText } = render(() => (
144
- <TimePicker unit="minute" disabled value={new Time(10, 30, 0)} />
150
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="minute" disabled value={new Time(10, 30, 0)} /></I18nProvider></ConfigProvider>
145
151
  ));
146
152
  expect(getByText("10:30")).toBeTruthy();
147
153
  });
148
154
 
149
155
  it("applies disabled style", () => {
150
- const { container } = render(() => <TimePicker disabled value={new Time(10, 30, 0)} />);
156
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker disabled value={new Time(10, 30, 0)} /></I18nProvider></ConfigProvider>);
151
157
  const div = container.querySelector("div.sd-time-field") as HTMLElement;
152
158
  expect(div.classList.contains("bg-base-100")).toBe(true);
153
159
  });
@@ -155,7 +161,7 @@ describe("TimePicker component", () => {
155
161
 
156
162
  describe("readonly state", () => {
157
163
  it("renders as div when readonly=true", () => {
158
- const { container } = render(() => <TimePicker readonly value={new Time(10, 30, 0)} />);
164
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker readonly value={new Time(10, 30, 0)} /></I18nProvider></ConfigProvider>);
159
165
  const input = container.querySelector("input:not([aria-hidden])");
160
166
  const div = container.querySelector("div.sd-time-field");
161
167
 
@@ -165,7 +171,7 @@ describe("TimePicker component", () => {
165
171
 
166
172
  it("displays value when readonly", () => {
167
173
  const { getByText } = render(() => (
168
- <TimePicker unit="minute" readonly value={new Time(10, 30, 0)} />
174
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker unit="minute" readonly value={new Time(10, 30, 0)} /></I18nProvider></ConfigProvider>
169
175
  ));
170
176
  expect(getByText("10:30")).toBeTruthy();
171
177
  });
@@ -173,13 +179,13 @@ describe("TimePicker component", () => {
173
179
 
174
180
  describe("size option", () => {
175
181
  it("applies small padding when size=sm", () => {
176
- const { container } = render(() => <TimePicker size="sm" />);
182
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker size="sm" /></I18nProvider></ConfigProvider>);
177
183
  const wrapper = container.firstChild as HTMLElement;
178
184
  expect(wrapper.classList.contains("py-0.5")).toBe(true);
179
185
  });
180
186
 
181
187
  it("applies large padding when size=lg", () => {
182
- const { container } = render(() => <TimePicker size="lg" />);
188
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker size="lg" /></I18nProvider></ConfigProvider>);
183
189
  const wrapper = container.firstChild as HTMLElement;
184
190
  expect(wrapper.classList.contains("py-2")).toBe(true);
185
191
  });
@@ -187,7 +193,7 @@ describe("TimePicker component", () => {
187
193
 
188
194
  describe("inset style", () => {
189
195
  it("applies relative to outer and inset style to content when inset=true", () => {
190
- const { container } = render(() => <TimePicker inset />);
196
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker inset /></I18nProvider></ConfigProvider>);
191
197
  const outer = container.firstChild as HTMLElement;
192
198
  expect(outer.classList.contains("relative")).toBe(true);
193
199
  expect(outer.classList.contains("border-none")).toBe(false);
@@ -198,7 +204,7 @@ describe("TimePicker component", () => {
198
204
  });
199
205
 
200
206
  it("shows content div and no input when inset + readonly", () => {
201
- const { container } = render(() => <TimePicker inset readonly value={new Time(14, 30, 0)} />);
207
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker inset readonly value={new Time(14, 30, 0)} /></I18nProvider></ConfigProvider>);
202
208
  const outer = container.firstChild as HTMLElement;
203
209
  expect(outer.classList.contains("relative")).toBe(true);
204
210
 
@@ -210,7 +216,7 @@ describe("TimePicker component", () => {
210
216
  });
211
217
 
212
218
  it("shows hidden content div and input when inset + editable", () => {
213
- const { container } = render(() => <TimePicker inset value={new Time(14, 30, 0)} />);
219
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker inset value={new Time(14, 30, 0)} /></I18nProvider></ConfigProvider>);
214
220
  const outer = container.firstChild as HTMLElement;
215
221
 
216
222
  const contentDiv = outer.querySelector("[data-time-field-content]") as HTMLElement;
@@ -221,7 +227,7 @@ describe("TimePicker component", () => {
221
227
  });
222
228
 
223
229
  it("shows NBSP in content div when inset + empty value", () => {
224
- const { container } = render(() => <TimePicker inset readonly />);
230
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker inset readonly /></I18nProvider></ConfigProvider>);
225
231
  const outer = container.firstChild as HTMLElement;
226
232
  const contentDiv = outer.querySelector("[data-time-field-content]") as HTMLElement;
227
233
  expect(contentDiv.textContent).toBe("\u00A0");
@@ -230,13 +236,13 @@ describe("TimePicker component", () => {
230
236
 
231
237
  describe("dark mode style", () => {
232
238
  it("applies dark mode border style", () => {
233
- const { container } = render(() => <TimePicker />);
239
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker /></I18nProvider></ConfigProvider>);
234
240
  const wrapper = container.firstChild as HTMLElement;
235
241
  expect(wrapper.classList.contains("dark:border-base-700")).toBe(true);
236
242
  });
237
243
 
238
244
  it("applies dark mode background style", () => {
239
- const { container } = render(() => <TimePicker />);
245
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker /></I18nProvider></ConfigProvider>);
240
246
  const wrapper = container.firstChild as HTMLElement;
241
247
  expect(wrapper.classList.contains("dark:bg-primary-950/30")).toBe(true);
242
248
  });
@@ -245,7 +251,7 @@ describe("TimePicker component", () => {
245
251
  describe("class merging", () => {
246
252
  it("merges custom class with existing styles", () => {
247
253
  // eslint-disable-next-line tailwindcss/no-custom-classname
248
- const { container } = render(() => <TimePicker class="my-custom-class" />);
254
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker class="my-custom-class" /></I18nProvider></ConfigProvider>);
249
255
  const wrapper = container.firstChild as HTMLElement;
250
256
  expect(wrapper.classList.contains("my-custom-class")).toBe(true);
251
257
  });
@@ -253,20 +259,20 @@ describe("TimePicker component", () => {
253
259
 
254
260
  describe("validation", () => {
255
261
  it("sets error message when required and value is empty", () => {
256
- const { container } = render(() => <TimePicker required value={undefined} />);
262
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker required value={undefined} /></I18nProvider></ConfigProvider>);
257
263
  const hiddenInput = container.querySelector("input[aria-hidden='true']") as HTMLInputElement;
258
- expect(hiddenInput.validationMessage).toBe("This field is required");
264
+ expect(hiddenInput.validationMessage).toBe("This is a required field");
259
265
  });
260
266
 
261
267
  it("is valid when required and value exists", () => {
262
- const { container } = render(() => <TimePicker required value={new Time(10, 0, 0)} />);
268
+ const { container } = render(() => <ConfigProvider clientName="test"><I18nProvider><TimePicker required value={new Time(10, 0, 0)} /></I18nProvider></ConfigProvider>);
263
269
  const hiddenInput = container.querySelector("input[aria-hidden='true']") as HTMLInputElement;
264
270
  expect(hiddenInput.validity.valid).toBe(true);
265
271
  });
266
272
 
267
273
  it("sets error message when min is violated", () => {
268
274
  const { container } = render(() => (
269
- <TimePicker min={new Time(12, 0, 0)} value={new Time(8, 0, 0)} />
275
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker min={new Time(12, 0, 0)} value={new Time(8, 0, 0)} /></I18nProvider></ConfigProvider>
270
276
  ));
271
277
  const hiddenInput = container.querySelector("input[aria-hidden='true']") as HTMLInputElement;
272
278
  expect(hiddenInput.validationMessage).not.toBe("");
@@ -274,7 +280,7 @@ describe("TimePicker component", () => {
274
280
 
275
281
  it("is valid when min is satisfied", () => {
276
282
  const { container } = render(() => (
277
- <TimePicker min={new Time(8, 0, 0)} value={new Time(12, 0, 0)} />
283
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker min={new Time(8, 0, 0)} value={new Time(12, 0, 0)} /></I18nProvider></ConfigProvider>
278
284
  ));
279
285
  const hiddenInput = container.querySelector("input[aria-hidden='true']") as HTMLInputElement;
280
286
  expect(hiddenInput.validity.valid).toBe(true);
@@ -282,7 +288,7 @@ describe("TimePicker component", () => {
282
288
 
283
289
  it("sets error message when max is violated", () => {
284
290
  const { container } = render(() => (
285
- <TimePicker max={new Time(12, 0, 0)} value={new Time(18, 0, 0)} />
291
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker max={new Time(12, 0, 0)} value={new Time(18, 0, 0)} /></I18nProvider></ConfigProvider>
286
292
  ));
287
293
  const hiddenInput = container.querySelector("input[aria-hidden='true']") as HTMLInputElement;
288
294
  expect(hiddenInput.validationMessage).not.toBe("");
@@ -290,7 +296,7 @@ describe("TimePicker component", () => {
290
296
 
291
297
  it("is valid when max is satisfied", () => {
292
298
  const { container } = render(() => (
293
- <TimePicker max={new Time(23, 59, 59)} value={new Time(12, 0, 0)} />
299
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker max={new Time(23, 59, 59)} value={new Time(12, 0, 0)} /></I18nProvider></ConfigProvider>
294
300
  ));
295
301
  const hiddenInput = container.querySelector("input[aria-hidden='true']") as HTMLInputElement;
296
302
  expect(hiddenInput.validity.valid).toBe(true);
@@ -298,7 +304,7 @@ describe("TimePicker component", () => {
298
304
 
299
305
  it("sets error message returned by validate function", () => {
300
306
  const { container } = render(() => (
301
- <TimePicker value={new Time(10, 0, 0)} validate={() => "커스텀 에러"} />
307
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker value={new Time(10, 0, 0)} validate={() => "커스텀 에러"} /></I18nProvider></ConfigProvider>
302
308
  ));
303
309
  const hiddenInput = container.querySelector("input[aria-hidden='true']") as HTMLInputElement;
304
310
  expect(hiddenInput.validationMessage).toBe("커스텀 에러");
@@ -306,7 +312,7 @@ describe("TimePicker component", () => {
306
312
 
307
313
  it("is valid when validate function returns undefined", () => {
308
314
  const { container } = render(() => (
309
- <TimePicker value={new Time(10, 0, 0)} validate={() => undefined} />
315
+ <ConfigProvider clientName="test"><I18nProvider><TimePicker value={new Time(10, 0, 0)} validate={() => undefined} /></I18nProvider></ConfigProvider>
310
316
  ));
311
317
  const hiddenInput = container.querySelector("input[aria-hidden='true']") as HTMLInputElement;
312
318
  expect(hiddenInput.validity.valid).toBe(true);
@@ -1,18 +1,36 @@
1
- import { describe, it, expect, vi } from "vitest";
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
2
  import { render, fireEvent, screen } from "@solidjs/testing-library";
3
3
  import { createSignal } from "solid-js";
4
4
  import { Numpad } from "../../../../src/components/form-control/numpad/Numpad";
5
+ import { I18nProvider } from "../../../../src/providers/i18n/I18nContext";
6
+ import { ConfigProvider } from "../../../../src/providers/ConfigContext";
5
7
 
6
8
  describe("Numpad", () => {
9
+ beforeEach(() => {
10
+ localStorage.setItem("test.i18n-locale", JSON.stringify("en"));
11
+ });
12
+
7
13
  describe("basic rendering", () => {
8
14
  it("renders root element with data-numpad attribute", () => {
9
- const { container } = render(() => <Numpad />);
15
+ const { container } = render(() => (
16
+ <ConfigProvider clientName="test">
17
+ <I18nProvider>
18
+ <Numpad />
19
+ </I18nProvider>
20
+ </ConfigProvider>
21
+ ));
10
22
  const root = container.querySelector("[data-numpad]");
11
23
  expect(root).toBeTruthy();
12
24
  });
13
25
 
14
26
  it("renders digit buttons 0-9", () => {
15
- render(() => <Numpad />);
27
+ render(() => (
28
+ <ConfigProvider clientName="test">
29
+ <I18nProvider>
30
+ <Numpad />
31
+ </I18nProvider>
32
+ </ConfigProvider>
33
+ ));
16
34
 
17
35
  for (let i = 0; i <= 9; i++) {
18
36
  expect(screen.getByText(String(i))).toBeInTheDocument();
@@ -20,18 +38,36 @@ describe("Numpad", () => {
20
38
  });
21
39
 
22
40
  it("renders decimal point button", () => {
23
- render(() => <Numpad />);
41
+ render(() => (
42
+ <ConfigProvider clientName="test">
43
+ <I18nProvider>
44
+ <Numpad />
45
+ </I18nProvider>
46
+ </ConfigProvider>
47
+ ));
24
48
  expect(screen.getByText(".")).toBeInTheDocument();
25
49
  });
26
50
 
27
51
  it("renders NumberInput (input)", () => {
28
- render(() => <Numpad />);
52
+ render(() => (
53
+ <ConfigProvider clientName="test">
54
+ <I18nProvider>
55
+ <Numpad />
56
+ </I18nProvider>
57
+ </ConfigProvider>
58
+ ));
29
59
  const input = screen.getByRole("textbox");
30
60
  expect(input).toBeInTheDocument();
31
61
  });
32
62
 
33
63
  it("renders C button with text-danger-500 class", () => {
34
- render(() => <Numpad />);
64
+ render(() => (
65
+ <ConfigProvider clientName="test">
66
+ <I18nProvider>
67
+ <Numpad />
68
+ </I18nProvider>
69
+ </ConfigProvider>
70
+ ));
35
71
  const cButton = screen
36
72
  .getAllByRole("button")
37
73
  .find((btn) => btn.className.includes("text-danger-500"));
@@ -39,7 +75,13 @@ describe("Numpad", () => {
39
75
  });
40
76
 
41
77
  it("renders BS button with text-warning-500 class", () => {
42
- render(() => <Numpad />);
78
+ render(() => (
79
+ <ConfigProvider clientName="test">
80
+ <I18nProvider>
81
+ <Numpad />
82
+ </I18nProvider>
83
+ </ConfigProvider>
84
+ ));
43
85
  const bsButton = screen
44
86
  .getAllByRole("button")
45
87
  .find((btn) => btn.className.includes("text-warning-500"));
@@ -47,12 +89,24 @@ describe("Numpad", () => {
47
89
  });
48
90
 
49
91
  it("does not render ENT button by default", () => {
50
- render(() => <Numpad />);
92
+ render(() => (
93
+ <ConfigProvider clientName="test">
94
+ <I18nProvider>
95
+ <Numpad />
96
+ </I18nProvider>
97
+ </ConfigProvider>
98
+ ));
51
99
  expect(screen.queryByText("ENT")).not.toBeInTheDocument();
52
100
  });
53
101
 
54
102
  it("does not render minus button by default", () => {
55
- render(() => <Numpad />);
103
+ render(() => (
104
+ <ConfigProvider clientName="test">
105
+ <I18nProvider>
106
+ <Numpad />
107
+ </I18nProvider>
108
+ </ConfigProvider>
109
+ ));
56
110
  // without useMinusButton, there should be no button with "-" text
57
111
  const minusButton = screen.getAllByRole("button").find((btn) => btn.textContent === "-");
58
112
  expect(minusButton).toBeFalsy();
@@ -62,7 +116,13 @@ describe("Numpad", () => {
62
116
  describe("digit input", () => {
63
117
  it("updates value on digit button click", () => {
64
118
  const handleChange = vi.fn();
65
- render(() => <Numpad onValueChange={handleChange} />);
119
+ render(() => (
120
+ <ConfigProvider clientName="test">
121
+ <I18nProvider>
122
+ <Numpad onValueChange={handleChange} />
123
+ </I18nProvider>
124
+ </ConfigProvider>
125
+ ));
66
126
 
67
127
  fireEvent.click(screen.getByText("1"));
68
128
  fireEvent.click(screen.getByText("2"));
@@ -73,7 +133,13 @@ describe("Numpad", () => {
73
133
 
74
134
  it("appends decimal point on decimal button click", () => {
75
135
  const handleChange = vi.fn();
76
- render(() => <Numpad onValueChange={handleChange} />);
136
+ render(() => (
137
+ <ConfigProvider clientName="test">
138
+ <I18nProvider>
139
+ <Numpad onValueChange={handleChange} />
140
+ </I18nProvider>
141
+ </ConfigProvider>
142
+ ));
77
143
 
78
144
  fireEvent.click(screen.getByText("1"));
79
145
  fireEvent.click(screen.getByText("."));
@@ -84,7 +150,13 @@ describe("Numpad", () => {
84
150
 
85
151
  it("ignores duplicate decimal point", () => {
86
152
  const handleChange = vi.fn();
87
- render(() => <Numpad onValueChange={handleChange} />);
153
+ render(() => (
154
+ <ConfigProvider clientName="test">
155
+ <I18nProvider>
156
+ <Numpad onValueChange={handleChange} />
157
+ </I18nProvider>
158
+ </ConfigProvider>
159
+ ));
88
160
 
89
161
  fireEvent.click(screen.getByText("1"));
90
162
  fireEvent.click(screen.getByText("."));
@@ -96,7 +168,13 @@ describe("Numpad", () => {
96
168
 
97
169
  it("allows clicking 0 multiple times", () => {
98
170
  const handleChange = vi.fn();
99
- render(() => <Numpad onValueChange={handleChange} />);
171
+ render(() => (
172
+ <ConfigProvider clientName="test">
173
+ <I18nProvider>
174
+ <Numpad onValueChange={handleChange} />
175
+ </I18nProvider>
176
+ </ConfigProvider>
177
+ ));
100
178
 
101
179
  fireEvent.click(screen.getByText("1"));
102
180
  fireEvent.click(screen.getByText("0"));
@@ -109,7 +187,13 @@ describe("Numpad", () => {
109
187
  describe("function buttons", () => {
110
188
  it("clears value on C button click", () => {
111
189
  const handleChange = vi.fn();
112
- render(() => <Numpad onValueChange={handleChange} />);
190
+ render(() => (
191
+ <ConfigProvider clientName="test">
192
+ <I18nProvider>
193
+ <Numpad onValueChange={handleChange} />
194
+ </I18nProvider>
195
+ </ConfigProvider>
196
+ ));
113
197
 
114
198
  // enter value
115
199
  fireEvent.click(screen.getByText("5"));
@@ -126,7 +210,13 @@ describe("Numpad", () => {
126
210
 
127
211
  it("removes last character on BS button click", () => {
128
212
  const handleChange = vi.fn();
129
- render(() => <Numpad onValueChange={handleChange} />);
213
+ render(() => (
214
+ <ConfigProvider clientName="test">
215
+ <I18nProvider>
216
+ <Numpad onValueChange={handleChange} />
217
+ </I18nProvider>
218
+ </ConfigProvider>
219
+ ));
130
220
 
131
221
  // enter 123
132
222
  fireEvent.click(screen.getByText("1"));
@@ -144,7 +234,13 @@ describe("Numpad", () => {
144
234
 
145
235
  it("returns undefined when all characters are removed with BS", () => {
146
236
  const handleChange = vi.fn();
147
- render(() => <Numpad onValueChange={handleChange} />);
237
+ render(() => (
238
+ <ConfigProvider clientName="test">
239
+ <I18nProvider>
240
+ <Numpad onValueChange={handleChange} />
241
+ </I18nProvider>
242
+ </ConfigProvider>
243
+ ));
148
244
 
149
245
  // enter 1
150
246
  fireEvent.click(screen.getByText("1"));
@@ -161,27 +257,51 @@ describe("Numpad", () => {
161
257
 
162
258
  describe("ENT button", () => {
163
259
  it("renders ENT button when useEnterButton=true", () => {
164
- render(() => <Numpad useEnterButton />);
260
+ render(() => (
261
+ <ConfigProvider clientName="test">
262
+ <I18nProvider>
263
+ <Numpad useEnterButton />
264
+ </I18nProvider>
265
+ </ConfigProvider>
266
+ ));
165
267
  expect(screen.getByText("ENT")).toBeInTheDocument();
166
268
  });
167
269
 
168
270
  it("calls onEnterButtonClick on ENT button click", () => {
169
271
  const handleEnter = vi.fn();
170
- render(() => <Numpad useEnterButton onEnterButtonClick={handleEnter} />);
272
+ render(() => (
273
+ <ConfigProvider clientName="test">
274
+ <I18nProvider>
275
+ <Numpad useEnterButton onEnterButtonClick={handleEnter} />
276
+ </I18nProvider>
277
+ </ConfigProvider>
278
+ ));
171
279
 
172
280
  fireEvent.click(screen.getByText("ENT"));
173
281
  expect(handleEnter).toHaveBeenCalledTimes(1);
174
282
  });
175
283
 
176
284
  it("disables ENT button when required and no value", () => {
177
- render(() => <Numpad useEnterButton required />);
285
+ render(() => (
286
+ <ConfigProvider clientName="test">
287
+ <I18nProvider>
288
+ <Numpad useEnterButton required />
289
+ </I18nProvider>
290
+ </ConfigProvider>
291
+ ));
178
292
 
179
293
  const entButton = screen.getByText("ENT").closest("button")!;
180
294
  expect(entButton.disabled).toBe(true);
181
295
  });
182
296
 
183
297
  it("enables ENT button when required and value exists", () => {
184
- render(() => <Numpad useEnterButton required value={123} />);
298
+ render(() => (
299
+ <ConfigProvider clientName="test">
300
+ <I18nProvider>
301
+ <Numpad useEnterButton required value={123} />
302
+ </I18nProvider>
303
+ </ConfigProvider>
304
+ ));
185
305
 
186
306
  const entButton = screen.getByText("ENT").closest("button")!;
187
307
  expect(entButton.disabled).toBe(false);
@@ -190,7 +310,13 @@ describe("Numpad", () => {
190
310
 
191
311
  describe("minus button", () => {
192
312
  it("renders minus button when useMinusButton=true", () => {
193
- render(() => <Numpad useMinusButton />);
313
+ render(() => (
314
+ <ConfigProvider clientName="test">
315
+ <I18nProvider>
316
+ <Numpad useMinusButton />
317
+ </I18nProvider>
318
+ </ConfigProvider>
319
+ ));
194
320
 
195
321
  const minusButton = screen.getAllByRole("button").find((btn) => btn.textContent === "-");
196
322
  expect(minusButton).toBeTruthy();
@@ -198,7 +324,13 @@ describe("Numpad", () => {
198
324
 
199
325
  it("toggles sign on minus button click (positive to negative)", () => {
200
326
  const handleChange = vi.fn();
201
- render(() => <Numpad useMinusButton onValueChange={handleChange} />);
327
+ render(() => (
328
+ <ConfigProvider clientName="test">
329
+ <I18nProvider>
330
+ <Numpad useMinusButton onValueChange={handleChange} />
331
+ </I18nProvider>
332
+ </ConfigProvider>
333
+ ));
202
334
 
203
335
  // enter 5
204
336
  fireEvent.click(screen.getByText("5"));
@@ -212,7 +344,13 @@ describe("Numpad", () => {
212
344
 
213
345
  it("toggles sign on minus button click (negative to positive)", () => {
214
346
  const handleChange = vi.fn();
215
- render(() => <Numpad useMinusButton value={-5} onValueChange={handleChange} />);
347
+ render(() => (
348
+ <ConfigProvider clientName="test">
349
+ <I18nProvider>
350
+ <Numpad useMinusButton value={-5} onValueChange={handleChange} />
351
+ </I18nProvider>
352
+ </ConfigProvider>
353
+ ));
216
354
 
217
355
  // click minus button
218
356
  const minusButton = screen.getAllByRole("button").find((btn) => btn.textContent === "-")!;
@@ -225,7 +363,13 @@ describe("Numpad", () => {
225
363
  describe("controlled mode", () => {
226
364
  it("reflects external value changes", () => {
227
365
  const [value, setValue] = createSignal<number | undefined>(100);
228
- render(() => <Numpad value={value()} onValueChange={setValue} />);
366
+ render(() => (
367
+ <ConfigProvider clientName="test">
368
+ <I18nProvider>
369
+ <Numpad value={value()} onValueChange={setValue} />
370
+ </I18nProvider>
371
+ </ConfigProvider>
372
+ ));
229
373
 
230
374
  const input = screen.getByRole("textbox");
231
375
  expect(input).toHaveValue("100");
@@ -236,7 +380,13 @@ describe("Numpad", () => {
236
380
 
237
381
  it("clears input when set to undefined externally", () => {
238
382
  const [value, setValue] = createSignal<number | undefined>(100);
239
- render(() => <Numpad value={value()} onValueChange={setValue} />);
383
+ render(() => (
384
+ <ConfigProvider clientName="test">
385
+ <I18nProvider>
386
+ <Numpad value={value()} onValueChange={setValue} />
387
+ </I18nProvider>
388
+ </ConfigProvider>
389
+ ));
240
390
 
241
391
  const input = screen.getByRole("textbox");
242
392
  expect(input).toHaveValue("100");