@object-ui/components 3.3.0 → 3.3.1
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/CHANGELOG.md +20 -0
- package/README.md +21 -1
- package/dist/index.css +6339 -2
- package/dist/index.js +17600 -17481
- package/dist/index.umd.cjs +36 -36
- package/dist/packages/components/src/custom/empty.d.ts +12 -1
- package/dist/packages/components/src/renderers/action/action-bar.d.ts +12 -1
- package/dist/packages/components/src/ui/chart.d.ts +10 -29
- package/package.json +65 -44
- package/.turbo/turbo-build.log +0 -84
- package/README_SHADCN_SYNC.md +0 -281
- package/TESTING.md +0 -335
- package/docs/FilterBuilder.md +0 -268
- package/metadata/Chart.component.yml +0 -30
- package/metadata/FilterBuilder.component.yml +0 -39
- package/metadata/GridLayout.component.yml +0 -27
- package/metadata/Menu.component.yml +0 -31
- package/metadata/ObjectForm.component.yml +0 -34
- package/metadata/ObjectGrid.component.yml +0 -72
- package/metadata/Page.component.yml +0 -24
- package/postcss.config.js +0 -14
- package/shadcn-components.json +0 -440
- package/src/SchemaRenderer.tsx +0 -28
- package/src/__tests__/PageRendererRegions.test.tsx +0 -668
- package/src/__tests__/README.md +0 -124
- package/src/__tests__/__snapshots__/snapshot-critical.test.tsx.snap +0 -811
- package/src/__tests__/__snapshots__/snapshot.test.tsx.snap +0 -327
- package/src/__tests__/accessibility.test.tsx +0 -137
- package/src/__tests__/action-bar.test.tsx +0 -206
- package/src/__tests__/api-consistency.test.tsx +0 -596
- package/src/__tests__/basic-renderers.test.tsx +0 -255
- package/src/__tests__/color-contrast.test.tsx +0 -212
- package/src/__tests__/complex-disclosure-renderers.test.tsx +0 -302
- package/src/__tests__/compliance.test.tsx +0 -72
- package/src/__tests__/config-field-renderer.test.tsx +0 -307
- package/src/__tests__/config-panel-renderer.test.tsx +0 -580
- package/src/__tests__/config-primitives.test.tsx +0 -106
- package/src/__tests__/edge-cases.test.tsx +0 -285
- package/src/__tests__/feedback-overlay-renderers.test.tsx +0 -349
- package/src/__tests__/filter-builder.test.tsx +0 -409
- package/src/__tests__/form-renderers.test.tsx +0 -364
- package/src/__tests__/layout-data-renderers.test.tsx +0 -340
- package/src/__tests__/mobile-accessibility.test.tsx +0 -120
- package/src/__tests__/navigation-overlay.test.tsx +0 -370
- package/src/__tests__/snapshot-critical.test.tsx +0 -317
- package/src/__tests__/snapshot.test.tsx +0 -205
- package/src/__tests__/test-utils.tsx +0 -190
- package/src/__tests__/use-config-draft.test.tsx +0 -295
- package/src/__tests__/view-compliance.test.tsx +0 -153
- package/src/__tests__/wcag-audit.test.tsx +0 -493
- package/src/custom/action-param-dialog.tsx +0 -264
- package/src/custom/button-group.tsx +0 -91
- package/src/custom/combobox.tsx +0 -104
- package/src/custom/config-field-renderer.tsx +0 -276
- package/src/custom/config-panel-renderer.tsx +0 -306
- package/src/custom/config-row.tsx +0 -50
- package/src/custom/date-picker.tsx +0 -61
- package/src/custom/empty.tsx +0 -112
- package/src/custom/field.tsx +0 -81
- package/src/custom/filter-builder.tsx +0 -418
- package/src/custom/index.ts +0 -21
- package/src/custom/input-group.tsx +0 -53
- package/src/custom/item.tsx +0 -201
- package/src/custom/kbd.tsx +0 -36
- package/src/custom/mobile-dialog-content.tsx +0 -67
- package/src/custom/native-select.tsx +0 -33
- package/src/custom/navigation-overlay.tsx +0 -334
- package/src/custom/section-header.tsx +0 -68
- package/src/custom/sort-builder.tsx +0 -129
- package/src/custom/spinner.tsx +0 -26
- package/src/custom/view-skeleton.tsx +0 -243
- package/src/custom/view-states.tsx +0 -153
- package/src/debug/DebugPanel.tsx +0 -313
- package/src/debug/__tests__/DebugPanel.test.tsx +0 -134
- package/src/debug/index.ts +0 -10
- package/src/hooks/use-config-draft.ts +0 -127
- package/src/hooks/use-mobile.tsx +0 -27
- package/src/index.css +0 -245
- package/src/index.ts +0 -47
- package/src/lib/use-sync-external-store-shim.ts +0 -10
- package/src/lib/use-sync-external-store-with-selector-shim.ts +0 -90
- package/src/lib/utils.tsx +0 -35
- package/src/new-components.test.ts +0 -73
- package/src/renderers/action/action-bar.tsx +0 -221
- package/src/renderers/action/action-button.tsx +0 -158
- package/src/renderers/action/action-group.tsx +0 -270
- package/src/renderers/action/action-icon.tsx +0 -150
- package/src/renderers/action/action-menu.tsx +0 -203
- package/src/renderers/action/index.ts +0 -19
- package/src/renderers/action/resolve-icon.ts +0 -35
- package/src/renderers/basic/button-group.tsx +0 -79
- package/src/renderers/basic/div.tsx +0 -60
- package/src/renderers/basic/html.tsx +0 -43
- package/src/renderers/basic/icon.tsx +0 -89
- package/src/renderers/basic/image.tsx +0 -49
- package/src/renderers/basic/index.ts +0 -18
- package/src/renderers/basic/navigation-menu.tsx +0 -81
- package/src/renderers/basic/pagination.tsx +0 -109
- package/src/renderers/basic/separator.tsx +0 -57
- package/src/renderers/basic/span.tsx +0 -63
- package/src/renderers/basic/text.tsx +0 -52
- package/src/renderers/complex/README-KANBAN.md +0 -208
- package/src/renderers/complex/TIMELINE.md +0 -353
- package/src/renderers/complex/__tests__/data-table-airtable-ux.test.tsx +0 -239
- package/src/renderers/complex/__tests__/data-table-batch-editing.test.tsx +0 -275
- package/src/renderers/complex/__tests__/data-table-cell-renderer.test.tsx +0 -120
- package/src/renderers/complex/__tests__/data-table-editing.test.tsx +0 -221
- package/src/renderers/complex/__tests__/data-table.test.ts +0 -76
- package/src/renderers/complex/carousel.tsx +0 -69
- package/src/renderers/complex/data-table.tsx +0 -1243
- package/src/renderers/complex/filter-builder.tsx +0 -77
- package/src/renderers/complex/index.ts +0 -16
- package/src/renderers/complex/resizable.tsx +0 -66
- package/src/renderers/complex/scroll-area.tsx +0 -58
- package/src/renderers/complex/table.tsx +0 -95
- package/src/renderers/data-display/alert.tsx +0 -46
- package/src/renderers/data-display/avatar.tsx +0 -38
- package/src/renderers/data-display/badge.tsx +0 -55
- package/src/renderers/data-display/breadcrumb.tsx +0 -61
- package/src/renderers/data-display/index.ts +0 -18
- package/src/renderers/data-display/kbd.tsx +0 -50
- package/src/renderers/data-display/list.tsx +0 -75
- package/src/renderers/data-display/statistic.tsx +0 -95
- package/src/renderers/data-display/table.tsx +0 -78
- package/src/renderers/data-display/tree-view.tsx +0 -176
- package/src/renderers/disclosure/accordion.tsx +0 -69
- package/src/renderers/disclosure/collapsible.tsx +0 -53
- package/src/renderers/disclosure/index.ts +0 -11
- package/src/renderers/disclosure/toggle-group.tsx +0 -79
- package/src/renderers/feedback/empty.tsx +0 -49
- package/src/renderers/feedback/index.ts +0 -16
- package/src/renderers/feedback/loading.tsx +0 -78
- package/src/renderers/feedback/progress.tsx +0 -29
- package/src/renderers/feedback/skeleton.tsx +0 -31
- package/src/renderers/feedback/sonner.tsx +0 -56
- package/src/renderers/feedback/spinner.tsx +0 -55
- package/src/renderers/feedback/toast.tsx +0 -59
- package/src/renderers/feedback/toaster.tsx +0 -23
- package/src/renderers/form/button.tsx +0 -103
- package/src/renderers/form/calendar.tsx +0 -34
- package/src/renderers/form/checkbox.tsx +0 -71
- package/src/renderers/form/combobox.tsx +0 -48
- package/src/renderers/form/command.tsx +0 -58
- package/src/renderers/form/date-picker.tsx +0 -84
- package/src/renderers/form/file-upload.tsx +0 -184
- package/src/renderers/form/form.tsx +0 -540
- package/src/renderers/form/index.ts +0 -26
- package/src/renderers/form/input-otp.tsx +0 -51
- package/src/renderers/form/input.tsx +0 -121
- package/src/renderers/form/label.tsx +0 -45
- package/src/renderers/form/radio-group.tsx +0 -63
- package/src/renderers/form/select.tsx +0 -94
- package/src/renderers/form/slider.tsx +0 -61
- package/src/renderers/form/switch.tsx +0 -48
- package/src/renderers/form/textarea.tsx +0 -76
- package/src/renderers/form/toggle.tsx +0 -42
- package/src/renderers/index.ts +0 -18
- package/src/renderers/layout/aspect-ratio.tsx +0 -51
- package/src/renderers/layout/card.tsx +0 -85
- package/src/renderers/layout/container.tsx +0 -122
- package/src/renderers/layout/flex.tsx +0 -132
- package/src/renderers/layout/grid.tsx +0 -178
- package/src/renderers/layout/index.ts +0 -19
- package/src/renderers/layout/page.tsx +0 -466
- package/src/renderers/layout/semantic.tsx +0 -48
- package/src/renderers/layout/stack.tsx +0 -132
- package/src/renderers/layout/tabs.tsx +0 -97
- package/src/renderers/navigation/header-bar.tsx +0 -118
- package/src/renderers/navigation/index.ts +0 -10
- package/src/renderers/navigation/sidebar.tsx +0 -208
- package/src/renderers/overlay/alert-dialog.tsx +0 -72
- package/src/renderers/overlay/context-menu.tsx +0 -100
- package/src/renderers/overlay/dialog.tsx +0 -77
- package/src/renderers/overlay/drawer.tsx +0 -77
- package/src/renderers/overlay/dropdown-menu.tsx +0 -99
- package/src/renderers/overlay/hover-card.tsx +0 -55
- package/src/renderers/overlay/index.ts +0 -18
- package/src/renderers/overlay/menubar.tsx +0 -76
- package/src/renderers/overlay/popover.tsx +0 -56
- package/src/renderers/overlay/sheet.tsx +0 -77
- package/src/renderers/overlay/tooltip.tsx +0 -67
- package/src/renderers/placeholders.tsx +0 -107
- package/src/stories/CRMApp.stories.tsx +0 -706
- package/src/stories/ConfigPanel.stories.tsx +0 -232
- package/src/stories/Guide.mdx +0 -55
- package/src/stories/MockedData.stories.tsx +0 -121
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +0 -1
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +0 -1
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +0 -1
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +0 -1
- package/src/stories/assets/youtube.svg +0 -1
- package/src/stories/button.css +0 -30
- package/src/stories/header.css +0 -32
- package/src/stories/page.css +0 -68
- package/src/stories-json/Accessibility.mdx +0 -297
- package/src/stories-json/EdgeCases.stories.tsx +0 -160
- package/src/stories-json/GettingStarted.mdx +0 -89
- package/src/stories-json/Introduction.mdx +0 -127
- package/src/stories-json/accordion.stories.tsx +0 -43
- package/src/stories-json/aggrid.stories.tsx +0 -103
- package/src/stories-json/alert.stories.tsx +0 -39
- package/src/stories-json/aspect-ratio.stories.tsx +0 -34
- package/src/stories-json/avatar.stories.tsx +0 -38
- package/src/stories-json/badge.stories.tsx +0 -53
- package/src/stories-json/breadcrumb.stories.tsx +0 -30
- package/src/stories-json/button-group.stories.tsx +0 -43
- package/src/stories-json/button.stories.tsx +0 -73
- package/src/stories-json/calendar.stories.tsx +0 -85
- package/src/stories-json/card.stories.tsx +0 -48
- package/src/stories-json/carousel.stories.tsx +0 -33
- package/src/stories-json/charts.stories.tsx +0 -195
- package/src/stories-json/chatbot.stories.tsx +0 -349
- package/src/stories-json/code-editor.stories.tsx +0 -92
- package/src/stories-json/collapsible.stories.tsx +0 -40
- package/src/stories-json/controls.stories.tsx +0 -36
- package/src/stories-json/crm-live-data.stories.tsx +0 -154
- package/src/stories-json/dashboard.stories.tsx +0 -318
- package/src/stories-json/data-table.stories.tsx +0 -136
- package/src/stories-json/data_display_extras.stories.tsx +0 -102
- package/src/stories-json/date-picker.stories.tsx +0 -28
- package/src/stories-json/detail-view.stories.tsx +0 -258
- package/src/stories-json/dialog.stories.tsx +0 -43
- package/src/stories-json/feedback_extras.stories.tsx +0 -40
- package/src/stories-json/feedback_others.stories.tsx +0 -46
- package/src/stories-json/form-variants.stories.tsx +0 -210
- package/src/stories-json/form_advanced.stories.tsx +0 -117
- package/src/stories-json/form_extras.stories.tsx +0 -123
- package/src/stories-json/grid.stories.tsx +0 -56
- package/src/stories-json/icon.stories.tsx +0 -36
- package/src/stories-json/input.stories.tsx +0 -52
- package/src/stories-json/kanban.stories.tsx +0 -295
- package/src/stories-json/layout_extended.stories.tsx +0 -76
- package/src/stories-json/layout_flex.stories.tsx +0 -107
- package/src/stories-json/list-view.stories.tsx +0 -97
- package/src/stories-json/markdown.stories.tsx +0 -129
- package/src/stories-json/menus.stories.tsx +0 -63
- package/src/stories-json/metric-card.stories.tsx +0 -143
- package/src/stories-json/navigation-menu.stories.tsx +0 -37
- package/src/stories-json/object-aggrid-advanced.stories.tsx +0 -389
- package/src/stories-json/object-aggrid.stories.tsx +0 -252
- package/src/stories-json/object-form.stories.tsx +0 -130
- package/src/stories-json/object-gantt.stories.tsx +0 -114
- package/src/stories-json/object-grid.stories.tsx +0 -315
- package/src/stories-json/object-map.stories.tsx +0 -116
- package/src/stories-json/object-view.stories.tsx +0 -118
- package/src/stories-json/overlay_extras.stories.tsx +0 -113
- package/src/stories-json/overlay_others.stories.tsx +0 -76
- package/src/stories-json/page.stories.tsx +0 -55
- package/src/stories-json/reports.stories.tsx +0 -163
- package/src/stories-json/resizable.stories.tsx +0 -44
- package/src/stories-json/select.stories.tsx +0 -34
- package/src/stories-json/separator.stories.tsx +0 -41
- package/src/stories-json/sidebar.stories.tsx +0 -147
- package/src/stories-json/statistic.stories.tsx +0 -44
- package/src/stories-json/tabs.stories.tsx +0 -51
- package/src/stories-json/timeline.stories.tsx +0 -188
- package/src/stories-json/typography.stories.tsx +0 -45
- package/src/types/config-panel.ts +0 -101
- package/src/ui/accordion.tsx +0 -66
- package/src/ui/alert-dialog.tsx +0 -149
- package/src/ui/alert.tsx +0 -67
- package/src/ui/aspect-ratio.tsx +0 -15
- package/src/ui/avatar.tsx +0 -58
- package/src/ui/badge.tsx +0 -44
- package/src/ui/breadcrumb.tsx +0 -123
- package/src/ui/button.tsx +0 -64
- package/src/ui/calendar.tsx +0 -221
- package/src/ui/card.tsx +0 -87
- package/src/ui/carousel.tsx +0 -270
- package/src/ui/chart.tsx +0 -377
- package/src/ui/checkbox.tsx +0 -38
- package/src/ui/collapsible.tsx +0 -19
- package/src/ui/command.tsx +0 -161
- package/src/ui/context-menu.tsx +0 -208
- package/src/ui/dialog.tsx +0 -130
- package/src/ui/drawer.tsx +0 -126
- package/src/ui/dropdown-menu.tsx +0 -208
- package/src/ui/form.tsx +0 -186
- package/src/ui/hover-card.tsx +0 -37
- package/src/ui/index.ts +0 -56
- package/src/ui/input-otp.tsx +0 -79
- package/src/ui/input.tsx +0 -30
- package/src/ui/label.tsx +0 -34
- package/src/ui/menubar.tsx +0 -264
- package/src/ui/navigation-menu.tsx +0 -136
- package/src/ui/pagination.tsx +0 -125
- package/src/ui/popover.tsx +0 -39
- package/src/ui/progress.tsx +0 -36
- package/src/ui/radio-group.tsx +0 -52
- package/src/ui/resizable.tsx +0 -53
- package/src/ui/scroll-area.tsx +0 -56
- package/src/ui/select.tsx +0 -168
- package/src/ui/separator.tsx +0 -39
- package/src/ui/sheet.tsx +0 -150
- package/src/ui/sidebar.tsx +0 -781
- package/src/ui/skeleton.tsx +0 -23
- package/src/ui/slider.tsx +0 -39
- package/src/ui/sonner.tsx +0 -53
- package/src/ui/switch.tsx +0 -37
- package/src/ui/table.tsx +0 -125
- package/src/ui/tabs.tsx +0 -63
- package/src/ui/textarea.tsx +0 -30
- package/src/ui/toast.tsx +0 -137
- package/src/ui/toggle-group.tsx +0 -69
- package/src/ui/toggle.tsx +0 -53
- package/src/ui/tooltip.tsx +0 -38
- package/src/ui/typography.tsx +0 -85
- package/tsconfig.json +0 -19
- package/vite.config.ts +0 -71
- package/vitest.config.ts +0 -5
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect, beforeAll } from 'vitest';
|
|
10
|
-
import {
|
|
11
|
-
renderComponent,
|
|
12
|
-
validateComponentRegistration,
|
|
13
|
-
getAllDisplayIssues,
|
|
14
|
-
checkDOMStructure,
|
|
15
|
-
} from './test-utils';
|
|
16
|
-
|
|
17
|
-
// Import renderers to ensure registration
|
|
18
|
-
beforeAll(async () => {
|
|
19
|
-
await import('../renderers');
|
|
20
|
-
}, 30000); // Increase timeout to 30 seconds for heavy renderer imports
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Comprehensive tests for basic renderer components
|
|
24
|
-
* These tests automatically detect display and rendering issues
|
|
25
|
-
*/
|
|
26
|
-
describe('Basic Renderers - Display Issue Detection', () => {
|
|
27
|
-
describe('Text Renderer', () => {
|
|
28
|
-
it('should be properly registered', () => {
|
|
29
|
-
const validation = validateComponentRegistration('text');
|
|
30
|
-
expect(validation.isRegistered).toBe(true);
|
|
31
|
-
expect(validation.hasConfig).toBe(true);
|
|
32
|
-
expect(validation.hasLabel).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should render text content without issues', () => {
|
|
36
|
-
const { container } = renderComponent({
|
|
37
|
-
type: 'text',
|
|
38
|
-
content: 'Hello World',
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
expect(container.textContent).toContain('Hello World');
|
|
42
|
-
const issues = getAllDisplayIssues(container);
|
|
43
|
-
expect(issues).toHaveLength(0);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should handle empty content gracefully', () => {
|
|
47
|
-
const { container } = renderComponent({
|
|
48
|
-
type: 'text',
|
|
49
|
-
content: '',
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const domCheck = checkDOMStructure(container);
|
|
53
|
-
// Empty text is acceptable, just verify it doesn't crash
|
|
54
|
-
expect(domCheck).toBeDefined();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should support value property as alias', () => {
|
|
58
|
-
const { container } = renderComponent({
|
|
59
|
-
type: 'text',
|
|
60
|
-
value: 'Test Value',
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
expect(container.textContent).toContain('Test Value');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should render with designer props correctly', () => {
|
|
67
|
-
const { container } = renderComponent(
|
|
68
|
-
{ type: 'text', content: 'Designer Test' },
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
// Verify it renders without errors
|
|
72
|
-
expect(container).toBeDefined();
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe('Div Renderer', () => {
|
|
77
|
-
it('should be properly registered', () => {
|
|
78
|
-
const validation = validateComponentRegistration('div');
|
|
79
|
-
expect(validation.isRegistered).toBe(true);
|
|
80
|
-
expect(validation.hasConfig).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should render container without issues', () => {
|
|
84
|
-
const { container } = renderComponent({
|
|
85
|
-
type: 'div',
|
|
86
|
-
className: 'test-class',
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const div = container.querySelector('div');
|
|
90
|
-
expect(div).toBeTruthy();
|
|
91
|
-
// className might be applied differently, just verify div exists
|
|
92
|
-
expect(div).toBeDefined();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should render children correctly', () => {
|
|
96
|
-
const { container } = renderComponent({
|
|
97
|
-
type: 'div',
|
|
98
|
-
body: [
|
|
99
|
-
{ type: 'text', content: 'Child 1' },
|
|
100
|
-
{ type: 'text', content: 'Child 2' },
|
|
101
|
-
],
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
expect(container.textContent).toContain('Child 1');
|
|
105
|
-
expect(container.textContent).toContain('Child 2');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should not have display issues', () => {
|
|
109
|
-
const { container } = renderComponent({
|
|
110
|
-
type: 'div',
|
|
111
|
-
body: [{ type: 'text', content: 'Content' }],
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const issues = getAllDisplayIssues(container);
|
|
115
|
-
// Should have no critical issues
|
|
116
|
-
expect(issues.filter(i => i.includes('missing accessible label'))).toHaveLength(0);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
describe('Span Renderer', () => {
|
|
121
|
-
it('should be properly registered', () => {
|
|
122
|
-
const validation = validateComponentRegistration('span');
|
|
123
|
-
expect(validation.isRegistered).toBe(true);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should render inline content', () => {
|
|
127
|
-
const { container } = renderComponent({
|
|
128
|
-
type: 'span',
|
|
129
|
-
body: [{ type: 'text', content: 'Inline text' }],
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const span = container.querySelector('span');
|
|
133
|
-
expect(span).toBeTruthy();
|
|
134
|
-
expect(span?.textContent).toContain('Inline text');
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe('Image Renderer', () => {
|
|
139
|
-
it('should be properly registered', () => {
|
|
140
|
-
const validation = validateComponentRegistration('image');
|
|
141
|
-
expect(validation.isRegistered).toBe(true);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should render with required alt attribute', () => {
|
|
145
|
-
const { container } = renderComponent({
|
|
146
|
-
type: 'image',
|
|
147
|
-
src: 'https://example.com/image.jpg',
|
|
148
|
-
alt: 'Test image',
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const img = container.querySelector('img');
|
|
152
|
-
expect(img).toBeTruthy();
|
|
153
|
-
expect(img?.getAttribute('alt')).toBe('Test image');
|
|
154
|
-
expect(img?.getAttribute('src')).toBe('https://example.com/image.jpg');
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should detect missing alt attribute', () => {
|
|
158
|
-
const { container } = renderComponent({
|
|
159
|
-
type: 'image',
|
|
160
|
-
src: 'https://example.com/image.jpg',
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const img = container.querySelector('img');
|
|
164
|
-
// If img has alt, it's good; if not, our check should detect it
|
|
165
|
-
if (img && !img.hasAttribute('alt')) {
|
|
166
|
-
const issues = getAllDisplayIssues(container);
|
|
167
|
-
const altIssues = issues.filter(i => i.includes('alt'));
|
|
168
|
-
expect(altIssues.length).toBeGreaterThan(0);
|
|
169
|
-
} else {
|
|
170
|
-
// Image renderer provides default alt, which is good
|
|
171
|
-
expect(img?.hasAttribute('alt') || true).toBe(true);
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe('Icon Renderer', () => {
|
|
177
|
-
it('should be properly registered', () => {
|
|
178
|
-
const validation = validateComponentRegistration('icon');
|
|
179
|
-
expect(validation.isRegistered).toBe(true);
|
|
180
|
-
// Icon may or may not have defaultProps, both are acceptable
|
|
181
|
-
expect(validation.hasConfig).toBe(true);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('should render icon without issues', () => {
|
|
185
|
-
const { container } = renderComponent({
|
|
186
|
-
type: 'icon',
|
|
187
|
-
name: 'star',
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// Icon should render an SVG
|
|
191
|
-
const svg = container.querySelector('svg');
|
|
192
|
-
expect(svg).toBeTruthy();
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('should apply size classes correctly', () => {
|
|
196
|
-
const { container } = renderComponent({
|
|
197
|
-
type: 'icon',
|
|
198
|
-
name: 'heart',
|
|
199
|
-
size: 24,
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
const svg = container.querySelector('svg');
|
|
203
|
-
expect(svg).toBeTruthy();
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
describe('Separator Renderer', () => {
|
|
208
|
-
it('should be properly registered', () => {
|
|
209
|
-
const validation = validateComponentRegistration('separator');
|
|
210
|
-
expect(validation.isRegistered).toBe(true);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('should render separator with proper role', () => {
|
|
214
|
-
const { container } = renderComponent({
|
|
215
|
-
type: 'separator',
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
const separator = container.querySelector('[role="separator"], hr, [data-orientation]');
|
|
219
|
-
expect(separator).toBeTruthy();
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('should support both orientations', () => {
|
|
223
|
-
const { container: horizontal } = renderComponent({
|
|
224
|
-
type: 'separator',
|
|
225
|
-
orientation: 'horizontal',
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
const { container: vertical } = renderComponent({
|
|
229
|
-
type: 'separator',
|
|
230
|
-
orientation: 'vertical',
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
expect(horizontal.querySelector('*')).toBeTruthy();
|
|
234
|
-
expect(vertical.querySelector('*')).toBeTruthy();
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
describe('HTML Renderer', () => {
|
|
239
|
-
it('should be properly registered', () => {
|
|
240
|
-
const validation = validateComponentRegistration('html');
|
|
241
|
-
expect(validation.isRegistered).toBe(true);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('should render HTML content safely', () => {
|
|
245
|
-
const { container } = renderComponent({
|
|
246
|
-
type: 'html',
|
|
247
|
-
html: '<p>HTML Content</p>',
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// HTML renderer uses dangerouslySetInnerHTML
|
|
251
|
-
const hasContent = container.querySelector('p') || container.textContent?.includes('HTML Content');
|
|
252
|
-
expect(hasContent).toBeTruthy();
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
});
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* WCAG 2.1 AA Color Contrast Tests.
|
|
11
|
-
*
|
|
12
|
-
* Verifies that key color combinations used in ObjectUI meet WCAG AA
|
|
13
|
-
* contrast ratio requirements:
|
|
14
|
-
* - 4.5:1 for normal text (< 18pt or < 14pt bold)
|
|
15
|
-
* - 3:1 for large text (≥ 18pt or ≥ 14pt bold)
|
|
16
|
-
*
|
|
17
|
-
* Tests both light and dark theme color values.
|
|
18
|
-
* Part of P2.3 Accessibility & Inclusive Design roadmap.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { describe, it, expect } from 'vitest';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Calculate relative luminance per WCAG 2.1.
|
|
25
|
-
* https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
|
|
26
|
-
*/
|
|
27
|
-
function relativeLuminance(r: number, g: number, b: number): number {
|
|
28
|
-
const [rs, gs, bs] = [r, g, b].map((c) => {
|
|
29
|
-
const sRGB = c / 255;
|
|
30
|
-
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
|
31
|
-
});
|
|
32
|
-
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Calculate contrast ratio between two colors.
|
|
37
|
-
* https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
|
|
38
|
-
*/
|
|
39
|
-
function contrastRatio(
|
|
40
|
-
fg: [number, number, number],
|
|
41
|
-
bg: [number, number, number]
|
|
42
|
-
): number {
|
|
43
|
-
const l1 = relativeLuminance(...fg);
|
|
44
|
-
const l2 = relativeLuminance(...bg);
|
|
45
|
-
const lighter = Math.max(l1, l2);
|
|
46
|
-
const darker = Math.min(l1, l2);
|
|
47
|
-
return (lighter + 0.05) / (darker + 0.05);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// WCAG AA thresholds
|
|
51
|
-
const AA_NORMAL_TEXT = 4.5;
|
|
52
|
-
const AA_LARGE_TEXT = 3.0;
|
|
53
|
-
|
|
54
|
-
// ── Light Theme Colors (based on Shadcn/Tailwind defaults) ──
|
|
55
|
-
|
|
56
|
-
const light = {
|
|
57
|
-
background: [255, 255, 255] as [number, number, number], // hsl(0 0% 100%)
|
|
58
|
-
foreground: [10, 10, 10] as [number, number, number], // hsl(0 0% 3.9%)
|
|
59
|
-
muted: [245, 245, 245] as [number, number, number], // hsl(0 0% 96.1%)
|
|
60
|
-
mutedForeground: [115, 115, 115] as [number, number, number], // hsl(0 0% 45.1%)
|
|
61
|
-
primary: [24, 24, 27] as [number, number, number], // hsl(240 5.9% 10%)
|
|
62
|
-
primaryForeground: [250, 250, 250] as [number, number, number], // hsl(0 0% 98%)
|
|
63
|
-
destructive: [239, 68, 68] as [number, number, number], // hsl(0 84.2% 60.2%) — red-500
|
|
64
|
-
destructiveForeground: [255, 255, 255] as [number, number, number],
|
|
65
|
-
card: [255, 255, 255] as [number, number, number],
|
|
66
|
-
cardForeground: [10, 10, 10] as [number, number, number],
|
|
67
|
-
border: [229, 229, 229] as [number, number, number], // hsl(0 0% 89.8%)
|
|
68
|
-
accent: [245, 245, 245] as [number, number, number],
|
|
69
|
-
accentForeground: [24, 24, 27] as [number, number, number],
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// ── Dark Theme Colors (based on Shadcn/Tailwind defaults) ──
|
|
73
|
-
|
|
74
|
-
const dark = {
|
|
75
|
-
background: [10, 10, 10] as [number, number, number], // hsl(0 0% 3.9%)
|
|
76
|
-
foreground: [250, 250, 250] as [number, number, number], // hsl(0 0% 98%)
|
|
77
|
-
muted: [38, 38, 38] as [number, number, number], // hsl(0 0% 14.9%)
|
|
78
|
-
mutedForeground: [163, 163, 163] as [number, number, number], // hsl(0 0% 63.9%)
|
|
79
|
-
primary: [250, 250, 250] as [number, number, number], // hsl(0 0% 98%)
|
|
80
|
-
primaryForeground: [24, 24, 27] as [number, number, number], // hsl(240 5.9% 10%)
|
|
81
|
-
destructive: [239, 68, 68] as [number, number, number], // hsl(0 84.2% 60.2%) — red-500 (same in dark)
|
|
82
|
-
destructiveForeground: [250, 250, 250] as [number, number, number],
|
|
83
|
-
card: [10, 10, 10] as [number, number, number],
|
|
84
|
-
cardForeground: [250, 250, 250] as [number, number, number],
|
|
85
|
-
border: [38, 38, 38] as [number, number, number],
|
|
86
|
-
accent: [38, 38, 38] as [number, number, number],
|
|
87
|
-
accentForeground: [250, 250, 250] as [number, number, number],
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
describe('WCAG 2.1 AA Color Contrast — Light Theme', () => {
|
|
91
|
-
it('foreground on background meets AA for normal text (4.5:1)', () => {
|
|
92
|
-
const ratio = contrastRatio(light.foreground, light.background);
|
|
93
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('primary foreground on primary meets AA for normal text', () => {
|
|
97
|
-
const ratio = contrastRatio(light.primaryForeground, light.primary);
|
|
98
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('destructive foreground on destructive meets AA for large text (3:1)', () => {
|
|
102
|
-
const ratio = contrastRatio(light.destructiveForeground, light.destructive);
|
|
103
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_LARGE_TEXT);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('muted foreground on background meets AA for large text (3:1)', () => {
|
|
107
|
-
const ratio = contrastRatio(light.mutedForeground, light.background);
|
|
108
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_LARGE_TEXT);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('muted foreground on muted background meets AA for large text (3:1)', () => {
|
|
112
|
-
const ratio = contrastRatio(light.mutedForeground, light.muted);
|
|
113
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_LARGE_TEXT);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('card foreground on card background meets AA for normal text', () => {
|
|
117
|
-
const ratio = contrastRatio(light.cardForeground, light.card);
|
|
118
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('accent foreground on accent background meets AA for normal text', () => {
|
|
122
|
-
const ratio = contrastRatio(light.accentForeground, light.accent);
|
|
123
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('foreground on muted background meets AA for normal text', () => {
|
|
127
|
-
const ratio = contrastRatio(light.foreground, light.muted);
|
|
128
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('primary on background meets AA for normal text', () => {
|
|
132
|
-
const ratio = contrastRatio(light.primary, light.background);
|
|
133
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('destructive on background meets AA for large text', () => {
|
|
137
|
-
const ratio = contrastRatio(light.destructive, light.background);
|
|
138
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_LARGE_TEXT);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
describe('WCAG 2.1 AA Color Contrast — Dark Theme', () => {
|
|
143
|
-
it('foreground on background meets AA for normal text (4.5:1)', () => {
|
|
144
|
-
const ratio = contrastRatio(dark.foreground, dark.background);
|
|
145
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('primary foreground on primary meets AA for normal text', () => {
|
|
149
|
-
const ratio = contrastRatio(dark.primaryForeground, dark.primary);
|
|
150
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('destructive foreground on destructive meets AA for large text (3:1)', () => {
|
|
154
|
-
const ratio = contrastRatio(dark.destructiveForeground, dark.destructive);
|
|
155
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_LARGE_TEXT);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('muted foreground on background meets AA for large text (3:1)', () => {
|
|
159
|
-
const ratio = contrastRatio(dark.mutedForeground, dark.background);
|
|
160
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_LARGE_TEXT);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('muted foreground on muted background meets AA for large text (3:1)', () => {
|
|
164
|
-
const ratio = contrastRatio(dark.mutedForeground, dark.muted);
|
|
165
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_LARGE_TEXT);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('card foreground on card background meets AA for normal text', () => {
|
|
169
|
-
const ratio = contrastRatio(dark.cardForeground, dark.card);
|
|
170
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('accent foreground on accent background meets AA for normal text', () => {
|
|
174
|
-
const ratio = contrastRatio(dark.accentForeground, dark.accent);
|
|
175
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('foreground on muted background meets AA for normal text', () => {
|
|
179
|
-
const ratio = contrastRatio(dark.foreground, dark.muted);
|
|
180
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('primary on background meets AA for normal text', () => {
|
|
184
|
-
const ratio = contrastRatio(dark.primary, dark.background);
|
|
185
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_NORMAL_TEXT);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('destructive on background meets AA for large text', () => {
|
|
189
|
-
const ratio = contrastRatio(dark.destructive, dark.background);
|
|
190
|
-
expect(ratio).toBeGreaterThanOrEqual(AA_LARGE_TEXT);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe('WCAG 2.1 AA Color Contrast — Utility Validation', () => {
|
|
195
|
-
it('contrastRatio returns 21:1 for black on white', () => {
|
|
196
|
-
const ratio = contrastRatio([0, 0, 0], [255, 255, 255]);
|
|
197
|
-
expect(ratio).toBeCloseTo(21, 0);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('contrastRatio returns 1:1 for same color', () => {
|
|
201
|
-
const ratio = contrastRatio([128, 128, 128], [128, 128, 128]);
|
|
202
|
-
expect(ratio).toBeCloseTo(1, 1);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('relativeLuminance returns 0 for black', () => {
|
|
206
|
-
expect(relativeLuminance(0, 0, 0)).toBe(0);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('relativeLuminance returns 1 for white', () => {
|
|
210
|
-
expect(relativeLuminance(255, 255, 255)).toBeCloseTo(1, 4);
|
|
211
|
-
});
|
|
212
|
-
});
|