@umituz/react-native-design-system 1.14.0 → 2.0.0
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/package.json +26 -19
- package/src/atoms/AtomicAvatar.tsx +161 -0
- package/src/atoms/AtomicButton.tsx +241 -0
- package/src/atoms/AtomicChip.tsx +226 -0
- package/src/atoms/AtomicDatePicker.tsx +255 -0
- package/src/atoms/AtomicFab.tsx +99 -0
- package/src/atoms/AtomicIcon.tsx +149 -0
- package/src/atoms/AtomicInput.tsx +308 -0
- package/src/atoms/AtomicPicker.tsx +310 -0
- package/src/atoms/AtomicProgress.tsx +149 -0
- package/src/atoms/AtomicText.tsx +55 -0
- package/src/atoms/__tests__/AtomicButton.test.tsx +107 -0
- package/src/atoms/__tests__/AtomicIcon.test.tsx +110 -0
- package/src/atoms/__tests__/AtomicInput.test.tsx +195 -0
- package/src/atoms/datepicker/components/DatePickerButton.tsx +112 -0
- package/src/atoms/datepicker/components/DatePickerModal.tsx +143 -0
- package/src/atoms/fab/styles/fabStyles.ts +98 -0
- package/src/atoms/fab/types/index.ts +88 -0
- package/src/atoms/index.ts +70 -0
- package/src/atoms/input/hooks/useInputState.ts +63 -0
- package/src/atoms/input/styles/inputStylesHelper.ts +120 -0
- package/src/atoms/picker/components/PickerChips.tsx +57 -0
- package/src/atoms/picker/components/PickerModal.tsx +214 -0
- package/src/atoms/picker/styles/pickerStyles.ts +223 -0
- package/src/atoms/picker/types/index.ts +42 -0
- package/src/index.ts +148 -79
- package/src/molecules/ConfirmationModal.tsx +42 -0
- package/src/molecules/ConfirmationModalContent.tsx +87 -0
- package/src/molecules/ConfirmationModalMain.tsx +91 -0
- package/src/molecules/FormField.tsx +155 -0
- package/src/molecules/IconContainer.tsx +79 -0
- package/src/molecules/ListItem.tsx +35 -0
- package/src/molecules/ScreenHeader.tsx +171 -0
- package/src/molecules/SearchBar.tsx +198 -0
- package/src/molecules/confirmation-modal/components.tsx +94 -0
- package/src/molecules/confirmation-modal/index.ts +7 -0
- package/src/molecules/confirmation-modal/styles/confirmationModalStyles.ts +133 -0
- package/src/molecules/confirmation-modal/types/index.ts +41 -0
- package/src/molecules/confirmation-modal/useConfirmationModal.ts +50 -0
- package/src/molecules/index.ts +19 -0
- package/src/molecules/listitem/index.ts +6 -0
- package/src/molecules/listitem/styles/listItemStyles.ts +37 -0
- package/src/molecules/listitem/types/index.ts +21 -0
- package/src/organisms/AppHeader.tsx +136 -0
- package/src/organisms/FormContainer.tsx +169 -0
- package/src/organisms/ScreenLayout.tsx +183 -0
- package/src/organisms/index.ts +31 -0
- package/src/responsive/config.ts +139 -0
- package/src/responsive/deviceDetection.ts +155 -0
- package/src/responsive/gridUtils.ts +79 -0
- package/src/responsive/index.ts +52 -0
- package/src/responsive/platformConstants.ts +98 -0
- package/src/responsive/responsive.ts +61 -0
- package/src/responsive/responsiveLayout.ts +137 -0
- package/src/responsive/responsiveSizing.ts +134 -0
- package/src/responsive/useResponsive.ts +140 -0
- package/src/responsive/validation.ts +158 -0
- package/src/theme/core/BaseTokens.ts +42 -0
- package/src/theme/core/ColorPalette.ts +29 -0
- package/src/theme/core/CustomColors.ts +122 -0
- package/src/theme/core/NavigationTheme.ts +72 -0
- package/src/theme/core/TokenFactory.ts +103 -0
- package/src/theme/core/colors/ColorUtils.ts +53 -0
- package/src/theme/core/colors/DarkColors.ts +146 -0
- package/src/theme/core/colors/LightColors.ts +146 -0
- package/src/theme/core/constants/DesignConstants.ts +31 -0
- package/src/theme/core/themes.ts +118 -0
- package/src/theme/core/tokens/BaseTokens.ts +144 -0
- package/src/theme/core/tokens/Borders.ts +43 -0
- package/src/theme/core/tokens/Sizes.ts +51 -0
- package/src/theme/core/tokens/Spacing.ts +38 -0
- package/src/theme/core/tokens/Typography.ts +143 -0
- package/src/theme/hooks/useAppDesignTokens.ts +45 -0
- package/src/theme/hooks/useCommonStyles.ts +248 -0
- package/src/theme/hooks/useThemedStyles.ts +68 -0
- package/src/theme/index.ts +94 -0
- package/src/theme/infrastructure/globalThemeStore.ts +69 -0
- package/src/theme/infrastructure/storage/ThemeStorage.ts +93 -0
- package/src/theme/infrastructure/stores/themeStore.ts +109 -0
- package/src/typography/__tests__/colorValidationUtils.test.ts +180 -0
- package/src/typography/__tests__/textColorUtils.test.ts +185 -0
- package/src/typography/__tests__/textStyleUtils.test.ts +168 -0
- package/src/typography/domain/entities/TypographyTypes.ts +88 -0
- package/src/typography/index.ts +53 -0
- package/src/typography/presentation/utils/colorValidationUtils.ts +133 -0
- package/src/typography/presentation/utils/textColorUtils.ts +205 -0
- package/src/typography/presentation/utils/textStyleUtils.ts +159 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for text color utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { getTextColor } from '../presentation/utils/textColorUtils';
|
|
6
|
+
import type { ColorVariant } from '../domain/entities/TypographyTypes';
|
|
7
|
+
|
|
8
|
+
import type { DesignTokens } from '@umituz/react-native-design-system-theme';
|
|
9
|
+
|
|
10
|
+
// Mock design tokens for testing
|
|
11
|
+
const mockTokens: DesignTokens = {
|
|
12
|
+
colors: {
|
|
13
|
+
textPrimary: '#000000',
|
|
14
|
+
textSecondary: '#666666',
|
|
15
|
+
textTertiary: '#999999',
|
|
16
|
+
textDisabled: '#CCCCCC',
|
|
17
|
+
textInverse: '#FFFFFF',
|
|
18
|
+
onSurface: '#000000',
|
|
19
|
+
onBackground: '#000000',
|
|
20
|
+
onPrimary: '#FFFFFF',
|
|
21
|
+
onSecondary: '#FFFFFF',
|
|
22
|
+
onSuccess: '#FFFFFF',
|
|
23
|
+
onError: '#FFFFFF',
|
|
24
|
+
onWarning: '#000000',
|
|
25
|
+
onInfo: '#FFFFFF',
|
|
26
|
+
success: '#4CAF50',
|
|
27
|
+
error: '#F44336',
|
|
28
|
+
warning: '#FF9800',
|
|
29
|
+
info: '#2196F3',
|
|
30
|
+
primary: '#2196F3',
|
|
31
|
+
secondary: '#FF9800',
|
|
32
|
+
tertiary: '#9C27B0',
|
|
33
|
+
surface: '#FFFFFF',
|
|
34
|
+
surfaceVariant: '#F5F5F5',
|
|
35
|
+
background: '#FFFFFF',
|
|
36
|
+
},
|
|
37
|
+
typography: {
|
|
38
|
+
displayLarge: { fontSize: 57, fontWeight: '400' },
|
|
39
|
+
displayMedium: { fontSize: 45, fontWeight: '400' },
|
|
40
|
+
displaySmall: { fontSize: 36, fontWeight: '400' },
|
|
41
|
+
headlineLarge: { fontSize: 32, fontWeight: '400' },
|
|
42
|
+
headlineMedium: { fontSize: 28, fontWeight: '400' },
|
|
43
|
+
headlineSmall: { fontSize: 24, fontWeight: '400' },
|
|
44
|
+
titleLarge: { fontSize: 22, fontWeight: '500' },
|
|
45
|
+
titleMedium: { fontSize: 16, fontWeight: '500' },
|
|
46
|
+
titleSmall: { fontSize: 14, fontWeight: '500' },
|
|
47
|
+
bodyLarge: { fontSize: 16, fontWeight: '400' },
|
|
48
|
+
bodyMedium: { fontSize: 14, fontWeight: '400' },
|
|
49
|
+
bodySmall: { fontSize: 12, fontWeight: '400' },
|
|
50
|
+
labelLarge: { fontSize: 14, fontWeight: '500' },
|
|
51
|
+
labelMedium: { fontSize: 12, fontWeight: '500' },
|
|
52
|
+
labelSmall: { fontSize: 11, fontWeight: '500' },
|
|
53
|
+
},
|
|
54
|
+
spacing: {},
|
|
55
|
+
shadows: {},
|
|
56
|
+
borderRadius: {},
|
|
57
|
+
iconSizes: {},
|
|
58
|
+
opacity: {},
|
|
59
|
+
avatarSizes: {},
|
|
60
|
+
borders: {},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
describe('getTextColor', () => {
|
|
66
|
+
test('should throw error when tokens is null', () => {
|
|
67
|
+
expect(() => getTextColor('textPrimary', null as any)).toThrow(
|
|
68
|
+
'Invalid design tokens: tokens and tokens.colors are required'
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('should throw error when tokens is undefined', () => {
|
|
73
|
+
expect(() => getTextColor('textPrimary', undefined as any)).toThrow(
|
|
74
|
+
'Invalid design tokens: tokens and tokens.colors are required'
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('should throw error when tokens.colors is null', () => {
|
|
79
|
+
expect(() => getTextColor('textPrimary', { colors: null } as any)).toThrow(
|
|
80
|
+
'Invalid design tokens: tokens and tokens.colors are required'
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('should return default textPrimary when color is undefined', () => {
|
|
85
|
+
const result = getTextColor(undefined, mockTokens);
|
|
86
|
+
expect(result).toBe('#000000');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('should return custom color as-is when not a variant', () => {
|
|
90
|
+
const customColor = '#FF5722';
|
|
91
|
+
const result = getTextColor(customColor, mockTokens);
|
|
92
|
+
expect(result).toBe(customColor);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should map textPrimary variant correctly', () => {
|
|
96
|
+
const result = getTextColor('textPrimary', mockTokens);
|
|
97
|
+
expect(result).toBe('#000000');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('should map textSecondary variant correctly', () => {
|
|
101
|
+
const result = getTextColor('textSecondary', mockTokens);
|
|
102
|
+
expect(result).toBe('#666666');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('should map semantic colors correctly', () => {
|
|
106
|
+
expect(getTextColor('success', mockTokens)).toBe('#4CAF50');
|
|
107
|
+
expect(getTextColor('error', mockTokens)).toBe('#F44336');
|
|
108
|
+
expect(getTextColor('warning', mockTokens)).toBe('#FF9800');
|
|
109
|
+
expect(getTextColor('info', mockTokens)).toBe('#2196F3');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('should map on-colors correctly', () => {
|
|
113
|
+
expect(getTextColor('onPrimary', mockTokens)).toBe('#FFFFFF');
|
|
114
|
+
expect(getTextColor('onError', mockTokens)).toBe('#FFFFFF');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('should handle legacy variants correctly', () => {
|
|
118
|
+
expect(getTextColor('primary', mockTokens)).toBe('#000000'); // Maps to textPrimary
|
|
119
|
+
expect(getTextColor('secondary', mockTokens)).toBe('#666666'); // Maps to textSecondary
|
|
120
|
+
expect(getTextColor('surfaceVariant', mockTokens)).toBe('#666666'); // Maps to textSecondary
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('should handle empty string', () => {
|
|
124
|
+
const result = getTextColor('', mockTokens);
|
|
125
|
+
expect(result).toBe('#000000');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('should handle hex color strings', () => {
|
|
129
|
+
const hexColor = '#RRGGBB';
|
|
130
|
+
const result = getTextColor(hexColor, mockTokens);
|
|
131
|
+
expect(result).toBe(hexColor);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('should handle rgb color strings', () => {
|
|
135
|
+
const rgbColor = 'rgb(255, 0, 0)';
|
|
136
|
+
const result = getTextColor(rgbColor, mockTokens);
|
|
137
|
+
expect(result).toBe(rgbColor);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('should return custom color as-is when not a variant', () => {
|
|
141
|
+
const customColor = 'unknownVariant';
|
|
142
|
+
const result = getTextColor(customColor, mockTokens);
|
|
143
|
+
expect(result).toBe(customColor);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('ColorVariant validation', () => {
|
|
152
|
+
test('should validate all defined color variants', () => {
|
|
153
|
+
const validVariants: ColorVariant[] = [
|
|
154
|
+
'textPrimary',
|
|
155
|
+
'textSecondary',
|
|
156
|
+
'textTertiary',
|
|
157
|
+
'textDisabled',
|
|
158
|
+
'textInverse',
|
|
159
|
+
'onSurface',
|
|
160
|
+
'onBackground',
|
|
161
|
+
'onPrimary',
|
|
162
|
+
'onSecondary',
|
|
163
|
+
'onSuccess',
|
|
164
|
+
'onError',
|
|
165
|
+
'onWarning',
|
|
166
|
+
'onInfo',
|
|
167
|
+
'success',
|
|
168
|
+
'error',
|
|
169
|
+
'warning',
|
|
170
|
+
'info',
|
|
171
|
+
'primary',
|
|
172
|
+
'secondary',
|
|
173
|
+
'tertiary',
|
|
174
|
+
'disabled',
|
|
175
|
+
'inverse',
|
|
176
|
+
'surfaceVariant',
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
validVariants.forEach(variant => {
|
|
180
|
+
const result = getTextColor(variant, mockTokens);
|
|
181
|
+
expect(typeof result).toBe('string');
|
|
182
|
+
expect(result.length).toBeGreaterThan(0);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for typography style utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { getTextStyle, isTextStyleVariant, getAllTextStyleVariants } from '../presentation/utils/textStyleUtils';
|
|
6
|
+
import type { TextStyleVariant } from '../domain/entities/TypographyTypes';
|
|
7
|
+
import type { DesignTokens } from '@umituz/react-native-design-system-theme';
|
|
8
|
+
|
|
9
|
+
const mockTokens: DesignTokens = {
|
|
10
|
+
colors: {
|
|
11
|
+
textPrimary: '#000000',
|
|
12
|
+
textSecondary: '#666666',
|
|
13
|
+
textTertiary: '#999999',
|
|
14
|
+
textDisabled: '#CCCCCC',
|
|
15
|
+
textInverse: '#FFFFFF',
|
|
16
|
+
onSurface: '#000000',
|
|
17
|
+
onBackground: '#000000',
|
|
18
|
+
onPrimary: '#FFFFFF',
|
|
19
|
+
onSecondary: '#FFFFFF',
|
|
20
|
+
onSuccess: '#FFFFFF',
|
|
21
|
+
onError: '#FFFFFF',
|
|
22
|
+
onWarning: '#000000',
|
|
23
|
+
onInfo: '#FFFFFF',
|
|
24
|
+
success: '#4CAF50',
|
|
25
|
+
error: '#F44336',
|
|
26
|
+
warning: '#FF9800',
|
|
27
|
+
info: '#2196F3',
|
|
28
|
+
primary: '#2196F3',
|
|
29
|
+
secondary: '#FF9800',
|
|
30
|
+
tertiary: '#9C27B0',
|
|
31
|
+
surface: '#FFFFFF',
|
|
32
|
+
surfaceVariant: '#F5F5F5',
|
|
33
|
+
background: '#FFFFFF',
|
|
34
|
+
},
|
|
35
|
+
typography: {
|
|
36
|
+
displayLarge: { fontSize: 57, fontWeight: '400' },
|
|
37
|
+
displayMedium: { fontSize: 45, fontWeight: '400' },
|
|
38
|
+
displaySmall: { fontSize: 36, fontWeight: '400' },
|
|
39
|
+
headlineLarge: { fontSize: 32, fontWeight: '400' },
|
|
40
|
+
headlineMedium: { fontSize: 28, fontWeight: '400' },
|
|
41
|
+
headlineSmall: { fontSize: 24, fontWeight: '400' },
|
|
42
|
+
titleLarge: { fontSize: 22, fontWeight: '500' },
|
|
43
|
+
titleMedium: { fontSize: 16, fontWeight: '500' },
|
|
44
|
+
titleSmall: { fontSize: 14, fontWeight: '500' },
|
|
45
|
+
bodyLarge: { fontSize: 16, fontWeight: '400' },
|
|
46
|
+
bodyMedium: { fontSize: 14, fontWeight: '400' },
|
|
47
|
+
bodySmall: { fontSize: 12, fontWeight: '400' },
|
|
48
|
+
labelLarge: { fontSize: 14, fontWeight: '500' },
|
|
49
|
+
labelMedium: { fontSize: 12, fontWeight: '500' },
|
|
50
|
+
labelSmall: { fontSize: 11, fontWeight: '500' },
|
|
51
|
+
},
|
|
52
|
+
spacing: {},
|
|
53
|
+
shadows: {},
|
|
54
|
+
borderRadius: {},
|
|
55
|
+
iconSizes: {},
|
|
56
|
+
opacity: {},
|
|
57
|
+
avatarSizes: {},
|
|
58
|
+
borders: {},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
describe('getTextStyle', () => {
|
|
62
|
+
test('should throw error when tokens is null', () => {
|
|
63
|
+
expect(() => getTextStyle('bodyLarge', null as any)).toThrow(
|
|
64
|
+
'Invalid design tokens: tokens and tokens.typography are required'
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('should throw error when tokens is undefined', () => {
|
|
69
|
+
expect(() => getTextStyle('bodyLarge', undefined as any)).toThrow(
|
|
70
|
+
'Invalid design tokens: tokens and tokens.typography are required'
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('should throw error when tokens.typography is null', () => {
|
|
75
|
+
expect(() => getTextStyle('bodyLarge', { typography: null } as any)).toThrow(
|
|
76
|
+
'Invalid design tokens: tokens and tokens.typography are required'
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('should return correct style for displayLarge', () => {
|
|
81
|
+
const result = getTextStyle('displayLarge', mockTokens);
|
|
82
|
+
expect(result).toEqual({ fontSize: 57, fontWeight: '400' });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('should return correct style for headlineMedium', () => {
|
|
86
|
+
const result = getTextStyle('headlineMedium', mockTokens);
|
|
87
|
+
expect(result).toEqual({ fontSize: 28, fontWeight: '400' });
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('should return correct style for titleSmall', () => {
|
|
91
|
+
const result = getTextStyle('titleSmall', mockTokens);
|
|
92
|
+
expect(result).toEqual({ fontSize: 14, fontWeight: '500' });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should return correct style for bodyMedium', () => {
|
|
96
|
+
const result = getTextStyle('bodyMedium', mockTokens);
|
|
97
|
+
expect(result).toEqual({ fontSize: 14, fontWeight: '400' });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('should return correct style for labelSmall', () => {
|
|
101
|
+
const result = getTextStyle('labelSmall', mockTokens);
|
|
102
|
+
expect(result).toEqual({ fontSize: 11, fontWeight: '500' });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('should return fallback style for unknown variant', () => {
|
|
106
|
+
const result = getTextStyle('unknownVariant' as TextStyleVariant, mockTokens);
|
|
107
|
+
expect(result).toEqual({ fontSize: 16, fontWeight: '400' }); // bodyLarge fallback
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('isTextStyleVariant', () => {
|
|
112
|
+
test('should return true for valid variants', () => {
|
|
113
|
+
expect(isTextStyleVariant('displayLarge')).toBe(true);
|
|
114
|
+
expect(isTextStyleVariant('headlineMedium')).toBe(true);
|
|
115
|
+
expect(isTextStyleVariant('titleSmall')).toBe(true);
|
|
116
|
+
expect(isTextStyleVariant('bodyMedium')).toBe(true);
|
|
117
|
+
expect(isTextStyleVariant('labelSmall')).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('should return false for invalid variants', () => {
|
|
121
|
+
expect(isTextStyleVariant('invalid')).toBe(false);
|
|
122
|
+
expect(isTextStyleVariant('display')).toBe(false);
|
|
123
|
+
expect(isTextStyleVariant('')).toBe(false);
|
|
124
|
+
expect(isTextStyleVariant('body')).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('getAllTextStyleVariants', () => {
|
|
129
|
+
test('should return all text style variants', () => {
|
|
130
|
+
const variants = getAllTextStyleVariants();
|
|
131
|
+
|
|
132
|
+
expect(variants).toHaveLength(15);
|
|
133
|
+
expect(variants).toContain('displayLarge');
|
|
134
|
+
expect(variants).toContain('displayMedium');
|
|
135
|
+
expect(variants).toContain('displaySmall');
|
|
136
|
+
expect(variants).toContain('headlineLarge');
|
|
137
|
+
expect(variants).toContain('headlineMedium');
|
|
138
|
+
expect(variants).toContain('headlineSmall');
|
|
139
|
+
expect(variants).toContain('titleLarge');
|
|
140
|
+
expect(variants).toContain('titleMedium');
|
|
141
|
+
expect(variants).toContain('titleSmall');
|
|
142
|
+
expect(variants).toContain('bodyLarge');
|
|
143
|
+
expect(variants).toContain('bodyMedium');
|
|
144
|
+
expect(variants).toContain('bodySmall');
|
|
145
|
+
expect(variants).toContain('labelLarge');
|
|
146
|
+
expect(variants).toContain('labelMedium');
|
|
147
|
+
expect(variants).toContain('labelSmall');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('should return readonly array', () => {
|
|
151
|
+
const variants = getAllTextStyleVariants();
|
|
152
|
+
expect(Array.isArray(variants)).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('TextStyleVariant validation', () => {
|
|
157
|
+
test('should validate all defined text style variants', () => {
|
|
158
|
+
const validVariants: TextStyleVariant[] = getAllTextStyleVariants();
|
|
159
|
+
|
|
160
|
+
validVariants.forEach(variant => {
|
|
161
|
+
const result = getTextStyle(variant, mockTokens);
|
|
162
|
+
expect(result).toHaveProperty('fontSize');
|
|
163
|
+
expect(result).toHaveProperty('fontWeight');
|
|
164
|
+
expect(typeof result.fontSize).toBe('number');
|
|
165
|
+
expect(typeof result.fontWeight).toBe('string');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typography Types - Material Design 3 Text Style Variants
|
|
3
|
+
*
|
|
4
|
+
* This file defines all typography-related types for React Native applications.
|
|
5
|
+
* These types are used by components like AtomicText to ensure type-safe typography usage.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Provides type definitions for Material Design 3 typography system
|
|
8
|
+
* @author Ümit UZ <umit@umituz.com>
|
|
9
|
+
* @since 1.0.0
|
|
10
|
+
* @version 1.2.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Material Design 3 Text Style Variants
|
|
15
|
+
*
|
|
16
|
+
* These variants correspond to Material Design 3 typography scale:
|
|
17
|
+
* - Display: Largest text (57px, 45px, 36px)
|
|
18
|
+
* - Headline: Section headers (32px, 28px, 24px)
|
|
19
|
+
* - Title: Card titles, list headers (22px, 16px, 14px)
|
|
20
|
+
* - Body: Main content text (16px, 14px, 12px)
|
|
21
|
+
* - Label: UI labels, buttons (14px, 12px, 11px)
|
|
22
|
+
*/
|
|
23
|
+
export type TextStyleVariant =
|
|
24
|
+
| 'displayLarge'
|
|
25
|
+
| 'displayMedium'
|
|
26
|
+
| 'displaySmall'
|
|
27
|
+
| 'headlineLarge'
|
|
28
|
+
| 'headlineMedium'
|
|
29
|
+
| 'headlineSmall'
|
|
30
|
+
| 'titleLarge'
|
|
31
|
+
| 'titleMedium'
|
|
32
|
+
| 'titleSmall'
|
|
33
|
+
| 'bodyLarge'
|
|
34
|
+
| 'bodyMedium'
|
|
35
|
+
| 'bodySmall'
|
|
36
|
+
| 'labelLarge'
|
|
37
|
+
| 'labelMedium'
|
|
38
|
+
| 'labelSmall';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Material Design 3 Text Color Variants
|
|
42
|
+
*
|
|
43
|
+
* TEXT COLORS (for text on surfaces):
|
|
44
|
+
* - textPrimary, textSecondary, textTertiary: General text colors
|
|
45
|
+
* - onSurface, onBackground: Text on surface/background
|
|
46
|
+
*
|
|
47
|
+
* ON COLORS (for text on colored backgrounds):
|
|
48
|
+
* - onPrimary, onSecondary: Text on primary/secondary colored backgrounds
|
|
49
|
+
* - onSuccess, onError, onWarning, onInfo: Text on semantic colored backgrounds
|
|
50
|
+
*
|
|
51
|
+
* SEMANTIC COLORS (can be used as text colors):
|
|
52
|
+
* - success, error, warning, info: Semantic colors (can be text or background)
|
|
53
|
+
*
|
|
54
|
+
* NOTE: 'primary' and 'secondary' are BACKGROUND colors, not text colors.
|
|
55
|
+
* Use 'onPrimary'/'onSecondary' for text on colored backgrounds, or
|
|
56
|
+
* 'textPrimary'/'textSecondary' for general text.
|
|
57
|
+
*/
|
|
58
|
+
export type ColorVariant =
|
|
59
|
+
// General text colors (Material Design 3)
|
|
60
|
+
| 'textPrimary'
|
|
61
|
+
| 'textSecondary'
|
|
62
|
+
| 'textTertiary'
|
|
63
|
+
| 'textDisabled'
|
|
64
|
+
| 'textInverse'
|
|
65
|
+
// Text on surfaces (Material Design 3)
|
|
66
|
+
| 'onSurface'
|
|
67
|
+
| 'onBackground'
|
|
68
|
+
// Text on colored backgrounds (Material Design 3)
|
|
69
|
+
| 'onPrimary'
|
|
70
|
+
| 'onSecondary'
|
|
71
|
+
| 'onSuccess'
|
|
72
|
+
| 'onError'
|
|
73
|
+
| 'onWarning'
|
|
74
|
+
| 'onInfo'
|
|
75
|
+
// Semantic colors (can be used as text)
|
|
76
|
+
| 'success'
|
|
77
|
+
| 'error'
|
|
78
|
+
| 'warning'
|
|
79
|
+
| 'info'
|
|
80
|
+
// Legacy support (deprecated - use textPrimary/textSecondary instead)
|
|
81
|
+
| 'primary'
|
|
82
|
+
| 'secondary'
|
|
83
|
+
| 'tertiary'
|
|
84
|
+
| 'disabled'
|
|
85
|
+
| 'inverse'
|
|
86
|
+
// Legacy: surfaceVariant is a background color, maps to textSecondary
|
|
87
|
+
| 'surfaceVariant';
|
|
88
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @umituz/react-native-design-system-typography - Public API
|
|
3
|
+
*
|
|
4
|
+
* Typography types and utilities for React Native applications
|
|
5
|
+
* Material Design 3 text styles and color variants
|
|
6
|
+
*
|
|
7
|
+
* This package provides:
|
|
8
|
+
* - Typography type definitions (TextStyleVariant, ColorVariant)
|
|
9
|
+
* - Text color utility functions
|
|
10
|
+
* - Type-safe typography helpers
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* import { TextStyleVariant, ColorVariant, getTextColor } from '@umituz/react-native-design-system-typography';
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// DOMAIN LAYER - Entities (Types)
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
export type {
|
|
21
|
+
TextStyleVariant,
|
|
22
|
+
ColorVariant,
|
|
23
|
+
} from './domain/entities/TypographyTypes';
|
|
24
|
+
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// PRESENTATION LAYER - Utilities
|
|
27
|
+
// =============================================================================
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
getTextColor,
|
|
31
|
+
} from './presentation/utils/textColorUtils';
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
getTextStyle,
|
|
35
|
+
isTextStyleVariant,
|
|
36
|
+
getAllTextStyleVariants,
|
|
37
|
+
clearTypographyCache,
|
|
38
|
+
} from './presentation/utils/textStyleUtils';
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
clearColorCache,
|
|
42
|
+
} from './presentation/utils/textColorUtils';
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
isValidHexColor,
|
|
46
|
+
isValidRgbColor,
|
|
47
|
+
isValidHslColor,
|
|
48
|
+
isValidNamedColor,
|
|
49
|
+
isValidColor,
|
|
50
|
+
getColorFormat,
|
|
51
|
+
normalizeColor,
|
|
52
|
+
} from './presentation/utils/colorValidationUtils';
|
|
53
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for validating color formats and values.
|
|
5
|
+
* These utilities ensure color strings are in valid formats.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Provides color validation utilities
|
|
8
|
+
* @author Ümit UZ <umit@umituz.com>
|
|
9
|
+
* @since 1.2.0
|
|
10
|
+
* @version 1.2.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Regular expressions for color format validation
|
|
15
|
+
*/
|
|
16
|
+
const COLOR_PATTERNS = {
|
|
17
|
+
// #RGB, #RRGGBB
|
|
18
|
+
hex: /^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/,
|
|
19
|
+
// rgb(R, G, B), rgba(R, G, B, A)
|
|
20
|
+
rgb: /^rgba?\(\s*(\d{1,3}%?)\s*,\s*(\d{1,3}%?)\s*,\s*(\d{1,3}%?)\s*(?:,\s*([01]?\.?\d*)\s*)?\)$/,
|
|
21
|
+
// hsl(H, S, L), hsla(H, S, L, A)
|
|
22
|
+
hsl: /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}%)\s*,\s*(\d{1,3}%)\s*(?:,\s*([01]?\.?\d*)\s*)?\)$/,
|
|
23
|
+
// CSS color names (limited to common ones)
|
|
24
|
+
named: /^(red|blue|green|yellow|orange|purple|pink|brown|black|white|gray|grey|transparent|inherit|initial|unset|currentcolor)$/i,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a string is a valid hex color
|
|
29
|
+
*
|
|
30
|
+
* @param color - Color string to validate
|
|
31
|
+
* @returns True if valid hex color
|
|
32
|
+
*/
|
|
33
|
+
export function isValidHexColor(color: string): boolean {
|
|
34
|
+
return COLOR_PATTERNS.hex.test(color);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if a string is a valid RGB/RGBA color
|
|
39
|
+
*
|
|
40
|
+
* @param color - Color string to validate
|
|
41
|
+
* @returns True if valid RGB/RGBA color
|
|
42
|
+
*/
|
|
43
|
+
export function isValidRgbColor(color: string): boolean {
|
|
44
|
+
if (!COLOR_PATTERNS.rgb.test(color)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Additional validation for RGB values
|
|
49
|
+
const match = color.match(/\d+/g);
|
|
50
|
+
if (!match || match.length < 3) return false;
|
|
51
|
+
|
|
52
|
+
const [r, g, b] = match.map(Number);
|
|
53
|
+
return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if a string is a valid HSL/HSLA color
|
|
58
|
+
*
|
|
59
|
+
* @param color - Color string to validate
|
|
60
|
+
* @returns True if valid HSL/HSLA color
|
|
61
|
+
*/
|
|
62
|
+
export function isValidHslColor(color: string): boolean {
|
|
63
|
+
if (!COLOR_PATTERNS.hsl.test(color)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Additional validation for HSL values
|
|
68
|
+
const match = color.match(/\d+/g);
|
|
69
|
+
if (!match || match.length < 3) return false;
|
|
70
|
+
|
|
71
|
+
const [h, s, l] = match.map(Number);
|
|
72
|
+
return h >= 0 && h <= 360 && s >= 0 && s <= 100 && l >= 0 && l <= 100;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if a string is a valid CSS named color
|
|
77
|
+
*
|
|
78
|
+
* @param color - Color string to validate
|
|
79
|
+
* @returns True if valid CSS named color
|
|
80
|
+
*/
|
|
81
|
+
export function isValidNamedColor(color: string): boolean {
|
|
82
|
+
return COLOR_PATTERNS.named.test(color);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if a string is a valid color in any supported format
|
|
87
|
+
*
|
|
88
|
+
* @param color - Color string to validate
|
|
89
|
+
* @returns True if valid color in any format
|
|
90
|
+
*/
|
|
91
|
+
export function isValidColor(color: string): boolean {
|
|
92
|
+
return isValidHexColor(color) ||
|
|
93
|
+
isValidRgbColor(color) ||
|
|
94
|
+
isValidHslColor(color) ||
|
|
95
|
+
isValidNamedColor(color);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get the format of a color string
|
|
100
|
+
*
|
|
101
|
+
* @param color - Color string to analyze
|
|
102
|
+
* @returns Color format or 'unknown' if not recognized
|
|
103
|
+
*/
|
|
104
|
+
export function getColorFormat(color: string): 'hex' | 'rgb' | 'hsl' | 'named' | 'unknown' {
|
|
105
|
+
if (isValidHexColor(color)) return 'hex';
|
|
106
|
+
if (isValidRgbColor(color)) return 'rgb';
|
|
107
|
+
if (isValidHslColor(color)) return 'hsl';
|
|
108
|
+
if (isValidNamedColor(color)) return 'named';
|
|
109
|
+
return 'unknown';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Normalize a color string to a consistent format
|
|
114
|
+
*
|
|
115
|
+
* @param color - Color string to normalize
|
|
116
|
+
* @returns Normalized color string
|
|
117
|
+
*/
|
|
118
|
+
export function normalizeColor(color: string): string {
|
|
119
|
+
if (!isValidColor(color)) {
|
|
120
|
+
return color;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Convert 3-digit hex to 6-digit
|
|
124
|
+
if (isValidHexColor(color) && color.length === 4) {
|
|
125
|
+
const r = color[1];
|
|
126
|
+
const g = color[2];
|
|
127
|
+
const b = color[3];
|
|
128
|
+
return `#${r}${r}${g}${g}${b}${b}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Convert to lowercase for consistency
|
|
132
|
+
return color.toLowerCase();
|
|
133
|
+
}
|