@simplysm/solid 13.0.84 → 13.0.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -28
- package/dist/components/data/list/ListItem.d.ts.map +1 -1
- package/dist/components/data/list/ListItem.js +11 -4
- package/dist/components/data/list/ListItem.js.map +2 -2
- package/dist/components/data/list/ListItem.styles.d.ts +2 -0
- package/dist/components/data/list/ListItem.styles.d.ts.map +1 -1
- package/dist/components/data/list/ListItem.styles.js +11 -1
- package/dist/components/data/list/ListItem.styles.js.map +1 -1
- package/dist/components/data/sheet/DataSheet.d.ts.map +1 -1
- package/dist/components/data/sheet/DataSheet.js +6 -9
- package/dist/components/data/sheet/DataSheet.js.map +2 -2
- package/dist/components/data/sheet/hooks/createDataSheetExpansion.d.ts.map +1 -1
- package/dist/components/data/sheet/hooks/createDataSheetExpansion.js +15 -17
- package/dist/components/data/sheet/hooks/createDataSheetExpansion.js.map +1 -1
- package/dist/components/data/sheet/hooks/createDataSheetReorder.d.ts.map +1 -1
- package/dist/components/data/sheet/hooks/createDataSheetReorder.js +12 -12
- package/dist/components/data/sheet/hooks/createDataSheetReorder.js.map +1 -1
- package/dist/components/data/sheet/hooks/createDataSheetSelection.d.ts.map +1 -1
- package/dist/components/data/sheet/hooks/createDataSheetSelection.js +9 -3
- package/dist/components/data/sheet/hooks/createDataSheetSelection.js.map +1 -1
- package/dist/components/disclosure/Dialog.d.ts.map +1 -1
- package/dist/components/disclosure/Dialog.js +3 -21
- package/dist/components/disclosure/Dialog.js.map +2 -2
- package/dist/components/disclosure/Dropdown.d.ts.map +1 -1
- package/dist/components/disclosure/Dropdown.js +1 -11
- package/dist/components/disclosure/Dropdown.js.map +2 -2
- package/dist/components/disclosure/Tabs.d.ts.map +1 -1
- package/dist/components/disclosure/Tabs.js +1 -3
- package/dist/components/disclosure/Tabs.js.map +2 -2
- package/dist/components/features/crud-detail/CrudDetail.js +103 -102
- package/dist/components/features/crud-detail/CrudDetail.js.map +2 -2
- package/dist/components/features/crud-sheet/CrudSheet.d.ts.map +1 -1
- package/dist/components/features/crud-sheet/CrudSheet.js +10 -5
- package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
- package/dist/components/features/data-select-button/DataSelectButton.d.ts.map +1 -1
- package/dist/components/features/data-select-button/DataSelectButton.js +30 -26
- package/dist/components/features/data-select-button/DataSelectButton.js.map +2 -2
- package/dist/components/features/permission-table/PermissionTable.js +5 -1
- package/dist/components/features/permission-table/PermissionTable.js.map +2 -2
- package/dist/components/feedback/busy/BusyContainer.d.ts.map +1 -1
- package/dist/components/feedback/busy/BusyContainer.js +1 -6
- package/dist/components/feedback/busy/BusyContainer.js.map +2 -2
- package/dist/components/form-control/DropdownTrigger.styles.js +1 -1
- package/dist/components/form-control/checkbox/SelectableBase.d.ts.map +1 -1
- package/dist/components/form-control/checkbox/SelectableBase.js +2 -4
- package/dist/components/form-control/checkbox/SelectableBase.js.map +2 -2
- package/dist/components/form-control/combobox/Combobox.d.ts +19 -5
- package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
- package/dist/components/form-control/combobox/Combobox.js +2 -4
- package/dist/components/form-control/combobox/Combobox.js.map +1 -1
- package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts +2 -2
- package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts.map +1 -1
- package/dist/components/form-control/date-range-picker/DateRangePicker.js +11 -3
- package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
- package/dist/components/form-control/editor/RichTextEditor.d.ts +2 -2
- package/dist/components/form-control/editor/RichTextEditor.d.ts.map +1 -1
- package/dist/components/form-control/editor/RichTextEditor.js +2 -4
- package/dist/components/form-control/editor/RichTextEditor.js.map +2 -2
- package/dist/components/form-control/field/DatePicker.d.ts +2 -2
- package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/DatePicker.js.map +1 -1
- package/dist/components/form-control/field/DateTimePicker.d.ts +2 -2
- package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/DateTimePicker.js.map +1 -1
- package/dist/components/form-control/field/Field.styles.d.ts +6 -7
- package/dist/components/form-control/field/Field.styles.d.ts.map +1 -1
- package/dist/components/form-control/field/Field.styles.js.map +1 -1
- package/dist/components/form-control/field/NumberInput.d.ts +2 -2
- package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
- package/dist/components/form-control/field/NumberInput.js +7 -7
- package/dist/components/form-control/field/NumberInput.js.map +2 -2
- package/dist/components/form-control/field/TextInput.d.ts +2 -2
- package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
- package/dist/components/form-control/field/TextInput.js.map +1 -1
- package/dist/components/form-control/field/Textarea.d.ts +2 -2
- package/dist/components/form-control/field/Textarea.d.ts.map +1 -1
- package/dist/components/form-control/field/Textarea.js +1 -3
- package/dist/components/form-control/field/Textarea.js.map +2 -2
- package/dist/components/form-control/field/TimePicker.d.ts +2 -2
- package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/TimePicker.js.map +1 -1
- package/dist/components/form-control/numpad/Numpad.d.ts.map +1 -1
- package/dist/components/form-control/numpad/Numpad.js +4 -17
- package/dist/components/form-control/numpad/Numpad.js.map +2 -2
- package/dist/components/form-control/select/Select.d.ts +2 -0
- package/dist/components/form-control/select/Select.d.ts.map +1 -1
- package/dist/components/form-control/select/Select.js +29 -15
- package/dist/components/form-control/select/Select.js.map +2 -2
- package/dist/components/form-control/state-preset/StatePreset.d.ts +1 -3
- package/dist/components/form-control/state-preset/StatePreset.d.ts.map +1 -1
- package/dist/components/form-control/state-preset/StatePreset.js +69 -95
- package/dist/components/form-control/state-preset/StatePreset.js.map +2 -2
- package/dist/components/layout/FormGroup.js +1 -1
- package/dist/components/layout/FormGroup.js.map +1 -1
- package/dist/components/layout/FormTable.js +3 -3
- package/dist/components/layout/FormTable.js.map +1 -1
- package/dist/components/layout/sidebar/Sidebar.d.ts.map +1 -1
- package/dist/components/layout/sidebar/Sidebar.js +3 -6
- package/dist/components/layout/sidebar/Sidebar.js.map +2 -2
- package/dist/components/layout/topbar/Topbar.js +1 -3
- package/dist/components/layout/topbar/Topbar.js.map +2 -2
- package/dist/hooks/createControllableStore.d.ts.map +1 -1
- package/dist/hooks/createControllableStore.js +8 -5
- package/dist/hooks/createControllableStore.js.map +1 -1
- package/dist/hooks/useLocalStorage.d.ts.map +1 -1
- package/dist/hooks/useLocalStorage.js +3 -2
- package/dist/hooks/useLocalStorage.js.map +1 -1
- package/dist/hooks/useSyncConfig.d.ts.map +1 -1
- package/dist/hooks/useSyncConfig.js +5 -4
- package/dist/hooks/useSyncConfig.js.map +1 -1
- package/dist/providers/i18n/locales/en.d.ts +2 -3
- package/dist/providers/i18n/locales/en.d.ts.map +1 -1
- package/dist/providers/i18n/locales/en.js +3 -4
- package/dist/providers/i18n/locales/en.js.map +1 -1
- package/dist/providers/i18n/locales/ko.d.ts +2 -3
- package/dist/providers/i18n/locales/ko.d.ts.map +1 -1
- package/dist/providers/i18n/locales/ko.js +3 -4
- package/dist/providers/i18n/locales/ko.js.map +1 -1
- package/dist/providers/shared-data/SharedDataProvider.d.ts.map +1 -1
- package/dist/providers/shared-data/SharedDataProvider.js +0 -1
- package/dist/providers/shared-data/SharedDataProvider.js.map +1 -1
- package/docs/display-feedback.md +279 -0
- package/docs/features.md +357 -213
- package/docs/form-controls.md +261 -403
- package/docs/layout-data.md +386 -0
- package/docs/providers-hooks.md +411 -0
- package/package.json +5 -5
- package/src/components/data/list/ListItem.styles.ts +14 -2
- package/src/components/data/list/ListItem.tsx +13 -4
- package/src/components/data/sheet/DataSheet.tsx +6 -10
- package/src/components/data/sheet/hooks/createDataSheetExpansion.ts +17 -18
- package/src/components/data/sheet/hooks/createDataSheetReorder.ts +12 -13
- package/src/components/data/sheet/hooks/createDataSheetSelection.ts +9 -3
- package/src/components/disclosure/Dialog.tsx +45 -59
- package/src/components/disclosure/Dropdown.tsx +4 -14
- package/src/components/disclosure/Tabs.tsx +12 -17
- package/src/components/features/crud-detail/CrudDetail.tsx +4 -4
- package/src/components/features/crud-sheet/CrudSheet.tsx +12 -5
- package/src/components/features/data-select-button/DataSelectButton.tsx +39 -32
- package/src/components/features/permission-table/PermissionTable.tsx +1 -1
- package/src/components/feedback/busy/BusyContainer.tsx +12 -18
- package/src/components/form-control/DropdownTrigger.styles.ts +1 -1
- package/src/components/form-control/checkbox/SelectableBase.tsx +10 -16
- package/src/components/form-control/combobox/Combobox.tsx +42 -16
- package/src/components/form-control/date-range-picker/DateRangePicker.tsx +7 -8
- package/src/components/form-control/editor/RichTextEditor.tsx +14 -16
- package/src/components/form-control/field/DatePicker.tsx +3 -2
- package/src/components/form-control/field/DateTimePicker.tsx +3 -2
- package/src/components/form-control/field/Field.styles.ts +6 -8
- package/src/components/form-control/field/NumberInput.tsx +9 -10
- package/src/components/form-control/field/TextInput.tsx +3 -2
- package/src/components/form-control/field/Textarea.tsx +14 -12
- package/src/components/form-control/field/TimePicker.tsx +3 -2
- package/src/components/form-control/numpad/Numpad.tsx +16 -18
- package/src/components/form-control/select/Select.tsx +41 -13
- package/src/components/form-control/state-preset/StatePreset.tsx +39 -71
- package/src/components/layout/FormGroup.tsx +1 -1
- package/src/components/layout/FormTable.tsx +3 -3
- package/src/components/layout/sidebar/Sidebar.tsx +2 -3
- package/src/components/layout/topbar/Topbar.tsx +2 -2
- package/src/hooks/createControllableStore.ts +8 -4
- package/src/hooks/useLocalStorage.ts +3 -2
- package/src/hooks/useSyncConfig.ts +5 -4
- package/src/providers/i18n/locales/en.ts +2 -3
- package/src/providers/i18n/locales/ko.ts +2 -3
- package/src/providers/shared-data/SharedDataProvider.tsx +0 -1
- package/tests/components/features/crud-detail/CrudDetail.spec.tsx +49 -0
- package/tests/components/features/data-select-button/DataSelectButton.spec.tsx +62 -7
- package/tests/components/form-control/combobox/Combobox.spec.tsx +3 -3
- package/tests/components/form-control/date-range-picker/DateRangePicker.spec.tsx +56 -0
- package/tests/components/form-control/select/SelectItem.spec.tsx +5 -0
- package/tests/providers/shared-data/SharedDataProvider.spec.tsx +0 -104
- package/docs/data.md +0 -204
- package/docs/disclosure.md +0 -146
- package/docs/display.md +0 -125
- package/docs/feedback.md +0 -156
- package/docs/helpers.md +0 -173
- package/docs/hooks.md +0 -146
- package/docs/layout.md +0 -94
- package/docs/providers.md +0 -180
|
@@ -395,9 +395,8 @@ describe("DataSelectButton", () => {
|
|
|
395
395
|
/>
|
|
396
396
|
));
|
|
397
397
|
|
|
398
|
-
const trigger = container.querySelector("[
|
|
399
|
-
expect(trigger.
|
|
400
|
-
expect(trigger.tabIndex).toBe(-1);
|
|
398
|
+
const trigger = container.querySelector("button[data-trigger]") as HTMLButtonElement;
|
|
399
|
+
expect(trigger.disabled).toBe(true);
|
|
401
400
|
});
|
|
402
401
|
|
|
403
402
|
it("sets required aria attribute", () => {
|
|
@@ -412,11 +411,67 @@ describe("DataSelectButton", () => {
|
|
|
412
411
|
/>
|
|
413
412
|
));
|
|
414
413
|
|
|
415
|
-
const trigger = container.querySelector("[
|
|
414
|
+
const trigger = container.querySelector("button[data-trigger]") as HTMLElement;
|
|
416
415
|
expect(trigger.getAttribute("aria-required")).toBe("true");
|
|
417
416
|
});
|
|
418
417
|
|
|
419
|
-
it("
|
|
418
|
+
it("trigger is a button element with aria-haspopup='dialog'", () => {
|
|
419
|
+
const load = createTestLoad();
|
|
420
|
+
const { container } = renderWithDialog(() => (
|
|
421
|
+
<DataSelectButton
|
|
422
|
+
load={load}
|
|
423
|
+
dialog={TestDialogComponent}
|
|
424
|
+
dialogProps={{ confirmKeys: [] }}
|
|
425
|
+
renderItem={(item: TestItem) => <span>{item.name}</span>}
|
|
426
|
+
/>
|
|
427
|
+
));
|
|
428
|
+
|
|
429
|
+
const trigger = container.querySelector("button[data-trigger]") as HTMLButtonElement;
|
|
430
|
+
expect(trigger).not.toBeNull();
|
|
431
|
+
expect(trigger.tagName).toBe("BUTTON");
|
|
432
|
+
expect(trigger.getAttribute("aria-haspopup")).toBe("dialog");
|
|
433
|
+
expect(trigger.getAttribute("type")).toBe("button");
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it("aria-expanded changes dynamically with dialog open state", async () => {
|
|
437
|
+
const load = createTestLoad();
|
|
438
|
+
|
|
439
|
+
const { container } = renderWithDialog(() => (
|
|
440
|
+
<DataSelectButton
|
|
441
|
+
load={load}
|
|
442
|
+
dialog={TestDialogComponent}
|
|
443
|
+
dialogProps={{ confirmKeys: [1] }}
|
|
444
|
+
renderItem={(item: TestItem) => <span>{item.name}</span>}
|
|
445
|
+
/>
|
|
446
|
+
));
|
|
447
|
+
|
|
448
|
+
const trigger = container.querySelector("button[data-trigger]") as HTMLButtonElement;
|
|
449
|
+
|
|
450
|
+
// Initially false
|
|
451
|
+
expect(trigger.getAttribute("aria-expanded")).toBe("false");
|
|
452
|
+
|
|
453
|
+
// Open dialog via search button
|
|
454
|
+
const searchBtn = container.querySelector("[data-search-button]") as HTMLButtonElement;
|
|
455
|
+
searchBtn.click();
|
|
456
|
+
|
|
457
|
+
// While dialog is open, aria-expanded should be true
|
|
458
|
+
await vi.waitFor(() => {
|
|
459
|
+
expect(trigger.getAttribute("aria-expanded")).toBe("true");
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// Confirm dialog to close it
|
|
463
|
+
const confirmBtn = document.querySelector(
|
|
464
|
+
"[data-testid='dialog-confirm']",
|
|
465
|
+
) as HTMLButtonElement;
|
|
466
|
+
confirmBtn.click();
|
|
467
|
+
|
|
468
|
+
// After dialog closes, aria-expanded should be false again
|
|
469
|
+
await vi.waitFor(() => {
|
|
470
|
+
expect(trigger.getAttribute("aria-expanded")).toBe("false");
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it("opens dialog on trigger click", async () => {
|
|
420
475
|
const load = createTestLoad();
|
|
421
476
|
const onValueChange = vi.fn();
|
|
422
477
|
|
|
@@ -430,8 +485,8 @@ describe("DataSelectButton", () => {
|
|
|
430
485
|
/>
|
|
431
486
|
));
|
|
432
487
|
|
|
433
|
-
const trigger = container.querySelector("[
|
|
434
|
-
trigger.
|
|
488
|
+
const trigger = container.querySelector("button[data-trigger]") as HTMLElement;
|
|
489
|
+
trigger.click();
|
|
435
490
|
|
|
436
491
|
await vi.waitFor(() => {
|
|
437
492
|
const confirmBtn = document.querySelector(
|
|
@@ -143,7 +143,7 @@ describe("Combobox component", () => {
|
|
|
143
143
|
onValueChange={handleChange}
|
|
144
144
|
allowsCustomValue
|
|
145
145
|
parseCustomValue={(text) => text}
|
|
146
|
-
renderValue={(v) => <>{v}</>}
|
|
146
|
+
renderValue={(v: string) => <>{v}</>}
|
|
147
147
|
/>
|
|
148
148
|
</I18nProvider></ConfigProvider>
|
|
149
149
|
));
|
|
@@ -178,7 +178,7 @@ describe("Combobox component", () => {
|
|
|
178
178
|
expect(handleChange).toHaveBeenCalledWith({ name: "테스트", custom: true });
|
|
179
179
|
});
|
|
180
180
|
|
|
181
|
-
it("
|
|
181
|
+
it("uses query text as value when allowsCustomValue is true without parseCustomValue", async () => {
|
|
182
182
|
const onValueChange = vi.fn();
|
|
183
183
|
const { container, getByRole } = render(() => (
|
|
184
184
|
<ConfigProvider clientName="test"><I18nProvider>
|
|
@@ -196,7 +196,7 @@ describe("Combobox component", () => {
|
|
|
196
196
|
fireEvent.keyDown(getByRole("combobox"), { key: "Enter" });
|
|
197
197
|
|
|
198
198
|
await waitFor(() => {
|
|
199
|
-
expect(onValueChange).toHaveBeenCalledWith(
|
|
199
|
+
expect(onValueChange).toHaveBeenCalledWith("custom text");
|
|
200
200
|
});
|
|
201
201
|
});
|
|
202
202
|
});
|
|
@@ -115,6 +115,62 @@ describe("DateRangePicker component", () => {
|
|
|
115
115
|
});
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
+
describe("required prop propagation", () => {
|
|
119
|
+
it("propagates required to child DatePickers in range mode", () => {
|
|
120
|
+
const { container } = render(() => (
|
|
121
|
+
<ConfigProvider clientName="test"><I18nProvider>
|
|
122
|
+
<DateRangePicker periodType="range" required />
|
|
123
|
+
</I18nProvider></ConfigProvider>
|
|
124
|
+
));
|
|
125
|
+
const wrapper = container.querySelector("[data-date-range-picker]");
|
|
126
|
+
const dateFields = wrapper?.querySelectorAll("[data-date-field]");
|
|
127
|
+
|
|
128
|
+
// range mode has 2 DatePickers (from + to)
|
|
129
|
+
expect(dateFields?.length).toBe(2);
|
|
130
|
+
|
|
131
|
+
// Each DatePicker's hidden input should have the required validation message
|
|
132
|
+
dateFields?.forEach((field) => {
|
|
133
|
+
const hiddenInput = field.querySelector("input[aria-hidden='true']") as HTMLInputElement;
|
|
134
|
+
expect(hiddenInput).toBeTruthy();
|
|
135
|
+
expect(hiddenInput.validationMessage).toBe("This is a required field");
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("propagates required to child DatePicker in day mode", () => {
|
|
140
|
+
const { container } = render(() => (
|
|
141
|
+
<ConfigProvider clientName="test"><I18nProvider>
|
|
142
|
+
<DateRangePicker periodType="day" required />
|
|
143
|
+
</I18nProvider></ConfigProvider>
|
|
144
|
+
));
|
|
145
|
+
const wrapper = container.querySelector("[data-date-range-picker]");
|
|
146
|
+
const dateFields = wrapper?.querySelectorAll("[data-date-field]");
|
|
147
|
+
|
|
148
|
+
// day mode has 1 DatePicker
|
|
149
|
+
expect(dateFields?.length).toBe(1);
|
|
150
|
+
|
|
151
|
+
const hiddenInput = dateFields?.[0].querySelector("input[aria-hidden='true']") as HTMLInputElement;
|
|
152
|
+
expect(hiddenInput).toBeTruthy();
|
|
153
|
+
expect(hiddenInput.validationMessage).toBe("This is a required field");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("propagates required to child DatePicker in month mode", () => {
|
|
157
|
+
const { container } = render(() => (
|
|
158
|
+
<ConfigProvider clientName="test"><I18nProvider>
|
|
159
|
+
<DateRangePicker periodType="month" required />
|
|
160
|
+
</I18nProvider></ConfigProvider>
|
|
161
|
+
));
|
|
162
|
+
const wrapper = container.querySelector("[data-date-range-picker]");
|
|
163
|
+
const dateFields = wrapper?.querySelectorAll("[data-date-field]");
|
|
164
|
+
|
|
165
|
+
// month mode has 1 DatePicker
|
|
166
|
+
expect(dateFields?.length).toBe(1);
|
|
167
|
+
|
|
168
|
+
const hiddenInput = dateFields?.[0].querySelector("input[aria-hidden='true']") as HTMLInputElement;
|
|
169
|
+
expect(hiddenInput).toBeTruthy();
|
|
170
|
+
expect(hiddenInput.validationMessage).toBe("This is a required field");
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
118
174
|
describe("from change - 'range' mode", () => {
|
|
119
175
|
it("calls onToChange(from) when from > to", () => {
|
|
120
176
|
const onFromChange = vi.fn();
|
|
@@ -23,6 +23,7 @@ describe("SelectItem component", () => {
|
|
|
23
23
|
toggleValue,
|
|
24
24
|
closeDropdown: vi.fn(),
|
|
25
25
|
setItemTemplate: vi.fn(),
|
|
26
|
+
size: () => "md" as const,
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
const { getByText } = render(() => (
|
|
@@ -43,6 +44,7 @@ describe("SelectItem component", () => {
|
|
|
43
44
|
toggleValue: vi.fn(),
|
|
44
45
|
closeDropdown,
|
|
45
46
|
setItemTemplate: vi.fn(),
|
|
47
|
+
size: () => "md" as const,
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
const { getByText } = render(() => (
|
|
@@ -63,6 +65,7 @@ describe("SelectItem component", () => {
|
|
|
63
65
|
toggleValue: vi.fn(),
|
|
64
66
|
closeDropdown,
|
|
65
67
|
setItemTemplate: vi.fn(),
|
|
68
|
+
size: () => "md" as const,
|
|
66
69
|
};
|
|
67
70
|
|
|
68
71
|
const { getByText } = render(() => (
|
|
@@ -84,6 +87,7 @@ describe("SelectItem component", () => {
|
|
|
84
87
|
toggleValue: vi.fn(),
|
|
85
88
|
closeDropdown: vi.fn(),
|
|
86
89
|
setItemTemplate: vi.fn(),
|
|
90
|
+
size: () => "md" as const,
|
|
87
91
|
};
|
|
88
92
|
|
|
89
93
|
render(() => (
|
|
@@ -106,6 +110,7 @@ describe("SelectItem component", () => {
|
|
|
106
110
|
toggleValue,
|
|
107
111
|
closeDropdown: vi.fn(),
|
|
108
112
|
setItemTemplate: vi.fn(),
|
|
113
|
+
size: () => "md" as const,
|
|
109
114
|
};
|
|
110
115
|
|
|
111
116
|
const { getByText } = render(() => (
|
|
@@ -418,108 +418,4 @@ describe("SharedDataProvider", () => {
|
|
|
418
418
|
result.unmount();
|
|
419
419
|
});
|
|
420
420
|
|
|
421
|
-
it("fetches eagerly after configure()", async () => {
|
|
422
|
-
const { serviceClientValue, mockClient } = createMockServiceClient();
|
|
423
|
-
const mockUsers: TestUser[] = [{ id: 1, name: "Alice" }];
|
|
424
|
-
|
|
425
|
-
const fetchFn = vi.fn(() => Promise.resolve(mockUsers));
|
|
426
|
-
|
|
427
|
-
const definitions: { user: SharedDataDefinition<TestUser> } = {
|
|
428
|
-
user: {
|
|
429
|
-
fetch: fetchFn,
|
|
430
|
-
getKey: (item) => item.id,
|
|
431
|
-
orderBy: [[(item) => item.name, "asc"]],
|
|
432
|
-
},
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
function ConfigureOnly() {
|
|
436
|
-
const shared = useTestSharedData();
|
|
437
|
-
shared.configure(() => definitions);
|
|
438
|
-
return <div data-testid="configured">configured</div>;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
const result = render(() => (
|
|
442
|
-
<NotificationContext.Provider value={createMockNotification()}>
|
|
443
|
-
<ServiceClientContext.Provider value={serviceClientValue}>
|
|
444
|
-
<SharedDataProvider>
|
|
445
|
-
<ConfigureOnly />
|
|
446
|
-
</SharedDataProvider>
|
|
447
|
-
</ServiceClientContext.Provider>
|
|
448
|
-
</NotificationContext.Provider>
|
|
449
|
-
));
|
|
450
|
-
|
|
451
|
-
await vi.waitFor(() => {
|
|
452
|
-
expect(result.getByTestId("configured").textContent).toBe("configured");
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
// Now configure() triggers eager init
|
|
456
|
-
await vi.waitFor(() => {
|
|
457
|
-
expect(fetchFn).toHaveBeenCalledTimes(1);
|
|
458
|
-
expect(mockClient.addListener).toHaveBeenCalledTimes(1);
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
result.unmount();
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
it("wait() resolves after data is loaded even without items() access", async () => {
|
|
465
|
-
const { serviceClientValue } = createMockServiceClient();
|
|
466
|
-
|
|
467
|
-
let resolveUsers!: (value: TestUser[]) => void;
|
|
468
|
-
const fetchPromise = new Promise<TestUser[]>((resolve) => {
|
|
469
|
-
resolveUsers = resolve;
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
const fetchFn = vi.fn(() => fetchPromise);
|
|
473
|
-
|
|
474
|
-
const definitions: { user: SharedDataDefinition<TestUser> } = {
|
|
475
|
-
user: {
|
|
476
|
-
fetch: fetchFn,
|
|
477
|
-
getKey: (item) => item.id,
|
|
478
|
-
orderBy: [[(item) => item.name, "asc"]],
|
|
479
|
-
},
|
|
480
|
-
};
|
|
481
|
-
|
|
482
|
-
let waitResolved = false;
|
|
483
|
-
|
|
484
|
-
// Component that calls configure() + wait() but never accesses items()
|
|
485
|
-
function ConfigureAndWait() {
|
|
486
|
-
const shared = useTestSharedData();
|
|
487
|
-
shared.configure(() => definitions);
|
|
488
|
-
|
|
489
|
-
// Call wait() immediately — should NOT resolve until fetch completes
|
|
490
|
-
void shared.wait().then(() => {
|
|
491
|
-
waitResolved = true;
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
return <div data-testid="ready">{String(waitResolved)}</div>;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
const result = render(() => (
|
|
498
|
-
<NotificationContext.Provider value={createMockNotification()}>
|
|
499
|
-
<ServiceClientContext.Provider value={serviceClientValue}>
|
|
500
|
-
<SharedDataProvider>
|
|
501
|
-
<ConfigureAndWait />
|
|
502
|
-
</SharedDataProvider>
|
|
503
|
-
</ServiceClientContext.Provider>
|
|
504
|
-
</NotificationContext.Provider>
|
|
505
|
-
));
|
|
506
|
-
|
|
507
|
-
// fetch should have been called (eager init)
|
|
508
|
-
await vi.waitFor(() => {
|
|
509
|
-
expect(fetchFn).toHaveBeenCalledTimes(1);
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
// wait() should NOT have resolved yet (fetch still pending)
|
|
513
|
-
expect(waitResolved).toBe(false);
|
|
514
|
-
|
|
515
|
-
// Resolve the fetch
|
|
516
|
-
resolveUsers([{ id: 1, name: "Alice" }]);
|
|
517
|
-
|
|
518
|
-
// wait() should now resolve
|
|
519
|
-
await vi.waitFor(() => {
|
|
520
|
-
expect(waitResolved).toBe(true);
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
result.unmount();
|
|
524
|
-
});
|
|
525
421
|
});
|
package/docs/data.md
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
# Data
|
|
2
|
-
|
|
3
|
-
## Table
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
interface TableProps extends JSX.HTMLAttributes<HTMLTableElement> {
|
|
7
|
-
inset?: boolean;
|
|
8
|
-
}
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Basic HTML table with consistent styling.
|
|
12
|
-
|
|
13
|
-
**Sub-components:** `Table.Row`, `Table.HeaderCell`, `Table.Cell`
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## List
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
interface ListProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
21
|
-
inset?: boolean;
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Vertical list with keyboard navigation (ArrowUp/Down, Home/End) and tree-view support (ArrowRight/Left to expand/collapse).
|
|
26
|
-
|
|
27
|
-
**Sub-component:** `List.Item`
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Pagination
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
interface PaginationProps extends JSX.HTMLAttributes<HTMLElement> {
|
|
35
|
-
page: number;
|
|
36
|
-
onPageChange?: (page: number) => void;
|
|
37
|
-
totalPageCount: number;
|
|
38
|
-
displayPageCount?: number;
|
|
39
|
-
size?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Page navigation control. `page` is 1-based. `displayPageCount` controls how many page numbers are visible at once.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## DataSheet
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
interface DataSheetProps<TItem> {
|
|
51
|
-
items?: TItem[];
|
|
52
|
-
storageKey?: string;
|
|
53
|
-
hideConfigBar?: boolean;
|
|
54
|
-
inset?: boolean;
|
|
55
|
-
contentStyle?: JSX.CSSProperties | string;
|
|
56
|
-
|
|
57
|
-
// Sorting
|
|
58
|
-
sorts?: SortingDef[];
|
|
59
|
-
onSortsChange?: (sorts: SortingDef[]) => void;
|
|
60
|
-
autoSort?: boolean;
|
|
61
|
-
|
|
62
|
-
// Pagination
|
|
63
|
-
page?: number;
|
|
64
|
-
onPageChange?: (page: number) => void;
|
|
65
|
-
totalPageCount?: number;
|
|
66
|
-
pageSize?: number;
|
|
67
|
-
displayPageCount?: number;
|
|
68
|
-
|
|
69
|
-
// Selection
|
|
70
|
-
selectionMode?: "single" | "multiple";
|
|
71
|
-
selection?: TItem[];
|
|
72
|
-
onSelectionChange?: (items: TItem[]) => void;
|
|
73
|
-
autoSelect?: boolean;
|
|
74
|
-
isItemSelectable?: (item: TItem) => boolean | string;
|
|
75
|
-
|
|
76
|
-
// Tree expansion
|
|
77
|
-
expandedItems?: TItem[];
|
|
78
|
-
onExpandedItemsChange?: (items: TItem[]) => void;
|
|
79
|
-
itemChildren?: (item: TItem, index: number) => TItem[] | undefined;
|
|
80
|
-
|
|
81
|
-
// Cell styling
|
|
82
|
-
cellClass?: (item: TItem, colKey: string) => string | undefined;
|
|
83
|
-
cellStyle?: (item: TItem, colKey: string) => string | undefined;
|
|
84
|
-
|
|
85
|
-
// Reordering
|
|
86
|
-
onItemsReorder?: (event: DataSheetReorderEvent<TItem>) => void;
|
|
87
|
-
|
|
88
|
-
class?: string;
|
|
89
|
-
children: JSX.Element;
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Advanced data grid with sorting, pagination, row selection, tree expansion, column reordering, and column configuration persistence.
|
|
94
|
-
|
|
95
|
-
- `storageKey` — persists column configuration (width, visibility, order) to sync storage
|
|
96
|
-
- `autoSort` — sorts items client-side without `onSortsChange`
|
|
97
|
-
- `autoSelect` — manages selection state internally
|
|
98
|
-
- `itemChildren` — enables tree-structured rows
|
|
99
|
-
|
|
100
|
-
**Sub-component:** `DataSheet.Column`
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
interface DataSheetColumnProps<TItem> {
|
|
104
|
-
key: string;
|
|
105
|
-
header?: string | string[];
|
|
106
|
-
headerContent?: () => JSX.Element;
|
|
107
|
-
headerStyle?: string;
|
|
108
|
-
summary?: () => JSX.Element;
|
|
109
|
-
tooltip?: string;
|
|
110
|
-
fixed?: boolean;
|
|
111
|
-
hidden?: boolean;
|
|
112
|
-
collapse?: boolean;
|
|
113
|
-
width?: string;
|
|
114
|
-
class?: string;
|
|
115
|
-
sortable?: boolean;
|
|
116
|
-
resizable?: boolean;
|
|
117
|
-
children: (ctx: DataSheetCellContext<TItem>) => JSX.Element;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
interface DataSheetCellContext<TItem> {
|
|
121
|
-
item: TItem;
|
|
122
|
-
index: number;
|
|
123
|
-
row: number;
|
|
124
|
-
depth: number;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
interface SortingDef {
|
|
128
|
-
key: string;
|
|
129
|
-
desc: boolean;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
interface DataSheetReorderEvent<TItem> {
|
|
133
|
-
item: TItem;
|
|
134
|
-
targetItem: TItem;
|
|
135
|
-
position: "before" | "after" | "inside";
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## Calendar
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
interface CalendarProps<TValue> extends Omit<JSX.HTMLAttributes<HTMLTableElement>, "children"> {
|
|
145
|
-
items: TValue[];
|
|
146
|
-
getItemDate: (item: TValue, index: number) => DateOnly;
|
|
147
|
-
renderItem: (item: TValue, index: number) => JSX.Element;
|
|
148
|
-
yearMonth?: DateOnly;
|
|
149
|
-
onYearMonthChange?: (value: DateOnly) => void;
|
|
150
|
-
weekStartDay?: number;
|
|
151
|
-
minDaysInFirstWeek?: number;
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
Monthly calendar view that renders items on their corresponding dates. `getItemDate` maps items to dates. `renderItem` renders each item cell.
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## Kanban
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
interface KanbanProps<TCardValue, TLaneValue> extends Omit<JSX.HTMLAttributes<HTMLDivElement>, "children" | "onDrop"> {
|
|
163
|
-
onDrop?: (info: KanbanDropInfo<TLaneValue, TCardValue>) => void;
|
|
164
|
-
selectedValues?: TCardValue[];
|
|
165
|
-
onSelectedValuesChange?: (values: TCardValue[]) => void;
|
|
166
|
-
children?: JSX.Element;
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
Kanban board with drag-and-drop card management.
|
|
171
|
-
|
|
172
|
-
**Sub-components:**
|
|
173
|
-
- `Kanban.Lane` — `{ value?: TLaneValue; busy?: boolean; collapsible?: boolean; collapsed?: boolean; onCollapsedChange?: (collapsed: boolean) => void }`
|
|
174
|
-
- `Kanban.Card` — `{ value?: TCardValue; draggable?: boolean; selectable?: boolean; contentClass?: string }`
|
|
175
|
-
- `Kanban.LaneTitle` — lane title slot
|
|
176
|
-
- `Kanban.LaneTools` — lane toolbar slot
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## Usage Examples
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
import { DataSheet, Pagination } from "@simplysm/solid";
|
|
184
|
-
|
|
185
|
-
<DataSheet
|
|
186
|
-
items={data()}
|
|
187
|
-
sorts={sorts()}
|
|
188
|
-
onSortsChange={setSorts}
|
|
189
|
-
page={page()}
|
|
190
|
-
onPageChange={setPage}
|
|
191
|
-
totalPageCount={totalPages()}
|
|
192
|
-
selectionMode="multiple"
|
|
193
|
-
selection={selected()}
|
|
194
|
-
onSelectionChange={setSelected}
|
|
195
|
-
storageKey="my-table"
|
|
196
|
-
>
|
|
197
|
-
<DataSheet.Column key="name" header="Name" sortable>
|
|
198
|
-
{(ctx) => ctx.item.name}
|
|
199
|
-
</DataSheet.Column>
|
|
200
|
-
<DataSheet.Column key="age" header="Age" sortable width="80px">
|
|
201
|
-
{(ctx) => ctx.item.age}
|
|
202
|
-
</DataSheet.Column>
|
|
203
|
-
</DataSheet>
|
|
204
|
-
```
|
package/docs/disclosure.md
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
# Disclosure
|
|
2
|
-
|
|
3
|
-
## Collapse
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
interface CollapseProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
7
|
-
open?: boolean;
|
|
8
|
-
}
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Collapsible content panel with CSS transition. Content is hidden when `open` is `false`.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Dropdown
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
interface DropdownProps {
|
|
19
|
-
position?: { x: number; y: number };
|
|
20
|
-
open?: boolean;
|
|
21
|
-
onOpenChange?: (open: boolean) => void;
|
|
22
|
-
maxHeight?: number;
|
|
23
|
-
disabled?: boolean;
|
|
24
|
-
keyboardNav?: boolean;
|
|
25
|
-
class?: string;
|
|
26
|
-
style?: JSX.CSSProperties;
|
|
27
|
-
children: JSX.Element;
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Dropdown popup anchored to a trigger element. When `position` is provided instead of a trigger, the popup appears at the given absolute coordinates (useful for context menus). `keyboardNav` enables ArrowUp/Down navigation. Default `maxHeight` is 300px.
|
|
32
|
-
|
|
33
|
-
**Sub-components:**
|
|
34
|
-
- `Dropdown.Trigger` -- the element that opens the dropdown on click
|
|
35
|
-
- `Dropdown.Content` -- the popup content
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## Dialog
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
interface DialogProps {
|
|
43
|
-
open?: boolean;
|
|
44
|
-
onOpenChange?: (open: boolean) => void;
|
|
45
|
-
withCloseButton?: boolean;
|
|
46
|
-
closeOnInteractOutside?: boolean;
|
|
47
|
-
closeOnEscape?: boolean;
|
|
48
|
-
resizable?: boolean;
|
|
49
|
-
draggable?: boolean;
|
|
50
|
-
mode?: "float" | "fill";
|
|
51
|
-
width?: number;
|
|
52
|
-
height?: number;
|
|
53
|
-
minWidth?: number;
|
|
54
|
-
minHeight?: number;
|
|
55
|
-
position?: "bottom-right" | "top-right";
|
|
56
|
-
headerStyle?: JSX.CSSProperties | string;
|
|
57
|
-
beforeClose?: () => boolean;
|
|
58
|
-
onCloseComplete?: () => void;
|
|
59
|
-
class?: string;
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Modal dialog with overlay. Supports both declarative (template) and programmatic usage.
|
|
64
|
-
|
|
65
|
-
- `mode="float"` -- centered floating dialog (default)
|
|
66
|
-
- `mode="fill"` -- full-screen dialog
|
|
67
|
-
- `beforeClose` -- return `false` to prevent closing
|
|
68
|
-
- `resizable` / `draggable` -- enable resize handles / drag-to-move (draggable defaults to `true`)
|
|
69
|
-
- `withCloseButton` -- show close button (defaults to `true`)
|
|
70
|
-
- `closeOnEscape` -- close on Escape key (defaults to `true`)
|
|
71
|
-
|
|
72
|
-
**Sub-components:**
|
|
73
|
-
- `Dialog.Header` -- dialog title bar
|
|
74
|
-
- `Dialog.Action` -- dialog action buttons area
|
|
75
|
-
|
|
76
|
-
### Programmatic API
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
interface DialogContextValue {
|
|
80
|
-
show<P>(
|
|
81
|
-
component: Component<P>,
|
|
82
|
-
props: Omit<P, "close">,
|
|
83
|
-
options?: DialogShowOptions,
|
|
84
|
-
): Promise<ExtractCloseResult<P> | undefined>;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
interface DialogProviderProps {
|
|
88
|
-
closeOnEscape?: boolean;
|
|
89
|
-
closeOnInteractOutside?: boolean;
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Use `DialogProvider` and `useDialog()` for programmatic dialog management. The shown component receives a `close(result?)` prop to close the dialog and return a value. `DialogProvider` accepts default options for `closeOnEscape` and `closeOnInteractOutside`.
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
const dialog = useDialog();
|
|
97
|
-
const result = await dialog.show(MyDialogContent, { data }, {
|
|
98
|
-
width: 600,
|
|
99
|
-
withCloseButton: true,
|
|
100
|
-
});
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Tabs
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
interface TabsProps {
|
|
109
|
-
value?: string;
|
|
110
|
-
onValueChange?: (value: string) => void;
|
|
111
|
-
size?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
112
|
-
class?: string;
|
|
113
|
-
style?: JSX.CSSProperties;
|
|
114
|
-
children?: JSX.Element;
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
Tab navigation. Content rendering is managed externally based on the selected `value`.
|
|
119
|
-
|
|
120
|
-
**Sub-component:** `Tabs.Tab` -- `{ value: string; disabled?: boolean; class?: string; children?: JSX.Element }`
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## Usage Examples
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
import { Dialog, Dropdown, Tabs, Collapse } from "@simplysm/solid";
|
|
128
|
-
|
|
129
|
-
// Declarative dialog
|
|
130
|
-
<Dialog open={isOpen()} onOpenChange={setIsOpen} width={500} withCloseButton>
|
|
131
|
-
<Dialog.Header>Edit User</Dialog.Header>
|
|
132
|
-
<form>...</form>
|
|
133
|
-
<Dialog.Action>
|
|
134
|
-
<Button onClick={() => setIsOpen(false)}>Cancel</Button>
|
|
135
|
-
<Button theme="primary" onClick={save}>Save</Button>
|
|
136
|
-
</Dialog.Action>
|
|
137
|
-
</Dialog>
|
|
138
|
-
|
|
139
|
-
// Tabs
|
|
140
|
-
<Tabs value={tab()} onValueChange={setTab}>
|
|
141
|
-
<Tabs.Tab value="general">General</Tabs.Tab>
|
|
142
|
-
<Tabs.Tab value="settings">Settings</Tabs.Tab>
|
|
143
|
-
</Tabs>
|
|
144
|
-
<Show when={tab() === "general"}>General content</Show>
|
|
145
|
-
<Show when={tab() === "settings"}>Settings content</Show>
|
|
146
|
-
```
|