@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.esm.js +18 -1
- package/dist/index.js +18 -1
- package/dist/setupTests.d.ts +124 -0
- package/dist/setupTests.esm.js +122 -0
- package/dist/setupTests.js +122 -0
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/index.css +1046 -0
- package/src/index.ts +18 -0
- package/src/plugins/theme-css-generator.ts +354 -0
- package/src/setupTests.ts +124 -0
- package/src/stories/README.md +39 -0
- package/src/stories/components/ThemeDebugger.tsx +143 -0
- package/src/stories/index.ts +29 -0
- package/src/stories/storybook-theme-imports.css +51 -0
- package/src/styles/base/fonts.css +30 -0
- package/src/styles/base/generated-theme-variables.css +573 -0
- package/src/styles/base/index.css +7 -0
- package/src/styles/base/reset.css +48 -0
- package/src/styles/base/theme.css +1068 -0
- package/src/styles/base/typography.css +68 -0
- package/src/styles/base/variables.css +5 -0
- package/src/styles/components/CLAUDE.md +62 -0
- package/src/styles/components/base/badge.css +428 -0
- package/src/styles/components/base/button.css +774 -0
- package/src/styles/components/base/card.css +601 -0
- package/src/styles/components/base/checkbox.css +442 -0
- package/src/styles/components/base/index.css +9 -0
- package/src/styles/components/base/input.css +887 -0
- package/src/styles/components/base/label.css +296 -0
- package/src/styles/components/data-display/chart.css +353 -0
- package/src/styles/components/data-display/data-grid.css +619 -0
- package/src/styles/components/data-display/index.css +9 -0
- package/src/styles/components/data-display/list.css +560 -0
- package/src/styles/components/data-display/table.css +498 -0
- package/src/styles/components/data-display/timeline.css +764 -0
- package/src/styles/components/data-display/tree.css +881 -0
- package/src/styles/components/feedback/alert.css +358 -0
- package/src/styles/components/feedback/index.css +7 -0
- package/src/styles/components/feedback/progress.css +435 -0
- package/src/styles/components/feedback/skeleton.css +337 -0
- package/src/styles/components/feedback/toast.css +564 -0
- package/src/styles/components/index.css +17 -0
- package/src/styles/components/navigation/breadcrumb.css +465 -0
- package/src/styles/components/navigation/index.css +9 -0
- package/src/styles/components/navigation/menu.css +572 -0
- package/src/styles/components/navigation/pagination.css +635 -0
- package/src/styles/components/navigation/sidebar.css +807 -0
- package/src/styles/components/navigation/stepper.css +519 -0
- package/src/styles/components/navigation/tabs.css +404 -0
- package/src/styles/components/overlay/backdrop.css +243 -0
- package/src/styles/components/overlay/index.css +8 -0
- package/src/styles/components/overlay/modal.css +482 -0
- package/src/styles/components/overlay/popover.css +607 -0
- package/src/styles/components/overlay/portal.css +213 -0
- package/src/styles/components/overlay/tooltip.css +488 -0
- package/src/styles/generated-theme-variables.css +573 -0
- package/src/styles/index.css +5 -0
- package/src/styles/layers/index.css +54 -0
- package/src/styles/layers/overrides.css +108 -0
- package/src/styles/layers/validation.css +159 -0
- package/src/styles/layers/validation.js +310 -0
- package/src/styles/themes/default.css +450 -0
- package/src/styles/themes/enterprise.css +370 -0
- package/src/styles/themes/harvey.css +436 -0
- package/src/styles/themes/index.css +4 -0
- package/src/styles/themes/stan-design.css +572 -0
- package/src/styles/utilities/advanced-transition-system.css +467 -0
- package/src/styles/utilities/battery-conscious-animations.css +289 -0
- package/src/styles/utilities/enterprise-mobile-experience.css +817 -0
- package/src/styles/utilities/hardware-acceleration.css +121 -0
- package/src/styles/utilities/index.css +20 -0
- package/src/styles/utilities/mobile-skeleton-loading.css +596 -0
- package/src/styles/utilities/semantic-input-system.css +451 -0
- package/src/styles/utilities/touch-friendly-interface.css +247 -0
- package/src/styles/utilities/touch-optimization.css +165 -0
- package/src/test-utils/index.ts +7 -0
- package/src/test-utils/theme-testing.tsx +219 -0
- package/src/testing/test-automation.ts +627 -0
- package/src/testing/test-utils.tsx +367 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/* Touch Optimization & Gesture Support - Story 5 */
|
|
2
|
+
|
|
3
|
+
/* Touch target optimization */
|
|
4
|
+
.touch-target {
|
|
5
|
+
min-height: 44px;
|
|
6
|
+
min-width: 44px;
|
|
7
|
+
touch-action: manipulation;
|
|
8
|
+
-webkit-tap-highlight-color: transparent;
|
|
9
|
+
cursor: pointer;
|
|
10
|
+
user-select: none;
|
|
11
|
+
-webkit-user-select: none;
|
|
12
|
+
-moz-user-select: none;
|
|
13
|
+
-ms-user-select: none;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Touch feedback animations */
|
|
17
|
+
.touch-feedback {
|
|
18
|
+
transition: transform 0.1s ease-out, opacity 0.1s ease-out;
|
|
19
|
+
will-change: transform;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.touch-feedback:active {
|
|
23
|
+
transform: scale(0.95);
|
|
24
|
+
opacity: 0.8;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.touch-feedback.touch-pressed {
|
|
28
|
+
transform: scale(0.95);
|
|
29
|
+
opacity: 0.8;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Touch ripple effect */
|
|
33
|
+
.touch-ripple {
|
|
34
|
+
position: relative;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.touch-ripple::after {
|
|
39
|
+
content: '';
|
|
40
|
+
position: absolute;
|
|
41
|
+
top: 50%;
|
|
42
|
+
left: 50%;
|
|
43
|
+
width: 0;
|
|
44
|
+
height: 0;
|
|
45
|
+
border-radius: 50%;
|
|
46
|
+
background: rgba(255, 255, 255, 0.3);
|
|
47
|
+
transform: translate(-50%, -50%);
|
|
48
|
+
transition: width 0.3s ease-out, height 0.3s ease-out, opacity 0.3s ease-out;
|
|
49
|
+
opacity: 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.touch-ripple.touch-active::after {
|
|
53
|
+
width: 100px;
|
|
54
|
+
height: 100px;
|
|
55
|
+
opacity: 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Touch-friendly button variants */
|
|
59
|
+
.touch-button {
|
|
60
|
+
@apply touch-target touch-feedback touch-ripple;
|
|
61
|
+
padding: 12px 24px;
|
|
62
|
+
border-radius: 8px;
|
|
63
|
+
font-weight: 500;
|
|
64
|
+
text-align: center;
|
|
65
|
+
display: inline-flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
justify-content: center;
|
|
68
|
+
gap: 8px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.touch-button:disabled {
|
|
72
|
+
opacity: 0.6;
|
|
73
|
+
cursor: not-allowed;
|
|
74
|
+
transform: none !important;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Touch-friendly input fields */
|
|
78
|
+
.touch-input {
|
|
79
|
+
@apply touch-target;
|
|
80
|
+
padding: 12px 16px;
|
|
81
|
+
border-radius: 8px;
|
|
82
|
+
border: 2px solid transparent;
|
|
83
|
+
transition: border-color 0.2s ease-out, box-shadow 0.2s ease-out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.touch-input:focus {
|
|
87
|
+
outline: none;
|
|
88
|
+
border-color: var(--cs-primary);
|
|
89
|
+
box-shadow: 0 0 0 3px rgba(var(--cs-primary-rgb), 0.1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Touch-friendly card interactions */
|
|
93
|
+
.touch-card {
|
|
94
|
+
@apply touch-feedback;
|
|
95
|
+
transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.touch-card:hover {
|
|
99
|
+
transform: translateY(-2px);
|
|
100
|
+
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.touch-card:active {
|
|
104
|
+
transform: translateY(0) scale(0.98);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Touch gesture indicators */
|
|
108
|
+
.touch-gesture-hint {
|
|
109
|
+
position: relative;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.touch-gesture-hint::before {
|
|
113
|
+
content: '';
|
|
114
|
+
position: absolute;
|
|
115
|
+
top: -8px;
|
|
116
|
+
right: -8px;
|
|
117
|
+
width: 16px;
|
|
118
|
+
height: 16px;
|
|
119
|
+
background: var(--cs-accent);
|
|
120
|
+
border-radius: 50%;
|
|
121
|
+
opacity: 0.8;
|
|
122
|
+
animation: pulse 2s infinite;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@keyframes pulse {
|
|
126
|
+
0%, 100% {
|
|
127
|
+
transform: scale(1);
|
|
128
|
+
opacity: 0.8;
|
|
129
|
+
}
|
|
130
|
+
50% {
|
|
131
|
+
transform: scale(1.2);
|
|
132
|
+
opacity: 0.4;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* Touch-friendly spacing utilities */
|
|
137
|
+
.touch-spacing-xs { padding: 8px; }
|
|
138
|
+
.touch-spacing-sm { padding: 12px; }
|
|
139
|
+
.touch-spacing-md { padding: 16px; }
|
|
140
|
+
.touch-spacing-lg { padding: 20px; }
|
|
141
|
+
.touch-spacing-xl { padding: 24px; }
|
|
142
|
+
|
|
143
|
+
/* Touch-friendly margins */
|
|
144
|
+
.touch-margin-xs { margin: 8px; }
|
|
145
|
+
.touch-margin-sm { margin: 12px; }
|
|
146
|
+
.touch-margin-md { margin: 16px; }
|
|
147
|
+
.touch-margin-lg { margin: 20px; }
|
|
148
|
+
.touch-margin-xl { margin: 24px; }
|
|
149
|
+
|
|
150
|
+
/* Touch-friendly grid layouts */
|
|
151
|
+
.touch-grid {
|
|
152
|
+
display: grid;
|
|
153
|
+
gap: 16px;
|
|
154
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.touch-grid-compact {
|
|
158
|
+
gap: 12px;
|
|
159
|
+
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.touch-grid-spacious {
|
|
163
|
+
gap: 24px;
|
|
164
|
+
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
165
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Test utilities for theme-aware testing
|
|
2
|
+
export * from './theme-testing';
|
|
3
|
+
|
|
4
|
+
// Re-export common testing utilities
|
|
5
|
+
export { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
|
6
|
+
export { userEvent } from '@testing-library/user-event';
|
|
7
|
+
export type { RenderResult, RenderOptions } from '@testing-library/react';
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, RenderOptions, RenderResult } from '@testing-library/react';
|
|
3
|
+
import { ThemeProvider } from '../themes/ThemeProvider';
|
|
4
|
+
import { defaultThemes } from '../themes/base-themes';
|
|
5
|
+
import type { MultiThemeConfig } from '../themes/types';
|
|
6
|
+
|
|
7
|
+
// Mock window.matchMedia for Jest environment
|
|
8
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
9
|
+
writable: true,
|
|
10
|
+
value: jest.fn().mockImplementation(query => ({
|
|
11
|
+
matches: false,
|
|
12
|
+
media: query,
|
|
13
|
+
onchange: null,
|
|
14
|
+
addListener: jest.fn(), // deprecated
|
|
15
|
+
removeListener: jest.fn(), // deprecated
|
|
16
|
+
addEventListener: jest.fn(),
|
|
17
|
+
removeEventListener: jest.fn(),
|
|
18
|
+
dispatchEvent: jest.fn(),
|
|
19
|
+
})),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Test utilities for theme-aware component testing
|
|
23
|
+
|
|
24
|
+
export interface ThemeTestingOptions extends Omit<RenderOptions, 'wrapper'> {
|
|
25
|
+
theme?: string;
|
|
26
|
+
themes?: Record<string, MultiThemeConfig>;
|
|
27
|
+
persistTheme?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Renders a component wrapped with ThemeProvider for testing
|
|
32
|
+
*/
|
|
33
|
+
export const renderWithTheme = (
|
|
34
|
+
component: React.ReactElement,
|
|
35
|
+
options: ThemeTestingOptions = {}
|
|
36
|
+
): RenderResult => {
|
|
37
|
+
const {
|
|
38
|
+
theme = 'stan-design',
|
|
39
|
+
themes = defaultThemes,
|
|
40
|
+
persistTheme = false,
|
|
41
|
+
...renderOptions
|
|
42
|
+
} = options;
|
|
43
|
+
|
|
44
|
+
const ThemeWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
45
|
+
<ThemeProvider
|
|
46
|
+
defaultTheme={theme}
|
|
47
|
+
themes={themes}
|
|
48
|
+
persistTheme={persistTheme}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
</ThemeProvider>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return render(component, {
|
|
55
|
+
wrapper: ThemeWrapper,
|
|
56
|
+
...renderOptions,
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Renders a component in all available themes for comprehensive testing
|
|
62
|
+
*/
|
|
63
|
+
export const renderWithAllThemes = (
|
|
64
|
+
component: React.ReactElement,
|
|
65
|
+
options: Omit<ThemeTestingOptions, 'theme'> = {}
|
|
66
|
+
): Record<string, RenderResult> => {
|
|
67
|
+
const { themes = defaultThemes, ...renderOptions } = options;
|
|
68
|
+
const results: Record<string, RenderResult> = {};
|
|
69
|
+
|
|
70
|
+
Object.keys(themes).forEach(themeName => {
|
|
71
|
+
// Create a unique container for each theme to avoid conflicts
|
|
72
|
+
const container = document.createElement('div');
|
|
73
|
+
container.id = `theme-test-${themeName}`;
|
|
74
|
+
document.body.appendChild(container);
|
|
75
|
+
|
|
76
|
+
results[themeName] = render(component, {
|
|
77
|
+
container,
|
|
78
|
+
wrapper: ({ children }) => (
|
|
79
|
+
<ThemeProvider
|
|
80
|
+
defaultTheme={themeName}
|
|
81
|
+
themes={themes}
|
|
82
|
+
persistTheme={false}
|
|
83
|
+
>
|
|
84
|
+
{children}
|
|
85
|
+
</ThemeProvider>
|
|
86
|
+
),
|
|
87
|
+
...renderOptions,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return results;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Cleanup function to remove theme test containers
|
|
96
|
+
*/
|
|
97
|
+
export const cleanupThemeTests = (): void => {
|
|
98
|
+
const containers = document.querySelectorAll('[id^="theme-test-"]');
|
|
99
|
+
containers.forEach(container => {
|
|
100
|
+
container.remove();
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Test helper to switch themes during testing
|
|
106
|
+
*/
|
|
107
|
+
export const createThemeSwitcher = (initialTheme: string = 'stan-design') => {
|
|
108
|
+
let currentTheme = initialTheme;
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
getCurrentTheme: () => currentTheme,
|
|
112
|
+
switchTo: (newTheme: string) => {
|
|
113
|
+
currentTheme = newTheme;
|
|
114
|
+
return currentTheme;
|
|
115
|
+
},
|
|
116
|
+
renderWithCurrentTheme: (component: React.ReactElement, options: Omit<ThemeTestingOptions, 'theme'> = {}) => {
|
|
117
|
+
return renderWithTheme(component, { theme: currentTheme, ...options });
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Helper to test theme-specific styles and properties
|
|
124
|
+
*/
|
|
125
|
+
export const getThemeStyles = (themeName: string, themes = defaultThemes) => {
|
|
126
|
+
const theme = themes[themeName];
|
|
127
|
+
if (!theme) {
|
|
128
|
+
throw new Error(`Theme '${themeName}' not found in provided themes`);
|
|
129
|
+
}
|
|
130
|
+
return theme;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Helper to test CSS custom properties applied by themes
|
|
135
|
+
*/
|
|
136
|
+
export const getThemeCSSVariables = (element: Element): Record<string, string> => {
|
|
137
|
+
const computedStyle = window.getComputedStyle(element);
|
|
138
|
+
const cssVariables: Record<string, string> = {};
|
|
139
|
+
|
|
140
|
+
// Get all CSS custom properties (variables starting with --)
|
|
141
|
+
for (let i = 0; i < computedStyle.length; i++) {
|
|
142
|
+
const property = computedStyle[i];
|
|
143
|
+
if (property.startsWith('--')) {
|
|
144
|
+
cssVariables[property] = computedStyle.getPropertyValue(property).trim();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return cssVariables;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Helper to wait for theme transitions to complete
|
|
153
|
+
*/
|
|
154
|
+
export const waitForThemeTransition = async (duration: number = 300): Promise<void> => {
|
|
155
|
+
return new Promise(resolve => {
|
|
156
|
+
setTimeout(resolve, duration);
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Mock localStorage for theme persistence testing
|
|
162
|
+
*/
|
|
163
|
+
export const createThemeStorageMock = () => {
|
|
164
|
+
const storage: Record<string, string> = {};
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
getItem: jest.fn((key: string) => storage[key] || null),
|
|
168
|
+
setItem: jest.fn((key: string, value: string) => {
|
|
169
|
+
storage[key] = value;
|
|
170
|
+
}),
|
|
171
|
+
removeItem: jest.fn((key: string) => {
|
|
172
|
+
delete storage[key];
|
|
173
|
+
}),
|
|
174
|
+
clear: jest.fn(() => {
|
|
175
|
+
Object.keys(storage).forEach(key => delete storage[key]);
|
|
176
|
+
}),
|
|
177
|
+
get storage() {
|
|
178
|
+
return { ...storage };
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Mock matchMedia for responsive and system theme testing
|
|
185
|
+
*/
|
|
186
|
+
export const createMatchMediaMock = (matches: boolean = false) => {
|
|
187
|
+
return jest.fn().mockImplementation((query: string) => ({
|
|
188
|
+
matches,
|
|
189
|
+
media: query,
|
|
190
|
+
onchange: null,
|
|
191
|
+
addListener: jest.fn(),
|
|
192
|
+
removeListener: jest.fn(),
|
|
193
|
+
addEventListener: jest.fn(),
|
|
194
|
+
removeEventListener: jest.fn(),
|
|
195
|
+
dispatchEvent: jest.fn(),
|
|
196
|
+
}));
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Test themes for consistent testing across all theme tests
|
|
201
|
+
*/
|
|
202
|
+
export const getTestThemes = () => Object.keys(defaultThemes);
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Helper to test theme contrast and accessibility
|
|
206
|
+
*/
|
|
207
|
+
export const getThemeContrastInfo = (themeName: string, themes = defaultThemes) => {
|
|
208
|
+
const theme = themes[themeName];
|
|
209
|
+
if (!theme) {
|
|
210
|
+
throw new Error(`Theme '${themeName}' not found`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
primaryColor: theme.colors.primary[500],
|
|
215
|
+
backgroundColor: theme.colors.text.primary,
|
|
216
|
+
textColor: theme.colors.text.primary,
|
|
217
|
+
borderColor: theme.colors.surface.border,
|
|
218
|
+
};
|
|
219
|
+
};
|