@hero-design/rn-work-uikit 1.1.0-alpha.0 → 1.2.0-alpha.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 (56) hide show
  1. package/.cursorrules +57 -0
  2. package/CHANGELOG.md +8 -3
  3. package/DEVELOPMENT.md +118 -0
  4. package/THEME_OVERRIDE.md +52 -0
  5. package/eslint.config.js +20 -0
  6. package/lib/index.js +1000 -4
  7. package/locales/en_AU.js +10 -0
  8. package/locales/en_AU.mjs +8 -0
  9. package/locales/en_CA.js +10 -0
  10. package/locales/en_CA.mjs +8 -0
  11. package/locales/index.js +11 -0
  12. package/locales/index.mjs +9 -0
  13. package/locales/types.js +2 -0
  14. package/locales/types.mjs +1 -0
  15. package/package.json +8 -4
  16. package/rollup.config.mjs +18 -2
  17. package/src/__tests__/__snapshots__/index.spec.tsx.snap +91 -116
  18. package/src/__tests__/index.spec.tsx +15 -0
  19. package/src/__tests__/theme-export-override.spec.ts +96 -0
  20. package/src/components/TextInput/ErrorOrHelpText.tsx +58 -0
  21. package/src/components/TextInput/FloatingLabel.tsx +120 -0
  22. package/src/components/TextInput/InputComponent.tsx +61 -0
  23. package/src/components/TextInput/InputRow.tsx +103 -0
  24. package/src/components/TextInput/MaxLengthMessage.tsx +66 -0
  25. package/src/components/TextInput/PrefixComponent.tsx +77 -0
  26. package/src/components/TextInput/StyledTextInput.tsx +134 -0
  27. package/src/components/TextInput/SuffixComponent.tsx +73 -0
  28. package/src/components/TextInput/__tests__/ErrorOrHelpText.spec.tsx +20 -0
  29. package/src/components/TextInput/__tests__/FloatingLabel.spec.tsx +203 -0
  30. package/src/components/TextInput/__tests__/InputComponent.spec.tsx +39 -0
  31. package/src/components/TextInput/__tests__/InputRow.spec.tsx +275 -0
  32. package/src/components/TextInput/__tests__/MaxLengthMessage.spec.tsx +17 -0
  33. package/src/components/TextInput/__tests__/PrefixComponent.spec.tsx +14 -0
  34. package/src/components/TextInput/__tests__/StyledTextInput.spec.tsx +114 -0
  35. package/src/components/TextInput/__tests__/SuffixComponent.spec.tsx +20 -0
  36. package/src/components/TextInput/__tests__/__snapshots__/StyledTextInput.spec.tsx.snap +571 -0
  37. package/src/components/TextInput/__tests__/__snapshots__/index.spec.tsx.snap +5671 -0
  38. package/src/components/TextInput/__tests__/getState.spec.tsx +89 -0
  39. package/src/components/TextInput/__tests__/index.spec.tsx +699 -0
  40. package/src/components/TextInput/constants.ts +1 -0
  41. package/src/components/TextInput/index.tsx +327 -0
  42. package/src/components/TextInput/types.ts +95 -0
  43. package/src/emotion.d.ts +15 -0
  44. package/src/index.ts +16 -1
  45. package/src/jest.d.ts +24 -0
  46. package/src/theme/ThemeProvider.ts +20 -0
  47. package/src/theme/ThemeSwitcher.tsx +76 -0
  48. package/src/theme/__tests__/ThemeProvider.spec.tsx +32 -0
  49. package/src/theme/__tests__/__snapshots__/index.spec.ts.snap +1851 -0
  50. package/src/theme/__tests__/index.spec.ts +7 -0
  51. package/src/theme/components/textInput.ts +92 -0
  52. package/src/theme/getTheme.ts +32 -0
  53. package/src/theme/index.ts +17 -0
  54. package/src/utils/__tests__/helpers.spec.ts +92 -0
  55. package/src/utils/helpers.ts +113 -0
  56. package/testUtils/renderWithTheme.tsx +6 -3
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ const locale = {
4
+ lang: 'en-AU',
5
+ dateTimeFormats: {
6
+ fullDate: 'dd/MM/yyyy',
7
+ },
8
+ };
9
+
10
+ module.exports = locale;
@@ -0,0 +1,8 @@
1
+ const locale = {
2
+ lang: 'en-AU',
3
+ dateTimeFormats: {
4
+ fullDate: 'dd/MM/yyyy',
5
+ },
6
+ };
7
+
8
+ export { locale as default };
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ const locale = {
4
+ lang: 'en-CA',
5
+ dateTimeFormats: {
6
+ fullDate: 'MMM dd, yyyy',
7
+ },
8
+ };
9
+
10
+ module.exports = locale;
@@ -0,0 +1,8 @@
1
+ const locale = {
2
+ lang: 'en-CA',
3
+ dateTimeFormats: {
4
+ fullDate: 'MMM dd, yyyy',
5
+ },
6
+ };
7
+
8
+ export { locale as default };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ var locales_en_AU = require('./en_AU.js');
4
+ var locales_en_CA = require('./en_CA.js');
5
+
6
+ var index = {
7
+ 'en-AU': locales_en_AU,
8
+ 'en-CA': locales_en_CA,
9
+ };
10
+
11
+ module.exports = index;
@@ -0,0 +1,9 @@
1
+ import locale from './en_AU.mjs';
2
+ import locale$1 from './en_CA.mjs';
3
+
4
+ var index = {
5
+ 'en-AU': locale,
6
+ 'en-CA': locale$1,
7
+ };
8
+
9
+ export { index as default };
@@ -0,0 +1,2 @@
1
+ 'use strict';
2
+
@@ -0,0 +1 @@
1
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/rn-work-uikit",
3
- "version": "1.1.0-alpha.0",
3
+ "version": "1.2.0-alpha.0",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -16,10 +16,14 @@
16
16
  "build:types": "tsc --noEmit false --emitDeclarationOnly --project tsconfig.prod.json",
17
17
  "build": "yarn build:js && yarn build:types",
18
18
  "build:watch": "yarn build:js -w & yarn build:types -w",
19
- "publish:npm": "yarn publish --access public"
19
+ "publish:npm": "yarn publish --access public",
20
+ "set-dev-env": "npm pkg set react-native=src/index.ts"
20
21
  },
21
22
  "dependencies": {
22
- "@hero-design/rn": "^8.101.3-alpha.0"
23
+ "@emotion/native": "^11.9.3",
24
+ "@emotion/primitives-core": "11.0.0",
25
+ "@emotion/react": "^11.9.3",
26
+ "@hero-design/rn": "^8.101.1"
23
27
  },
24
28
  "peerDependencies": {
25
29
  "@hero-design/react-native-month-year-picker": "^8.43.1",
@@ -45,7 +49,6 @@
45
49
  "@babel/preset-react": "^7.26.3",
46
50
  "@babel/preset-typescript": "^7.27.0",
47
51
  "@babel/runtime": "^7.25.0",
48
- "@emotion/jest": "^11.11.0",
49
52
  "@eslint/compat": "^1.1.1",
50
53
  "@eslint/eslintrc": "^3.1.0",
51
54
  "@eslint/js": "^9.8.0",
@@ -107,6 +110,7 @@
107
110
  "react-test-renderer": "18.2.0",
108
111
  "rollup": "^4.24.3",
109
112
  "rollup-plugin-copy": "^3.5.0",
113
+ "rollup-plugin-delete": "^3.0.1",
110
114
  "rollup-plugin-flow": "^1.1.1",
111
115
  "rollup-plugin-visualizer": "^5.12.0",
112
116
  "ts-jest": "^29.1.1",
package/rollup.config.mjs CHANGED
@@ -7,12 +7,15 @@ import replace from '@rollup/plugin-replace';
7
7
  import flow from 'rollup-plugin-flow';
8
8
  import { visualizer } from 'rollup-plugin-visualizer';
9
9
  import pkg from './package.json' assert { type: 'json' };
10
+ import copy from 'rollup-plugin-copy';
11
+ import del from 'rollup-plugin-delete';
10
12
 
11
13
  const extensions = ['.js', '.jsx', '.ts', '.tsx'];
12
14
 
13
15
  const generateBuildStats = process.env.GENERATE_BUILD_STATS === 'true';
14
16
  const bundleTemplate = process.env.BUNDLE_TEMPLATE || 'treemap';
15
- const fileName = process.env.FILE_NAME || `stats/${pkg.version}/rn-work-uikit-stats.html`;
17
+ const fileName =
18
+ process.env.FILE_NAME || `stats/${pkg.version}/rn-work-uikit-stats.html`;
16
19
 
17
20
  export default [
18
21
  {
@@ -63,6 +66,19 @@ export default [
63
66
  }),
64
67
  ]
65
68
  : []),
69
+ del({ targets: ['locales', 'types'] }),
70
+ copy({
71
+ targets: [
72
+ {
73
+ src: 'node_modules/@hero-design/rn/locales',
74
+ dest: '.',
75
+ },
76
+ {
77
+ src: 'node_modules/@hero-design/rn/types',
78
+ dest: '.',
79
+ },
80
+ ],
81
+ }),
66
82
  ],
67
83
  },
68
- ];
84
+ ];
@@ -1,126 +1,120 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`first test to ensure configure correctly should render TextInput with props in snapshot 1`] = `
4
- <View
5
- style={
4
+ <Pressable
5
+ accessibilityState={
6
6
  {
7
- "flex": 1,
7
+ "disabled": false,
8
8
  }
9
9
  }
10
+ disabled={false}
11
+ onPress={[Function]}
12
+ style={
13
+ [
14
+ [
15
+ {
16
+ "flexDirection": "row",
17
+ "marginTop": 7.846153846153847,
18
+ "minHeight": 54.92307692307692,
19
+ "paddingHorizontal": 15.692307692307693,
20
+ "width": "100%",
21
+ },
22
+ ],
23
+ {},
24
+ ]
25
+ }
26
+ testID="text-input"
10
27
  >
11
28
  <View
12
- pointerEvents="auto"
29
+ pointerEvents="none"
30
+ style={
31
+ [
32
+ [
33
+ {
34
+ "borderColor": "#e8e9ea",
35
+ "borderRadius": 8,
36
+ "borderWidth": 2,
37
+ },
38
+ ],
39
+ [
40
+ {
41
+ "backgroundColor": "#ffffff",
42
+ },
43
+ {},
44
+ ],
45
+ ]
46
+ }
47
+ testID="text-input-border"
48
+ themeFocused={false}
49
+ themeState="filled"
50
+ />
51
+ <View
13
52
  style={
14
53
  [
15
54
  [
16
55
  {
56
+ "backgroundColor": "transparent",
57
+ "borderRadius": 8,
58
+ "flex": 1,
59
+ "flexDirection": "column",
60
+ "marginBottom": 7.846153846153847,
17
61
  "marginTop": 7.846153846153847,
18
- "width": "100%",
62
+ "overflow": "hidden",
19
63
  },
20
64
  ],
21
65
  undefined,
22
66
  ]
23
67
  }
24
- testID="text-input"
25
68
  >
26
69
  <View
27
- onLayout={[Function]}
28
70
  style={
29
71
  [
30
72
  [
31
73
  {
32
74
  "alignItems": "center",
33
- "backgroundColor": "#ffffff",
34
- "borderRadius": 8,
35
75
  "flexDirection": "row",
36
- "padding": 15.692307692307693,
76
+ "flexGrow": 2,
77
+ "flexShrink": 1,
78
+ "gap": 3.9230769230769234,
37
79
  },
38
80
  ],
39
81
  undefined,
40
82
  ]
41
83
  }
42
84
  >
43
- <View
44
- style={
45
- [
46
- [
47
- {
48
- "borderColor": "#001f23",
49
- "borderRadius": 8,
50
- "borderWidth": 1,
51
- },
52
- ],
53
- [
54
- {
55
- "backgroundColor": "#ffffff",
56
- },
57
- undefined,
58
- ],
59
- ]
60
- }
61
- testID="text-input-border"
62
- themeFocused={false}
63
- themeState="filled"
64
- />
65
- <View />
66
85
  <AnimatedView
67
- pointerEvents="none"
68
86
  style={
69
- [
70
- [
71
- {
72
- "alignItems": "center",
73
- "flexDirection": "row",
74
- "left": 23.53846153846154,
75
- "position": "absolute",
76
- "right": 15.692307692307693,
77
- "top": -10.461538461538462,
78
- "zIndex": 1,
79
- },
80
- ],
81
- [
82
- {
83
- "transformOrigin": "left top",
84
- },
85
- {
86
- "transform": [
87
- {
88
- "translateY": {
89
- "addListener": [MockFunction],
90
- "removeListener": [MockFunction],
91
- "setValue": [MockFunction],
92
- },
93
- },
94
- {
95
- "scale": {
96
- "addListener": [MockFunction],
97
- "removeListener": [MockFunction],
98
- "setValue": [MockFunction],
99
- },
100
- },
101
- ],
102
- },
103
- ],
104
- ]
87
+ {
88
+ "opacity": {
89
+ "_offset": 0,
90
+ "_value": 0,
91
+ "addListener": [MockFunction],
92
+ "interpolate": [MockFunction],
93
+ "removeAllListeners": [MockFunction],
94
+ "removeListener": [MockFunction],
95
+ "setValue": [MockFunction],
96
+ },
97
+ }
105
98
  }
106
- themeHasPrefix={false}
107
- themeVariant="text"
108
99
  />
109
- <View
100
+ <AnimatedView
101
+ accessibilityElementsHidden={false}
102
+ accessibilityLabel="Text input field"
110
103
  style={
111
- [
112
- [
113
- {
114
- "alignItems": "center",
115
- "alignSelf": "stretch",
116
- "flexDirection": "row",
117
- "flexGrow": 2,
118
- "flexShrink": 1,
119
- },
120
- ],
121
- undefined,
122
- ]
104
+ {
105
+ "flex": 1,
106
+ "opacity": {
107
+ "_offset": 0,
108
+ "_value": 0,
109
+ "addListener": [MockFunction],
110
+ "interpolate": [MockFunction],
111
+ "removeAllListeners": [MockFunction],
112
+ "removeListener": [MockFunction],
113
+ "setValue": [MockFunction],
114
+ },
115
+ }
123
116
  }
117
+ testID="input-row-input-wrapper"
124
118
  >
125
119
  <TextInput
126
120
  accessibilityState={
@@ -139,13 +133,12 @@ exports[`first test to ensure configure correctly should render TextInput with p
139
133
  [
140
134
  [
141
135
  {
142
- "alignSelf": "stretch",
143
136
  "flexGrow": 2,
144
137
  "fontFamily": "BeVietnamPro-Regular",
145
138
  "fontSize": 15.692307692307693,
146
139
  "height": undefined,
147
- "marginHorizontal": 7.846153846153847,
148
140
  "maxHeight": 141.23076923076923,
141
+ "minHeight": 23.53846153846154,
149
142
  "paddingVertical": 0,
150
143
  "textAlignVertical": "center",
151
144
  },
@@ -155,62 +148,44 @@ exports[`first test to ensure configure correctly should render TextInput with p
155
148
  "backgroundColor": "#ffffff",
156
149
  "color": "#001f23",
157
150
  },
158
- undefined,
151
+ {},
159
152
  ],
160
153
  ]
161
154
  }
162
- testID="text-input-text-input"
155
+ testID="text-input"
163
156
  themeVariant="text"
164
157
  value="test value"
165
158
  />
166
- </View>
159
+ </AnimatedView>
167
160
  </View>
168
161
  <View
169
162
  style={
170
163
  [
171
164
  [
172
165
  {
173
- "minHeight": 15.692307692307693,
174
- "paddingHorizontal": 15.692307692307693,
175
- "paddingTop": 1.9615384615384617,
166
+ "alignItems": "flex-start",
167
+ "flexDirection": "row",
168
+ "justifyContent": "space-between",
176
169
  },
177
170
  ],
178
171
  undefined,
179
172
  ]
180
173
  }
181
- >
182
- <View
183
- style={
184
- [
185
- [
186
- {
187
- "alignItems": "flex-start",
188
- "flexDirection": "row",
189
- "justifyContent": "space-between",
190
- },
191
- ],
192
- undefined,
193
- ]
194
- }
195
- />
196
- </View>
174
+ />
197
175
  </View>
198
176
  <View
199
- pointerEvents="box-none"
200
- position="bottom"
201
177
  style={
202
178
  [
203
179
  [
204
180
  {
205
- "elevation": 9999,
206
- "flexDirection": "column-reverse",
207
- "paddingHorizontal": 23.53846153846154,
208
- "paddingVertical": 15.692307692307693,
181
+ "alignItems": "center",
182
+ "flexDirection": "row",
183
+ "justifyContent": "flex-end",
209
184
  },
210
185
  ],
211
186
  undefined,
212
187
  ]
213
188
  }
214
189
  />
215
- </View>
190
+ </Pressable>
216
191
  `;
@@ -16,3 +16,18 @@ describe('first test to ensure configure correctly', () => {
16
16
  expect(toJSON()).toMatchSnapshot();
17
17
  });
18
18
  });
19
+
20
+ describe('Export Completeness', () => {
21
+ it('should include all exports from @hero-design/rn', () => {
22
+ // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
23
+ const baseRN = require('@hero-design/rn');
24
+ // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
25
+ const workUIKit = require('../index');
26
+
27
+ const baseExports = Object.keys(baseRN);
28
+
29
+ baseExports.forEach((exportName) => {
30
+ expect(workUIKit).toHaveProperty(exportName);
31
+ });
32
+ });
33
+ });
@@ -0,0 +1,96 @@
1
+ // Mock the entire theme module to avoid import resolution issues
2
+ jest.mock('../theme', () => {
3
+ const mockWorkTheme = {
4
+ colors: { text: 'work-color' },
5
+ fonts: { base: 'work-font' },
6
+ space: { base: 'work-space' },
7
+ __hd__: {
8
+ textInput: {
9
+ colors: {
10
+ text: '#WORK_THEME_COLOR',
11
+ background: '#WORK_BACKGROUND',
12
+ },
13
+ space: { containerPadding: 'WORK_PADDING' },
14
+ fonts: { text: 'WORK_FONT' },
15
+ },
16
+ button: { base: 'button' },
17
+ accordion: { base: 'accordion' },
18
+ },
19
+ };
20
+
21
+ const mockGetTheme = jest.fn(() => mockWorkTheme);
22
+
23
+ return {
24
+ __esModule: true,
25
+ default: mockWorkTheme,
26
+ getTheme: mockGetTheme,
27
+ Theme: {},
28
+ CustomTheme: {},
29
+ ThemeProvider: jest.fn(),
30
+ ThemeSwitcher: jest.fn(),
31
+ useTheme: jest.fn(),
32
+ withTheme: jest.fn(),
33
+ };
34
+ });
35
+
36
+ // Mock the base @hero-design/rn
37
+ jest.mock('@hero-design/rn', () => ({
38
+ getTheme: jest.fn(() => ({
39
+ colors: { text: 'base-color' },
40
+ __hd__: {
41
+ textInput: {
42
+ colors: { text: 'BASE_THEME_COLOR' },
43
+ space: { containerPadding: 'BASE_PADDING' },
44
+ },
45
+ },
46
+ })),
47
+ // Mock styled function
48
+ styled: jest.fn(() => jest.fn(() => 'MockStyledComponent')),
49
+ // Mock Typography component
50
+ Typography: {
51
+ Caption: 'MockCaption',
52
+ },
53
+ // Re-export everything else as is
54
+ Button: 'MockButton',
55
+ TextInput: 'MockTextInput',
56
+ ThemeProvider: 'MockBaseThemeProvider',
57
+ useTheme: jest.fn(),
58
+ }));
59
+
60
+ describe('Theme Export Override', () => {
61
+ beforeEach(() => {
62
+ // Clear module cache to ensure fresh imports
63
+ jest.resetModules();
64
+ });
65
+
66
+ it('should export work-specific theme with overridden textInput from index', () => {
67
+ // Re-import to get the mocked version
68
+ // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
69
+ const { theme: workTheme } = require('../index');
70
+
71
+ // Verify that the theme has the work-specific textInput overrides
72
+ expect(workTheme.__hd__.textInput.colors.text).toBe('#WORK_THEME_COLOR');
73
+ expect(workTheme.__hd__.textInput.colors.background).toBe(
74
+ '#WORK_BACKGROUND'
75
+ );
76
+ expect(workTheme.__hd__.textInput.space.containerPadding).toBe(
77
+ 'WORK_PADDING'
78
+ );
79
+ expect(workTheme.__hd__.textInput.fonts.text).toBe('WORK_FONT');
80
+ });
81
+
82
+ it('should export work-specific getTheme function with overridden textInput', () => {
83
+ // Re-import to get the mocked version
84
+ // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
85
+ const { getTheme: workGetTheme } = require('../index');
86
+
87
+ const workTheme = workGetTheme();
88
+
89
+ // Verify that getTheme returns work-specific textInput theme
90
+ expect(workTheme.__hd__.textInput.colors.text).toBe('#WORK_THEME_COLOR');
91
+ expect(workTheme.__hd__.textInput.space.containerPadding).toBe(
92
+ 'WORK_PADDING'
93
+ );
94
+ expect(workTheme.__hd__.textInput.fonts.text).toBe('WORK_FONT');
95
+ });
96
+ });
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { Icon } from '@hero-design/rn';
3
+ import {
4
+ StyledErrorRow,
5
+ StyledError,
6
+ StyledHelperText,
7
+ } from './StyledTextInput';
8
+
9
+ export interface ErrorOrHelpTextProps {
10
+ /** Error message to display (takes priority) */
11
+ error?: string;
12
+ /** Helper text to display when no error */
13
+ helpText?: string;
14
+ }
15
+
16
+ /**
17
+ * ErrorOrHelpText Component
18
+ *
19
+ * Displays either error messages or help text below the TextInput.
20
+ * Shows error messages with a danger icon when present, otherwise displays help text.
21
+ *
22
+ * Key Features:
23
+ * - Conditional rendering: error takes priority over help text
24
+ * - Error messages include a danger icon for visual clarity
25
+ * - Uses styled components for consistent theming
26
+ * - Includes proper test IDs for testing
27
+ *
28
+ * Rendering Logic:
29
+ * 1. If error exists: Show error message with danger icon
30
+ * 2. If no error but helpText exists: Show help text
31
+ * 3. If neither exists: Render nothing
32
+ *
33
+ * @param props - The component props (see ErrorOrHelpTextProps interface for details)
34
+ */
35
+ const ErrorOrHelpText: React.FC<ErrorOrHelpTextProps> = ({
36
+ error,
37
+ helpText,
38
+ }) => {
39
+ return error ? (
40
+ // Error state: Show error message with danger icon
41
+ <StyledErrorRow>
42
+ <Icon
43
+ testID="input-error-icon"
44
+ icon="circle-info"
45
+ size="xsmall"
46
+ intent="danger"
47
+ />
48
+ <StyledError testID="input-error-message">{error}</StyledError>
49
+ </StyledErrorRow>
50
+ ) : (
51
+ // Help text state: Show help text if provided
52
+ !!helpText && <StyledHelperText>{helpText}</StyledHelperText>
53
+ );
54
+ };
55
+
56
+ ErrorOrHelpText.displayName = 'ErrorOrHelpText';
57
+
58
+ export default React.memo(ErrorOrHelpText);