@qwickapps/react-framework 1.3.5 → 1.4.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/README.md +1691 -2
- package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts +66 -0
- package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +7 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/Html.d.ts +28 -18
- package/dist/components/Html.d.ts.map +1 -1
- package/dist/components/Logo.d.ts +12 -35
- package/dist/components/Logo.d.ts.map +1 -1
- package/dist/components/Markdown.d.ts +18 -13
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/QwickApp.d.ts +16 -3
- package/dist/components/QwickApp.d.ts.map +1 -1
- package/dist/components/QwickIcon.d.ts +23 -0
- package/dist/components/QwickIcon.d.ts.map +1 -0
- package/dist/components/SafeSpan.d.ts +12 -5
- package/dist/components/SafeSpan.d.ts.map +1 -1
- package/dist/components/Scaffold.d.ts.map +1 -1
- package/dist/components/base/ModelView.d.ts +101 -0
- package/dist/components/base/ModelView.d.ts.map +1 -0
- package/dist/components/base/index.d.ts +11 -0
- package/dist/components/base/index.d.ts.map +1 -0
- package/dist/components/blocks/Article.d.ts +12 -2
- package/dist/components/blocks/Article.d.ts.map +1 -1
- package/dist/components/blocks/Code.d.ts +13 -2
- package/dist/components/blocks/Code.d.ts.map +1 -1
- package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -1
- package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
- package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
- package/dist/components/blocks/Footer.d.ts.map +1 -1
- package/dist/components/blocks/HeroBlock.d.ts +27 -13
- package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
- package/dist/components/blocks/Image.d.ts +41 -0
- package/dist/components/blocks/Image.d.ts.map +1 -0
- package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
- package/dist/components/blocks/Section.d.ts +16 -2
- package/dist/components/blocks/Section.d.ts.map +1 -1
- package/dist/components/blocks/Text.d.ts +41 -0
- package/dist/components/blocks/Text.d.ts.map +1 -0
- package/dist/components/blocks/index.d.ts +4 -0
- package/dist/components/blocks/index.d.ts.map +1 -1
- package/dist/components/buttons/Button.d.ts +23 -7
- package/dist/components/buttons/Button.d.ts.map +1 -1
- package/dist/components/forms/FormBlock.d.ts +19 -13
- package/dist/components/forms/FormBlock.d.ts.map +1 -1
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/input/ChoiceInputField.d.ts +17 -11
- package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
- package/dist/components/input/HtmlInputField.d.ts +17 -11
- package/dist/components/input/HtmlInputField.d.ts.map +1 -1
- package/dist/components/input/SelectInputField.d.ts +16 -10
- package/dist/components/input/SelectInputField.d.ts.map +1 -1
- package/dist/components/input/SwitchInputField.d.ts +16 -10
- package/dist/components/input/SwitchInputField.d.ts.map +1 -1
- package/dist/components/input/TextField.d.ts.map +1 -1
- package/dist/components/input/TextInputField.d.ts +16 -11
- package/dist/components/input/TextInputField.d.ts.map +1 -1
- package/dist/components/layout/GridCell.d.ts +23 -6
- package/dist/components/layout/GridCell.d.ts.map +1 -1
- package/dist/components/layout/GridLayout.d.ts +24 -23
- package/dist/components/layout/GridLayout.d.ts.map +1 -1
- package/dist/components/pages/FormPage.d.ts.map +1 -1
- package/dist/components/pages/Page.d.ts +49 -87
- package/dist/components/pages/Page.d.ts.map +1 -1
- package/dist/components/pages/index.d.ts +2 -2
- package/dist/components/pages/index.d.ts.map +1 -1
- package/dist/config/AppConfig.d.ts +49 -0
- package/dist/config/AppConfig.d.ts.map +1 -0
- package/dist/config/AppConfigBuilder.d.ts +75 -0
- package/dist/config/AppConfigBuilder.d.ts.map +1 -0
- package/dist/config/index.d.ts +13 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/types.d.ts +130 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.esm.js +451 -0
- package/dist/config.js +455 -0
- package/dist/contexts/PrintModeContext.d.ts +27 -0
- package/dist/contexts/PrintModeContext.d.ts.map +1 -0
- package/dist/contexts/QwickAppContext.d.ts +2 -2
- package/dist/contexts/QwickAppContext.d.ts.map +1 -1
- package/dist/contexts/index.d.ts +2 -0
- package/dist/contexts/index.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/usePrintMode.d.ts +39 -0
- package/dist/hooks/usePrintMode.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +10951 -6238
- package/dist/index.js +11014 -6287
- package/dist/schemas/CodeSchema.d.ts +2 -1
- package/dist/schemas/CodeSchema.d.ts.map +1 -1
- package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
- package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
- package/dist/schemas/ContentSchema.d.ts +2 -1
- package/dist/schemas/ContentSchema.d.ts.map +1 -1
- package/dist/schemas/GridCellSchema.d.ts +25 -0
- package/dist/schemas/GridCellSchema.d.ts.map +1 -0
- package/dist/schemas/GridLayoutSchema.d.ts +23 -0
- package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
- package/dist/schemas/HtmlSchema.d.ts +14 -0
- package/dist/schemas/HtmlSchema.d.ts.map +1 -0
- package/dist/schemas/ImageSchema.d.ts +32 -0
- package/dist/schemas/ImageSchema.d.ts.map +1 -0
- package/dist/schemas/LogoSchema.d.ts +35 -0
- package/dist/schemas/LogoSchema.d.ts.map +1 -0
- package/dist/schemas/MarkdownSchema.d.ts +14 -0
- package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
- package/dist/schemas/PageTemplateSchema.d.ts +31 -0
- package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
- package/dist/schemas/PrintConfigSchema.d.ts +31 -0
- package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
- package/dist/schemas/SectionSchema.d.ts +2 -1
- package/dist/schemas/SectionSchema.d.ts.map +1 -1
- package/dist/schemas/TextSchema.d.ts +37 -0
- package/dist/schemas/TextSchema.d.ts.map +1 -0
- package/dist/schemas/ViewModelSchema.d.ts +23 -0
- package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
- package/dist/schemas/index.d.ts +15 -1
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
- package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
- package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
- package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
- package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
- package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
- package/dist/schemas/transformers/registry.d.ts +15 -0
- package/dist/schemas/transformers/registry.d.ts.map +1 -0
- package/dist/schemas/types/Serializable.d.ts +46 -0
- package/dist/schemas/types/Serializable.d.ts.map +1 -0
- package/dist/utils/htmlTransform.d.ts.map +1 -1
- package/dist/utils/reactUtils.d.ts +12 -3
- package/dist/utils/reactUtils.d.ts.map +1 -1
- package/package.json +17 -3
- package/src/{components/__tests__ → __tests__/components}/AccessibilityProvider.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Article.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Breadcrumbs.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Button.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/CardListGrid.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/ChoiceInputField.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Code.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Content.integration.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Content.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/CoverImageHeader.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/ErrorBoundary.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/FeatureCard.integration.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/FeatureGrid.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/Footer.test.tsx +4 -4
- package/src/{components/__tests__ → __tests__/components}/FormBlock.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/HeroBlock.integration.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/HeroBlock.test.tsx +233 -7
- package/src/{components/__tests__ → __tests__/components}/Html.test.tsx +11 -2
- package/src/{components/__tests__ → __tests__/components}/HtmlInputField.test.tsx +3 -3
- package/src/__tests__/components/Logo.test.js +3 -3
- package/src/{components/__tests__ → __tests__/components}/Markdown.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/PageBannerHeader.test.tsx +3 -3
- package/src/{components/__tests__ → __tests__/components}/PaletteSwitcher.test.tsx +3 -3
- package/src/{components/__tests__ → __tests__/components}/ProductCard.test.tsx +4 -4
- package/src/{components/__tests__ → __tests__/components}/SafeSpan.integration.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/SafeSpan.simple.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/SafeSpan.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Section.integration.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Section.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/SelectInputField.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/TextInputField.test.tsx +3 -3
- package/src/{components/__tests__ → __tests__/components}/ThemeSwitcher.test.tsx +3 -3
- package/src/__tests__/components/base/ModelView.test.tsx +220 -0
- package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
- package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
- package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
- package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
- package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
- package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
- package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
- package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
- package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
- package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
- package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
- package/src/{components/layout/CollapsibleLayout/__tests__ → __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
- package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
- package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
- package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
- package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
- package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
- package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
- package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
- package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
- package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
- package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
- package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
- package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
- package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
- package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
- package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
- package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
- package/src/{utils/__tests__ → __tests__/utils}/nested-dom-fix.test.tsx +1 -1
- package/src/components/ErrorBoundary.tsx +8 -8
- package/src/components/Html.tsx +147 -44
- package/src/components/Logo.tsx +198 -100
- package/src/components/Markdown.tsx +125 -16
- package/src/components/QwickApp.tsx +64 -31
- package/src/components/QwickIcon.tsx +59 -0
- package/src/components/SafeSpan.tsx +65 -10
- package/src/components/Scaffold.tsx +2 -8
- package/src/components/base/ModelView.tsx +199 -0
- package/src/components/base/index.ts +11 -0
- package/src/components/blocks/Article.tsx +57 -18
- package/src/components/blocks/Code.md +529 -0
- package/src/components/blocks/Code.tsx +102 -15
- package/src/components/blocks/CoverImageHeader.tsx +9 -4
- package/src/components/blocks/FeatureCard.tsx +1 -2
- package/src/components/blocks/FeatureGrid.tsx +19 -1
- package/src/components/blocks/Footer.tsx +13 -1
- package/src/components/blocks/HeroBlock.tsx +87 -20
- package/src/components/blocks/Image.tsx +395 -0
- package/src/components/blocks/PageBannerHeader.tsx +14 -12
- package/src/components/blocks/ProductCard.tsx +1 -1
- package/src/components/blocks/Section.tsx +113 -8
- package/src/components/blocks/Text.tsx +285 -0
- package/src/components/blocks/index.ts +4 -0
- package/src/components/buttons/Button.tsx +184 -15
- package/src/components/forms/FormBlock.tsx +70 -17
- package/src/components/index.ts +5 -0
- package/src/components/input/ChoiceInputField.tsx +48 -18
- package/src/components/input/HtmlInputField.tsx +48 -18
- package/src/components/input/SelectInputField.tsx +48 -16
- package/src/components/input/SwitchInputField.tsx +48 -17
- package/src/components/input/TextField.tsx +41 -1
- package/src/components/input/TextInputField.tsx +52 -18
- package/src/components/layout/GridCell.tsx +118 -9
- package/src/components/layout/GridLayout.tsx +125 -24
- package/src/components/pages/FormPage.tsx +0 -1
- package/src/components/pages/Page.css +304 -332
- package/src/components/pages/Page.tsx +307 -255
- package/src/components/pages/index.ts +2 -2
- package/src/config/AppConfig.ts +133 -0
- package/src/config/AppConfigBuilder.ts +421 -0
- package/src/config/__tests__/AppConfig.test.ts +385 -0
- package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
- package/src/config/index.ts +24 -0
- package/src/config/types.ts +170 -0
- package/src/config.ts +25 -0
- package/src/contexts/PrintModeContext.tsx +332 -0
- package/src/contexts/QwickAppContext.tsx +2 -2
- package/src/contexts/index.ts +2 -0
- package/src/hooks/index.ts +5 -1
- package/src/hooks/usePrintMode.ts +73 -0
- package/src/index.ts +3 -0
- package/src/schemas/CodeSchema.ts +3 -3
- package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
- package/src/schemas/ContentSchema.ts +2 -1
- package/src/schemas/GridCellSchema.ts +164 -0
- package/src/schemas/GridLayoutSchema.ts +133 -0
- package/src/schemas/HtmlSchema.ts +47 -0
- package/src/schemas/ImageSchema.ts +235 -0
- package/src/schemas/LogoSchema.ts +241 -0
- package/src/schemas/MarkdownSchema.ts +47 -0
- package/src/schemas/PageTemplateSchema.ts +186 -0
- package/src/schemas/PrintConfigSchema.ts +207 -0
- package/src/schemas/README.md +661 -0
- package/src/schemas/SectionSchema.ts +2 -1
- package/src/schemas/TextSchema.ts +329 -0
- package/src/schemas/ViewModelSchema.ts +115 -0
- package/src/schemas/index.ts +21 -2
- package/src/schemas/transformers/ComponentTransformer.ts +403 -0
- package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
- package/src/schemas/transformers/registry.ts +72 -0
- package/src/schemas/types/Serializable.ts +51 -0
- package/src/stories/AccessibilityProvider.stories.tsx +253 -253
- package/src/stories/Article.stories.tsx +433 -433
- package/src/stories/Button.stories.tsx +1 -1
- package/src/stories/CardListGrid.stories.tsx +451 -451
- package/src/stories/ChoiceInputField.stories.tsx +503 -503
- package/src/stories/Code.stories.tsx +1 -1
- package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
- package/src/stories/Content.stories.tsx +393 -393
- package/src/stories/CoverImageHeader.stories.tsx +701 -701
- package/src/stories/DataBinding.advanced.stories.tsx +432 -432
- package/src/stories/DataProvider.stories.tsx +1192 -1192
- package/src/stories/FeatureCard.stories.tsx +557 -557
- package/src/stories/FeatureGrid.stories.tsx +594 -594
- package/src/stories/Footer.stories.tsx +640 -640
- package/src/stories/FormBlock.stories.tsx +760 -760
- package/src/stories/FormComponents.stories.tsx +349 -541
- package/src/stories/GridCell.stories.tsx +417 -0
- package/src/stories/GridLayout.stories.tsx +353 -0
- package/src/stories/HeroBlock.stories.tsx +862 -373
- package/src/stories/HtmlInputField.stories.tsx +474 -474
- package/src/stories/Image.stories.tsx +819 -0
- package/src/stories/Introduction.stories.tsx +667 -667
- package/src/stories/LayoutBlocks.stories.tsx +324 -324
- package/src/stories/Logo.stories.tsx +165 -6
- package/src/stories/Markdown.stories.tsx +137 -137
- package/src/stories/ModelView.stories.tsx +477 -0
- package/src/stories/Page.stories.tsx +688 -688
- package/src/stories/PageBannerHeader.stories.tsx +864 -864
- package/src/stories/PaletteSwitcher.stories.tsx +119 -119
- package/src/stories/ProductCard.stories.tsx +424 -424
- package/src/stories/QwickApp.stories.tsx +368 -368
- package/src/stories/ResponsiveMenu.stories.tsx +249 -249
- package/src/stories/SafeSpan.stories.tsx +531 -531
- package/src/stories/Section.stories.tsx +90 -2
- package/src/stories/SelectInputField.stories.tsx +524 -524
- package/src/stories/Text.stories.tsx +560 -0
- package/src/stories/TextInputField.stories.tsx +443 -443
- package/src/stories/ThemeSwitcher.stories.tsx +123 -123
- package/src/utils/htmlTransform.tsx +74 -53
- package/src/utils/reactUtils.tsx +57 -6
- package/dist/index.bundled.css +0 -12
- /package/src/{hooks/__tests__ → __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
- /package/src/{schemas/__tests__ → __tests__/schemas}/builders.test.ts +0 -0
- /package/src/{utils/__tests__ → __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
- /package/src/{utils/__tests__ → __tests__/utils}/htmlTransform.test.tsx +0 -0
- /package/src/{utils/__tests__ → __tests__/utils}/optional-logging.test.ts +0 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Button Serialization Tests
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive test suite for Button component serialization functionality.
|
|
5
|
+
* Tests the Serializable interface implementation and action handling.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
12
|
+
import '@testing-library/jest-dom';
|
|
13
|
+
import Button, { ButtonAction } from '../../../components/buttons/Button';
|
|
14
|
+
import { ComponentTransformer } from '../../../schemas/transformers/ComponentTransformer';
|
|
15
|
+
// Import registry to ensure component registration happens
|
|
16
|
+
import '../../../schemas/transformers/registry';
|
|
17
|
+
|
|
18
|
+
// Mock React Router for navigation tests
|
|
19
|
+
const mockNavigate = jest.fn();
|
|
20
|
+
jest.mock('react-router-dom', () => ({
|
|
21
|
+
useNavigate: () => mockNavigate,
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
describe('Button Serialization', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
jest.clearAllMocks();
|
|
27
|
+
// Clear any global handlers
|
|
28
|
+
(global as any).testHandler = undefined;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('Basic Serialization', () => {
|
|
32
|
+
test('should serialize basic button with label', () => {
|
|
33
|
+
const button = new Button({ label: 'Click me', variant: 'primary' });
|
|
34
|
+
const json = button.toJson();
|
|
35
|
+
|
|
36
|
+
expect(json).toEqual({
|
|
37
|
+
children: '', // extractTextFromReactNode returns empty string for undefined
|
|
38
|
+
label: 'Click me',
|
|
39
|
+
variant: 'primary',
|
|
40
|
+
buttonSize: undefined,
|
|
41
|
+
href: undefined,
|
|
42
|
+
target: undefined,
|
|
43
|
+
disabled: undefined,
|
|
44
|
+
loading: undefined,
|
|
45
|
+
fullWidth: undefined,
|
|
46
|
+
action: undefined,
|
|
47
|
+
dataSource: undefined,
|
|
48
|
+
bindingOptions: undefined
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('should serialize button with children text', () => {
|
|
53
|
+
const button = new Button({ children: 'Button text' });
|
|
54
|
+
const json = button.toJson();
|
|
55
|
+
|
|
56
|
+
expect(json.children).toBe('Button text');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('should serialize button with children ReactNode', () => {
|
|
60
|
+
const children = <span>Complex <strong>content</strong></span>;
|
|
61
|
+
const button = new Button({ children });
|
|
62
|
+
const json = button.toJson();
|
|
63
|
+
|
|
64
|
+
expect(json.children).toBe('Complex content');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('should serialize all button properties', () => {
|
|
68
|
+
const button = new Button({
|
|
69
|
+
label: 'Submit',
|
|
70
|
+
variant: 'contained',
|
|
71
|
+
buttonSize: 'large',
|
|
72
|
+
href: 'https://example.com',
|
|
73
|
+
target: '_blank',
|
|
74
|
+
disabled: true,
|
|
75
|
+
loading: false,
|
|
76
|
+
fullWidth: true
|
|
77
|
+
});
|
|
78
|
+
const json = button.toJson();
|
|
79
|
+
|
|
80
|
+
expect(json).toMatchObject({
|
|
81
|
+
label: 'Submit',
|
|
82
|
+
variant: 'contained',
|
|
83
|
+
buttonSize: 'large',
|
|
84
|
+
href: 'https://example.com',
|
|
85
|
+
target: '_blank',
|
|
86
|
+
disabled: true,
|
|
87
|
+
loading: false,
|
|
88
|
+
fullWidth: true
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('Action Serialization', () => {
|
|
94
|
+
test('should serialize navigation action', () => {
|
|
95
|
+
const action: ButtonAction = {
|
|
96
|
+
type: 'navigate',
|
|
97
|
+
url: '/dashboard',
|
|
98
|
+
target: '_self'
|
|
99
|
+
};
|
|
100
|
+
const button = new Button({ label: 'Go to Dashboard', action });
|
|
101
|
+
const json = button.toJson();
|
|
102
|
+
|
|
103
|
+
expect(json.action).toEqual(action);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('should serialize form submit action', () => {
|
|
107
|
+
const action: ButtonAction = {
|
|
108
|
+
type: 'submit',
|
|
109
|
+
form: 'contact-form'
|
|
110
|
+
};
|
|
111
|
+
const button = new Button({ label: 'Submit Form', action });
|
|
112
|
+
const json = button.toJson();
|
|
113
|
+
|
|
114
|
+
expect(json.action).toEqual(action);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('should serialize external link action', () => {
|
|
118
|
+
const action: ButtonAction = {
|
|
119
|
+
type: 'external',
|
|
120
|
+
url: 'https://external.com',
|
|
121
|
+
target: '_blank'
|
|
122
|
+
};
|
|
123
|
+
const button = new Button({ label: 'External Link', action });
|
|
124
|
+
const json = button.toJson();
|
|
125
|
+
|
|
126
|
+
expect(json.action).toEqual(action);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('should serialize custom action', () => {
|
|
130
|
+
const action: ButtonAction = {
|
|
131
|
+
type: 'custom',
|
|
132
|
+
customHandler: 'handleCustomAction'
|
|
133
|
+
};
|
|
134
|
+
const button = new Button({ label: 'Custom Action', action });
|
|
135
|
+
const json = button.toJson();
|
|
136
|
+
|
|
137
|
+
expect(json.action).toEqual(action);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('Data Binding Serialization', () => {
|
|
142
|
+
test('should serialize data binding properties', () => {
|
|
143
|
+
const dataSource = { endpoint: '/api/buttons/1' };
|
|
144
|
+
const bindingOptions = { cache: true, cacheTTL: 5000 };
|
|
145
|
+
|
|
146
|
+
const button = new Button({
|
|
147
|
+
label: 'Dynamic Button',
|
|
148
|
+
dataSource,
|
|
149
|
+
bindingOptions
|
|
150
|
+
});
|
|
151
|
+
const json = button.toJson();
|
|
152
|
+
|
|
153
|
+
expect(json.dataSource).toEqual(dataSource);
|
|
154
|
+
expect(json.bindingOptions).toEqual(bindingOptions);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('Deserialization', () => {
|
|
159
|
+
test('should deserialize basic button from JSON', () => {
|
|
160
|
+
const json = {
|
|
161
|
+
label: 'Click me',
|
|
162
|
+
variant: 'primary',
|
|
163
|
+
buttonSize: 'medium'
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const element = Button.fromJson(json);
|
|
167
|
+
expect(React.isValidElement(element)).toBe(true);
|
|
168
|
+
expect(element.type).toBe(Button);
|
|
169
|
+
expect(element.props).toMatchObject(json);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('should deserialize button with action', () => {
|
|
173
|
+
const json = {
|
|
174
|
+
label: 'Navigate',
|
|
175
|
+
action: {
|
|
176
|
+
type: 'navigate',
|
|
177
|
+
url: '/page'
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const element = Button.fromJson(json);
|
|
182
|
+
expect(element.props.action).toEqual(json.action);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('should deserialize button with children', () => {
|
|
186
|
+
const json = {
|
|
187
|
+
children: 'Button content',
|
|
188
|
+
variant: 'outlined'
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const element = Button.fromJson(json);
|
|
192
|
+
expect(element.props.children).toBe('Button content');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('Round-trip Serialization', () => {
|
|
197
|
+
test('should maintain data integrity through serialize-deserialize cycle', () => {
|
|
198
|
+
const originalProps = {
|
|
199
|
+
label: 'Test Button',
|
|
200
|
+
variant: 'contained' as const,
|
|
201
|
+
buttonSize: 'large' as const,
|
|
202
|
+
disabled: false,
|
|
203
|
+
fullWidth: true,
|
|
204
|
+
action: {
|
|
205
|
+
type: 'navigate' as const,
|
|
206
|
+
url: '/test-page'
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const button = new Button(originalProps);
|
|
211
|
+
const json = button.toJson();
|
|
212
|
+
const deserializedElement = Button.fromJson(json);
|
|
213
|
+
const newButton = new Button(deserializedElement.props);
|
|
214
|
+
const finalJson = newButton.toJson();
|
|
215
|
+
|
|
216
|
+
expect(finalJson.label).toBe(originalProps.label);
|
|
217
|
+
expect(finalJson.variant).toBe(originalProps.variant);
|
|
218
|
+
expect(finalJson.buttonSize).toBe(originalProps.buttonSize);
|
|
219
|
+
expect(finalJson.disabled).toBe(originalProps.disabled);
|
|
220
|
+
expect(finalJson.fullWidth).toBe(originalProps.fullWidth);
|
|
221
|
+
expect(finalJson.action).toEqual(originalProps.action);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('should handle complex children content in round-trip', () => {
|
|
225
|
+
const originalProps = {
|
|
226
|
+
children: 'Multi-word button text',
|
|
227
|
+
variant: 'text' as const
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const button = new Button(originalProps);
|
|
231
|
+
const json = button.toJson();
|
|
232
|
+
const deserializedElement = Button.fromJson(json);
|
|
233
|
+
|
|
234
|
+
expect(deserializedElement.props.children).toBe('Multi-word button text');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('Component Registration', () => {
|
|
239
|
+
test('should be registered with ComponentTransformer', () => {
|
|
240
|
+
const registeredComponents = ComponentTransformer.getRegisteredComponents();
|
|
241
|
+
expect(registeredComponents).toContain('Button');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('should have correct tagName and version', () => {
|
|
245
|
+
expect(Button.tagName).toBe('Button');
|
|
246
|
+
expect(Button.version).toBe('1.0.0');
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe('Action Handling', () => {
|
|
251
|
+
// Mock window.open for external action tests
|
|
252
|
+
const mockWindowOpen = jest.fn();
|
|
253
|
+
const originalOpen = window.open;
|
|
254
|
+
|
|
255
|
+
beforeAll(() => {
|
|
256
|
+
window.open = mockWindowOpen;
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
afterAll(() => {
|
|
260
|
+
window.open = originalOpen;
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
beforeEach(() => {
|
|
264
|
+
mockWindowOpen.mockClear();
|
|
265
|
+
mockNavigate.mockClear();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test('should handle navigation action click', () => {
|
|
269
|
+
const action: ButtonAction = {
|
|
270
|
+
type: 'navigate',
|
|
271
|
+
url: '/dashboard'
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
render(<Button label="Navigate" action={action} />);
|
|
275
|
+
const button = screen.getByRole('button');
|
|
276
|
+
|
|
277
|
+
fireEvent.click(button);
|
|
278
|
+
|
|
279
|
+
// Note: In test environment, the navigate hook might not work as expected
|
|
280
|
+
// This test verifies the click handler is called without error
|
|
281
|
+
expect(button).toBeInTheDocument();
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test('should handle external action click', () => {
|
|
285
|
+
const action: ButtonAction = {
|
|
286
|
+
type: 'external',
|
|
287
|
+
url: 'https://external.com',
|
|
288
|
+
target: '_blank'
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
render(<Button label="External" action={action} />);
|
|
292
|
+
const button = screen.getByRole('button');
|
|
293
|
+
|
|
294
|
+
fireEvent.click(button);
|
|
295
|
+
|
|
296
|
+
expect(mockWindowOpen).toHaveBeenCalledWith(
|
|
297
|
+
'https://external.com',
|
|
298
|
+
'_blank',
|
|
299
|
+
'noopener,noreferrer'
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test('should handle form submit action', () => {
|
|
304
|
+
// Create a mock form element
|
|
305
|
+
const form = document.createElement('form');
|
|
306
|
+
form.id = 'test-form';
|
|
307
|
+
form.requestSubmit = jest.fn();
|
|
308
|
+
document.body.appendChild(form);
|
|
309
|
+
|
|
310
|
+
const action: ButtonAction = {
|
|
311
|
+
type: 'submit',
|
|
312
|
+
form: 'test-form'
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
render(<Button label="Submit" action={action} />);
|
|
316
|
+
const button = screen.getByRole('button');
|
|
317
|
+
|
|
318
|
+
fireEvent.click(button);
|
|
319
|
+
|
|
320
|
+
expect(form.requestSubmit).toHaveBeenCalled();
|
|
321
|
+
|
|
322
|
+
// Cleanup
|
|
323
|
+
document.body.removeChild(form);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test('should handle custom action with global handler', () => {
|
|
327
|
+
const customHandler = jest.fn();
|
|
328
|
+
(global as any).testHandler = customHandler;
|
|
329
|
+
|
|
330
|
+
const action: ButtonAction = {
|
|
331
|
+
type: 'custom',
|
|
332
|
+
customHandler: 'testHandler'
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
render(<Button label="Custom" action={action} />);
|
|
336
|
+
const button = screen.getByRole('button');
|
|
337
|
+
|
|
338
|
+
fireEvent.click(button);
|
|
339
|
+
|
|
340
|
+
expect(customHandler).toHaveBeenCalled();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test('should not execute action when disabled', () => {
|
|
344
|
+
const action: ButtonAction = {
|
|
345
|
+
type: 'external',
|
|
346
|
+
url: 'https://external.com'
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
render(<Button label="Disabled" action={action} disabled />);
|
|
350
|
+
const button = screen.getByRole('button');
|
|
351
|
+
|
|
352
|
+
fireEvent.click(button);
|
|
353
|
+
|
|
354
|
+
expect(mockWindowOpen).not.toHaveBeenCalled();
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test('should not execute action when loading', () => {
|
|
358
|
+
const action: ButtonAction = {
|
|
359
|
+
type: 'external',
|
|
360
|
+
url: 'https://external.com'
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
render(<Button label="Loading" action={action} loading />);
|
|
364
|
+
const button = screen.getByRole('button');
|
|
365
|
+
|
|
366
|
+
fireEvent.click(button);
|
|
367
|
+
|
|
368
|
+
expect(mockWindowOpen).not.toHaveBeenCalled();
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe('Error Handling', () => {
|
|
373
|
+
test('should handle missing action properties gracefully', () => {
|
|
374
|
+
const action: ButtonAction = {
|
|
375
|
+
type: 'navigate'
|
|
376
|
+
// Missing url property
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
render(<Button label="Invalid" action={action} />);
|
|
380
|
+
const button = screen.getByRole('button');
|
|
381
|
+
|
|
382
|
+
// Should not throw error when clicking
|
|
383
|
+
expect(() => fireEvent.click(button)).not.toThrow();
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test('should handle non-existent form ID gracefully', () => {
|
|
387
|
+
const action: ButtonAction = {
|
|
388
|
+
type: 'submit',
|
|
389
|
+
form: 'non-existent-form'
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
render(<Button label="Submit" action={action} />);
|
|
393
|
+
const button = screen.getByRole('button');
|
|
394
|
+
|
|
395
|
+
// Should not throw error when clicking
|
|
396
|
+
expect(() => fireEvent.click(button)).not.toThrow();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test('should handle non-existent custom handler gracefully', () => {
|
|
400
|
+
const action: ButtonAction = {
|
|
401
|
+
type: 'custom',
|
|
402
|
+
customHandler: 'nonExistentHandler'
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
render(<Button label="Custom" action={action} />);
|
|
406
|
+
const button = screen.getByRole('button');
|
|
407
|
+
|
|
408
|
+
// Should not throw error when clicking
|
|
409
|
+
expect(() => fireEvent.click(button)).not.toThrow();
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
describe('Integration with Original onClick', () => {
|
|
414
|
+
test('should call both action and original onClick', () => {
|
|
415
|
+
const originalClick = jest.fn();
|
|
416
|
+
const action: ButtonAction = {
|
|
417
|
+
type: 'custom',
|
|
418
|
+
customHandler: 'testHandler'
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
(global as any).testHandler = jest.fn();
|
|
422
|
+
|
|
423
|
+
render(<Button label="Both" action={action} onClick={originalClick} />);
|
|
424
|
+
const button = screen.getByRole('button');
|
|
425
|
+
|
|
426
|
+
fireEvent.click(button);
|
|
427
|
+
|
|
428
|
+
expect((global as any).testHandler).toHaveBeenCalled();
|
|
429
|
+
expect(originalClick).toHaveBeenCalled();
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
test('should work with only onClick (no action)', () => {
|
|
433
|
+
const onClick = jest.fn();
|
|
434
|
+
|
|
435
|
+
render(<Button label="Click Only" onClick={onClick} />);
|
|
436
|
+
const button = screen.getByRole('button');
|
|
437
|
+
|
|
438
|
+
fireEvent.click(button);
|
|
439
|
+
|
|
440
|
+
expect(onClick).toHaveBeenCalled();
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
});
|