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.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/package.json +56 -0
- package/src/__tests__/components.test.tsx +368 -0
- package/src/__tests__/tokens.test.ts +47 -0
- package/src/components/badges/Badge.tsx +74 -0
- package/src/components/buttons/DestructiveButton.tsx +82 -0
- package/src/components/buttons/GhostButton.tsx +77 -0
- package/src/components/buttons/IconButton.tsx +61 -0
- package/src/components/buttons/PrimaryButton.tsx +82 -0
- package/src/components/buttons/SecondaryButton.tsx +84 -0
- package/src/components/cards/CardView.tsx +45 -0
- package/src/components/display/Avatar.tsx +53 -0
- package/src/components/display/Chip.tsx +64 -0
- package/src/components/display/CountBadge.tsx +58 -0
- package/src/components/display/DividerLine.tsx +29 -0
- package/src/components/display/LazyImageView.tsx +57 -0
- package/src/components/display/ListRow.tsx +76 -0
- package/src/components/display/SectionHeader.tsx +55 -0
- package/src/components/display/StarRatingView.tsx +70 -0
- package/src/components/feedback/EmptyStateView.tsx +93 -0
- package/src/components/feedback/ErrorStateView.tsx +75 -0
- package/src/components/feedback/LoadingView.tsx +57 -0
- package/src/components/feedback/ProgressBar.tsx +42 -0
- package/src/components/feedback/PullToRefresh.tsx +38 -0
- package/src/components/feedback/ShimmerView.tsx +47 -0
- package/src/components/feedback/ToastView.tsx +71 -0
- package/src/components/inputs/CheckboxInput.tsx +73 -0
- package/src/components/inputs/Dropdown.tsx +110 -0
- package/src/components/inputs/QuantityStepper.tsx +81 -0
- package/src/components/inputs/RadioButtonInput.tsx +73 -0
- package/src/components/inputs/SearchInput.tsx +107 -0
- package/src/components/inputs/SecureInput.tsx +105 -0
- package/src/components/inputs/SegmentedControl.tsx +70 -0
- package/src/components/inputs/SliderInput.tsx +131 -0
- package/src/components/inputs/TextInput.tsx +90 -0
- package/src/components/inputs/ToggleSwitch.tsx +50 -0
- package/src/components/layout/KeyboardAvoidingScrollView.tsx +40 -0
- package/src/components/navigation/AppBar.tsx +53 -0
- package/src/components/navigation/BackButton.tsx +63 -0
- package/src/components/navigation/BottomNavigationBar.tsx +65 -0
- package/src/components/navigation/PagerView.tsx +40 -0
- package/src/components/navigation/TabBar.tsx +68 -0
- package/src/components/navigation/TabBarItem.ts +8 -0
- package/src/components/overlay/BottomSheet.tsx +64 -0
- package/src/components/overlay/CollapsibleView.tsx +82 -0
- package/src/components/overlay/ModalDialog.tsx +47 -0
- package/src/index.ts +54 -0
- package/src/tokens/colors.ts +84 -0
- package/src/tokens/index.ts +4 -0
- package/src/tokens/radius.ts +8 -0
- package/src/tokens/spacing.ts +10 -0
- package/src/tokens/typography.ts +27 -0
- 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
|
+
[](https://www.typescriptlang.org)
|
|
4
|
+
[](https://www.npmjs.com/package/syzygy-ui-rn)
|
|
5
|
+
[](https://reactnative.dev)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](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
|
+
});
|