@qwickapps/react-framework 1.3.1 → 1.3.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 +123 -1
- package/dist/components/AccessibilityProvider.d.ts +64 -0
- package/dist/components/AccessibilityProvider.d.ts.map +1 -0
- package/dist/components/Breadcrumbs.d.ts +39 -0
- package/dist/components/Breadcrumbs.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +39 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/QwickApp.d.ts.map +1 -1
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/index.bundled.css +12 -0
- package/dist/index.esm.js +910 -44
- package/dist/index.js +916 -47
- package/dist/templates/TemplateResolver.d.ts.map +1 -1
- package/dist/utils/htmlTransform.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +15 -3
- package/dist/utils/logger.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/components/AccessibilityProvider.tsx +466 -0
- package/src/components/Breadcrumbs.tsx +223 -0
- package/src/components/ErrorBoundary.tsx +216 -0
- package/src/components/QwickApp.tsx +17 -11
- package/src/components/__tests__/AccessibilityProvider.test.tsx +330 -0
- package/src/components/__tests__/Breadcrumbs.test.tsx +268 -0
- package/src/components/__tests__/ErrorBoundary.test.tsx +163 -0
- package/src/components/index.ts +3 -0
- package/src/stories/AccessibilityProvider.stories.tsx +284 -0
- package/src/stories/Breadcrumbs.stories.tsx +304 -0
- package/src/stories/ErrorBoundary.stories.tsx +159 -0
- package/src/stories/{form/FormComponents.stories.tsx → FormComponents.stories.tsx} +8 -8
- package/src/templates/TemplateResolver.ts +2 -6
- package/src/utils/__tests__/nested-dom-fix.test.tsx +53 -0
- package/src/utils/__tests__/optional-logging.test.ts +83 -0
- package/src/utils/htmlTransform.tsx +69 -3
- package/src/utils/logger.ts +60 -5
- package/dist/schemas/Builders.d.ts +0 -7
- package/dist/schemas/Builders.d.ts.map +0 -1
- package/dist/schemas/types.d.ts +0 -7
- package/dist/schemas/types.d.ts.map +0 -1
- package/dist/types/DataBinding.d.ts +0 -7
- package/dist/types/DataBinding.d.ts.map +0 -1
- package/dist/types/DataProvider.d.ts +0 -7
- package/dist/types/DataProvider.d.ts.map +0 -1
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface BreadcrumbItem {
|
|
4
|
+
label: string;
|
|
5
|
+
href?: string;
|
|
6
|
+
icon?: React.ReactNode;
|
|
7
|
+
current?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface BreadcrumbsProps {
|
|
11
|
+
items: BreadcrumbItem[];
|
|
12
|
+
separator?: React.ReactNode;
|
|
13
|
+
className?: string;
|
|
14
|
+
onNavigate?: (item: BreadcrumbItem, index: number) => void;
|
|
15
|
+
maxItems?: number;
|
|
16
|
+
showRoot?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generic Breadcrumbs component for navigation hierarchy
|
|
21
|
+
*
|
|
22
|
+
* Features:
|
|
23
|
+
* - Accessible navigation with proper ARIA labels
|
|
24
|
+
* - Customizable separators and icons
|
|
25
|
+
* - Responsive design with item truncation
|
|
26
|
+
* - Support for custom navigation handlers
|
|
27
|
+
* - Keyboard navigation support
|
|
28
|
+
* - Screen reader friendly
|
|
29
|
+
*/
|
|
30
|
+
export const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
|
|
31
|
+
items,
|
|
32
|
+
separator = '/',
|
|
33
|
+
className = '',
|
|
34
|
+
onNavigate,
|
|
35
|
+
maxItems,
|
|
36
|
+
showRoot = true
|
|
37
|
+
}) => {
|
|
38
|
+
// Filter and prepare items
|
|
39
|
+
let displayItems = showRoot ? items : items.slice(1);
|
|
40
|
+
|
|
41
|
+
// Handle max items with ellipsis
|
|
42
|
+
if (maxItems && displayItems.length > maxItems) {
|
|
43
|
+
const firstItems = displayItems.slice(0, 1);
|
|
44
|
+
const lastItems = displayItems.slice(-Math.max(1, maxItems - 2));
|
|
45
|
+
displayItems = [
|
|
46
|
+
...firstItems,
|
|
47
|
+
{ label: '...', href: undefined, current: false },
|
|
48
|
+
...lastItems
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const handleItemClick = (e: React.MouseEvent, item: BreadcrumbItem, index: number) => {
|
|
53
|
+
if (onNavigate) {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
onNavigate(item, index);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleKeyDown = (e: React.KeyboardEvent, item: BreadcrumbItem, index: number) => {
|
|
60
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
if (onNavigate) {
|
|
63
|
+
onNavigate(item, index);
|
|
64
|
+
} else if (item.href) {
|
|
65
|
+
window.location.href = item.href;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (displayItems.length <= 1) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<nav
|
|
76
|
+
className={`breadcrumbs ${className}`}
|
|
77
|
+
role="navigation"
|
|
78
|
+
aria-label="Breadcrumb navigation"
|
|
79
|
+
style={{
|
|
80
|
+
display: 'flex',
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
fontSize: '14px',
|
|
83
|
+
color: '#6b7280',
|
|
84
|
+
...defaultStyles.nav
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<ol
|
|
88
|
+
style={{
|
|
89
|
+
display: 'flex',
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
listStyle: 'none',
|
|
92
|
+
margin: 0,
|
|
93
|
+
padding: 0,
|
|
94
|
+
gap: '8px'
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
{displayItems.map((item, index) => {
|
|
98
|
+
const isLast = index === displayItems.length - 1;
|
|
99
|
+
const isClickable = (item.href || onNavigate) && !item.current && item.label !== '...';
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<li key={`${item.label}-${index}`} style={{ display: 'flex', alignItems: 'center' }}>
|
|
103
|
+
{isClickable ? (
|
|
104
|
+
<a
|
|
105
|
+
href={item.href}
|
|
106
|
+
onClick={(e) => handleItemClick(e, item, index)}
|
|
107
|
+
onKeyDown={(e) => handleKeyDown(e, item, index)}
|
|
108
|
+
style={{
|
|
109
|
+
...defaultStyles.link,
|
|
110
|
+
color: isLast ? '#374151' : '#6b7280',
|
|
111
|
+
textDecoration: 'none',
|
|
112
|
+
display: 'flex',
|
|
113
|
+
alignItems: 'center',
|
|
114
|
+
gap: '4px'
|
|
115
|
+
}}
|
|
116
|
+
tabIndex={0}
|
|
117
|
+
aria-current={item.current ? 'page' : undefined}
|
|
118
|
+
>
|
|
119
|
+
{item.icon && (
|
|
120
|
+
<span
|
|
121
|
+
style={{ display: 'flex', alignItems: 'center' }}
|
|
122
|
+
aria-hidden="true"
|
|
123
|
+
>
|
|
124
|
+
{item.icon}
|
|
125
|
+
</span>
|
|
126
|
+
)}
|
|
127
|
+
<span>{item.label}</span>
|
|
128
|
+
</a>
|
|
129
|
+
) : (
|
|
130
|
+
<span
|
|
131
|
+
style={{
|
|
132
|
+
...defaultStyles.current,
|
|
133
|
+
color: isLast ? '#111827' : '#6b7280',
|
|
134
|
+
fontWeight: isLast ? 600 : 400,
|
|
135
|
+
display: 'flex',
|
|
136
|
+
alignItems: 'center',
|
|
137
|
+
gap: '4px'
|
|
138
|
+
}}
|
|
139
|
+
aria-current={item.current ? 'page' : undefined}
|
|
140
|
+
>
|
|
141
|
+
{item.icon && (
|
|
142
|
+
<span
|
|
143
|
+
style={{ display: 'flex', alignItems: 'center' }}
|
|
144
|
+
aria-hidden="true"
|
|
145
|
+
>
|
|
146
|
+
{item.icon}
|
|
147
|
+
</span>
|
|
148
|
+
)}
|
|
149
|
+
<span>{item.label}</span>
|
|
150
|
+
</span>
|
|
151
|
+
)}
|
|
152
|
+
|
|
153
|
+
{!isLast && (
|
|
154
|
+
<span
|
|
155
|
+
style={{
|
|
156
|
+
display: 'flex',
|
|
157
|
+
alignItems: 'center',
|
|
158
|
+
marginLeft: '8px',
|
|
159
|
+
color: '#d1d5db',
|
|
160
|
+
fontSize: '12px'
|
|
161
|
+
}}
|
|
162
|
+
aria-hidden="true"
|
|
163
|
+
>
|
|
164
|
+
{separator}
|
|
165
|
+
</span>
|
|
166
|
+
)}
|
|
167
|
+
</li>
|
|
168
|
+
);
|
|
169
|
+
})}
|
|
170
|
+
</ol>
|
|
171
|
+
</nav>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Default styles
|
|
176
|
+
const defaultStyles = {
|
|
177
|
+
nav: {
|
|
178
|
+
padding: '8px 0'
|
|
179
|
+
},
|
|
180
|
+
link: {
|
|
181
|
+
transition: 'color 0.2s ease',
|
|
182
|
+
cursor: 'pointer',
|
|
183
|
+
borderRadius: '4px',
|
|
184
|
+
padding: '4px',
|
|
185
|
+
margin: '-4px'
|
|
186
|
+
},
|
|
187
|
+
current: {
|
|
188
|
+
padding: '4px'
|
|
189
|
+
}
|
|
190
|
+
} as const;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Hook for managing breadcrumb state
|
|
194
|
+
*/
|
|
195
|
+
export const useBreadcrumbs = () => {
|
|
196
|
+
const [breadcrumbs, setBreadcrumbs] = React.useState<BreadcrumbItem[]>([]);
|
|
197
|
+
|
|
198
|
+
const addBreadcrumb = React.useCallback((item: BreadcrumbItem) => {
|
|
199
|
+
setBreadcrumbs(prev => [...prev, item]);
|
|
200
|
+
}, []);
|
|
201
|
+
|
|
202
|
+
const removeBreadcrumb = React.useCallback((index: number) => {
|
|
203
|
+
setBreadcrumbs(prev => prev.filter((_, i) => i !== index));
|
|
204
|
+
}, []);
|
|
205
|
+
|
|
206
|
+
const setBreadcrumbsCurrent = React.useCallback((items: BreadcrumbItem[]) => {
|
|
207
|
+
setBreadcrumbs(items);
|
|
208
|
+
}, []);
|
|
209
|
+
|
|
210
|
+
const clearBreadcrumbs = React.useCallback(() => {
|
|
211
|
+
setBreadcrumbs([]);
|
|
212
|
+
}, []);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
breadcrumbs,
|
|
216
|
+
addBreadcrumb,
|
|
217
|
+
removeBreadcrumb,
|
|
218
|
+
setBreadcrumbs: setBreadcrumbsCurrent,
|
|
219
|
+
clearBreadcrumbs
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export default Breadcrumbs;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
2
|
+
import { Button } from './buttons/Button';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
fallback?: ReactNode;
|
|
7
|
+
onError?: (error: Error, errorInfo: ErrorInfo) => void;
|
|
8
|
+
showErrorDetails?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface State {
|
|
12
|
+
hasError: boolean;
|
|
13
|
+
error: Error | null;
|
|
14
|
+
errorInfo: ErrorInfo | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generic ErrorBoundary component for catching and handling React errors
|
|
19
|
+
*
|
|
20
|
+
* Features:
|
|
21
|
+
* - Catches JavaScript errors anywhere in child component tree
|
|
22
|
+
* - Displays fallback UI with retry functionality
|
|
23
|
+
* - Shows error details in development mode
|
|
24
|
+
* - Customizable error handling and fallback UI
|
|
25
|
+
* - Automatic error logging
|
|
26
|
+
*/
|
|
27
|
+
export class ErrorBoundary extends Component<Props, State> {
|
|
28
|
+
constructor(props: Props) {
|
|
29
|
+
super(props);
|
|
30
|
+
this.state = {
|
|
31
|
+
hasError: false,
|
|
32
|
+
error: null,
|
|
33
|
+
errorInfo: null
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static getDerivedStateFromError(error: Error): State {
|
|
38
|
+
// Update state so the next render will show the fallback UI
|
|
39
|
+
return {
|
|
40
|
+
hasError: true,
|
|
41
|
+
error,
|
|
42
|
+
errorInfo: null
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
47
|
+
// Log error details
|
|
48
|
+
this.setState({
|
|
49
|
+
error,
|
|
50
|
+
errorInfo
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Log to console for debugging
|
|
54
|
+
console.error('ErrorBoundary caught an error:', error, errorInfo);
|
|
55
|
+
|
|
56
|
+
// Custom error handler
|
|
57
|
+
if (this.props.onError) {
|
|
58
|
+
this.props.onError(error, errorInfo);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Send error to logging service if available
|
|
62
|
+
if (typeof window !== 'undefined') {
|
|
63
|
+
// @ts-ignore - Global error logging service
|
|
64
|
+
if (window.qwickapps?.logError) {
|
|
65
|
+
window.qwickapps.logError(error, errorInfo);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
handleRetry = () => {
|
|
71
|
+
this.setState({
|
|
72
|
+
hasError: false,
|
|
73
|
+
error: null,
|
|
74
|
+
errorInfo: null
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
handleRefresh = () => {
|
|
79
|
+
if (typeof window !== 'undefined') {
|
|
80
|
+
window.location.reload();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
render() {
|
|
85
|
+
if (this.state.hasError) {
|
|
86
|
+
// Custom fallback UI
|
|
87
|
+
if (this.props.fallback) {
|
|
88
|
+
return this.props.fallback;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Default error UI
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
className="error-boundary"
|
|
95
|
+
role="alert"
|
|
96
|
+
style={{
|
|
97
|
+
padding: '2rem',
|
|
98
|
+
textAlign: 'center',
|
|
99
|
+
backgroundColor: '#fef2f2',
|
|
100
|
+
border: '1px solid #fecaca',
|
|
101
|
+
borderRadius: '8px',
|
|
102
|
+
margin: '1rem',
|
|
103
|
+
color: '#991b1b'
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<div style={{ marginBottom: '1.5rem' }}>
|
|
107
|
+
<h2 style={{
|
|
108
|
+
fontSize: '1.5rem',
|
|
109
|
+
fontWeight: 'bold',
|
|
110
|
+
marginBottom: '0.5rem',
|
|
111
|
+
color: '#991b1b'
|
|
112
|
+
}}>
|
|
113
|
+
Something went wrong
|
|
114
|
+
</h2>
|
|
115
|
+
<p style={{
|
|
116
|
+
color: '#7f1d1d',
|
|
117
|
+
marginBottom: '1rem'
|
|
118
|
+
}}>
|
|
119
|
+
An unexpected error occurred in the application. Please try again or refresh the page.
|
|
120
|
+
</p>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div style={{
|
|
124
|
+
display: 'flex',
|
|
125
|
+
gap: '0.75rem',
|
|
126
|
+
justifyContent: 'center',
|
|
127
|
+
marginBottom: '1rem'
|
|
128
|
+
}}>
|
|
129
|
+
<Button
|
|
130
|
+
variant="contained"
|
|
131
|
+
onClick={this.handleRetry}
|
|
132
|
+
style={{
|
|
133
|
+
backgroundColor: '#dc2626',
|
|
134
|
+
color: 'white'
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
Try Again
|
|
138
|
+
</Button>
|
|
139
|
+
|
|
140
|
+
<Button
|
|
141
|
+
variant="outlined"
|
|
142
|
+
onClick={this.handleRefresh}
|
|
143
|
+
style={{
|
|
144
|
+
borderColor: '#dc2626',
|
|
145
|
+
color: '#dc2626'
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
Refresh Page
|
|
149
|
+
</Button>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
{/* Show error details in development or when explicitly enabled */}
|
|
153
|
+
{(process.env.NODE_ENV === 'development' || this.props.showErrorDetails) && this.state.error && (
|
|
154
|
+
<details
|
|
155
|
+
style={{
|
|
156
|
+
textAlign: 'left',
|
|
157
|
+
marginTop: '1rem',
|
|
158
|
+
padding: '1rem',
|
|
159
|
+
backgroundColor: '#f9fafb',
|
|
160
|
+
border: '1px solid #d1d5db',
|
|
161
|
+
borderRadius: '6px'
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
<summary style={{
|
|
165
|
+
cursor: 'pointer',
|
|
166
|
+
fontWeight: 'bold',
|
|
167
|
+
marginBottom: '0.5rem',
|
|
168
|
+
color: '#374151'
|
|
169
|
+
}}>
|
|
170
|
+
Error Details (Development Mode)
|
|
171
|
+
</summary>
|
|
172
|
+
<pre style={{
|
|
173
|
+
fontSize: '0.75rem',
|
|
174
|
+
color: '#374151',
|
|
175
|
+
whiteSpace: 'pre-wrap',
|
|
176
|
+
overflow: 'auto'
|
|
177
|
+
}}>
|
|
178
|
+
{this.state.error.toString()}
|
|
179
|
+
{this.state.errorInfo?.componentStack && (
|
|
180
|
+
<>
|
|
181
|
+
<br />
|
|
182
|
+
<br />
|
|
183
|
+
Component Stack:
|
|
184
|
+
{this.state.errorInfo.componentStack}
|
|
185
|
+
</>
|
|
186
|
+
)}
|
|
187
|
+
</pre>
|
|
188
|
+
</details>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return this.props.children;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Higher-order component that wraps a component with ErrorBoundary
|
|
200
|
+
*/
|
|
201
|
+
export function withErrorBoundary<P extends object>(
|
|
202
|
+
WrappedComponent: React.ComponentType<P>,
|
|
203
|
+
errorBoundaryProps?: Omit<Props, 'children'>
|
|
204
|
+
) {
|
|
205
|
+
const WithErrorBoundaryComponent = (props: P) => (
|
|
206
|
+
<ErrorBoundary {...errorBoundaryProps}>
|
|
207
|
+
<WrappedComponent {...props} />
|
|
208
|
+
</ErrorBoundary>
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
WithErrorBoundaryComponent.displayName = `withErrorBoundary(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
|
|
212
|
+
|
|
213
|
+
return WithErrorBoundaryComponent;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export default ErrorBoundary;
|
|
@@ -35,6 +35,8 @@ import { QwickAppContext, type QwickAppContextValue, type QwickAppProps } from '
|
|
|
35
35
|
import { type TemplateResolverConfig } from '../types';
|
|
36
36
|
import './QwickApp.css';
|
|
37
37
|
import Scaffold from './Scaffold';
|
|
38
|
+
import { ErrorBoundary } from './ErrorBoundary';
|
|
39
|
+
import { AccessibilityProvider } from './AccessibilityProvider';
|
|
38
40
|
// Auth logic moved to AuthProvider - QwickApp now focuses on app infrastructure
|
|
39
41
|
|
|
40
42
|
// RouteConfig moved to AuthProvider for auth-specific routing
|
|
@@ -125,17 +127,21 @@ export const QwickApp: React.FC<QwickAppComponentProps> = ({
|
|
|
125
127
|
) : content;
|
|
126
128
|
|
|
127
129
|
const appContent = (
|
|
128
|
-
<
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
130
|
+
<ErrorBoundary>
|
|
131
|
+
<AccessibilityProvider>
|
|
132
|
+
<div className={`qwick-app ${className || ''}`} style={style}>
|
|
133
|
+
<ThemeProvider
|
|
134
|
+
appId={appId}
|
|
135
|
+
defaultTheme={defaultTheme}
|
|
136
|
+
defaultPalette={defaultPalette}
|
|
137
|
+
>
|
|
138
|
+
<QwickAppContext.Provider value={contextValue}>
|
|
139
|
+
{wrappedContent}
|
|
140
|
+
</QwickAppContext.Provider>
|
|
141
|
+
</ThemeProvider>
|
|
142
|
+
</div>
|
|
143
|
+
</AccessibilityProvider>
|
|
144
|
+
</ErrorBoundary>
|
|
139
145
|
);
|
|
140
146
|
|
|
141
147
|
// If router is provided, wrap the entire app with it
|