syzygy-ui-rn 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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +151 -0
  3. package/package.json +56 -0
  4. package/src/__tests__/components.test.tsx +368 -0
  5. package/src/__tests__/tokens.test.ts +47 -0
  6. package/src/components/badges/Badge.tsx +74 -0
  7. package/src/components/buttons/DestructiveButton.tsx +82 -0
  8. package/src/components/buttons/GhostButton.tsx +77 -0
  9. package/src/components/buttons/IconButton.tsx +61 -0
  10. package/src/components/buttons/PrimaryButton.tsx +82 -0
  11. package/src/components/buttons/SecondaryButton.tsx +84 -0
  12. package/src/components/cards/CardView.tsx +45 -0
  13. package/src/components/display/Avatar.tsx +53 -0
  14. package/src/components/display/Chip.tsx +64 -0
  15. package/src/components/display/CountBadge.tsx +58 -0
  16. package/src/components/display/DividerLine.tsx +29 -0
  17. package/src/components/display/LazyImageView.tsx +57 -0
  18. package/src/components/display/ListRow.tsx +76 -0
  19. package/src/components/display/SectionHeader.tsx +55 -0
  20. package/src/components/display/StarRatingView.tsx +70 -0
  21. package/src/components/feedback/EmptyStateView.tsx +93 -0
  22. package/src/components/feedback/ErrorStateView.tsx +75 -0
  23. package/src/components/feedback/LoadingView.tsx +57 -0
  24. package/src/components/feedback/ProgressBar.tsx +42 -0
  25. package/src/components/feedback/PullToRefresh.tsx +38 -0
  26. package/src/components/feedback/ShimmerView.tsx +47 -0
  27. package/src/components/feedback/ToastView.tsx +71 -0
  28. package/src/components/inputs/CheckboxInput.tsx +73 -0
  29. package/src/components/inputs/Dropdown.tsx +110 -0
  30. package/src/components/inputs/QuantityStepper.tsx +81 -0
  31. package/src/components/inputs/RadioButtonInput.tsx +73 -0
  32. package/src/components/inputs/SearchInput.tsx +107 -0
  33. package/src/components/inputs/SecureInput.tsx +105 -0
  34. package/src/components/inputs/SegmentedControl.tsx +70 -0
  35. package/src/components/inputs/SliderInput.tsx +131 -0
  36. package/src/components/inputs/TextInput.tsx +90 -0
  37. package/src/components/inputs/ToggleSwitch.tsx +50 -0
  38. package/src/components/layout/KeyboardAvoidingScrollView.tsx +40 -0
  39. package/src/components/navigation/AppBar.tsx +53 -0
  40. package/src/components/navigation/BackButton.tsx +63 -0
  41. package/src/components/navigation/BottomNavigationBar.tsx +65 -0
  42. package/src/components/navigation/PagerView.tsx +40 -0
  43. package/src/components/navigation/TabBar.tsx +68 -0
  44. package/src/components/navigation/TabBarItem.ts +8 -0
  45. package/src/components/overlay/BottomSheet.tsx +64 -0
  46. package/src/components/overlay/CollapsibleView.tsx +82 -0
  47. package/src/components/overlay/ModalDialog.tsx +47 -0
  48. package/src/index.ts +54 -0
  49. package/src/tokens/colors.ts +84 -0
  50. package/src/tokens/index.ts +4 -0
  51. package/src/tokens/radius.ts +8 -0
  52. package/src/tokens/spacing.ts +10 -0
  53. package/src/tokens/typography.ts +27 -0
  54. package/src/transitions/navigationTransitions.ts +66 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ayush Kumar Sethi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # syzygy-ui-rn
2
+
3
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
4
+ [![npm](https://img.shields.io/npm/v/syzygy-ui-rn)](https://www.npmjs.com/package/syzygy-ui-rn)
5
+ [![Platform](https://img.shields.io/badge/Platform-iOS%20%7C%20Android%20%7C%20Web-lightgrey)](https://reactnative.dev)
6
+ [![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![CI](https://github.com/Syzygy-Hub/syzygy-ui-rn/actions/workflows/node.yml/badge.svg)](https://github.com/Syzygy-Hub/syzygy-ui-rn/actions/workflows/node.yml)
8
+
9
+ <picture>
10
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Syzygy-Hub/syzygy-brand-assets/main/Assets/syzygy-banner-dark-2400.png">
11
+ <img src="https://raw.githubusercontent.com/Syzygy-Hub/syzygy-brand-assets/main/Assets/syzygy-banner-light-2400.png" alt="Syzygy" width="500">
12
+ </picture>
13
+
14
+ Production-ready React Native component library with design tokens, Dark Mode, and zero third-party dependencies.
15
+
16
+ ## Requirements
17
+ - React Native 0.70+
18
+ - React 18+
19
+ - TypeScript 5+
20
+
21
+ ## Installation
22
+ ```sh
23
+ npm install syzygy-ui-rn
24
+ ```
25
+
26
+ ## Components
27
+ - **Buttons:** PrimaryButton, SecondaryButton, DestructiveButton, GhostButton, IconButton
28
+ - **Inputs:** TextInput, SecureInput, SearchInput (debounced, with clear button), ToggleSwitch, CheckboxInput, RadioButtonInput, SliderInput, Dropdown, SegmentedControl, QuantityStepper
29
+ - **Display:** Avatar, DividerLine, Chip, ListRow, SectionHeader, LazyImageView, StarRatingView, CountBadge, PagerView (swipeable paged content, not navigation chrome; `onPageChange` reports the current page index for you to use as local state — e.g. a carousel — or feed into a navigator, as needed)
30
+ - **Feedback:** LoadingView, EmptyStateView, ToastView, ShimmerView, ProgressBar, PullToRefresh, ErrorStateView
31
+ - **Overlay:** ModalDialog, BottomSheet, CollapsibleView
32
+ - **Navigation:** BackButton, TabBar, BottomNavigationBar, AppBar — presentational only; this library has no navigation dependency, so wire `onPress`/`onSelectionChange` into your own navigator
33
+ - **Cards:** CardView
34
+ - **Badges:** Badge
35
+ - **Transitions:** `slideTransition`, `crossFadeTransition`, `slideVerticalTransition`, `modalPresentationTransition` — `Animated`-driven style helpers
36
+
37
+ See [CHANGELOG.md](CHANGELOG.md) for version history.
38
+
39
+ ## Design Tokens
40
+
41
+ All tokens live under `src/tokens/` and are consumed as plain objects — e.g. `spacing.md`, `getColors(scheme).primary`.
42
+
43
+ ### Colors (`getColors`)
44
+ `getColors(scheme)` returns a full `ColorPalette` for `'light'` or `'dark'` (falls back to light for `'unspecified'`/`null`/`undefined`):
45
+
46
+ | Token | Light | Dark |
47
+ |---|---|---|
48
+ | `background` | `#FFFFFF` | `#101012` |
49
+ | `surface` | `#F5F5F7` | `#1C1C1F` |
50
+ | `surfaceAlt` | `#ECECEE` | `#27272B` |
51
+ | `border` | `#D8D8DC` | `#3A3A3F` |
52
+ | `textPrimary` | `#1A1A1E` | `#F5F5F7` |
53
+ | `textSecondary` | `#6B6B72` | `#A5A5AC` |
54
+ | `primary` | `#2F6FED` | `#5C8DF6` |
55
+ | `secondary` | `#ECECEE` | `#27272B` |
56
+ | `destructive` / `error` | `#E5484D` | `#F2555A` |
57
+ | `success` | `#1F9254` | `#3DD68C` |
58
+ | `warning` | `#B7791F` | `#E3A008` |
59
+ | `disabled` | `#D8D8DC` | `#3A3A3F` |
60
+
61
+ Each color also has a matching `*Text` counterpart (e.g. `primaryText`) for text placed on top of it.
62
+
63
+ ### Typography (`fontSizes`, `fontWeights`, `lineHeights`)
64
+
65
+ | `fontSizes` | Value | | `lineHeights` | Value |
66
+ |---|---|---|---|---|
67
+ | `xs` | 12 | | `xs` | 16 |
68
+ | `sm` | 14 | | `sm` | 20 |
69
+ | `md` | 16 | | `md` | 22 |
70
+ | `lg` | 18 | | `lg` | 24 |
71
+ | `xl` | 22 | | `xl` | 28 |
72
+ | `xxl` | 28 | | `xxl` | 34 |
73
+
74
+ `fontWeights`: `regular` (400) · `medium` (500) · `semibold` (600) · `bold` (700)
75
+
76
+ ### Spacing (`spacing`)
77
+
78
+ | Token | Value |
79
+ |---|---|
80
+ | `xs` | 4 |
81
+ | `sm` | 8 |
82
+ | `md` | 16 |
83
+ | `lg` | 24 |
84
+ | `xl` | 32 |
85
+ | `xxl` | 48 |
86
+
87
+ ### Corner Radius (`radius`)
88
+
89
+ | Token | Value |
90
+ |---|---|
91
+ | `sm` | 4 |
92
+ | `md` | 8 |
93
+ | `lg` | 16 |
94
+ | `full` | 9999 (pill/capsule shapes) |
95
+
96
+ ## Usage
97
+
98
+ ```tsx
99
+ import { PrimaryButton, TextInput, Badge } from 'syzygy-ui-rn';
100
+
101
+ <PrimaryButton title="Get Started" onPress={() => {}} />
102
+ <TextInput label="Email" value={email} onChangeText={setEmail} />
103
+ <Badge text="New" variant="primary" />
104
+ ```
105
+
106
+ See the [Components](#components) list above for everything else available.
107
+
108
+ ## Contributing & Releases
109
+
110
+ ### Making a release
111
+ Releases are fully automated. To publish a new version:
112
+
113
+ 1. Make your changes and ensure all tests pass:
114
+ ```sh
115
+ npm test
116
+ npm run typecheck
117
+ npm run lint
118
+ ```
119
+
120
+ 2. Commit with the release prefix:
121
+ ```sh
122
+ git commit -m "release: v1.2.0 — description of changes"
123
+ git push origin main
124
+ ```
125
+
126
+ 3. The CI pipeline will automatically:
127
+ - Run all tests
128
+ - Sync package.json version to match the commit message
129
+ - Publish to npm via OIDC trusted publishing (no tokens needed)
130
+ - Create a GitHub release with the version tag
131
+
132
+ ### Version format
133
+ Follow semver: `v{major}.{minor}.{patch}`
134
+ - Patch: `v1.0.1` — bug fixes
135
+ - Minor: `v1.1.0` — new components or features
136
+ - Major: `v2.0.0` — breaking changes
137
+
138
+ ### Scripts
139
+
140
+ | Script | Description |
141
+ |--------|-------------|
142
+ | `npm run build` | Compile TypeScript to dist/ |
143
+ | `npm test` | Run Jest test suite |
144
+ | `npm run lint` | Run ESLint |
145
+ | `npm run lint:fix` | Run ESLint with auto-fix |
146
+ | `npm run typecheck` | Type check without emitting files |
147
+ | `npm run clean` | Remove dist/ and node_modules/ |
148
+ | `npm run reinstall` | Clean and reinstall all dependencies |
149
+
150
+ ## License
151
+ MIT
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "syzygy-ui-rn",
3
+ "version": "2.0.0",
4
+ "description": "Production-ready React Native component library with design tokens, Dark Mode, and zero third-party dependencies",
5
+ "main": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "author": "Ayush Kumar Sethi <aks5686@gmail.com>",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/Syzygy-Hub/syzygy-ui-rn"
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "test": "jest",
16
+ "clean": "rm -rf dist node_modules",
17
+ "reinstall": "npm run clean && npm install",
18
+ "prebuild": "rm -rf dist",
19
+ "lint": "eslint src --ext .ts,.tsx",
20
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
21
+ "typecheck": "tsc --noEmit",
22
+ "prepare": "npm run build"
23
+ },
24
+ "peerDependencies": {
25
+ "react": ">=18.0.0",
26
+ "react-native": ">=0.70.0"
27
+ },
28
+ "devDependencies": {
29
+ "typescript": "^5.4.0",
30
+ "@types/react": "^18.2.0",
31
+ "@types/react-native": "^0.72.0",
32
+ "jest": "^29.7.0",
33
+ "@types/jest": "^29.5.0",
34
+ "ts-jest": "^29.1.0",
35
+ "metro-react-native-babel-preset": "^0.77.0",
36
+ "react": "18.2.0",
37
+ "react-native": "^0.73.0",
38
+ "react-test-renderer": "18.2.0",
39
+ "@types/react-test-renderer": "^18.0.0",
40
+ "eslint": "^8.57.0",
41
+ "@typescript-eslint/parser": "^7.0.0",
42
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
43
+ "eslint-plugin-react": "^7.34.0",
44
+ "eslint-plugin-react-hooks": "^4.6.0",
45
+ "eslint-plugin-react-native": "^4.1.0",
46
+ "eslint-plugin-import": "^2.29.0",
47
+ "eslint-config-prettier": "^9.1.0",
48
+ "prettier": "^3.2.0"
49
+ },
50
+ "files": [
51
+ "src"
52
+ ],
53
+ "publishConfig": {
54
+ "access": "public"
55
+ }
56
+ }
@@ -0,0 +1,368 @@
1
+ import React from 'react';
2
+ import { Text } from 'react-native';
3
+ import renderer from 'react-test-renderer';
4
+
5
+ import { CardView } from '../components/cards/CardView';
6
+ import { Badge } from '../components/badges/Badge';
7
+ import { DestructiveButton } from '../components/buttons/DestructiveButton';
8
+ import { GhostButton } from '../components/buttons/GhostButton';
9
+ import { IconButton } from '../components/buttons/IconButton';
10
+ import { PrimaryButton } from '../components/buttons/PrimaryButton';
11
+ import { SecondaryButton } from '../components/buttons/SecondaryButton';
12
+ import { SliderInput } from '../components/inputs/SliderInput';
13
+ import { Dropdown } from '../components/inputs/Dropdown';
14
+ import { SegmentedControl } from '../components/inputs/SegmentedControl';
15
+ import { QuantityStepper } from '../components/inputs/QuantityStepper';
16
+ import { Avatar } from '../components/display/Avatar';
17
+ import { DividerLine } from '../components/display/DividerLine';
18
+ import { Chip } from '../components/display/Chip';
19
+ import { ListRow } from '../components/display/ListRow';
20
+ import { SectionHeader } from '../components/display/SectionHeader';
21
+ import { LazyImageView } from '../components/display/LazyImageView';
22
+ import { StarRatingView } from '../components/display/StarRatingView';
23
+ import { CountBadge } from '../components/display/CountBadge';
24
+ import { EmptyStateView } from '../components/feedback/EmptyStateView';
25
+ import { ShimmerView } from '../components/feedback/ShimmerView';
26
+ import { PullToRefresh } from '../components/feedback/PullToRefresh';
27
+ import { ErrorStateView } from '../components/feedback/ErrorStateView';
28
+ import { LoadingView } from '../components/feedback/LoadingView';
29
+ import { ProgressBar } from '../components/feedback/ProgressBar';
30
+ import { ToastView } from '../components/feedback/ToastView';
31
+ import { CheckboxInput } from '../components/inputs/CheckboxInput';
32
+ import { RadioButtonInput } from '../components/inputs/RadioButtonInput';
33
+ import { SearchInput } from '../components/inputs/SearchInput';
34
+ import { SecureInput } from '../components/inputs/SecureInput';
35
+ import { TextInput } from '../components/inputs/TextInput';
36
+ import { ToggleSwitch } from '../components/inputs/ToggleSwitch';
37
+ import { KeyboardAvoidingScrollView } from '../components/layout/KeyboardAvoidingScrollView';
38
+ import { AppBar } from '../components/navigation/AppBar';
39
+ import { BackButton } from '../components/navigation/BackButton';
40
+ import { BottomNavigationBar } from '../components/navigation/BottomNavigationBar';
41
+ import { PagerView } from '../components/navigation/PagerView';
42
+ import { TabBar } from '../components/navigation/TabBar';
43
+ import { BottomSheet } from '../components/overlay/BottomSheet';
44
+ import { CollapsibleView } from '../components/overlay/CollapsibleView';
45
+ import { ModalDialog } from '../components/overlay/ModalDialog';
46
+
47
+ describe('component smoke tests', () => {
48
+ it('renders PrimaryButton', () => {
49
+ const tree = renderer.create(
50
+ <PrimaryButton title="Continue" onPress={() => {}} />
51
+ );
52
+ expect(tree.toJSON()).toBeTruthy();
53
+ });
54
+
55
+ it('renders SecondaryButton', () => {
56
+ const tree = renderer.create(
57
+ <SecondaryButton title="Cancel" onPress={() => {}} />
58
+ );
59
+ expect(tree.toJSON()).toBeTruthy();
60
+ });
61
+
62
+ it('renders DestructiveButton', () => {
63
+ const tree = renderer.create(
64
+ <DestructiveButton title="Delete" onPress={() => {}} />
65
+ );
66
+ expect(tree.toJSON()).toBeTruthy();
67
+ });
68
+
69
+ it('renders GhostButton', () => {
70
+ const tree = renderer.create(<GhostButton title="Skip" onPress={() => {}} />);
71
+ expect(tree.toJSON()).toBeTruthy();
72
+ });
73
+
74
+ it('renders IconButton', () => {
75
+ const tree = renderer.create(
76
+ <IconButton
77
+ icon={<Text>+</Text>}
78
+ onPress={() => {}}
79
+ accessibilityLabel="Add"
80
+ />
81
+ );
82
+ expect(tree.toJSON()).toBeTruthy();
83
+ });
84
+
85
+ it('renders TextInput', () => {
86
+ const tree = renderer.create(
87
+ <TextInput label="Email" value="" onChangeText={() => {}} />
88
+ );
89
+ expect(tree.toJSON()).toBeTruthy();
90
+ });
91
+
92
+ it('renders TextInput with error', () => {
93
+ const tree = renderer.create(
94
+ <TextInput
95
+ label="Email"
96
+ value=""
97
+ onChangeText={() => {}}
98
+ error="Required"
99
+ />
100
+ );
101
+ expect(tree.toJSON()).toBeTruthy();
102
+ });
103
+
104
+ it('renders TextInput with maxLength counter', () => {
105
+ const tree = renderer.create(
106
+ <TextInput
107
+ label="Bio"
108
+ value="Hello"
109
+ onChangeText={() => {}}
110
+ maxLength={100}
111
+ />
112
+ );
113
+ expect(tree.toJSON()).toBeTruthy();
114
+ });
115
+
116
+ it('renders SecureInput', () => {
117
+ const tree = renderer.create(
118
+ <SecureInput label="Password" value="" onChangeText={() => {}} />
119
+ );
120
+ expect(tree.toJSON()).toBeTruthy();
121
+ });
122
+
123
+ it('renders LoadingView', () => {
124
+ const tree = renderer.create(<LoadingView message="Loading data" />);
125
+ expect(tree.toJSON()).toBeTruthy();
126
+ });
127
+
128
+ it('renders EmptyStateView', () => {
129
+ const tree = renderer.create(
130
+ <EmptyStateView
131
+ title="Nothing here"
132
+ subtitle="Try again later"
133
+ ctaLabel="Retry"
134
+ onCtaPress={() => {}}
135
+ />
136
+ );
137
+ expect(tree.toJSON()).toBeTruthy();
138
+ });
139
+
140
+ it('renders ToastView', () => {
141
+ const tree = renderer.create(<ToastView message="Saved!" variant="success" />);
142
+ expect(tree.toJSON()).toBeTruthy();
143
+ });
144
+
145
+ it('renders CardView', () => {
146
+ const tree = renderer.create(
147
+ <CardView>
148
+ <Text>Card content</Text>
149
+ </CardView>
150
+ );
151
+ expect(tree.toJSON()).toBeTruthy();
152
+ });
153
+
154
+ it('renders Badge', () => {
155
+ const tree = renderer.create(<Badge text="New" variant="primary" />);
156
+ expect(tree.toJSON()).toBeTruthy();
157
+ });
158
+
159
+ it('renders BackButton', () => {
160
+ const tree = renderer.create(<BackButton onPress={() => {}} />);
161
+ expect(tree.toJSON()).toBeTruthy();
162
+ });
163
+
164
+ it('renders SearchInput', () => {
165
+ const tree = renderer.create(<SearchInput value="" onChangeText={() => {}} />);
166
+ expect(tree.toJSON()).toBeTruthy();
167
+ });
168
+
169
+ it('renders ToggleSwitch', () => {
170
+ const tree = renderer.create(
171
+ <ToggleSwitch label="Notifications" value={true} onValueChange={() => {}} />
172
+ );
173
+ expect(tree.toJSON()).toBeTruthy();
174
+ });
175
+
176
+ it('renders CheckboxInput', () => {
177
+ const tree = renderer.create(
178
+ <CheckboxInput label="Remember me" checked={false} onValueChange={() => {}} />
179
+ );
180
+ expect(tree.toJSON()).toBeTruthy();
181
+ });
182
+
183
+ it('renders RadioButtonInput', () => {
184
+ const tree = renderer.create(
185
+ <RadioButtonInput label="Option A" selected={true} onPress={() => {}} />
186
+ );
187
+ expect(tree.toJSON()).toBeTruthy();
188
+ });
189
+
190
+ it('renders SliderInput', () => {
191
+ const tree = renderer.create(
192
+ <SliderInput label="Volume" value={0.5} onValueChange={() => {}} />
193
+ );
194
+ expect(tree.toJSON()).toBeTruthy();
195
+ });
196
+
197
+ it('renders Dropdown', () => {
198
+ const tree = renderer.create(
199
+ <Dropdown
200
+ label="Country"
201
+ selection="USA"
202
+ options={['USA', 'Canada']}
203
+ onSelectionChange={() => {}}
204
+ optionTitle={(o) => o}
205
+ />
206
+ );
207
+ expect(tree.toJSON()).toBeTruthy();
208
+ });
209
+
210
+ it('renders SegmentedControl', () => {
211
+ const tree = renderer.create(
212
+ <SegmentedControl
213
+ options={['Day', 'Week']}
214
+ selection="Day"
215
+ onSelectionChange={() => {}}
216
+ optionTitle={(o) => o}
217
+ />
218
+ );
219
+ expect(tree.toJSON()).toBeTruthy();
220
+ });
221
+
222
+ it('renders QuantityStepper', () => {
223
+ const tree = renderer.create(<QuantityStepper value={1} onValueChange={() => {}} />);
224
+ expect(tree.toJSON()).toBeTruthy();
225
+ });
226
+
227
+ it('renders Avatar', () => {
228
+ const tree = renderer.create(<Avatar initials="AK" />);
229
+ expect(tree.toJSON()).toBeTruthy();
230
+ });
231
+
232
+ it('renders DividerLine', () => {
233
+ const tree = renderer.create(<DividerLine />);
234
+ expect(tree.toJSON()).toBeTruthy();
235
+ });
236
+
237
+ it('renders Chip', () => {
238
+ const tree = renderer.create(<Chip text="Swift" onRemove={() => {}} />);
239
+ expect(tree.toJSON()).toBeTruthy();
240
+ });
241
+
242
+ it('renders ListRow', () => {
243
+ const tree = renderer.create(<ListRow title="Settings" subtitle="Manage preferences" />);
244
+ expect(tree.toJSON()).toBeTruthy();
245
+ });
246
+
247
+ it('renders SectionHeader', () => {
248
+ const tree = renderer.create(<SectionHeader title="Recent Activity" />);
249
+ expect(tree.toJSON()).toBeTruthy();
250
+ });
251
+
252
+ it('renders LazyImageView', () => {
253
+ const tree = renderer.create(<LazyImageView uri={null} />);
254
+ expect(tree.toJSON()).toBeTruthy();
255
+ });
256
+
257
+ it('renders StarRatingView', () => {
258
+ const tree = renderer.create(<StarRatingView rating={3} />);
259
+ expect(tree.toJSON()).toBeTruthy();
260
+ });
261
+
262
+ it('renders CountBadge', () => {
263
+ const tree = renderer.create(<CountBadge count={3} />);
264
+ expect(tree.toJSON()).toBeTruthy();
265
+ });
266
+
267
+ it('renders ShimmerView', () => {
268
+ const tree = renderer.create(<ShimmerView />);
269
+ expect(tree.toJSON()).toBeTruthy();
270
+ // ShimmerView runs an infinite Animated.loop; unmount to stop it,
271
+ // otherwise its timers keep firing after the Jest environment tears down.
272
+ tree.unmount();
273
+ });
274
+
275
+ it('renders ProgressBar', () => {
276
+ const tree = renderer.create(<ProgressBar progress={0.5} />);
277
+ expect(tree.toJSON()).toBeTruthy();
278
+ });
279
+
280
+ it('renders PullToRefresh', () => {
281
+ const tree = renderer.create(
282
+ <PullToRefresh refreshing={false} onRefresh={() => {}}>
283
+ <Text>Content</Text>
284
+ </PullToRefresh>
285
+ );
286
+ expect(tree.toJSON()).toBeTruthy();
287
+ });
288
+
289
+ it('renders ErrorStateView', () => {
290
+ const tree = renderer.create(
291
+ <ErrorStateView title="Something went wrong" onRetryPress={() => {}} />
292
+ );
293
+ expect(tree.toJSON()).toBeTruthy();
294
+ });
295
+
296
+ it('renders ModalDialog', () => {
297
+ const tree = renderer.create(
298
+ <ModalDialog visible={true} onDismiss={() => {}}>
299
+ <Text>Content</Text>
300
+ </ModalDialog>
301
+ );
302
+ expect(tree.toJSON()).toBeTruthy();
303
+ });
304
+
305
+ it('renders BottomSheet', () => {
306
+ const tree = renderer.create(
307
+ <BottomSheet visible={true} onDismiss={() => {}}>
308
+ <Text>Content</Text>
309
+ </BottomSheet>
310
+ );
311
+ expect(tree.toJSON()).toBeTruthy();
312
+ tree.unmount();
313
+ });
314
+
315
+ it('renders CollapsibleView', () => {
316
+ const tree = renderer.create(
317
+ <CollapsibleView title="Details">
318
+ <Text>Content</Text>
319
+ </CollapsibleView>
320
+ );
321
+ expect(tree.toJSON()).toBeTruthy();
322
+ });
323
+
324
+ it('renders TabBar', () => {
325
+ const tree = renderer.create(
326
+ <TabBar
327
+ items={[{ tag: 'home', icon: <Text>H</Text>, label: 'Home' }]}
328
+ selection="home"
329
+ onSelectionChange={() => {}}
330
+ />
331
+ );
332
+ expect(tree.toJSON()).toBeTruthy();
333
+ });
334
+
335
+ it('renders BottomNavigationBar', () => {
336
+ const tree = renderer.create(
337
+ <BottomNavigationBar
338
+ items={[{ tag: 'home', icon: <Text>H</Text>, label: 'Home' }]}
339
+ selection="home"
340
+ onSelectionChange={() => {}}
341
+ />
342
+ );
343
+ expect(tree.toJSON()).toBeTruthy();
344
+ });
345
+
346
+ it('renders AppBar', () => {
347
+ const tree = renderer.create(<AppBar title="Settings" />);
348
+ expect(tree.toJSON()).toBeTruthy();
349
+ });
350
+
351
+ it('renders PagerView', () => {
352
+ const tree = renderer.create(
353
+ <PagerView>
354
+ <Text>Page 1</Text>
355
+ </PagerView>
356
+ );
357
+ expect(tree.toJSON()).toBeTruthy();
358
+ });
359
+
360
+ it('renders KeyboardAvoidingScrollView', () => {
361
+ const tree = renderer.create(
362
+ <KeyboardAvoidingScrollView>
363
+ <Text>Content</Text>
364
+ </KeyboardAvoidingScrollView>
365
+ );
366
+ expect(tree.toJSON()).toBeTruthy();
367
+ });
368
+ });
@@ -0,0 +1,47 @@
1
+ import { spacing, radius, lightColors, darkColors, fontSizes } from '../tokens';
2
+
3
+ describe('spacing tokens', () => {
4
+ it('defines the expected scale', () => {
5
+ expect(spacing).toEqual({
6
+ xs: 4,
7
+ sm: 8,
8
+ md: 16,
9
+ lg: 24,
10
+ xl: 32,
11
+ xxl: 48,
12
+ });
13
+ });
14
+ });
15
+
16
+ describe('radius tokens', () => {
17
+ it('defines the expected scale', () => {
18
+ expect(radius).toEqual({
19
+ sm: 4,
20
+ md: 8,
21
+ lg: 16,
22
+ full: 9999,
23
+ });
24
+ });
25
+ });
26
+
27
+ describe('color tokens', () => {
28
+ it('defines matching keys for light and dark palettes', () => {
29
+ expect(Object.keys(lightColors).sort()).toEqual(Object.keys(darkColors).sort());
30
+ });
31
+
32
+ it('has core semantic colors defined', () => {
33
+ ['background', 'primary', 'destructive', 'success', 'warning', 'error'].forEach(
34
+ (key) => {
35
+ expect(lightColors).toHaveProperty(key);
36
+ expect(darkColors).toHaveProperty(key);
37
+ }
38
+ );
39
+ });
40
+ });
41
+
42
+ describe('typography tokens', () => {
43
+ it('defines the expected font sizes', () => {
44
+ expect(fontSizes.md).toBe(16);
45
+ expect(fontSizes.xxl).toBeGreaterThan(fontSizes.xs);
46
+ });
47
+ });