@finsweet/webflow-apps-utils 1.0.2 → 1.0.3
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 +162 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/providers/GlobalProvider.mdx +322 -0
- package/dist/providers/GlobalProvider.svelte +58 -0
- package/dist/providers/GlobalProvider.svelte.d.ts +4 -0
- package/dist/providers/configuratorUtils.d.ts +37 -0
- package/dist/providers/configuratorUtils.js +219 -0
- package/dist/providers/globalContext.svelte.d.ts +18 -0
- package/dist/providers/globalContext.svelte.js +439 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +7 -0
- package/dist/providers/types.d.ts +103 -0
- package/dist/providers/types.js +6 -0
- package/dist/router/README.md +2 -2
- package/dist/stores/index.d.ts +0 -1
- package/dist/stores/index.js +0 -1
- package/dist/types/webflow.d.ts +31 -47
- package/dist/ui/components/LoadingScreen.svelte +2 -1
- package/dist/ui/components/button/Button.svelte +1 -1
- package/dist/ui/components/button-group/ButtonGroup.stories.js +112 -0
- package/dist/ui/components/{ButtonGroup.svelte → button-group/ButtonGroup.svelte} +20 -33
- package/dist/ui/components/button-group/ButtonGroup.svelte.d.ts +13 -0
- package/dist/ui/components/button-group/index.d.ts +2 -0
- package/dist/ui/components/button-group/index.js +1 -0
- package/dist/ui/components/button-group/types.d.ts +32 -0
- package/dist/ui/components/checkbox/Checkbox.stories.d.ts +55 -0
- package/dist/ui/components/checkbox/Checkbox.stories.js +162 -0
- package/dist/ui/components/checkbox/Checkbox.svelte +141 -0
- package/dist/ui/components/checkbox/Checkbox.svelte.d.ts +4 -0
- package/dist/ui/components/checkbox/index.d.ts +2 -0
- package/dist/ui/components/checkbox/index.js +1 -0
- package/dist/ui/components/checkbox/types.d.ts +32 -0
- package/dist/ui/components/controlled-buttons/ControlledButtons.stories.d.ts +32 -0
- package/dist/ui/components/controlled-buttons/ControlledButtons.stories.js +152 -0
- package/dist/ui/components/{buttons/FooterButton.svelte → controlled-buttons/ControlledButtons.svelte} +18 -67
- package/dist/ui/components/controlled-buttons/ControlledButtons.svelte.d.ts +4 -0
- package/dist/ui/components/controlled-buttons/index.d.ts +2 -0
- package/dist/ui/components/controlled-buttons/index.js +1 -0
- package/dist/ui/components/{buttons → controlled-buttons}/types.d.ts +11 -4
- package/dist/ui/components/divider/Divider.stories.svelte +134 -0
- package/dist/ui/components/{clickable/Clickable.stories.svelte.d.ts → divider/Divider.stories.svelte.d.ts} +4 -4
- package/dist/ui/components/divider/Divider.svelte +30 -0
- package/dist/ui/components/divider/Divider.svelte.d.ts +4 -0
- package/dist/ui/components/divider/index.d.ts +2 -0
- package/dist/ui/components/divider/index.js +1 -0
- package/dist/ui/components/divider/types.d.ts +23 -0
- package/dist/ui/components/divider/types.js +1 -0
- package/dist/ui/components/iframe/Iframe.stories.svelte +122 -0
- package/dist/ui/components/{ToggleItem.svelte.d.ts → iframe/Iframe.stories.svelte.d.ts} +7 -8
- package/dist/ui/components/iframe/Iframe.svelte +75 -0
- package/dist/ui/components/iframe/Iframe.svelte.d.ts +4 -0
- package/dist/ui/components/iframe/index.d.ts +2 -0
- package/dist/ui/components/iframe/index.js +1 -0
- package/dist/ui/components/iframe/types.d.ts +38 -0
- package/dist/ui/components/iframe/types.js +1 -0
- package/dist/ui/components/index.d.ts +12 -39
- package/dist/ui/components/index.js +12 -39
- package/dist/ui/components/input/Input.stories.d.ts +24 -0
- package/dist/ui/components/input/Input.stories.js +98 -0
- package/dist/ui/components/input/Input.svelte +321 -80
- package/dist/ui/components/input/types.d.ts +27 -1
- package/dist/ui/components/layout/Layout.stories.svelte +3 -3
- package/dist/ui/components/layout/Layout.svelte +3 -5
- package/dist/ui/components/layout/common/EditModeMessage.svelte +24 -12
- package/dist/ui/components/layout/{ExampleLayout.svelte → examples/ExampleLayout.svelte} +34 -22
- package/dist/ui/components/layout/examples/Wrapper.svelte +9 -0
- package/dist/ui/components/{NoSettingsNeeded.svelte.d.ts → layout/examples/Wrapper.svelte.d.ts} +3 -3
- package/dist/ui/components/layout/examples/index.d.ts +2 -0
- package/dist/ui/components/layout/examples/index.js +2 -0
- package/dist/ui/components/layout/index.d.ts +2 -1
- package/dist/ui/components/layout/index.js +2 -1
- package/dist/ui/components/modal/Example.svelte +320 -0
- package/dist/ui/components/modal/Example.svelte.d.ts +3 -0
- package/dist/ui/components/modal/Modal.stories.svelte +18 -0
- package/dist/ui/components/modal/Modal.stories.svelte.d.ts +26 -0
- package/dist/ui/components/modal/Modal.svelte +490 -0
- package/dist/ui/components/modal/Modal.svelte.d.ts +130 -0
- package/dist/ui/components/modal/index.d.ts +2 -0
- package/dist/ui/components/modal/index.js +1 -0
- package/dist/ui/components/modal/types.d.ts +75 -0
- package/dist/ui/components/modal/types.js +1 -0
- package/dist/ui/components/notification/Notification.stories.svelte +228 -0
- package/dist/ui/components/{ToggleList.svelte.d.ts → notification/Notification.stories.svelte.d.ts} +9 -21
- package/dist/ui/components/notification/Notification.svelte +289 -0
- package/dist/ui/components/notification/Notification.svelte.d.ts +67 -0
- package/dist/ui/components/notification/index.d.ts +2 -0
- package/dist/ui/components/notification/index.js +1 -0
- package/dist/ui/components/notification/types.d.ts +68 -0
- package/dist/ui/components/notification/types.js +1 -0
- package/dist/ui/components/section/Section.stories.svelte +263 -0
- package/dist/ui/components/section/Section.stories.svelte.d.ts +27 -0
- package/dist/ui/components/section/Section.svelte +324 -0
- package/dist/ui/components/section/Section.svelte.d.ts +5 -0
- package/dist/ui/components/section/index.d.ts +2 -0
- package/dist/ui/components/section/index.js +1 -0
- package/dist/ui/components/section/types.d.ts +106 -0
- package/dist/ui/components/section/types.js +1 -0
- package/dist/ui/components/{ImageUpload.svelte → shared/ImageUpload.svelte} +3 -3
- package/dist/ui/components/{SelectBodyOrDivBlock.svelte → shared/SelectBodyOrDivBlock.svelte} +1 -1
- package/dist/ui/components/shared/index.d.ts +2 -0
- package/dist/ui/components/shared/index.js +2 -0
- package/dist/ui/index.css +33 -5
- package/dist/utils/api/checkIfAppModeIsDesign.d.ts +1 -2
- package/dist/utils/api/checkIfAppModeIsDesign.js +1 -2
- package/dist/utils/api/clipboard/handlePaste.d.ts +6 -37
- package/dist/utils/api/clipboard/handlePaste.js +2 -6
- package/dist/utils/api/getAllAssets.d.ts +1 -2
- package/dist/utils/api/getAllAssets.js +1 -2
- package/dist/utils/api/getFinsweetComponentsEnvironment.d.ts +1 -2
- package/dist/utils/api/getFinsweetComponentsEnvironment.js +3 -6
- package/dist/utils/api/index.d.ts +0 -1
- package/dist/utils/api/index.js +0 -1
- package/dist/utils/api/insertWithXSCP.d.ts +1 -2
- package/dist/utils/api/insertWithXSCP.js +1 -2
- package/dist/utils/auth/crossWindowLogin.d.ts +3 -0
- package/dist/utils/auth/crossWindowLogin.js +3 -0
- package/dist/utils/auth/index.d.ts +9 -25
- package/dist/utils/auth/index.js +9 -25
- package/dist/utils/browser-storage/localStorage.d.ts +4 -12
- package/dist/utils/browser-storage/localStorage.js +4 -12
- package/dist/utils/browser-storage/sessionStorage.d.ts +4 -12
- package/dist/utils/browser-storage/sessionStorage.js +4 -12
- package/dist/utils/custom-code/api.d.ts +3 -7
- package/dist/utils/custom-code/api.js +3 -7
- package/dist/utils/helpers/cleanupTooltipMessage.d.ts +1 -2
- package/dist/utils/helpers/cleanupTooltipMessage.js +1 -2
- package/dist/utils/helpers/goto.d.ts +1 -4
- package/dist/utils/helpers/goto.js +2 -7
- package/dist/utils/helpers/index.d.ts +1 -0
- package/dist/utils/helpers/index.js +1 -0
- package/dist/utils/helpers/noop.d.ts +1 -1
- package/dist/utils/helpers/noop.js +1 -1
- package/dist/utils/helpers/numbers.d.ts +4 -14
- package/dist/utils/helpers/numbers.js +4 -14
- package/dist/utils/helpers/objectsToModuleExports.d.ts +1 -3
- package/dist/utils/helpers/objectsToModuleExports.js +1 -3
- package/dist/utils/helpers/trimText.d.ts +1 -8
- package/dist/utils/helpers/trimText.js +1 -8
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/logger/index.d.ts +0 -2
- package/dist/utils/logger/index.js +0 -2
- package/dist/utils/webflow-canvas/attributes/getAllWebflowElementAttributes.d.ts +1 -3
- package/dist/utils/webflow-canvas/attributes/getAllWebflowElementAttributes.js +1 -3
- package/dist/utils/webflow-canvas/attributes/getInstanceNamesFromObject.d.ts +1 -5
- package/dist/utils/webflow-canvas/attributes/getInstanceNamesFromObject.js +1 -5
- package/dist/utils/webflow-canvas/attributes/getWebflowElementAttribute.d.ts +1 -4
- package/dist/utils/webflow-canvas/attributes/getWebflowElementAttribute.js +1 -4
- package/dist/utils/webflow-canvas/attributes/getWebflowElementTextContent.d.ts +1 -3
- package/dist/utils/webflow-canvas/attributes/getWebflowElementTextContent.js +1 -3
- package/dist/utils/webflow-canvas/attributes/removeWebflowElementAttribute.d.ts +1 -4
- package/dist/utils/webflow-canvas/attributes/removeWebflowElementAttribute.js +1 -4
- package/dist/utils/webflow-canvas/attributes/setStyles.d.ts +1 -3
- package/dist/utils/webflow-canvas/attributes/setStyles.js +1 -3
- package/dist/utils/webflow-canvas/attributes/setWebflowElementAttribute.d.ts +1 -8
- package/dist/utils/webflow-canvas/attributes/setWebflowElementAttribute.js +1 -13
- package/dist/utils/webflow-canvas/findInstanceElement.d.ts +0 -6
- package/dist/utils/webflow-canvas/findInstanceElement.js +1 -7
- package/dist/utils/webflow-canvas/getAllPages.d.ts +3 -10
- package/dist/utils/webflow-canvas/getAllPages.js +3 -10
- package/dist/utils/webflow-canvas/getSiteStagingUrl.d.ts +1 -4
- package/dist/utils/webflow-canvas/getSiteStagingUrl.js +1 -4
- package/dist/utils/webflow-canvas/index.d.ts +1 -0
- package/dist/utils/webflow-canvas/index.js +1 -0
- package/package.json +9 -2
- package/dist/stores/globalStore.d.ts +0 -10
- package/dist/stores/globalStore.js +0 -10
- package/dist/ui/components/ButtonGroup.svelte.d.ts +0 -28
- package/dist/ui/components/Checkbox.svelte +0 -94
- package/dist/ui/components/Checkbox.svelte.d.ts +0 -36
- package/dist/ui/components/Copy.svelte +0 -329
- package/dist/ui/components/Copy.svelte.d.ts +0 -35
- package/dist/ui/components/CustomModal.svelte +0 -192
- package/dist/ui/components/CustomModal.svelte.d.ts +0 -45
- package/dist/ui/components/DisableInEditMode.svelte +0 -66
- package/dist/ui/components/DisableInEditMode.svelte.d.ts +0 -33
- package/dist/ui/components/Divider.svelte +0 -31
- package/dist/ui/components/Divider.svelte.d.ts +0 -31
- package/dist/ui/components/Header.svelte +0 -30
- package/dist/ui/components/Header.svelte.d.ts +0 -20
- package/dist/ui/components/Iframe.svelte +0 -89
- package/dist/ui/components/Iframe.svelte.d.ts +0 -40
- package/dist/ui/components/InjectComponent.svelte +0 -297
- package/dist/ui/components/InjectComponent.svelte.d.ts +0 -27
- package/dist/ui/components/Modal.svelte +0 -139
- package/dist/ui/components/Modal.svelte.d.ts +0 -42
- package/dist/ui/components/Navbar.svelte +0 -132
- package/dist/ui/components/Navbar.svelte.d.ts +0 -29
- package/dist/ui/components/NoSettingsNeeded.svelte +0 -31
- package/dist/ui/components/Notification.svelte +0 -193
- package/dist/ui/components/Notification.svelte.d.ts +0 -64
- package/dist/ui/components/PlusMinusButton.svelte +0 -91
- package/dist/ui/components/PlusMinusButton.svelte.d.ts +0 -22
- package/dist/ui/components/PreviewBar.svelte +0 -40
- package/dist/ui/components/PreviewBar.svelte.d.ts +0 -20
- package/dist/ui/components/ScrollableContent.svelte +0 -18
- package/dist/ui/components/ScrollableContent.svelte.d.ts +0 -31
- package/dist/ui/components/Section.svelte +0 -97
- package/dist/ui/components/Section.svelte.d.ts +0 -50
- package/dist/ui/components/Spacer.svelte +0 -9
- package/dist/ui/components/Spacer.svelte.d.ts +0 -22
- package/dist/ui/components/SpinnerPlusMinus.svelte +0 -75
- package/dist/ui/components/SpinnerPlusMinus.svelte.d.ts +0 -23
- package/dist/ui/components/SpinnerUpDown.svelte +0 -194
- package/dist/ui/components/SpinnerUpDown.svelte.d.ts +0 -31
- package/dist/ui/components/Tabs.svelte +0 -71
- package/dist/ui/components/Tabs.svelte.d.ts +0 -26
- package/dist/ui/components/ToggleItem.svelte +0 -29
- package/dist/ui/components/ToggleList.svelte +0 -57
- package/dist/ui/components/buttons/FooterButton.svelte.d.ts +0 -10
- package/dist/ui/components/buttons/index.d.ts +0 -5
- package/dist/ui/components/buttons/index.js +0 -5
- package/dist/ui/components/clickable/Clickable.stories.svelte +0 -213
- package/dist/ui/components/clickable/Clickable.svelte +0 -93
- package/dist/ui/components/clickable/Clickable.svelte.d.ts +0 -4
- package/dist/ui/components/clickable/index.d.ts +0 -2
- package/dist/ui/components/clickable/index.js +0 -1
- package/dist/ui/components/clickable/types.d.ts +0 -17
- package/dist/utils/api/copyPaste/index.d.ts +0 -18
- /package/dist/ui/components/{buttons → button-group}/types.js +0 -0
- /package/dist/ui/components/{clickable → checkbox}/types.js +0 -0
- /package/dist/{utils/api/copyPaste/index.js → ui/components/controlled-buttons/types.js} +0 -0
- /package/dist/ui/components/layout/{ExampleLayout.svelte.d.ts → examples/ExampleLayout.svelte.d.ts} +0 -0
- /package/dist/ui/components/{ImageUpload.svelte.d.ts → shared/ImageUpload.svelte.d.ts} +0 -0
- /package/dist/ui/components/{SelectBodyOrDivBlock.svelte.d.ts → shared/SelectBodyOrDivBlock.svelte.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Shared UI components and utilities for Finsweet's Webflow applications.
|
|
6
6
|
|
|
7
|
+
🔗 **[Live Storybook Documentation](https://webflow-apps-utils.pages.dev)**
|
|
8
|
+
|
|
7
9
|
## Installation
|
|
8
10
|
|
|
9
11
|
```bash
|
|
@@ -15,7 +17,165 @@ pnpm add @finsweet/webflow-apps-utils
|
|
|
15
17
|
```typescript
|
|
16
18
|
import '@finsweet/webflow-apps-utils/index.css';
|
|
17
19
|
|
|
18
|
-
import { Button, Modal, Input,
|
|
20
|
+
import { Button, Modal, Input, siteInfo } from '@finsweet/webflow-apps-utils';
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Project Structure
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
src/
|
|
27
|
+
├── lib/
|
|
28
|
+
│ ├── router/ # Client-side routing utilities
|
|
29
|
+
│ ├── stores/ # Stores for state management
|
|
30
|
+
│ ├── types/ # TypeScript type definitions
|
|
31
|
+
│ ├── ui/ # UI components and styling
|
|
32
|
+
│ │ ├── components/ # Reusable UI components
|
|
33
|
+
│ │ └── icons/ # SVG icon components
|
|
34
|
+
│ └── utils/ # Helper functions and utilities
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Coding Standards
|
|
38
|
+
|
|
39
|
+
### Naming Conventions
|
|
40
|
+
|
|
41
|
+
#### File and Folder Names
|
|
42
|
+
|
|
43
|
+
- **Folders**: Use `kebab-case` for all directory names
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
✅ custom-code/
|
|
47
|
+
✅ webflow-canvas/
|
|
48
|
+
✅ browser-storage/
|
|
49
|
+
❌ customCode/
|
|
50
|
+
❌ WebflowCanvas/
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- **Files**: Use `camelCase` and `PascalCase` for all file names
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
✅ localStorage.ts
|
|
57
|
+
✅ handlePaste.ts
|
|
58
|
+
✅ getAllAssets.ts
|
|
59
|
+
✅ Button.svelte
|
|
60
|
+
✅ Form.svelte
|
|
61
|
+
❌ local-storage.ts
|
|
62
|
+
❌ handle_paste.ts
|
|
63
|
+
❌ form.svelte
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Code Naming
|
|
68
|
+
|
|
69
|
+
- **Components**: Use `PascalCase` for component names
|
|
70
|
+
- **Functions**: Use `camelCase` for function names
|
|
71
|
+
- **Variables**: Use `camelCase` for variable names
|
|
72
|
+
- **Constants**: Use `UPPERCASE` for constants
|
|
73
|
+
- **Types/Interfaces**: Use `PascalCase` for type definitions
|
|
74
|
+
|
|
75
|
+
### Documentation Standards
|
|
76
|
+
|
|
77
|
+
- Keep function documentation concise
|
|
78
|
+
|
|
79
|
+
## Testing UI Components
|
|
80
|
+
|
|
81
|
+
### Testing Strategy
|
|
82
|
+
|
|
83
|
+
All UI components should include comprehensive tests covering:
|
|
84
|
+
|
|
85
|
+
- **Basic Rendering**: Default props, custom props, and various configurations
|
|
86
|
+
- **Component Variants**: All available variants (primary, secondary, danger, etc.)
|
|
87
|
+
- **Interactive States**: Disabled, loading, invalid, and other state combinations
|
|
88
|
+
- **User Interactions**: Click events, keyboard navigation, and form submissions
|
|
89
|
+
- **Accessibility**: ARIA attributes, focus management, and screen reader support
|
|
90
|
+
- **Visual Features**: Icons, tooltips, custom styling, and layout variations
|
|
91
|
+
|
|
92
|
+
### Test Structure
|
|
93
|
+
|
|
94
|
+
Use Vitest and Testing Library for component testing:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
|
|
98
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
99
|
+
import Component from './Component.svelte';
|
|
100
|
+
|
|
101
|
+
describe('Component Name', () => {
|
|
102
|
+
describe('Basic Rendering', () => {
|
|
103
|
+
it('renders with default props', () => {
|
|
104
|
+
render(Component);
|
|
105
|
+
const element = screen.getByRole('...');
|
|
106
|
+
expect(element).toBeInTheDocument();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('Variants', () => {
|
|
111
|
+
// Test all component variants
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('States', () => {
|
|
115
|
+
// Test disabled, loading, invalid states
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('Events', () => {
|
|
119
|
+
// Test user interactions
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('Accessibility', () => {
|
|
123
|
+
// Test ARIA attributes and keyboard navigation
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Testing Best Practices
|
|
129
|
+
|
|
130
|
+
- **Comprehensive Coverage**: Test all props, variants, and states
|
|
131
|
+
- **User-Centric Tests**: Focus on user interactions rather than implementation details
|
|
132
|
+
- **Accessibility Testing**: Verify ARIA attributes, keyboard navigation, and screen reader support
|
|
133
|
+
- **Async Behavior**: Use `waitFor` for testing tooltips, modals, and dynamic content
|
|
134
|
+
- **Event Mocking**: Mock external dependencies and event handlers properly
|
|
135
|
+
- **State Verification**: Check both visual state and underlying attributes
|
|
136
|
+
|
|
137
|
+
### Example: Button Component Testing
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Testing component variants
|
|
141
|
+
const variants: ButtonVariant[] = ['primary', 'secondary', 'danger', 'cms'];
|
|
142
|
+
variants.forEach((variant) => {
|
|
143
|
+
it(`renders ${variant} variant correctly`, () => {
|
|
144
|
+
render(Button, { variant });
|
|
145
|
+
const button = screen.getByRole('button');
|
|
146
|
+
expect(button).toHaveClass(`button--${variant}`);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Testing interactive states
|
|
151
|
+
it('renders loading state', () => {
|
|
152
|
+
render(Button, { loading: true });
|
|
153
|
+
const button = screen.getByRole('button');
|
|
154
|
+
expect(button).toBeDisabled();
|
|
155
|
+
expect(button).toHaveAttribute('aria-busy', 'true');
|
|
156
|
+
expect(button).toHaveClass('button--loading');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Testing accessibility
|
|
160
|
+
it('has proper ARIA attributes', () => {
|
|
161
|
+
render(Button, { ariaLabel: 'Custom label', loading: true });
|
|
162
|
+
const button = screen.getByRole('button');
|
|
163
|
+
expect(button).toHaveAttribute('aria-label', 'Custom label');
|
|
164
|
+
expect(button).toHaveAttribute('aria-busy', 'true');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Testing async interactions (tooltips)
|
|
168
|
+
it('shows tooltip on hover', async () => {
|
|
169
|
+
render(Button, {
|
|
170
|
+
tooltip: { message: 'Help text', listener: 'hover', listenerout: 'hover' }
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const button = screen.getByRole('button');
|
|
174
|
+
const tooltipTarget = button.closest('.target');
|
|
175
|
+
await fireEvent.mouseEnter(tooltipTarget || button);
|
|
176
|
+
|
|
177
|
+
expect(screen.getByText('Help text')).toBeInTheDocument();
|
|
178
|
+
});
|
|
19
179
|
```
|
|
20
180
|
|
|
21
181
|
## Development
|
|
@@ -32,6 +192,7 @@ pnpm storybook # Component development
|
|
|
32
192
|
pnpm build # Build library
|
|
33
193
|
pnpm test # Run tests
|
|
34
194
|
pnpm lint # Check code quality
|
|
195
|
+
pnpm check # Check code quality
|
|
35
196
|
```
|
|
36
197
|
|
|
37
198
|
## Publishing
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# GlobalProvider
|
|
2
|
+
|
|
3
|
+
The `GlobalProvider` is a context management system built on Svelte 5 that manages multiple application contexts in a type-safe and reactive way.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multiple Contexts**: Manage different types of state (form, app, data, etc.) in separate contexts
|
|
8
|
+
- **Type Safety**: Full TypeScript support with branded types and generics
|
|
9
|
+
- **Reactive**: Built on Svelte 5 runes for optimal reactivity
|
|
10
|
+
- **Event System**: Subscribe to context changes and global events (batched for performance)
|
|
11
|
+
|
|
12
|
+
## Basic Usage
|
|
13
|
+
|
|
14
|
+
### Wrap Your App
|
|
15
|
+
|
|
16
|
+
```svelte
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import { GlobalProvider } from '$lib/providers';
|
|
19
|
+
|
|
20
|
+
const initialContexts = {
|
|
21
|
+
app: {
|
|
22
|
+
editMode: false,
|
|
23
|
+
repairMode: false,
|
|
24
|
+
title: 'My App'
|
|
25
|
+
},
|
|
26
|
+
form: {
|
|
27
|
+
formKey: null,
|
|
28
|
+
formUpdateKey: null
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<GlobalProvider {initialContexts} debug={true}>
|
|
34
|
+
<App />
|
|
35
|
+
</GlobalProvider>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Default Contexts
|
|
39
|
+
|
|
40
|
+
The `GlobalProvider` automatically creates these default contexts:
|
|
41
|
+
|
|
42
|
+
- **`app`**: Application state (editMode, repairMode, title, configurator)
|
|
43
|
+
- **`form`**: Form state (formKey, formUpdateKey)
|
|
44
|
+
- **`data`**: General data state
|
|
45
|
+
|
|
46
|
+
### Use Contexts in Components
|
|
47
|
+
|
|
48
|
+
```svelte
|
|
49
|
+
<script lang="ts">
|
|
50
|
+
import { useAppContext, useFormContext, useDataContext } from '$lib/providers';
|
|
51
|
+
|
|
52
|
+
const appContext = useAppContext();
|
|
53
|
+
const formContext = useFormContext();
|
|
54
|
+
const dataContext = useDataContext();
|
|
55
|
+
|
|
56
|
+
let appData = $derived(appContext.get());
|
|
57
|
+
let formData = $derived(formContext.get());
|
|
58
|
+
|
|
59
|
+
function toggleEditMode() {
|
|
60
|
+
appContext.update((current) => ({
|
|
61
|
+
...current,
|
|
62
|
+
editMode: !current?.editMode
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<div>
|
|
68
|
+
<p>Edit Mode: {appData?.editMode}</p>
|
|
69
|
+
<p>Form Key: {formData?.formKey}</p>
|
|
70
|
+
<button onclick={toggleEditMode}>Toggle Edit Mode</button>
|
|
71
|
+
</div>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
### Context Operations
|
|
77
|
+
|
|
78
|
+
#### `get(): T | null`
|
|
79
|
+
|
|
80
|
+
Returns the current context data. Returns `undefined` if the context has been reset (completely removed).
|
|
81
|
+
|
|
82
|
+
#### `set(data: Partial<T>): void`
|
|
83
|
+
|
|
84
|
+
Sets context data (merges with existing data).
|
|
85
|
+
|
|
86
|
+
#### `update(updater: (current: T | null) => T): void`
|
|
87
|
+
|
|
88
|
+
Updates context data using an updater function.
|
|
89
|
+
|
|
90
|
+
#### `clear(): void`
|
|
91
|
+
|
|
92
|
+
Clears context data (sets to null). The context remains active but with null data.
|
|
93
|
+
|
|
94
|
+
#### `reset(): void`
|
|
95
|
+
|
|
96
|
+
Completely removes the context. After reset, `hasContext()` returns false and `get()` returns `undefined`.
|
|
97
|
+
|
|
98
|
+
#### `subscribe(callback: (data: T | null) => void): () => void`
|
|
99
|
+
|
|
100
|
+
Subscribes to context changes. Returns unsubscribe function. **Note**: Events are batched and emitted asynchronously for performance.
|
|
101
|
+
|
|
102
|
+
### Global Operations
|
|
103
|
+
|
|
104
|
+
- `getContext<T>(key: string): ContextOperations<T>` - Get context operations for a key
|
|
105
|
+
- `hasContext(key: string): boolean` - Check if context exists
|
|
106
|
+
- `removeContext(key: string): void` - Remove a specific context
|
|
107
|
+
- `clearAll(): void` - Clear all context data (set to null)
|
|
108
|
+
- `resetAll(): void` - Reset all contexts (completely remove them)
|
|
109
|
+
- `resetByKey(key: string): void` - Reset a specific context by key
|
|
110
|
+
- `getActiveContexts(): string[]` - Get list of active context keys
|
|
111
|
+
- `getAllContexts(): Record<string, unknown>` - Get all context data
|
|
112
|
+
- `getContextMetadata(key: string)` - Get metadata (version, updatedAt, isActive) for a context
|
|
113
|
+
- `subscribe(callback): () => void` - Subscribe to global context events
|
|
114
|
+
|
|
115
|
+
## Examples
|
|
116
|
+
|
|
117
|
+
### App State Management
|
|
118
|
+
|
|
119
|
+
```svelte
|
|
120
|
+
<script lang="ts">
|
|
121
|
+
import { useAppContext } from '$lib/providers';
|
|
122
|
+
|
|
123
|
+
const appContext = useAppContext();
|
|
124
|
+
let appData = $derived(appContext.get());
|
|
125
|
+
|
|
126
|
+
function handleSubmit() {
|
|
127
|
+
appContext.set({ editMode: true });
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<form onsubmit={handleSubmit}>
|
|
132
|
+
<p>Edit Mode: {appData?.editMode}</p>
|
|
133
|
+
<button type="submit">Submit</button>
|
|
134
|
+
</form>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Custom Context
|
|
138
|
+
|
|
139
|
+
```svelte
|
|
140
|
+
<script lang="ts">
|
|
141
|
+
import { useContext } from '$lib/providers';
|
|
142
|
+
|
|
143
|
+
type UserContext = {
|
|
144
|
+
id: string;
|
|
145
|
+
name: string;
|
|
146
|
+
preferences: { theme: 'light' | 'dark' };
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const userContext = useContext<UserContext>('user');
|
|
150
|
+
let userData = $derived(userContext.get());
|
|
151
|
+
|
|
152
|
+
function updateTheme(theme: 'light' | 'dark') {
|
|
153
|
+
userContext.update((current) => ({
|
|
154
|
+
...current!,
|
|
155
|
+
preferences: { ...current!.preferences, theme }
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
</script>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Typed Data Context
|
|
162
|
+
|
|
163
|
+
```svelte
|
|
164
|
+
<script lang="ts">
|
|
165
|
+
import { useDataContext } from '$lib/providers';
|
|
166
|
+
|
|
167
|
+
type AppDataType = {
|
|
168
|
+
users: Array<{ id: string; name: string; email: string }>;
|
|
169
|
+
products: Array<{ id: string; title: string; price: number }>;
|
|
170
|
+
currentPage: number;
|
|
171
|
+
totalPages: number;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const dataContext = useDataContext<AppDataType>();
|
|
175
|
+
let appData = $derived(dataContext.get());
|
|
176
|
+
|
|
177
|
+
function loadUsers(users: AppDataType['users']) {
|
|
178
|
+
dataContext.set({
|
|
179
|
+
state: {
|
|
180
|
+
...appData?.state,
|
|
181
|
+
users,
|
|
182
|
+
currentPage: 1
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function nextPage() {
|
|
188
|
+
if (appData?.state && appData.state.currentPage < appData.state.totalPages) {
|
|
189
|
+
dataContext.update((current) => ({
|
|
190
|
+
state: {
|
|
191
|
+
...current!.state!,
|
|
192
|
+
currentPage: current!.state!.currentPage + 1
|
|
193
|
+
}
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
</script>
|
|
198
|
+
|
|
199
|
+
<div>
|
|
200
|
+
<p>Users: {appData?.state?.users?.length || 0}</p>
|
|
201
|
+
<p>Page: {appData?.state?.currentPage || 1} of {appData?.state?.totalPages || 1}</p>
|
|
202
|
+
<button onclick={nextPage}>Next Page</button>
|
|
203
|
+
</div>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Configurator Support
|
|
207
|
+
|
|
208
|
+
The `GlobalProvider` includes built-in support for configurator state management with automatic change detection.
|
|
209
|
+
|
|
210
|
+
### Using the Configurator Context
|
|
211
|
+
|
|
212
|
+
```svelte
|
|
213
|
+
<script lang="ts">
|
|
214
|
+
import { useConfiguratorContext, useAppContext } from '$lib/providers';
|
|
215
|
+
|
|
216
|
+
// Define your configurator type
|
|
217
|
+
type MyConfiguratorType = {
|
|
218
|
+
theme: 'light' | 'dark';
|
|
219
|
+
layout: 'grid' | 'list';
|
|
220
|
+
itemsPerPage: number;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Use typed configurator context
|
|
224
|
+
const configurator = useConfiguratorContext<MyConfiguratorType>();
|
|
225
|
+
|
|
226
|
+
// Or use typed app context
|
|
227
|
+
const appContext = useAppContext<MyConfiguratorType>();
|
|
228
|
+
|
|
229
|
+
// Set configurator data with watch options (fully typed)
|
|
230
|
+
configurator.setConfigurator(
|
|
231
|
+
{ theme: 'dark', layout: 'grid', itemsPerPage: 10 },
|
|
232
|
+
{ watchKeys: ['theme'], debounceMs: 100 }
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// Check if configurator has changed
|
|
236
|
+
let hasChanged = $derived(configurator.hasChanged);
|
|
237
|
+
let currentConfig = $derived(configurator.configurator); // Type: MyConfiguratorType | null
|
|
238
|
+
let cachedConfig = $derived(configurator.configuratorCache); // Type: MyConfiguratorType | null
|
|
239
|
+
|
|
240
|
+
// Save current state to cache
|
|
241
|
+
function saveToCache() {
|
|
242
|
+
configurator.saveToCache();
|
|
243
|
+
}
|
|
244
|
+
</script>
|
|
245
|
+
|
|
246
|
+
<div>
|
|
247
|
+
<p>Has Changed: {hasChanged}</p>
|
|
248
|
+
<p>Current Theme: {currentConfig?.theme}</p>
|
|
249
|
+
<p>Cached Theme: {cachedConfig?.theme}</p>
|
|
250
|
+
<button onclick={saveToCache}>Save to Cache</button>
|
|
251
|
+
</div>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Configurator API
|
|
255
|
+
|
|
256
|
+
- `configurator` - Current configurator data
|
|
257
|
+
- `configuratorCache` - Cached configurator data
|
|
258
|
+
- `hasChanged` - Boolean indicating if configurator differs from cache
|
|
259
|
+
- `watchOptions` - Current watch configuration
|
|
260
|
+
- `setConfigurator(data, watchOptions?)` - Set configurator data
|
|
261
|
+
- `setConfiguratorCache(data)` - Set cache data
|
|
262
|
+
- `saveToCache()` - Save current configurator to cache
|
|
263
|
+
- `updateWatchOptions(options)` - Update watch configuration
|
|
264
|
+
|
|
265
|
+
### Watch Options
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
interface ConfiguratorWatchOptions {
|
|
269
|
+
watchAll?: boolean; // Watch all keys (default: true)
|
|
270
|
+
watchKeys?: string[]; // Specific keys to watch
|
|
271
|
+
debounceMs?: number; // Debounce delay (default: 50ms)
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## TypeScript Support
|
|
276
|
+
|
|
277
|
+
### Generic Context Usage
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import type { ContextOperations, AppContextData, DataContextData } from '$lib/providers';
|
|
281
|
+
|
|
282
|
+
// For custom contexts
|
|
283
|
+
const userContext: ContextOperations<UserType> = useContext<UserType>('user');
|
|
284
|
+
|
|
285
|
+
// For typed app context with configurator
|
|
286
|
+
type MyConfiguratorType = {
|
|
287
|
+
theme: 'light' | 'dark';
|
|
288
|
+
layout: 'grid' | 'list';
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const appContext = useAppContext<MyConfiguratorType>();
|
|
292
|
+
const configurator = useConfiguratorContext<MyConfiguratorType>();
|
|
293
|
+
|
|
294
|
+
// For typed data context
|
|
295
|
+
type MyDataType = {
|
|
296
|
+
users: User[];
|
|
297
|
+
products: Product[];
|
|
298
|
+
currentPage: number;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const dataContext = useDataContext<MyDataType>();
|
|
302
|
+
|
|
303
|
+
// The configurator and configuratorCache will both be typed as MyConfiguratorType | null
|
|
304
|
+
// The data context state will be typed as MyDataType | null
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Type Definitions
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// Your configurator type
|
|
311
|
+
type MyConfiguratorType = {
|
|
312
|
+
theme: 'light' | 'dark';
|
|
313
|
+
layout: 'grid' | 'list';
|
|
314
|
+
itemsPerPage: number;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// App context will be typed as AppContextData<MyConfiguratorType>
|
|
318
|
+
type MyAppContextType = AppContextData<MyConfiguratorType>;
|
|
319
|
+
|
|
320
|
+
// Data context will be typed as DataContextData<MyDataType>
|
|
321
|
+
type MyDataContextType = DataContextData<MyDataType>;
|
|
322
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getFinsweetComponentsEnvironment } from '../utils/index.js';
|
|
3
|
+
|
|
4
|
+
import { createGlobalContext, setGlobalContext } from './globalContext.svelte';
|
|
5
|
+
import type { GlobalProviderProps } from './types';
|
|
6
|
+
|
|
7
|
+
let { initialContexts = {}, debug = false, children }: GlobalProviderProps = $props();
|
|
8
|
+
|
|
9
|
+
// Default contexts with configurator support
|
|
10
|
+
const defaultContexts = {
|
|
11
|
+
app: {
|
|
12
|
+
editMode: false,
|
|
13
|
+
repairMode: false,
|
|
14
|
+
title: null,
|
|
15
|
+
configurator: {
|
|
16
|
+
configurator: null,
|
|
17
|
+
configuratorCache: null,
|
|
18
|
+
hasChanged: false,
|
|
19
|
+
watchOptions: {
|
|
20
|
+
watchAll: true,
|
|
21
|
+
watchKeys: [],
|
|
22
|
+
debounceMs: 100
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
form: {
|
|
27
|
+
formKey: null,
|
|
28
|
+
formUpdateKey: null
|
|
29
|
+
},
|
|
30
|
+
data: {
|
|
31
|
+
state: null
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Merge provided contexts with defaults
|
|
36
|
+
const mergedContexts = {
|
|
37
|
+
...defaultContexts,
|
|
38
|
+
...initialContexts
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Create and set the global context
|
|
42
|
+
const globalContext = createGlobalContext(mergedContexts, debug);
|
|
43
|
+
setGlobalContext(globalContext);
|
|
44
|
+
const { development } = getFinsweetComponentsEnvironment();
|
|
45
|
+
|
|
46
|
+
// Development mode debugging
|
|
47
|
+
if (development && typeof window !== 'undefined') {
|
|
48
|
+
globalContext.subscribe((event) => {
|
|
49
|
+
console.log('🌍 Global Context Event:', event);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
{#if children}{@render children()}{/if}
|
|
55
|
+
|
|
56
|
+
<style>
|
|
57
|
+
/* Global provider styles - currently using display: contents */
|
|
58
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ConfiguratorState, ConfiguratorWatchOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create default configurator state
|
|
4
|
+
*/
|
|
5
|
+
export declare function createDefaultConfiguratorState<T = Record<string, any>>(): ConfiguratorState<T>;
|
|
6
|
+
/**
|
|
7
|
+
* Deep compare two objects with performance optimizations
|
|
8
|
+
*/
|
|
9
|
+
export declare function deepCompare<T>(obj1: T, obj2: T): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Compare specific keys of two objects with optimization
|
|
12
|
+
*/
|
|
13
|
+
export declare function compareKeys<T extends Record<string, any>>(obj1: T | null, obj2: T | null, keys: string[]): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Determine if configurator has changed with performance optimization
|
|
16
|
+
*/
|
|
17
|
+
export declare function hasConfiguratorChanged<T extends Record<string, any>>(configurator: T | null, configuratorCache: T | null, watchOptions: ConfiguratorWatchOptions): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Create a debounced function for configurator updates
|
|
20
|
+
*/
|
|
21
|
+
export declare function createDebouncedUpdate<T extends (...args: any[]) => void>(fn: T, delay: number): T;
|
|
22
|
+
/**
|
|
23
|
+
* Extract specific keys from an object
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractKeys<T extends Record<string, any>>(obj: T | null, keys: string[]): Partial<T> | null;
|
|
26
|
+
/**
|
|
27
|
+
* Get all keys from an object (for watchAll mode)
|
|
28
|
+
*/
|
|
29
|
+
export declare function getAllKeys<T extends Record<string, any>>(obj: T | null): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Validate watch options
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateWatchOptions(options: ConfiguratorWatchOptions): ConfiguratorWatchOptions;
|
|
34
|
+
/**
|
|
35
|
+
* Create a snapshot of configurator state for comparison
|
|
36
|
+
*/
|
|
37
|
+
export declare function createConfiguratorSnapshot<T extends Record<string, any>>(configurator: T | null, watchOptions: ConfiguratorWatchOptions): T | Partial<T> | null;
|