@simplysm/solid 13.0.72 → 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 (210) 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.js +10 -10
  6. package/dist/components/data/sheet/DataSheet.js.map +2 -2
  7. package/dist/components/data/sheet/DataSheetConfigDialog.d.ts.map +1 -1
  8. package/dist/components/data/sheet/DataSheetConfigDialog.js +27 -9
  9. package/dist/components/data/sheet/DataSheetConfigDialog.js.map +2 -2
  10. package/dist/components/disclosure/Dialog.d.ts +1 -1
  11. package/dist/components/disclosure/Dialog.d.ts.map +1 -1
  12. package/dist/components/disclosure/Dialog.js +5 -5
  13. package/dist/components/disclosure/Dialog.js.map +2 -2
  14. package/dist/components/disclosure/dialogZIndex.d.ts +1 -1
  15. package/dist/components/features/crud-detail/CrudDetail.js +23 -23
  16. package/dist/components/features/crud-detail/CrudDetail.js.map +2 -2
  17. package/dist/components/features/crud-sheet/CrudSheet.js +49 -49
  18. package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
  19. package/dist/components/features/crud-sheet/types.d.ts +4 -4
  20. package/dist/components/features/crud-sheet/types.d.ts.map +1 -1
  21. package/dist/components/features/data-select-button/DataSelectButton.d.ts +25 -7
  22. package/dist/components/features/data-select-button/DataSelectButton.d.ts.map +1 -1
  23. package/dist/components/features/data-select-button/DataSelectButton.js +27 -12
  24. package/dist/components/features/data-select-button/DataSelectButton.js.map +2 -2
  25. package/dist/components/features/permission-table/PermissionTable.js +4 -4
  26. package/dist/components/features/permission-table/PermissionTable.js.map +2 -2
  27. package/dist/components/features/shared-data/SharedDataSelect.d.ts +22 -10
  28. package/dist/components/features/shared-data/SharedDataSelect.d.ts.map +1 -1
  29. package/dist/components/features/shared-data/SharedDataSelect.js +113 -29
  30. package/dist/components/features/shared-data/SharedDataSelect.js.map +2 -2
  31. package/dist/components/features/shared-data/SharedDataSelectButton.d.ts +3 -3
  32. package/dist/components/features/shared-data/SharedDataSelectButton.d.ts.map +1 -1
  33. package/dist/components/features/shared-data/SharedDataSelectButton.js.map +1 -1
  34. package/dist/components/features/shared-data/SharedDataSelectList.js +5 -4
  35. package/dist/components/features/shared-data/SharedDataSelectList.js.map +2 -2
  36. package/dist/components/feedback/notification/NotificationBanner.js +3 -3
  37. package/dist/components/feedback/notification/NotificationBanner.js.map +2 -2
  38. package/dist/components/feedback/notification/NotificationBell.d.ts.map +1 -1
  39. package/dist/components/feedback/notification/NotificationBell.js +12 -5
  40. package/dist/components/feedback/notification/NotificationBell.js.map +2 -2
  41. package/dist/components/feedback/notification/NotificationProvider.d.ts.map +1 -1
  42. package/dist/components/feedback/notification/NotificationProvider.js +3 -1
  43. package/dist/components/feedback/notification/NotificationProvider.js.map +2 -2
  44. package/dist/components/form-control/ThemeToggle.d.ts.map +1 -1
  45. package/dist/components/form-control/ThemeToggle.js +9 -6
  46. package/dist/components/form-control/ThemeToggle.js.map +2 -2
  47. package/dist/components/form-control/checkbox/Checkbox.d.ts.map +1 -1
  48. package/dist/components/form-control/checkbox/Checkbox.js +3 -1
  49. package/dist/components/form-control/checkbox/Checkbox.js.map +2 -2
  50. package/dist/components/form-control/checkbox/CheckboxGroup.js +1 -1
  51. package/dist/components/form-control/checkbox/CheckboxGroup.js.map +2 -2
  52. package/dist/components/form-control/checkbox/Radio.d.ts.map +1 -1
  53. package/dist/components/form-control/checkbox/Radio.js +3 -1
  54. package/dist/components/form-control/checkbox/Radio.js.map +2 -2
  55. package/dist/components/form-control/checkbox/RadioGroup.js +1 -1
  56. package/dist/components/form-control/checkbox/RadioGroup.js.map +2 -2
  57. package/dist/components/form-control/color-picker/ColorPicker.d.ts.map +1 -1
  58. package/dist/components/form-control/color-picker/ColorPicker.js +3 -1
  59. package/dist/components/form-control/color-picker/ColorPicker.js.map +2 -2
  60. package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
  61. package/dist/components/form-control/combobox/Combobox.js +9 -5
  62. package/dist/components/form-control/combobox/Combobox.js.map +2 -2
  63. package/dist/components/form-control/date-range-picker/DateRangePicker.js +9 -9
  64. package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
  65. package/dist/components/form-control/editor/EditorToolbar.js +3 -3
  66. package/dist/components/form-control/editor/EditorToolbar.js.map +2 -2
  67. package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
  68. package/dist/components/form-control/field/DatePicker.js +9 -3
  69. package/dist/components/form-control/field/DatePicker.js.map +2 -2
  70. package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
  71. package/dist/components/form-control/field/DateTimePicker.js +9 -3
  72. package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
  73. package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
  74. package/dist/components/form-control/field/NumberInput.js +9 -3
  75. package/dist/components/form-control/field/NumberInput.js.map +2 -2
  76. package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
  77. package/dist/components/form-control/field/TextInput.js +10 -4
  78. package/dist/components/form-control/field/TextInput.js.map +2 -2
  79. package/dist/components/form-control/field/Textarea.d.ts.map +1 -1
  80. package/dist/components/form-control/field/Textarea.js +9 -3
  81. package/dist/components/form-control/field/Textarea.js.map +2 -2
  82. package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
  83. package/dist/components/form-control/field/TimePicker.js +9 -3
  84. package/dist/components/form-control/field/TimePicker.js.map +2 -2
  85. package/dist/components/form-control/numpad/Numpad.d.ts.map +1 -1
  86. package/dist/components/form-control/numpad/Numpad.js +5 -1
  87. package/dist/components/form-control/numpad/Numpad.js.map +2 -2
  88. package/dist/components/form-control/select/Select.js +7 -7
  89. package/dist/components/form-control/select/Select.js.map +2 -2
  90. package/dist/components/form-control/state-preset/StatePreset.d.ts.map +1 -1
  91. package/dist/components/form-control/state-preset/StatePreset.js +42 -20
  92. package/dist/components/form-control/state-preset/StatePreset.js.map +2 -2
  93. package/dist/components/layout/sidebar/SidebarContainer.js +3 -3
  94. package/dist/components/layout/sidebar/SidebarContainer.js.map +2 -2
  95. package/dist/components/layout/sidebar/SidebarMenu.d.ts.map +1 -1
  96. package/dist/components/layout/sidebar/SidebarMenu.js +5 -2
  97. package/dist/components/layout/sidebar/SidebarMenu.js.map +2 -2
  98. package/dist/components/layout/topbar/Topbar.js +3 -4
  99. package/dist/components/layout/topbar/Topbar.js.map +2 -2
  100. package/dist/components/layout/topbar/TopbarMenu.js +3 -3
  101. package/dist/components/layout/topbar/TopbarMenu.js.map +2 -2
  102. package/dist/hooks/createSelectionGroup.d.ts +2 -2
  103. package/dist/hooks/createSelectionGroup.d.ts.map +1 -1
  104. package/dist/hooks/createSelectionGroup.js +5 -2
  105. package/dist/hooks/createSelectionGroup.js.map +2 -2
  106. package/dist/providers/i18n/I18nContext.d.ts +0 -4
  107. package/dist/providers/i18n/I18nContext.d.ts.map +1 -1
  108. package/dist/providers/i18n/I18nContext.js +1 -5
  109. package/dist/providers/i18n/I18nContext.js.map +2 -2
  110. package/dist/providers/i18n/locales/en.d.ts +38 -0
  111. package/dist/providers/i18n/locales/en.d.ts.map +1 -1
  112. package/dist/providers/i18n/locales/en.js +39 -1
  113. package/dist/providers/i18n/locales/en.js.map +1 -1
  114. package/dist/providers/i18n/locales/ko.d.ts +38 -0
  115. package/dist/providers/i18n/locales/ko.d.ts.map +1 -1
  116. package/dist/providers/i18n/locales/ko.js +39 -1
  117. package/dist/providers/i18n/locales/ko.js.map +1 -1
  118. package/package.json +6 -6
  119. package/src/components/data/calendar/Calendar.tsx +3 -4
  120. package/src/components/data/sheet/DataSheet.tsx +11 -11
  121. package/src/components/data/sheet/DataSheetConfigDialog.tsx +12 -10
  122. package/src/components/data/sheet/types.ts +1 -1
  123. package/src/components/disclosure/Dialog.tsx +10 -10
  124. package/src/components/disclosure/dialogZIndex.ts +1 -1
  125. package/src/components/features/crud-detail/CrudDetail.tsx +25 -25
  126. package/src/components/features/crud-sheet/CrudSheet.tsx +53 -53
  127. package/src/components/features/crud-sheet/types.ts +4 -4
  128. package/src/components/features/data-select-button/DataSelectButton.tsx +51 -21
  129. package/src/components/features/permission-table/PermissionTable.tsx +3 -3
  130. package/src/components/features/shared-data/SharedDataSelect.tsx +172 -33
  131. package/src/components/features/shared-data/SharedDataSelectButton.tsx +3 -2
  132. package/src/components/features/shared-data/SharedDataSelectList.tsx +4 -4
  133. package/src/components/feedback/notification/NotificationBanner.tsx +3 -3
  134. package/src/components/feedback/notification/NotificationBell.tsx +6 -4
  135. package/src/components/feedback/notification/NotificationProvider.tsx +3 -1
  136. package/src/components/form-control/ThemeToggle.tsx +10 -6
  137. package/src/components/form-control/checkbox/Checkbox.tsx +4 -1
  138. package/src/components/form-control/checkbox/CheckboxGroup.tsx +1 -1
  139. package/src/components/form-control/checkbox/Radio.tsx +4 -1
  140. package/src/components/form-control/checkbox/RadioGroup.tsx +1 -1
  141. package/src/components/form-control/color-picker/ColorPicker.tsx +4 -1
  142. package/src/components/form-control/combobox/Combobox.tsx +6 -3
  143. package/src/components/form-control/date-range-picker/DateRangePicker.tsx +8 -8
  144. package/src/components/form-control/editor/EditorToolbar.tsx +23 -23
  145. package/src/components/form-control/field/DatePicker.tsx +6 -3
  146. package/src/components/form-control/field/DateTimePicker.tsx +6 -3
  147. package/src/components/form-control/field/NumberInput.tsx +6 -3
  148. package/src/components/form-control/field/TextInput.tsx +7 -4
  149. package/src/components/form-control/field/Textarea.tsx +6 -3
  150. package/src/components/form-control/field/TimePicker.tsx +6 -3
  151. package/src/components/form-control/numpad/Numpad.tsx +3 -1
  152. package/src/components/form-control/select/Select.tsx +7 -7
  153. package/src/components/form-control/state-preset/StatePreset.tsx +14 -12
  154. package/src/components/layout/sidebar/SidebarContainer.tsx +3 -3
  155. package/src/components/layout/sidebar/SidebarMenu.tsx +3 -1
  156. package/src/components/layout/topbar/Topbar.tsx +3 -3
  157. package/src/components/layout/topbar/TopbarMenu.tsx +3 -3
  158. package/src/hooks/createSelectionGroup.tsx +8 -4
  159. package/src/providers/i18n/I18nContext.tsx +0 -7
  160. package/src/providers/i18n/locales/en.ts +38 -0
  161. package/src/providers/i18n/locales/ko.ts +38 -0
  162. package/tailwind.config.ts +2 -2
  163. package/tests/components/data/kanban/Kanban.selection.spec.tsx +34 -24
  164. package/tests/components/disclosure/Dialog.spec.tsx +28 -28
  165. package/tests/components/disclosure/DialogProvider.spec.tsx +51 -25
  166. package/tests/components/features/address/AddressSearch.spec.tsx +12 -4
  167. package/tests/components/features/crud-detail/CrudDetail.spec.tsx +1 -0
  168. package/tests/components/features/crud-sheet/CrudSheet.spec.tsx +30 -6
  169. package/tests/components/features/data-select-button/DataSelectButton.spec.tsx +77 -56
  170. package/tests/components/features/permission-table/PermissionTable.spec.tsx +12 -8
  171. package/tests/components/features/shared-data/SharedDataSelect.spec.tsx +172 -0
  172. package/tests/components/features/shared-data/SharedDataSelectList.spec.tsx +14 -2
  173. package/tests/components/feedback/notification/LiveRegion.spec.tsx +20 -9
  174. package/tests/components/feedback/notification/NotificationBanner.spec.tsx +64 -46
  175. package/tests/components/feedback/notification/NotificationBell.spec.tsx +70 -51
  176. package/tests/components/feedback/notification/NotificationContext.spec.tsx +105 -78
  177. package/tests/components/form-control/checkbox/Checkbox.spec.tsx +25 -20
  178. package/tests/components/form-control/checkbox/CheckboxGroup.spec.tsx +53 -30
  179. package/tests/components/form-control/checkbox/Radio.spec.tsx +25 -20
  180. package/tests/components/form-control/checkbox/RadioGroup.spec.tsx +53 -30
  181. package/tests/components/form-control/color-picker/ColorPicker.spec.tsx +24 -15
  182. package/tests/components/form-control/combobox/Combobox.spec.tsx +92 -59
  183. package/tests/components/form-control/date-range-picker/DateRangePicker.spec.tsx +2 -2
  184. package/tests/components/form-control/field/DatePicker.spec.tsx +50 -44
  185. package/tests/components/form-control/field/DateTimePicker.spec.tsx +51 -45
  186. package/tests/components/form-control/field/NumberInput.spec.tsx +53 -47
  187. package/tests/components/form-control/field/TextInput.spec.tsx +50 -44
  188. package/tests/components/form-control/field/Textarea.spec.tsx +35 -29
  189. package/tests/components/form-control/field/TimePicker.spec.tsx +43 -37
  190. package/tests/components/form-control/numpad/Numpad.spec.tsx +175 -25
  191. package/tests/components/form-control/select/Select.spec.tsx +5 -0
  192. package/tests/components/form-control/select/SelectItem.spec.tsx +1 -0
  193. package/tests/components/layout/sidebar/Sidebar.spec.tsx +79 -35
  194. package/tests/components/layout/sidebar/SidebarContainer.spec.tsx +1 -0
  195. package/tests/components/layout/sidebar/SidebarMenu.spec.tsx +28 -17
  196. package/tests/components/layout/topbar/TopbarActions.spec.tsx +41 -23
  197. package/tests/components/layout/topbar/createTopbarActions.spec.tsx +1 -0
  198. package/tests/hooks/usePrint.spec.tsx +1 -1
  199. package/tests/hooks/useRouterLink.spec.tsx +2 -0
  200. package/tests/hooks/useSyncConfig.spec.tsx +1 -0
  201. package/tests/providers/ErrorLoggerProvider.spec.tsx +1 -0
  202. package/tests/providers/PwaUpdateProvider.spec.tsx +16 -6
  203. package/tests/providers/ServiceClientContext.spec.tsx +40 -25
  204. package/tests/providers/i18n/I18nContext.spec.tsx +3 -4
  205. package/tests/providers/shared-data/SharedDataProvider.spec.tsx +2 -0
  206. package/dist/hooks/usePrint.d.ts +0 -3
  207. package/dist/hooks/usePrint.d.ts.map +0 -1
  208. package/dist/hooks/usePrint.js +0 -5
  209. package/dist/hooks/usePrint.js.map +0 -6
  210. package/src/hooks/usePrint.ts +0 -2
@@ -5,7 +5,7 @@ import { Icon } from "../../display/Icon";
5
5
  import { twMerge } from "tailwind-merge";
6
6
  import { Button } from "../../form-control/Button";
7
7
  import { useSidebarContextOptional } from "../sidebar/SidebarContext";
8
- import { useI18nOptional } from "../../../providers/i18n/I18nContext";
8
+ import { useI18n } from "../../../providers/i18n/I18nContext";
9
9
  import { TopbarActions } from "./TopbarActions";
10
10
  import { TopbarContainer } from "./TopbarContainer";
11
11
  import { TopbarMenu } from "./TopbarMenu";
@@ -71,7 +71,7 @@ const TopbarBase: ParentComponent<TopbarProps> = (props) => {
71
71
 
72
72
  // Optional use of SidebarContext (toggle button not shown if Context doesn't exist)
73
73
  const sidebarContext = useSidebarContextOptional();
74
- const i18n = useI18nOptional();
74
+ const i18n = useI18n();
75
75
 
76
76
  const handleToggle = () => {
77
77
  sidebarContext?.setToggle((v) => !v);
@@ -82,7 +82,7 @@ const TopbarBase: ParentComponent<TopbarProps> = (props) => {
82
82
  return (
83
83
  <header {...rest} data-topbar class={getClassName()}>
84
84
  <Show when={sidebarContext}>
85
- <Button variant="ghost" onClick={handleToggle} class="p-2" aria-label={i18n?.t("topbar.toggleSidebar") ?? "Toggle sidebar"}>
85
+ <Button variant="ghost" onClick={handleToggle} class="p-2" aria-label={i18n.t("topbar.toggleSidebar")}>
86
86
  <Icon icon={IconMenu2} size="1.5em" />
87
87
  </Button>
88
88
  </Show>
@@ -16,7 +16,7 @@ import { Button } from "../../form-control/Button";
16
16
  import { Dropdown } from "../../disclosure/Dropdown";
17
17
  import { List } from "../../data/list/List";
18
18
  import { ListItem } from "../../data/list/ListItem";
19
- import { useI18nOptional } from "../../../providers/i18n/I18nContext";
19
+ import { useI18n } from "../../../providers/i18n/I18nContext";
20
20
 
21
21
  const desktopNavBaseClass = clsx("hidden sm:flex", "flex-row gap-1", "items-center");
22
22
  const mobileWrapperClass = clsx("flex sm:hidden");
@@ -64,7 +64,7 @@ export interface TopbarMenuProps extends Omit<JSX.HTMLAttributes<HTMLElement>, "
64
64
  export const TopbarMenu: Component<TopbarMenuProps> = (props) => {
65
65
  const [local, rest] = splitProps(props, ["menus", "class"]);
66
66
  const [mobileMenuOpen, setMobileMenuOpen] = createSignal(false);
67
- const i18n = useI18nOptional();
67
+ const i18n = useI18n();
68
68
 
69
69
  return (
70
70
  <>
@@ -79,7 +79,7 @@ export const TopbarMenu: Component<TopbarMenuProps> = (props) => {
79
79
  <Dropdown.Trigger>
80
80
  <Button
81
81
  variant="ghost"
82
- aria-label={i18n?.t("topbarMenu.menu") ?? "Menu"}
82
+ aria-label={i18n.t("topbarMenu.menu")}
83
83
  aria-haspopup="menu"
84
84
  aria-expanded={mobileMenuOpen()}
85
85
  >
@@ -10,6 +10,7 @@ import { twMerge } from "tailwind-merge";
10
10
  import { createControllableSignal } from "./createControllableSignal";
11
11
  import { Invalid } from "../components/form-control/Invalid";
12
12
  import type { CheckboxSize } from "../components/form-control/checkbox/Checkbox.styles";
13
+ import { useI18n } from "../providers/i18n/I18nContext";
13
14
 
14
15
  interface SelectionItemComponentProps {
15
16
  value: boolean;
@@ -42,14 +43,14 @@ interface MultiGroupConfig {
42
43
  mode: "multiple";
43
44
  contextName: string;
44
45
  ItemComponent: (props: SelectionItemComponentProps) => JSX.Element;
45
- emptyErrorMsg: string;
46
+ emptyErrorMsgKey: string;
46
47
  }
47
48
 
48
49
  interface SingleGroupConfig {
49
50
  mode: "single";
50
51
  contextName: string;
51
52
  ItemComponent: (props: SelectionItemComponentProps) => JSX.Element;
52
- emptyErrorMsg: string;
53
+ emptyErrorMsgKey: string;
53
54
  }
54
55
 
55
56
  interface SelectionGroupItemProps<TValue> {
@@ -148,6 +149,9 @@ export function createSelectionGroup(config: MultiGroupConfig | SingleGroupConfi
148
149
  }
149
150
 
150
151
  const GroupInner: ParentComponent<MultiGroupProps<unknown>> = (props) => {
152
+ const i18n = useI18n();
153
+ const resolvedErrorMsg = () => i18n.t(config.emptyErrorMsgKey);
154
+
151
155
  const [local, rest] = splitProps(props, [
152
156
  "value",
153
157
  "onValueChange",
@@ -205,11 +209,11 @@ export function createSelectionGroup(config: MultiGroupConfig | SingleGroupConfi
205
209
  const errorMsg = createMemo(() => {
206
210
  if (config.mode === "multiple") {
207
211
  const v = local.value ?? [];
208
- if (local.required && v.length === 0) return config.emptyErrorMsg;
212
+ if (local.required && v.length === 0) return resolvedErrorMsg();
209
213
  return (local.validate as ((v: unknown[]) => string | undefined) | undefined)?.(v);
210
214
  } else {
211
215
  const v = local.value as unknown | undefined;
212
- if (local.required && (v === undefined || v === null)) return config.emptyErrorMsg;
216
+ if (local.required && (v === undefined || v === null)) return resolvedErrorMsg();
213
217
  return (local.validate as ((v: unknown | undefined) => string | undefined) | undefined)?.(
214
218
  v,
215
219
  );
@@ -38,13 +38,6 @@ export function useI18n(): I18nContextValue {
38
38
  return context;
39
39
  }
40
40
 
41
- /**
42
- * Get i18n context value (optional, returns undefined if not in provider)
43
- */
44
- export function useI18nOptional(): I18nContextValue | undefined {
45
- return useContext(I18nContext);
46
- }
47
-
48
41
  /**
49
42
  * I18n Provider component
50
43
  *
@@ -144,6 +144,7 @@ export default {
144
144
  },
145
145
  sharedDataSelectList: {
146
146
  searchPlaceholder: "Search...",
147
+ unspecified: "Unspecified",
147
148
  },
148
149
  dataSelectButton: {
149
150
  deselect: "Deselect",
@@ -158,4 +159,41 @@ export default {
158
159
  topbarMenu: {
159
160
  menu: "Menu",
160
161
  },
162
+ notificationBell: {
163
+ notifications: "Notifications",
164
+ clearAll: "Clear All",
165
+ noNotifications: "No notifications",
166
+ unreadCount: "{{count}} notifications",
167
+ },
168
+ notificationProvider: {
169
+ prefix: "Notification:",
170
+ },
171
+ themeToggle: {
172
+ light: "Light mode",
173
+ system: "System settings",
174
+ dark: "Dark mode",
175
+ },
176
+ combobox: {
177
+ searching: "Searching...",
178
+ noResults: "No results found",
179
+ },
180
+ numpad: {
181
+ enter: "ENT",
182
+ },
183
+ sidebarMenu: {
184
+ menu: "MENU",
185
+ },
186
+ validation: {
187
+ required: "This is a required field",
188
+ requiredField: "Required field",
189
+ requiredSelection: "This is a required selection",
190
+ selectItem: "Please select an item",
191
+ minLength: "Enter at least {{min}} characters",
192
+ maxLength: "Enter up to {{max}} characters",
193
+ invalidFormat: "The input format is invalid",
194
+ minValue: "Minimum value is {{min}}",
195
+ maxValue: "Maximum value is {{max}}",
196
+ minDate: "Must be greater than or equal to {{min}}",
197
+ maxDate: "Must be less than or equal to {{max}}",
198
+ },
161
199
  };
@@ -144,6 +144,7 @@ export default {
144
144
  },
145
145
  sharedDataSelectList: {
146
146
  searchPlaceholder: "검색...",
147
+ unspecified: "미지정",
147
148
  },
148
149
  dataSelectButton: {
149
150
  deselect: "선택 해제",
@@ -158,4 +159,41 @@ export default {
158
159
  topbarMenu: {
159
160
  menu: "메뉴",
160
161
  },
162
+ notificationBell: {
163
+ notifications: "알림",
164
+ clearAll: "모두 지우기",
165
+ noNotifications: "알림 없음",
166
+ unreadCount: "{{count}}개의 알림",
167
+ },
168
+ notificationProvider: {
169
+ prefix: "알림:",
170
+ },
171
+ themeToggle: {
172
+ light: "라이트 모드",
173
+ system: "시스템 설정",
174
+ dark: "다크 모드",
175
+ },
176
+ combobox: {
177
+ searching: "검색 중...",
178
+ noResults: "결과 없음",
179
+ },
180
+ numpad: {
181
+ enter: "입력",
182
+ },
183
+ sidebarMenu: {
184
+ menu: "메뉴",
185
+ },
186
+ validation: {
187
+ required: "필수 입력 항목입니다",
188
+ requiredField: "필수 항목",
189
+ requiredSelection: "필수 선택 항목입니다",
190
+ selectItem: "항목을 선택해 주세요",
191
+ minLength: "{{min}}자 이상 입력해 주세요",
192
+ maxLength: "{{max}}자 이하로 입력해 주세요",
193
+ invalidFormat: "입력 형식이 올바르지 않습니다",
194
+ minValue: "최솟값은 {{min}}입니다",
195
+ maxValue: "최댓값은 {{max}}입니다",
196
+ minDate: "{{min}} 이상이어야 합니다",
197
+ maxDate: "{{max}} 이하이어야 합니다",
198
+ },
161
199
  };
@@ -50,8 +50,8 @@ export default {
50
50
  "sidebar-backdrop": "99",
51
51
  "busy": "500",
52
52
  "dropdown": "1000",
53
- "modal-backdrop": "1999",
54
- "modal": "2000",
53
+ "dialog-backdrop": "1999",
54
+ "dialog": "2000",
55
55
  "notification": "3000",
56
56
  },
57
57
  },
@@ -1,38 +1,48 @@
1
1
  import { render, fireEvent } from "@solidjs/testing-library";
2
2
  import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
3
3
  import { Kanban } from "../../../../src/components/data/kanban/Kanban";
4
+ import { I18nProvider } from "../../../../src/providers/i18n/I18nContext";
5
+ import { ConfigProvider } from "../../../../src/providers/ConfigContext";
4
6
 
5
7
  describe("Kanban selection system", () => {
8
+ beforeEach(() => {
9
+ localStorage.setItem("test.i18n-locale", JSON.stringify("en"));
10
+ });
11
+
6
12
  function renderKanban(options?: {
7
13
  selectedValues?: unknown[];
8
14
  onSelectedValuesChange?: (v: unknown[]) => void;
9
15
  selectable?: boolean;
10
16
  }) {
11
17
  return render(() => (
12
- <Kanban
13
- selectedValues={options?.selectedValues}
14
- onSelectedValuesChange={options?.onSelectedValuesChange}
15
- class="h-[400px]"
16
- >
17
- <Kanban.Lane value="lane-1">
18
- <Kanban.LaneTitle>Lane 1</Kanban.LaneTitle>
19
- <Kanban.Card value={1} selectable={options?.selectable ?? true} contentClass="p-2">
20
- Card 1
21
- </Kanban.Card>
22
- <Kanban.Card value={2} selectable={options?.selectable ?? true} contentClass="p-2">
23
- Card 2
24
- </Kanban.Card>
25
- <Kanban.Card value={3} selectable={false} contentClass="p-2">
26
- Card 3 (not selectable)
27
- </Kanban.Card>
28
- </Kanban.Lane>
29
- <Kanban.Lane value="lane-2">
30
- <Kanban.LaneTitle>Lane 2</Kanban.LaneTitle>
31
- <Kanban.Card value={4} selectable contentClass="p-2">
32
- Card 4
33
- </Kanban.Card>
34
- </Kanban.Lane>
35
- </Kanban>
18
+ <ConfigProvider clientName="test">
19
+ <I18nProvider>
20
+ <Kanban
21
+ selectedValues={options?.selectedValues}
22
+ onSelectedValuesChange={options?.onSelectedValuesChange}
23
+ class="h-[400px]"
24
+ >
25
+ <Kanban.Lane value="lane-1">
26
+ <Kanban.LaneTitle>Lane 1</Kanban.LaneTitle>
27
+ <Kanban.Card value={1} selectable={options?.selectable ?? true} contentClass="p-2">
28
+ Card 1
29
+ </Kanban.Card>
30
+ <Kanban.Card value={2} selectable={options?.selectable ?? true} contentClass="p-2">
31
+ Card 2
32
+ </Kanban.Card>
33
+ <Kanban.Card value={3} selectable={false} contentClass="p-2">
34
+ Card 3 (not selectable)
35
+ </Kanban.Card>
36
+ </Kanban.Lane>
37
+ <Kanban.Lane value="lane-2">
38
+ <Kanban.LaneTitle>Lane 2</Kanban.LaneTitle>
39
+ <Kanban.Card value={4} selectable contentClass="p-2">
40
+ Card 4
41
+ </Kanban.Card>
42
+ </Kanban.Lane>
43
+ </Kanban>
44
+ </I18nProvider>
45
+ </ConfigProvider>
36
46
  ));
37
47
  }
38
48
 
@@ -34,7 +34,7 @@ describe("Dialog", () => {
34
34
  expect(content).toBeNull();
35
35
  });
36
36
 
37
- it("sets data-modal attribute", async () => {
37
+ it("sets data-dialog attribute", async () => {
38
38
  render(() => (
39
39
  <ConfigProvider clientName="test"><I18nProvider>
40
40
  <Dialog open={true}>
@@ -44,8 +44,8 @@ describe("Dialog", () => {
44
44
  </I18nProvider></ConfigProvider>
45
45
  ));
46
46
  await waitFor(() => {
47
- const modal = document.querySelector("[data-modal]");
48
- expect(modal).not.toBeNull();
47
+ const dialog = document.querySelector("[data-dialog]");
48
+ expect(dialog).not.toBeNull();
49
49
  });
50
50
  });
51
51
 
@@ -60,7 +60,7 @@ describe("Dialog", () => {
60
60
  ));
61
61
 
62
62
  await waitFor(() => {
63
- const header = document.querySelector("[data-modal-header]");
63
+ const header = document.querySelector("[data-dialog-header]");
64
64
  expect(header?.textContent).toContain("테스트 제목");
65
65
  });
66
66
  });
@@ -80,7 +80,7 @@ describe("Dialog", () => {
80
80
  const content = document.querySelector('[data-testid="content"]');
81
81
  expect(content).not.toBeNull();
82
82
  });
83
- const header = document.querySelector("[data-modal-header]");
83
+ const header = document.querySelector("[data-dialog-header]");
84
84
  expect(header).toBeNull();
85
85
  });
86
86
 
@@ -112,9 +112,9 @@ describe("Dialog", () => {
112
112
  </I18nProvider></ConfigProvider>
113
113
  ));
114
114
  await waitFor(() => {
115
- expect(document.querySelector("[data-modal]")).not.toBeNull();
115
+ expect(document.querySelector("[data-dialog]")).not.toBeNull();
116
116
  });
117
- const closeBtn = document.querySelector("[data-modal-close]");
117
+ const closeBtn = document.querySelector("[data-dialog-close]");
118
118
  expect(closeBtn).toBeNull();
119
119
  });
120
120
  });
@@ -131,9 +131,9 @@ describe("Dialog", () => {
131
131
  </I18nProvider></ConfigProvider>
132
132
  ));
133
133
  await waitFor(() => {
134
- expect(document.querySelector("[data-modal-close]")).not.toBeNull();
134
+ expect(document.querySelector("[data-dialog-close]")).not.toBeNull();
135
135
  });
136
- fireEvent.click(document.querySelector("[data-modal-close]")!);
136
+ fireEvent.click(document.querySelector("[data-dialog-close]")!);
137
137
  expect(handleOpenChange).toHaveBeenCalledWith(false);
138
138
  });
139
139
 
@@ -148,9 +148,9 @@ describe("Dialog", () => {
148
148
  </I18nProvider></ConfigProvider>
149
149
  ));
150
150
  await waitFor(() => {
151
- expect(document.querySelector("[data-modal-backdrop]")).not.toBeNull();
151
+ expect(document.querySelector("[data-dialog-backdrop]")).not.toBeNull();
152
152
  });
153
- fireEvent.click(document.querySelector("[data-modal-backdrop]")!);
153
+ fireEvent.click(document.querySelector("[data-dialog-backdrop]")!);
154
154
  expect(handleOpenChange).toHaveBeenCalledWith(false);
155
155
  });
156
156
 
@@ -165,9 +165,9 @@ describe("Dialog", () => {
165
165
  </I18nProvider></ConfigProvider>
166
166
  ));
167
167
  await waitFor(() => {
168
- expect(document.querySelector("[data-modal-backdrop]")).not.toBeNull();
168
+ expect(document.querySelector("[data-dialog-backdrop]")).not.toBeNull();
169
169
  });
170
- fireEvent.click(document.querySelector("[data-modal-backdrop]")!);
170
+ fireEvent.click(document.querySelector("[data-dialog-backdrop]")!);
171
171
  expect(handleOpenChange).not.toHaveBeenCalled();
172
172
  });
173
173
 
@@ -182,7 +182,7 @@ describe("Dialog", () => {
182
182
  </I18nProvider></ConfigProvider>
183
183
  ));
184
184
  await waitFor(() => {
185
- expect(document.querySelector("[data-modal]")).not.toBeNull();
185
+ expect(document.querySelector("[data-dialog]")).not.toBeNull();
186
186
  });
187
187
  fireEvent.keyDown(document, { key: "Escape" });
188
188
  expect(handleOpenChange).toHaveBeenCalledWith(false);
@@ -199,7 +199,7 @@ describe("Dialog", () => {
199
199
  </I18nProvider></ConfigProvider>
200
200
  ));
201
201
  await waitFor(() => {
202
- expect(document.querySelector("[data-modal]")).not.toBeNull();
202
+ expect(document.querySelector("[data-dialog]")).not.toBeNull();
203
203
  });
204
204
  fireEvent.keyDown(document, { key: "Escape" });
205
205
  expect(handleOpenChange).toHaveBeenCalledWith(false);
@@ -216,7 +216,7 @@ describe("Dialog", () => {
216
216
  </I18nProvider></ConfigProvider>
217
217
  ));
218
218
  await waitFor(() => {
219
- expect(document.querySelector("[data-modal]")).not.toBeNull();
219
+ expect(document.querySelector("[data-dialog]")).not.toBeNull();
220
220
  });
221
221
  fireEvent.keyDown(document, { key: "Escape" });
222
222
  expect(handleOpenChange).not.toHaveBeenCalled();
@@ -233,9 +233,9 @@ describe("Dialog", () => {
233
233
  </I18nProvider></ConfigProvider>
234
234
  ));
235
235
  await waitFor(() => {
236
- expect(document.querySelector("[data-modal-close]")).not.toBeNull();
236
+ expect(document.querySelector("[data-dialog-close]")).not.toBeNull();
237
237
  });
238
- fireEvent.click(document.querySelector("[data-modal-close]")!);
238
+ fireEvent.click(document.querySelector("[data-dialog-close]")!);
239
239
  expect(handleOpenChange).not.toHaveBeenCalled();
240
240
  });
241
241
  });
@@ -251,7 +251,7 @@ describe("Dialog", () => {
251
251
  </I18nProvider></ConfigProvider>
252
252
  ));
253
253
  await waitFor(() => {
254
- const dialog = document.querySelector("[data-modal-dialog]") as HTMLElement;
254
+ const dialog = document.querySelector("[data-dialog-panel]") as HTMLElement;
255
255
  expect(dialog).not.toBeNull();
256
256
  expect(dialog.getAttribute("role")).toBe("dialog");
257
257
  expect(dialog.getAttribute("aria-modal")).toBe("true");
@@ -269,7 +269,7 @@ describe("Dialog", () => {
269
269
  ));
270
270
 
271
271
  await waitFor(() => {
272
- const dialog = document.querySelector("[data-modal-dialog]") as HTMLElement;
272
+ const dialog = document.querySelector("[data-dialog-panel]") as HTMLElement;
273
273
  const headerId = dialog.getAttribute("aria-labelledby");
274
274
  expect(headerId).toBeTruthy();
275
275
  const header = document.getElementById(headerId!);
@@ -287,7 +287,7 @@ describe("Dialog", () => {
287
287
  ));
288
288
 
289
289
  await waitFor(() => {
290
- const dialog = document.querySelector("[data-modal-dialog]") as HTMLElement;
290
+ const dialog = document.querySelector("[data-dialog-panel]") as HTMLElement;
291
291
  expect(dialog).not.toBeNull();
292
292
  expect(dialog.hasAttribute("aria-labelledby")).toBe(false);
293
293
  });
@@ -303,7 +303,7 @@ describe("Dialog", () => {
303
303
  </I18nProvider></ConfigProvider>
304
304
  ));
305
305
  await waitFor(() => {
306
- const dialog = document.querySelector("[data-modal-dialog]") as HTMLElement;
306
+ const dialog = document.querySelector("[data-dialog-panel]") as HTMLElement;
307
307
  expect(dialog).not.toBeNull();
308
308
  expect(dialog.getAttribute("role")).toBe("dialog");
309
309
  expect(dialog.hasAttribute("aria-modal")).toBe(false);
@@ -324,7 +324,7 @@ describe("Dialog", () => {
324
324
  await waitFor(() => {
325
325
  expect(document.querySelector('[data-testid="content"]')).not.toBeNull();
326
326
  });
327
- const backdrop = document.querySelector("[data-modal-backdrop]");
327
+ const backdrop = document.querySelector("[data-dialog-backdrop]");
328
328
  expect(backdrop).toBeNull();
329
329
  });
330
330
  });
@@ -340,7 +340,7 @@ describe("Dialog", () => {
340
340
  </I18nProvider></ConfigProvider>
341
341
  ));
342
342
  await waitFor(() => {
343
- const dialog = document.querySelector("[data-modal-dialog]") as HTMLElement;
343
+ const dialog = document.querySelector("[data-dialog-panel]") as HTMLElement;
344
344
  expect(dialog).not.toBeNull();
345
345
  expect(dialog.style.width).toBe("100%");
346
346
  expect(dialog.style.height).toBe("100%");
@@ -359,7 +359,7 @@ describe("Dialog", () => {
359
359
  </I18nProvider></ConfigProvider>
360
360
  ));
361
361
  await waitFor(() => {
362
- const dialog = document.querySelector("[data-modal-dialog]") as HTMLElement;
362
+ const dialog = document.querySelector("[data-dialog-panel]") as HTMLElement;
363
363
  expect(dialog).not.toBeNull();
364
364
  expect(dialog.style.width).toBe("400px");
365
365
  expect(dialog.style.height).toBe("300px");
@@ -376,7 +376,7 @@ describe("Dialog", () => {
376
376
  </I18nProvider></ConfigProvider>
377
377
  ));
378
378
  await waitFor(() => {
379
- const dialog = document.querySelector("[data-modal-dialog]") as HTMLElement;
379
+ const dialog = document.querySelector("[data-dialog-panel]") as HTMLElement;
380
380
  expect(dialog).not.toBeNull();
381
381
  expect(dialog.style.minWidth).toBe("300px");
382
382
  expect(dialog.style.minHeight).toBe("200px");
@@ -410,7 +410,7 @@ describe("Dialog", () => {
410
410
  </I18nProvider></ConfigProvider>
411
411
  ));
412
412
  await waitFor(() => {
413
- expect(document.querySelector("[data-modal]")).not.toBeNull();
413
+ expect(document.querySelector("[data-dialog]")).not.toBeNull();
414
414
  });
415
415
  const bars = document.querySelectorAll("[data-resize-bar]");
416
416
  expect(bars.length).toBe(0);
@@ -428,7 +428,7 @@ describe("Dialog", () => {
428
428
  </I18nProvider></ConfigProvider>
429
429
  ));
430
430
  await waitFor(() => {
431
- const dialog = document.querySelector("[data-modal-dialog]") as HTMLElement;
431
+ const dialog = document.querySelector("[data-dialog-panel]") as HTMLElement;
432
432
  expect(dialog).not.toBeNull();
433
433
  expect(dialog.classList.contains("transition-[opacity,transform]")).toBe(true);
434
434
  expect(dialog.classList.contains("duration-200")).toBe(true);
@@ -1,15 +1,17 @@
1
1
  import { render, fireEvent, waitFor } from "@solidjs/testing-library";
2
- import { describe, it, expect } from "vitest";
2
+ import { describe, it, expect, beforeEach } from "vitest";
3
3
  import { DialogProvider } from "../../../src/components/disclosure/DialogProvider";
4
4
  import { useDialog } from "../../../src/components/disclosure/DialogContext";
5
5
  import { useDialogInstance } from "../../../src/components/disclosure/DialogInstanceContext";
6
+ import { I18nProvider } from "../../../src/providers/i18n/I18nContext";
7
+ import { ConfigProvider } from "../../../src/providers/ConfigContext";
6
8
 
7
9
  // dialog content component for testing
8
10
  function TestContent() {
9
11
  const dialog = useDialogInstance<string>();
10
12
  return (
11
13
  <div>
12
- <span data-testid="modal-content">다이얼로그 내용</span>
14
+ <span data-testid="dialog-content">다이얼로그 내용</span>
13
15
  <button data-testid="close-btn" onClick={() => dialog?.close("result")}>
14
16
  닫기
15
17
  </button>
@@ -53,25 +55,37 @@ function TestAppNoHeader() {
53
55
  }
54
56
 
55
57
  describe("DialogProvider", () => {
58
+ beforeEach(() => {
59
+ localStorage.setItem("test.i18n-locale", JSON.stringify("en"));
60
+ });
61
+
56
62
  it("displays dialog via show()", async () => {
57
63
  render(() => (
58
- <DialogProvider>
59
- <TestApp />
60
- </DialogProvider>
64
+ <ConfigProvider clientName="test">
65
+ <I18nProvider>
66
+ <DialogProvider>
67
+ <TestApp />
68
+ </DialogProvider>
69
+ </I18nProvider>
70
+ </ConfigProvider>
61
71
  ));
62
72
 
63
73
  fireEvent.click(document.querySelector('[data-testid="open-btn"]')!);
64
74
 
65
75
  await waitFor(() => {
66
- expect(document.querySelector('[data-testid="modal-content"]')).not.toBeNull();
76
+ expect(document.querySelector('[data-testid="dialog-content"]')).not.toBeNull();
67
77
  });
68
78
  });
69
79
 
70
80
  it("closes dialog when close is called via useDialogInstance", async () => {
71
81
  render(() => (
72
- <DialogProvider>
73
- <TestApp />
74
- </DialogProvider>
82
+ <ConfigProvider clientName="test">
83
+ <I18nProvider>
84
+ <DialogProvider>
85
+ <TestApp />
86
+ </DialogProvider>
87
+ </I18nProvider>
88
+ </ConfigProvider>
75
89
  ));
76
90
 
77
91
  fireEvent.click(document.querySelector('[data-testid="open-btn"]')!);
@@ -84,15 +98,19 @@ describe("DialogProvider", () => {
84
98
 
85
99
  // dialog content is removed after close animation fallback timer (200ms)
86
100
  await waitFor(() => {
87
- expect(document.querySelector('[data-testid="modal-content"]')).toBeNull();
101
+ expect(document.querySelector('[data-testid="dialog-content"]')).toBeNull();
88
102
  });
89
103
  });
90
104
 
91
105
  it("closes dialog when close() is called", async () => {
92
106
  render(() => (
93
- <DialogProvider>
94
- <TestApp />
95
- </DialogProvider>
107
+ <ConfigProvider clientName="test">
108
+ <I18nProvider>
109
+ <DialogProvider>
110
+ <TestApp />
111
+ </DialogProvider>
112
+ </I18nProvider>
113
+ </ConfigProvider>
96
114
  ));
97
115
 
98
116
  fireEvent.click(document.querySelector('[data-testid="open-btn"]')!);
@@ -104,39 +122,47 @@ describe("DialogProvider", () => {
104
122
  fireEvent.click(document.querySelector('[data-testid="close-no-result"]')!);
105
123
 
106
124
  await waitFor(() => {
107
- expect(document.querySelector('[data-testid="modal-content"]')).toBeNull();
125
+ expect(document.querySelector('[data-testid="dialog-content"]')).toBeNull();
108
126
  });
109
127
  });
110
128
 
111
129
  it("displays dialog header", async () => {
112
130
  render(() => (
113
- <DialogProvider>
114
- <TestApp />
115
- </DialogProvider>
131
+ <ConfigProvider clientName="test">
132
+ <I18nProvider>
133
+ <DialogProvider>
134
+ <TestApp />
135
+ </DialogProvider>
136
+ </I18nProvider>
137
+ </ConfigProvider>
116
138
  ));
117
139
 
118
140
  fireEvent.click(document.querySelector('[data-testid="open-btn"]')!);
119
141
 
120
142
  await waitFor(() => {
121
- const modal = document.querySelector("[data-modal]");
122
- expect(modal).not.toBeNull();
123
- expect(modal!.textContent).toContain("테스트 다이얼로그");
143
+ const dialog = document.querySelector("[data-dialog]");
144
+ expect(dialog).not.toBeNull();
145
+ expect(dialog!.textContent).toContain("테스트 다이얼로그");
124
146
  });
125
147
  });
126
148
 
127
149
  it("does not render header when header is not provided", async () => {
128
150
  render(() => (
129
- <DialogProvider>
130
- <TestAppNoHeader />
131
- </DialogProvider>
151
+ <ConfigProvider clientName="test">
152
+ <I18nProvider>
153
+ <DialogProvider>
154
+ <TestAppNoHeader />
155
+ </DialogProvider>
156
+ </I18nProvider>
157
+ </ConfigProvider>
132
158
  ));
133
159
 
134
160
  fireEvent.click(document.querySelector('[data-testid="open-btn"]')!);
135
161
 
136
162
  await waitFor(() => {
137
- expect(document.querySelector('[data-testid="modal-content"]')).not.toBeNull();
163
+ expect(document.querySelector('[data-testid="dialog-content"]')).not.toBeNull();
138
164
  });
139
- const header = document.querySelector("[data-modal-header]");
165
+ const header = document.querySelector("[data-dialog-header]");
140
166
  expect(header).toBeNull();
141
167
  });
142
168
  });