@hero-design/rn-work-uikit 1.5.0 → 1.6.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/rn-work-uikit",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -23,7 +23,7 @@
23
23
  "@emotion/native": "^11.9.3",
24
24
  "@emotion/primitives-core": "11.0.0",
25
25
  "@emotion/react": "^11.9.3",
26
- "@hero-design/rn": "^8.103.6",
26
+ "@hero-design/rn": "^8.103.7",
27
27
  "hero-editor": "^1.15.5"
28
28
  },
29
29
  "peerDependencies": {
@@ -4,6 +4,17 @@ exports[`DatePicker renders correctly (snapshot) 1`] = `
4
4
  <TouchableOpacity
5
5
  disabled={false}
6
6
  onPress={[Function]}
7
+ style={
8
+ [
9
+ [
10
+ {},
11
+ ],
12
+ undefined,
13
+ ]
14
+ }
15
+ testID="date-picker-ios-touchable-opacity"
16
+ themeGroupStyleEnabled={false}
17
+ themeHasError={false}
7
18
  >
8
19
  <View
9
20
  pointerEvents="none"
@@ -643,6 +654,17 @@ exports[`Dialog renders DatePickerAndroid when OS is android 1`] = `
643
654
  <TouchableOpacity
644
655
  disabled={false}
645
656
  onPress={[Function]}
657
+ style={
658
+ [
659
+ [
660
+ {},
661
+ ],
662
+ undefined,
663
+ ]
664
+ }
665
+ testID="date-picker-android-touchable-opacity"
666
+ themeGroupStyleEnabled={false}
667
+ themeHasError={false}
646
668
  >
647
669
  <View
648
670
  pointerEvents="none"
@@ -966,6 +988,17 @@ exports[`Dialog renders DatePickerIOS when OS is iOS 1`] = `
966
988
  <TouchableOpacity
967
989
  disabled={false}
968
990
  onPress={[Function]}
991
+ style={
992
+ [
993
+ [
994
+ {},
995
+ ],
996
+ undefined,
997
+ ]
998
+ }
999
+ testID="date-picker-ios-touchable-opacity"
1000
+ themeGroupStyleEnabled={false}
1001
+ themeHasError={false}
969
1002
  >
970
1003
  <View
971
1004
  pointerEvents="none"
@@ -3,6 +3,9 @@ import { within } from '@testing-library/react-native';
3
3
  import theme from '../../../theme';
4
4
  import TextInput from '../../TextInput';
5
5
  import Select from '../../Select';
6
+ import DatePicker from '../../DatePicker';
7
+ import TimePicker from '../../TimePicker';
8
+ import RichTextEditor from '../../RichTextEditor';
6
9
  import FormGroup from '..';
7
10
  import renderWithTheme from '../../../../testUtils/renderWithTheme';
8
11
  import { noop } from '../../../utils/functions';
@@ -179,30 +182,63 @@ describe('FormGroup', () => {
179
182
  expect(borderKeys).toHaveLength(0);
180
183
  });
181
184
 
182
- it('renders enhanced TextInput/Select components with correct styles', () => {
185
+ it('renders all component types (TextInput, Select, Select.Multi, DatePicker, TimePicker, RichTextEditor) with correct styles', () => {
183
186
  const { getByTestId } = renderWithTheme(
184
187
  <FormGroup>
185
188
  <TextInput
186
- label="Enhanced Text Input"
187
- value="Enhanced Text Input"
188
- testID="enhanced-text-input"
189
+ label="Text Input"
190
+ value="Text Input"
191
+ testID="mixed-text-input"
192
+ />
193
+ <DatePicker
194
+ label="Date Picker"
195
+ value={new Date('1995-12-17T00:00:00.000Z')}
196
+ testID="mixed-date-picker"
197
+ onChange={noop}
198
+ confirmLabel="Confirm"
199
+ />
200
+ <TimePicker
201
+ label="Time Picker"
202
+ value={new Date('December 17, 1995 03:24:00')}
203
+ testID="mixed-time-picker"
204
+ onChange={noop}
205
+ confirmLabel="Confirm"
206
+ />
207
+ <RichTextEditor
208
+ label="Rich Text Editor"
209
+ value={[{ type: 'paragraph', children: [{ text: 'Content' }] }]}
210
+ onChange={noop}
211
+ name="mixed-rich-text-editor"
212
+ testID="mixed-rich-text-editor"
189
213
  />
190
214
  <Select
191
- label="Enhanced Select"
215
+ label="Single Select"
192
216
  value="option1"
193
- testID="enhanced-select"
217
+ testID="mixed-select"
218
+ onConfirm={noop}
219
+ options={[
220
+ { text: 'Option 1', value: 'option1' },
221
+ { text: 'Option 2', value: 'option2' },
222
+ ]}
223
+ />
224
+ <Select.Multi
225
+ label="Multi Select"
226
+ value={['option1', 'option2']}
227
+ testID="mixed-select-multi"
194
228
  onConfirm={noop}
229
+ footerLabel="Confirm"
195
230
  options={[
196
231
  { text: 'Option 1', value: 'option1' },
197
232
  { text: 'Option 2', value: 'option2' },
233
+ { text: 'Option 3', value: 'option3' },
198
234
  ]}
199
235
  />
200
236
  </FormGroup>
201
237
  );
202
238
 
203
- // Enhanced TextInput should have correct border styling
239
+ // TextInput should have top border radius removed (first component)
204
240
  expect(
205
- within(getByTestId('enhanced-text-input'))
241
+ within(getByTestId('mixed-text-input'))
206
242
  .getByTestId('text-input-border')
207
243
  .props.style.flat()
208
244
  ).toEqual(
@@ -214,64 +250,41 @@ describe('FormGroup', () => {
214
250
  ])
215
251
  );
216
252
 
217
- // Enhanced Select should have correct border styling
253
+ // DatePicker should have no border radius (middle component)
218
254
  expect(
219
- within(getByTestId('enhanced-select'))
255
+ within(getByTestId('mixed-date-picker'))
220
256
  .getByTestId('text-input-border')
221
257
  .props.style.flat()
222
258
  ).toEqual(
223
259
  expect.arrayContaining([
224
260
  expect.objectContaining({
225
- borderTopLeftRadius: 0,
226
- borderTopRightRadius: 0,
261
+ borderRadius: 0,
227
262
  }),
228
263
  ])
229
264
  );
230
- });
231
265
 
232
- it('renders mixed components (TextInput, Select, Select.Multi) with correct styles', () => {
233
- const { getByTestId } = renderWithTheme(
234
- <FormGroup>
235
- <TextInput
236
- label="Text Input"
237
- value="Text Input"
238
- testID="mixed-text-input"
239
- />
240
- <Select
241
- label="Single Select"
242
- value="option1"
243
- testID="mixed-select"
244
- onConfirm={noop}
245
- options={[
246
- { text: 'Option 1', value: 'option1' },
247
- { text: 'Option 2', value: 'option2' },
248
- ]}
249
- />
250
- <Select.Multi
251
- label="Multi Select"
252
- value={['option1', 'option2']}
253
- testID="mixed-select-multi"
254
- onConfirm={noop}
255
- footerLabel="Confirm"
256
- options={[
257
- { text: 'Option 1', value: 'option1' },
258
- { text: 'Option 2', value: 'option2' },
259
- { text: 'Option 3', value: 'option3' },
260
- ]}
261
- />
262
- </FormGroup>
266
+ // TimePicker should have no border radius (middle component)
267
+ expect(
268
+ within(getByTestId('mixed-time-picker'))
269
+ .getByTestId('text-input-border')
270
+ .props.style.flat()
271
+ ).toEqual(
272
+ expect.arrayContaining([
273
+ expect.objectContaining({
274
+ borderRadius: 0,
275
+ }),
276
+ ])
263
277
  );
264
278
 
265
- // TextInput should have top border radius removed
279
+ // RichTextEditor should have no border radius (middle component)
266
280
  expect(
267
- within(getByTestId('mixed-text-input'))
281
+ within(getByTestId('mixed-rich-text-editor'))
268
282
  .getByTestId('text-input-border')
269
283
  .props.style.flat()
270
284
  ).toEqual(
271
285
  expect.arrayContaining([
272
286
  expect.objectContaining({
273
- borderBottomLeftRadius: 0,
274
- borderBottomRightRadius: 0,
287
+ borderRadius: 0,
275
288
  }),
276
289
  ])
277
290
  );
@@ -289,7 +302,7 @@ describe('FormGroup', () => {
289
302
  ])
290
303
  );
291
304
 
292
- // Select.Multi should have bottom border radius removed
305
+ // Select.Multi should have bottom border radius removed (last component)
293
306
  expect(
294
307
  within(getByTestId('mixed-select-multi'))
295
308
  .getByTestId('text-input-border')
@@ -1,12 +1,15 @@
1
+ import type { ComponentType, ReactElement } from 'react';
1
2
  import React, { useMemo } from 'react';
2
- import type { ComponentType, ReactElement, Ref } from 'react';
3
- import type { StyleProp, ViewStyle } from 'react-native';
4
3
 
5
4
  import { plainSerializer } from 'hero-editor';
6
- import TextInput from '../TextInput';
5
+ import { InternalTextInput } from '../TextInput';
7
6
  import RichTextEditorInput from './RichTextEditorInput';
8
- import type { EditorValue, TextUnit } from './types';
9
7
  import { StyledWrapper } from './StyledRichTextEditor';
8
+ import type {
9
+ EditorValue,
10
+ InternalRichTextEditorProps,
11
+ TextUnit,
12
+ } from './types';
10
13
 
11
14
  export interface RichTextEditorRef {
12
15
  requestBlur: VoidFunction;
@@ -15,22 +18,6 @@ export interface RichTextEditorRef {
15
18
  setReadOnly: (readOnly: boolean) => void;
16
19
  }
17
20
 
18
- export interface RichTextEditorProps {
19
- autoFocus?: boolean;
20
- error?: string;
21
- value?: EditorValue;
22
- name: string;
23
- onChange: (data: EditorValue) => void;
24
- onCursorChange?: (params: { position: { top: number } }) => void;
25
- placeholder?: string;
26
- style?: StyleProp<ViewStyle>;
27
- label: string;
28
- helpText?: string;
29
- required?: boolean;
30
- testID?: string;
31
- forwardedRef?: Ref<RichTextEditorRef>;
32
- }
33
-
34
21
  const defaultValue: EditorValue = [
35
22
  {
36
23
  type: 'paragraph',
@@ -38,7 +25,7 @@ const defaultValue: EditorValue = [
38
25
  },
39
26
  ];
40
27
 
41
- const RichTextEditor: ComponentType<RichTextEditorProps> = ({
28
+ const RichTextEditor: ComponentType<InternalRichTextEditorProps> = ({
42
29
  autoFocus = true,
43
30
  name,
44
31
  placeholder = '',
@@ -46,18 +33,20 @@ const RichTextEditor: ComponentType<RichTextEditorProps> = ({
46
33
  onCursorChange,
47
34
  error = '',
48
35
  style = {},
36
+ textStyle,
49
37
  label,
50
38
  helpText,
51
39
  required,
52
40
  testID,
53
41
  forwardedRef,
54
42
  value = defaultValue,
55
- }: RichTextEditorProps): ReactElement => {
43
+ groupStyleEnabled = false,
44
+ }: InternalRichTextEditorProps): ReactElement => {
56
45
  const plain = useMemo(() => plainSerializer(value), [value]);
57
46
 
58
47
  return (
59
- <StyledWrapper>
60
- <TextInput
48
+ <StyledWrapper groupStyleEnabled={groupStyleEnabled}>
49
+ <InternalTextInput
61
50
  autoFocus={autoFocus}
62
51
  error={error}
63
52
  label={label}
@@ -66,7 +55,9 @@ const RichTextEditor: ComponentType<RichTextEditorProps> = ({
66
55
  placeholder={placeholder}
67
56
  value={plain}
68
57
  style={style}
58
+ textStyle={textStyle}
69
59
  helpText={helpText}
60
+ groupStyleEnabled={groupStyleEnabled}
70
61
  renderInputValue={(inputProps, ref) => (
71
62
  <RichTextEditorInput
72
63
  {...inputProps}
@@ -2,9 +2,15 @@ import { styled } from '@hero-design/rn';
2
2
  import { View } from 'react-native';
3
3
  import { WebView } from 'react-native-webview';
4
4
 
5
- export const StyledWrapper = styled(View)(({ theme }) => ({
6
- marginBottom: theme.__hd__.richTextEditor.space.wrapperMarginBottom,
7
- }));
5
+ export const StyledWrapper = styled(View)<{ groupStyleEnabled?: boolean }>(
6
+ ({ theme, groupStyleEnabled }) => ({
7
+ ...(groupStyleEnabled
8
+ ? {}
9
+ : {
10
+ marginBottom: theme.__hd__.richTextEditor.space.wrapperMarginBottom,
11
+ }),
12
+ })
13
+ );
8
14
 
9
15
  export const StyledWebView = styled(WebView)(({ theme }) => ({
10
16
  minHeight: theme.__hd__.richTextEditor.sizes.editorMinHeight,
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+ import RichTextEditor from '../RichTextEditor';
3
+ import renderWithTheme from '../../../../testUtils/renderWithTheme';
4
+
5
+ const initialValue = [
6
+ { type: 'paragraph', children: [{ text: 'Hello world' }] },
7
+ ];
8
+
9
+ describe('RichTextEditor zIndex', () => {
10
+ it('applies correct zIndex logic when groupStyleEnabled is true', () => {
11
+ const { getByTestId } = renderWithTheme(
12
+ <RichTextEditor
13
+ label="Test Editor"
14
+ value={initialValue}
15
+ onChange={jest.fn()}
16
+ name="test-editor"
17
+ testID="rich-text-editor"
18
+ groupStyleEnabled
19
+ />
20
+ );
21
+
22
+ const container = getByTestId('rich-text-editor');
23
+
24
+ // Initially should have zIndex 0 (normal state)
25
+ expect(container.props.style.flat()).toEqual(
26
+ expect.arrayContaining([
27
+ expect.objectContaining({
28
+ zIndex: 0,
29
+ }),
30
+ ])
31
+ );
32
+ });
33
+
34
+ it('applies correct zIndex when has error', () => {
35
+ const { getByTestId } = renderWithTheme(
36
+ <RichTextEditor
37
+ label="Test Editor"
38
+ value={initialValue}
39
+ onChange={jest.fn()}
40
+ name="test-editor"
41
+ testID="rich-text-editor"
42
+ error="This is an error"
43
+ groupStyleEnabled
44
+ />
45
+ );
46
+
47
+ const container = getByTestId('rich-text-editor');
48
+
49
+ // Should have zIndex 1 when has error (medium priority)
50
+ expect(container.props.style.flat()).toEqual(
51
+ expect.arrayContaining([
52
+ expect.objectContaining({
53
+ zIndex: 1,
54
+ }),
55
+ ])
56
+ );
57
+ });
58
+
59
+ it('does not apply zIndex when groupStyleEnabled is false', () => {
60
+ const { getByTestId } = renderWithTheme(
61
+ <RichTextEditor
62
+ label="Test Editor"
63
+ value={initialValue}
64
+ onChange={jest.fn()}
65
+ name="test-editor"
66
+ testID="rich-text-editor"
67
+ groupStyleEnabled={false}
68
+ error="This is an error"
69
+ />
70
+ );
71
+
72
+ const container = getByTestId('rich-text-editor');
73
+
74
+ // Should not have zIndex applied when groupStyleEnabled is false
75
+ const containerStyle = container.props.style.flat();
76
+ const hasZIndex = containerStyle.some(
77
+ (style: Record<string, unknown>) => style && style.zIndex !== undefined
78
+ );
79
+ expect(hasZIndex).toBe(false);
80
+ });
81
+ });
@@ -1,9 +1,7 @@
1
1
  import React, { forwardRef } from 'react';
2
- import RichTextEditor, {
3
- RichTextEditorProps,
4
- RichTextEditorRef,
5
- } from './RichTextEditor';
2
+ import RichTextEditor, { RichTextEditorRef } from './RichTextEditor';
6
3
  import MentionList from './MentionList';
4
+ import type { RichTextEditorProps } from './types';
7
5
 
8
6
  import Toolbar from './EditorToolbar';
9
7
 
@@ -1,4 +1,4 @@
1
- import type { StyleProp, ViewStyle } from 'react-native';
1
+ import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
2
2
 
3
3
  import { Ref } from 'react';
4
4
  import type { TextInputProps } from '../TextInput';
@@ -83,5 +83,16 @@ export interface RichTextEditorProps {
83
83
  }
84
84
 
85
85
  export interface InternalRichTextEditorProps extends RichTextEditorProps {
86
+ /**
87
+ * Input text style
88
+ */
89
+ textStyle?: StyleProp<TextStyle>;
90
+ /**
91
+ * Whether the component is used within a FormGroup
92
+ */
93
+ groupStyleEnabled?: boolean;
94
+ /**
95
+ * Custom TextInput component to be used instead of the default one
96
+ */
86
97
  TextInputComponent?: React.ComponentType<TextInputProps>;
87
98
  }
@@ -28,7 +28,6 @@ import type {
28
28
  TextInputProps,
29
29
  TextInputRef,
30
30
  } from './types';
31
- import Group from './Group';
32
31
 
33
32
  export type {
34
33
  TextInputHandles,
@@ -356,6 +355,4 @@ const TextInput = React.forwardRef<TextInputHandles, TextInputProps>(
356
355
 
357
356
  TextInput.displayName = 'TextInput';
358
357
 
359
- export default Object.assign(TextInput, {
360
- Group,
361
- });
358
+ export default TextInput;