@object-ui/react 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.
Files changed (102) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +21 -1
  3. package/dist/context/AppShellContext.d.ts +26 -0
  4. package/dist/context/AppShellContext.js +67 -0
  5. package/dist/context/index.d.ts +1 -7
  6. package/dist/context/index.js +1 -7
  7. package/package.json +32 -9
  8. package/.turbo/turbo-build.log +0 -4
  9. package/src/LazyPluginLoader.tsx +0 -174
  10. package/src/SchemaRenderer.tsx +0 -266
  11. package/src/__tests__/LazyPluginLoader.test.tsx +0 -176
  12. package/src/__tests__/SchemaRenderer.aria.test.tsx +0 -118
  13. package/src/__tests__/SchemaRenderer.debug.test.tsx +0 -69
  14. package/src/__tests__/SchemaRenderer.errorBoundary.test.tsx +0 -114
  15. package/src/__tests__/SchemaRenderer.expressions.test.tsx +0 -131
  16. package/src/__tests__/SchemaRendererProvider.smoke.test.tsx +0 -132
  17. package/src/__tests__/ThemeProvider.test.tsx +0 -451
  18. package/src/__tests__/useNavigationOverlay.test.ts +0 -578
  19. package/src/__tests__/useOffline.test.ts +0 -198
  20. package/src/__tests__/usePageTransition.test.ts +0 -214
  21. package/src/__tests__/usePageVariables.test.tsx +0 -334
  22. package/src/__tests__/usePerformance.test.ts +0 -137
  23. package/src/__tests__/useViewData.test.tsx +0 -422
  24. package/src/__tests__/view-switching.test.tsx +0 -456
  25. package/src/components/form/FieldFactory.tsx +0 -516
  26. package/src/components/form/FormRenderer.test.tsx +0 -744
  27. package/src/components/form/FormRenderer.tsx +0 -258
  28. package/src/components/form/README.md +0 -352
  29. package/src/components/form/index.ts +0 -12
  30. package/src/context/ActionContext.tsx +0 -213
  31. package/src/context/DndContext.tsx +0 -260
  32. package/src/context/NotificationContext.tsx +0 -280
  33. package/src/context/SchemaRendererContext.tsx +0 -48
  34. package/src/context/ThemeContext.tsx +0 -325
  35. package/src/context/__tests__/ActionContext.test.tsx +0 -174
  36. package/src/context/__tests__/DndContext.test.tsx +0 -150
  37. package/src/context/__tests__/NotificationContext.test.tsx +0 -176
  38. package/src/context/__tests__/useDataScope.test.tsx +0 -91
  39. package/src/context/index.ts +0 -13
  40. package/src/hooks/__tests__/useActionEngine.test.ts +0 -170
  41. package/src/hooks/__tests__/useActionRunner.test.ts +0 -153
  42. package/src/hooks/__tests__/useAnimation.test.ts +0 -82
  43. package/src/hooks/__tests__/useColumnSummary.test.ts +0 -93
  44. package/src/hooks/__tests__/useCrudShortcuts.test.ts +0 -85
  45. package/src/hooks/__tests__/useDataRefresh.test.ts +0 -115
  46. package/src/hooks/__tests__/useDebugMode.test.ts +0 -83
  47. package/src/hooks/__tests__/useDensityMode.test.ts +0 -81
  48. package/src/hooks/__tests__/useDiscovery.test.tsx +0 -247
  49. package/src/hooks/__tests__/useDynamicApp.test.ts +0 -161
  50. package/src/hooks/__tests__/useETagCache.test.ts +0 -153
  51. package/src/hooks/__tests__/useExpression.test.ts +0 -86
  52. package/src/hooks/__tests__/useFocusTrap.test.ts +0 -294
  53. package/src/hooks/__tests__/useKeyboardShortcuts.test.ts +0 -144
  54. package/src/hooks/__tests__/usePageTransition.test.ts +0 -158
  55. package/src/hooks/__tests__/usePerformanceBudget.test.ts +0 -157
  56. package/src/hooks/__tests__/useReducedMotion.test.ts +0 -104
  57. package/src/hooks/__tests__/useSchemaPersistence.test.ts +0 -220
  58. package/src/hooks/__tests__/useViewSharing.test.ts +0 -121
  59. package/src/hooks/__tests__/useViewTransition.test.ts +0 -61
  60. package/src/hooks/index.ts +0 -36
  61. package/src/hooks/useActionEngine.ts +0 -84
  62. package/src/hooks/useActionRunner.ts +0 -119
  63. package/src/hooks/useAnimation.ts +0 -117
  64. package/src/hooks/useClientNotifications.ts +0 -251
  65. package/src/hooks/useColumnSummary.ts +0 -87
  66. package/src/hooks/useCrudShortcuts.ts +0 -126
  67. package/src/hooks/useDataRefresh.ts +0 -58
  68. package/src/hooks/useDebugMode.ts +0 -95
  69. package/src/hooks/useDensityMode.ts +0 -102
  70. package/src/hooks/useDiscovery.ts +0 -168
  71. package/src/hooks/useDynamicApp.ts +0 -142
  72. package/src/hooks/useETagCache.ts +0 -404
  73. package/src/hooks/useExpression.ts +0 -56
  74. package/src/hooks/useFocusManagement.ts +0 -248
  75. package/src/hooks/useFocusTrap.ts +0 -138
  76. package/src/hooks/useGlobalUndo.ts +0 -98
  77. package/src/hooks/useKeyboardShortcuts.ts +0 -119
  78. package/src/hooks/useNavigationOverlay.ts +0 -190
  79. package/src/hooks/useOffline.ts +0 -301
  80. package/src/hooks/usePageTransition.ts +0 -187
  81. package/src/hooks/usePageVariables.tsx +0 -157
  82. package/src/hooks/usePerformance.ts +0 -246
  83. package/src/hooks/usePerformanceBudget.ts +0 -313
  84. package/src/hooks/useReducedMotion.ts +0 -53
  85. package/src/hooks/useSchemaPersistence.ts +0 -222
  86. package/src/hooks/useViewData.ts +0 -201
  87. package/src/hooks/useViewSharing.ts +0 -140
  88. package/src/hooks/useViewTransition.ts +0 -144
  89. package/src/index.ts +0 -41
  90. package/src/spec-bridge/SpecBridge.ts +0 -64
  91. package/src/spec-bridge/__tests__/P1SpecBridge.test.ts +0 -855
  92. package/src/spec-bridge/__tests__/SpecBridge.test.ts +0 -550
  93. package/src/spec-bridge/bridges/dashboard.ts +0 -180
  94. package/src/spec-bridge/bridges/form-view.ts +0 -109
  95. package/src/spec-bridge/bridges/list-view.ts +0 -187
  96. package/src/spec-bridge/bridges/page.ts +0 -136
  97. package/src/spec-bridge/index.ts +0 -14
  98. package/src/spec-bridge/types.ts +0 -38
  99. package/src/utils/i18n.ts +0 -10
  100. package/tsconfig.json +0 -17
  101. package/tsconfig.tsbuildinfo +0 -1
  102. package/vitest.config.ts +0 -2
@@ -1,176 +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, vi } from 'vitest';
10
- import { render, screen, waitFor } from '@testing-library/react';
11
- import React from 'react';
12
- import { createLazyPlugin, preloadPlugin } from '../LazyPluginLoader';
13
-
14
- describe('createLazyPlugin', () => {
15
- it('should create a lazy-loaded component', async () => {
16
- const TestComponent = () => <div>Test Component</div>;
17
-
18
- const LazyComponent = createLazyPlugin(
19
- () => Promise.resolve({ default: TestComponent })
20
- );
21
-
22
- render(<LazyComponent />);
23
-
24
- await waitFor(() => {
25
- expect(screen.getByText('Test Component')).toBeInTheDocument();
26
- });
27
- });
28
-
29
- it('should show custom fallback while loading', async () => {
30
- const TestComponent = () => <div>Test Component</div>;
31
-
32
- const LazyComponent = createLazyPlugin(
33
- () => new Promise<{ default: React.ComponentType }>((resolve) => {
34
- setTimeout(() => resolve({ default: TestComponent }), 100);
35
- }),
36
- { fallback: <div>Loading...</div> }
37
- );
38
-
39
- render(<LazyComponent />);
40
-
41
- expect(screen.getByText('Loading...')).toBeInTheDocument();
42
-
43
- await waitFor(() => {
44
- expect(screen.getByText('Test Component')).toBeInTheDocument();
45
- });
46
- });
47
-
48
- it('should pass props to the lazy component', async () => {
49
- interface TestProps {
50
- message: string;
51
- }
52
-
53
- const TestComponent = ({ message }: TestProps) => <div>{message}</div>;
54
-
55
- const LazyComponent = createLazyPlugin<TestProps>(
56
- () => Promise.resolve({ default: TestComponent })
57
- );
58
-
59
- render(<LazyComponent message="Hello World" />);
60
-
61
- await waitFor(() => {
62
- expect(screen.getByText('Hello World')).toBeInTheDocument();
63
- });
64
- });
65
-
66
- it('should handle loading errors gracefully', async () => {
67
- // Mock console.error to avoid noise in test output
68
- const originalError = console.error;
69
- console.error = vi.fn();
70
-
71
- const LazyComponent = createLazyPlugin<any>(
72
- () => Promise.reject(new Error('Load error'))
73
- );
74
-
75
- expect(() => render(<LazyComponent />)).not.toThrow();
76
-
77
- // Restore console.error
78
- console.error = originalError;
79
- });
80
-
81
- it('should preload a plugin without rendering', async () => {
82
- const TestComponent = () => <div>Preloaded</div>;
83
- const importFn = vi.fn(() => Promise.resolve({ default: TestComponent }));
84
-
85
- const result = await preloadPlugin(importFn);
86
-
87
- expect(importFn).toHaveBeenCalledTimes(1);
88
- expect(result).toEqual({ default: TestComponent });
89
- });
90
-
91
- it('should render errorFallback when component fails to load', async () => {
92
- const originalError = console.error;
93
- console.error = vi.fn();
94
-
95
- const ErrorFallback = ({ error, retry }: { error: Error; retry: () => void }) => (
96
- <div>
97
- <span>Error: {error.message}</span>
98
- <button onClick={retry}>Retry</button>
99
- </div>
100
- );
101
-
102
- const LazyComponent = createLazyPlugin(
103
- () => Promise.reject(new Error('chunk failed')),
104
- { retries: 0, errorFallback: ErrorFallback }
105
- );
106
-
107
- render(<LazyComponent />);
108
-
109
- await waitFor(() => {
110
- expect(screen.getByText('Error: chunk failed')).toBeInTheDocument();
111
- });
112
-
113
- expect(screen.getByRole('button', { name: 'Retry' })).toBeInTheDocument();
114
-
115
- console.error = originalError;
116
- });
117
-
118
- it('should not retry when retries=0', async () => {
119
- const originalError = console.error;
120
- console.error = vi.fn();
121
-
122
- const importFn = vi.fn(() => Promise.reject(new Error('fail')));
123
-
124
- const ErrorFallback = ({ error }: { error: Error; retry: () => void }) => (
125
- <div>Error: {error.message}</div>
126
- );
127
-
128
- const LazyComponent = createLazyPlugin(importFn, {
129
- retries: 0,
130
- errorFallback: ErrorFallback,
131
- });
132
-
133
- render(<LazyComponent />);
134
-
135
- await waitFor(() => {
136
- expect(screen.getByText('Error: fail')).toBeInTheDocument();
137
- });
138
-
139
- // Called only once — no retries
140
- expect(importFn).toHaveBeenCalledTimes(1);
141
-
142
- console.error = originalError;
143
- });
144
-
145
- it('should retry on failure then succeed', async () => {
146
- const originalError = console.error;
147
- console.error = vi.fn();
148
-
149
- const TestComponent = () => <div>Success after retry</div>;
150
-
151
- let callCount = 0;
152
- const importFn = vi.fn(() => {
153
- callCount++;
154
- if (callCount <= 2) {
155
- return Promise.reject(new Error('transient error'));
156
- }
157
- return Promise.resolve({ default: TestComponent as React.ComponentType });
158
- });
159
-
160
- const LazyComponent = createLazyPlugin(importFn, {
161
- retries: 3,
162
- retryDelay: 10,
163
- });
164
-
165
- render(<LazyComponent />);
166
-
167
- await waitFor(() => {
168
- expect(screen.getByText('Success after retry')).toBeInTheDocument();
169
- }, { timeout: 2000 });
170
-
171
- // First call + 2 retries (fails) + 1 success = called 3 times
172
- expect(importFn).toHaveBeenCalledTimes(3);
173
-
174
- console.error = originalError;
175
- });
176
- });
@@ -1,118 +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, beforeEach, afterEach } from 'vitest';
10
- import { render, screen } from '@testing-library/react';
11
- import React from 'react';
12
- import { ComponentRegistry } from '@object-ui/core';
13
- import { SchemaRenderer } from '../SchemaRenderer';
14
-
15
- // A simple test component that forwards ARIA attributes
16
- const TestWidget: React.FC<any> = (props) => (
17
- <div
18
- data-testid="test-widget"
19
- aria-label={props['aria-label']}
20
- aria-describedby={props['aria-describedby']}
21
- role={props['role']}
22
- >
23
- {props.content || 'Test'}
24
- </div>
25
- );
26
-
27
- describe('SchemaRenderer AriaProps injection', () => {
28
- beforeEach(() => {
29
- ComponentRegistry.register('test-widget', TestWidget);
30
- });
31
-
32
- afterEach(() => {
33
- ComponentRegistry.unregister?.('test-widget');
34
- });
35
-
36
- it('should inject aria-label from ariaLabel string', () => {
37
- render(
38
- <SchemaRenderer
39
- schema={{
40
- type: 'test-widget',
41
- ariaLabel: 'Close dialog',
42
- }}
43
- />
44
- );
45
- const el = screen.getByTestId('test-widget');
46
- expect(el).toHaveAttribute('aria-label', 'Close dialog');
47
- });
48
-
49
- it('should resolve ariaLabel from I18nLabel object', () => {
50
- render(
51
- <SchemaRenderer
52
- schema={{
53
- type: 'test-widget',
54
- ariaLabel: { key: 'dialog.close', defaultValue: 'Close dialog' },
55
- }}
56
- />
57
- );
58
- const el = screen.getByTestId('test-widget');
59
- expect(el).toHaveAttribute('aria-label', 'Close dialog');
60
- });
61
-
62
- it('should inject aria-describedby from ariaDescribedBy', () => {
63
- render(
64
- <SchemaRenderer
65
- schema={{
66
- type: 'test-widget',
67
- ariaDescribedBy: 'help-text-1',
68
- }}
69
- />
70
- );
71
- const el = screen.getByTestId('test-widget');
72
- expect(el).toHaveAttribute('aria-describedby', 'help-text-1');
73
- });
74
-
75
- it('should inject role from schema', () => {
76
- render(
77
- <SchemaRenderer
78
- schema={{
79
- type: 'test-widget',
80
- role: 'navigation',
81
- }}
82
- />
83
- );
84
- const el = screen.getByTestId('test-widget');
85
- expect(el).toHaveAttribute('role', 'navigation');
86
- });
87
-
88
- it('should inject all ARIA props together', () => {
89
- render(
90
- <SchemaRenderer
91
- schema={{
92
- type: 'test-widget',
93
- ariaLabel: 'Main nav',
94
- ariaDescribedBy: 'nav-desc',
95
- role: 'navigation',
96
- }}
97
- />
98
- );
99
- const el = screen.getByTestId('test-widget');
100
- expect(el).toHaveAttribute('aria-label', 'Main nav');
101
- expect(el).toHaveAttribute('aria-describedby', 'nav-desc');
102
- expect(el).toHaveAttribute('role', 'navigation');
103
- });
104
-
105
- it('should not inject ARIA attrs when not in schema', () => {
106
- render(
107
- <SchemaRenderer
108
- schema={{
109
- type: 'test-widget',
110
- content: 'Hello',
111
- }}
112
- />
113
- );
114
- const el = screen.getByTestId('test-widget');
115
- expect(el).not.toHaveAttribute('aria-label');
116
- expect(el).not.toHaveAttribute('aria-describedby');
117
- });
118
- });
@@ -1,69 +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, vi, beforeEach, afterEach } from 'vitest';
10
- import { render } from '@testing-library/react';
11
- import React from 'react';
12
- import { ComponentRegistry } from '@object-ui/core';
13
- import { SchemaRenderer } from '../SchemaRenderer';
14
- import { SchemaRendererProvider } from '../context/SchemaRendererContext';
15
-
16
- // Suppress console.warn from deprecated namespace registration
17
- const originalWarn = console.warn;
18
- beforeEach(() => {
19
- console.warn = vi.fn();
20
- });
21
- afterEach(() => {
22
- console.warn = originalWarn;
23
- });
24
-
25
- // A simple test component that passes all props through to a div
26
- const TestDiv: React.FC<any> = (props) => {
27
- const { schema, ...rest } = props;
28
- return <div data-testid="test-div" {...rest} />;
29
- };
30
-
31
- describe('SchemaRenderer debug attributes', () => {
32
- beforeEach(() => {
33
- ComponentRegistry.register('test-debug-div', TestDiv);
34
- });
35
-
36
- it('should NOT inject data-debug-* attributes when debug is off', () => {
37
- const { getByTestId } = render(
38
- <SchemaRendererProvider dataSource={{}}>
39
- <SchemaRenderer schema={{ type: 'test-debug-div', id: 'myBtn' }} />
40
- </SchemaRendererProvider>,
41
- );
42
- const el = getByTestId('test-div');
43
- expect(el.getAttribute('data-debug-type')).toBeNull();
44
- expect(el.getAttribute('data-debug-id')).toBeNull();
45
- });
46
-
47
- it('should inject data-debug-type when debug is enabled', () => {
48
- const { getByTestId } = render(
49
- <SchemaRendererProvider dataSource={{}} debug={true}>
50
- <SchemaRenderer schema={{ type: 'test-debug-div', id: 'myBtn' }} />
51
- </SchemaRendererProvider>,
52
- );
53
- const el = getByTestId('test-div');
54
- expect(el.getAttribute('data-debug-type')).toBe('test-debug-div');
55
- expect(el.getAttribute('data-debug-id')).toBe('myBtn');
56
- });
57
-
58
- it('should inject data-debug-type when debugFlags.enabled is true', () => {
59
- const { getByTestId } = render(
60
- <SchemaRendererProvider dataSource={{}} debugFlags={{ enabled: true }}>
61
- <SchemaRenderer schema={{ type: 'test-debug-div' }} />
62
- </SchemaRendererProvider>,
63
- );
64
- const el = getByTestId('test-div');
65
- expect(el.getAttribute('data-debug-type')).toBe('test-debug-div');
66
- // No id on schema, so data-debug-id should not be present
67
- expect(el.getAttribute('data-debug-id')).toBeNull();
68
- });
69
- });
@@ -1,114 +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, beforeEach, afterEach, vi } from 'vitest';
10
- import { render, screen, fireEvent } from '@testing-library/react';
11
- import React from 'react';
12
- import { ComponentRegistry } from '@object-ui/core';
13
- import { SchemaRenderer, SchemaErrorBoundary } from '../SchemaRenderer';
14
-
15
- // Suppress console.error from React error boundary during tests
16
- const originalConsoleError = console.error;
17
- beforeEach(() => {
18
- console.error = vi.fn();
19
- });
20
- afterEach(() => {
21
- console.error = originalConsoleError;
22
- });
23
-
24
- const CrashingWidget: React.FC = () => {
25
- throw new Error('Widget exploded!');
26
- };
27
-
28
- const StableWidget: React.FC<any> = (props) => (
29
- <div data-testid="stable-widget">{props.content || 'Stable'}</div>
30
- );
31
-
32
- describe('SchemaErrorBoundary', () => {
33
- it('should render children normally when no error', () => {
34
- render(
35
- <SchemaErrorBoundary>
36
- <div data-testid="child">OK</div>
37
- </SchemaErrorBoundary>
38
- );
39
- expect(screen.getByTestId('child')).toBeInTheDocument();
40
- });
41
-
42
- it('should catch render errors and display fallback', () => {
43
- render(
44
- <SchemaErrorBoundary componentType="crashing-widget">
45
- <CrashingWidget />
46
- </SchemaErrorBoundary>
47
- );
48
- expect(screen.getByRole('alert')).toBeInTheDocument();
49
- expect(screen.getByText(/failed to render/i)).toBeInTheDocument();
50
- expect(screen.getByText(/"crashing-widget"/)).toBeInTheDocument();
51
- expect(screen.getByText('Widget exploded!')).toBeInTheDocument();
52
- });
53
-
54
- it('should display retry button that resets the error', () => {
55
- render(
56
- <SchemaErrorBoundary>
57
- <CrashingWidget />
58
- </SchemaErrorBoundary>
59
- );
60
- expect(screen.getByRole('alert')).toBeInTheDocument();
61
- const retryButton = screen.getByText('Retry');
62
- expect(retryButton).toBeInTheDocument();
63
- });
64
-
65
- it('should work without componentType prop', () => {
66
- render(
67
- <SchemaErrorBoundary>
68
- <CrashingWidget />
69
- </SchemaErrorBoundary>
70
- );
71
- expect(screen.getByText(/failed to render/i)).toBeInTheDocument();
72
- });
73
- });
74
-
75
- describe('SchemaRenderer error boundary integration', () => {
76
- beforeEach(() => {
77
- ComponentRegistry.register('crashing-widget', CrashingWidget);
78
- ComponentRegistry.register('stable-widget', StableWidget);
79
- });
80
-
81
- afterEach(() => {
82
- ComponentRegistry.unregister?.('crashing-widget');
83
- ComponentRegistry.unregister?.('stable-widget');
84
- });
85
-
86
- it('should catch errors from a crashing component', () => {
87
- render(
88
- <SchemaRenderer schema={{ type: 'crashing-widget' }} />
89
- );
90
- expect(screen.getByRole('alert')).toBeInTheDocument();
91
- expect(screen.getByText('Widget exploded!')).toBeInTheDocument();
92
- });
93
-
94
- it('should not affect stable components', () => {
95
- render(
96
- <SchemaRenderer schema={{ type: 'stable-widget', content: 'Hello' }} />
97
- );
98
- expect(screen.getByTestId('stable-widget')).toBeInTheDocument();
99
- expect(screen.getByText('Hello')).toBeInTheDocument();
100
- });
101
-
102
- it('should isolate crashes between multiple renderers', () => {
103
- render(
104
- <div>
105
- <SchemaRenderer schema={{ type: 'crashing-widget' }} />
106
- <SchemaRenderer schema={{ type: 'stable-widget', content: 'Still works' }} />
107
- </div>
108
- );
109
- // Crashing one should not affect the other
110
- expect(screen.getByRole('alert')).toBeInTheDocument();
111
- expect(screen.getByTestId('stable-widget')).toBeInTheDocument();
112
- expect(screen.getByText('Still works')).toBeInTheDocument();
113
- });
114
- });
@@ -1,131 +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, beforeEach, afterEach } from 'vitest';
10
- import { render, screen } from '@testing-library/react';
11
- import React from 'react';
12
- import { ComponentRegistry } from '@object-ui/core';
13
- import { SchemaRenderer } from '../SchemaRenderer';
14
- import { SchemaRendererContext } from '../context/SchemaRendererContext';
15
-
16
- // Simple test component
17
- const TestComponent = (props: any) => (
18
- <div data-testid="test-component" data-disabled={props.disabled || undefined}>
19
- {props.content || props.label || 'Test'}
20
- </div>
21
- );
22
-
23
- describe('SchemaRenderer Expression Integration', () => {
24
- beforeEach(() => {
25
- ComponentRegistry.register('test-component', TestComponent);
26
- });
27
-
28
- afterEach(() => {
29
- ComponentRegistry.unregister?.('test-component');
30
- });
31
-
32
- describe('visible / hidden expressions', () => {
33
- it('renders when visible is true', () => {
34
- render(<SchemaRenderer schema={{ type: 'test-component', visible: true }} />);
35
- expect(screen.getByTestId('test-component')).toBeInTheDocument();
36
- });
37
-
38
- it('does not render when visible is false', () => {
39
- const { container } = render(<SchemaRenderer schema={{ type: 'test-component', visible: false }} />);
40
- expect(container.innerHTML).toBe('');
41
- });
42
-
43
- it('evaluates visible expression string', () => {
44
- render(
45
- <SchemaRendererContext.Provider value={{ dataSource: { role: 'admin' } }}>
46
- <SchemaRenderer schema={{ type: 'test-component', visible: '${data.role === "admin"}' }} />
47
- </SchemaRendererContext.Provider>
48
- );
49
- expect(screen.getByTestId('test-component')).toBeInTheDocument();
50
- });
51
-
52
- it('hides when visible expression evaluates to false', () => {
53
- const { container } = render(
54
- <SchemaRendererContext.Provider value={{ dataSource: { role: 'viewer' } }}>
55
- <SchemaRenderer schema={{ type: 'test-component', visible: '${data.role === "admin"}' }} />
56
- </SchemaRendererContext.Provider>
57
- );
58
- expect(container.innerHTML).toBe('');
59
- });
60
-
61
- it('hides when hidden is true', () => {
62
- const { container } = render(<SchemaRenderer schema={{ type: 'test-component', hidden: true }} />);
63
- expect(container.innerHTML).toBe('');
64
- });
65
-
66
- it('shows when hidden is false', () => {
67
- render(<SchemaRenderer schema={{ type: 'test-component', hidden: false }} />);
68
- expect(screen.getByTestId('test-component')).toBeInTheDocument();
69
- });
70
-
71
- it('hides with hiddenOn expression', () => {
72
- const { container } = render(
73
- <SchemaRendererContext.Provider value={{ dataSource: { status: 'draft' } }}>
74
- <SchemaRenderer schema={{ type: 'test-component', hiddenOn: '${data.status === "draft"}' }} />
75
- </SchemaRendererContext.Provider>
76
- );
77
- expect(container.innerHTML).toBe('');
78
- });
79
-
80
- it('visible takes precedence over hidden', () => {
81
- render(<SchemaRenderer schema={{ type: 'test-component', visible: true, hidden: true }} />);
82
- expect(screen.getByTestId('test-component')).toBeInTheDocument();
83
- });
84
- });
85
-
86
- describe('disabled expressions', () => {
87
- it('passes disabled=true when disabled expression is true', () => {
88
- render(<SchemaRenderer schema={{ type: 'test-component', disabled: true }} />);
89
- expect(screen.getByTestId('test-component')).toHaveAttribute('data-disabled', 'true');
90
- });
91
-
92
- it('evaluates disabled expression string', () => {
93
- render(
94
- <SchemaRendererContext.Provider value={{ dataSource: { status: 'locked' } }}>
95
- <SchemaRenderer schema={{ type: 'test-component', disabled: '${data.status === "locked"}' }} />
96
- </SchemaRendererContext.Provider>
97
- );
98
- expect(screen.getByTestId('test-component')).toHaveAttribute('data-disabled', 'true');
99
- });
100
-
101
- it('does not set disabled when expression is false', () => {
102
- render(
103
- <SchemaRendererContext.Provider value={{ dataSource: { status: 'active' } }}>
104
- <SchemaRenderer schema={{ type: 'test-component', disabled: '${data.status === "locked"}' }} />
105
- </SchemaRendererContext.Provider>
106
- );
107
- expect(screen.getByTestId('test-component')).not.toHaveAttribute('data-disabled');
108
- });
109
-
110
- it('evaluates disabledOn expression', () => {
111
- render(
112
- <SchemaRendererContext.Provider value={{ dataSource: { readOnly: true } }}>
113
- <SchemaRenderer schema={{ type: 'test-component', disabledOn: '${data.readOnly}' }} />
114
- </SchemaRendererContext.Provider>
115
- );
116
- expect(screen.getByTestId('test-component')).toHaveAttribute('data-disabled', 'true');
117
- });
118
- });
119
-
120
- describe('defaults', () => {
121
- it('renders by default when no visibility props are set', () => {
122
- render(<SchemaRenderer schema={{ type: 'test-component' }} />);
123
- expect(screen.getByTestId('test-component')).toBeInTheDocument();
124
- });
125
-
126
- it('is not disabled by default', () => {
127
- render(<SchemaRenderer schema={{ type: 'test-component' }} />);
128
- expect(screen.getByTestId('test-component')).not.toHaveAttribute('data-disabled');
129
- });
130
- });
131
- });