@hero-design/rn-work-uikit 1.3.1 → 1.5.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/CHANGELOG.md +28 -0
- package/lib/index.js +19933 -735
- package/package.json +3 -2
- package/src/components/DatePicker/__tests__/__snapshots__/index.spec.tsx.snap +3 -3
- package/src/components/FormGroup/__tests__/__snapshots__/index.spec.tsx.snap +903 -0
- package/src/components/FormGroup/__tests__/index.spec.tsx +306 -0
- package/src/components/FormGroup/__tests__/utils.spec.ts +73 -0
- package/src/components/FormGroup/index.tsx +106 -0
- package/src/components/FormGroup/utils.ts +67 -0
- package/src/components/RichTextEditor/EditorEvent.ts +7 -0
- package/src/components/RichTextEditor/EditorToolbar.tsx +216 -0
- package/src/components/RichTextEditor/MentionList.tsx +99 -0
- package/src/components/RichTextEditor/RichTextEditor.tsx +88 -0
- package/src/components/RichTextEditor/RichTextEditorInput.tsx +292 -0
- package/src/components/RichTextEditor/StyledRichTextEditor.tsx +15 -0
- package/src/components/RichTextEditor/StyledToolbar.ts +32 -0
- package/src/components/RichTextEditor/__mocks__/hero-editor.js +3 -0
- package/src/components/RichTextEditor/__mocks__/heroEditorApp.ts +2 -0
- package/src/components/RichTextEditor/__tests__/EditorToolbar.spec.tsx +144 -0
- package/src/components/RichTextEditor/__tests__/MentionList.spec.tsx +105 -0
- package/src/components/RichTextEditor/__tests__/RichTextEditorInput.spec.tsx +136 -0
- package/src/components/RichTextEditor/__tests__/__snapshots__/EditorToolbar.spec.tsx.snap +414 -0
- package/src/components/RichTextEditor/__tests__/__snapshots__/MentionList.spec.tsx.snap +13 -0
- package/src/components/RichTextEditor/constants.ts +9 -0
- package/src/{hero-editor.d.ts → components/RichTextEditor/hero-editor.d.ts} +6 -0
- package/src/components/RichTextEditor/heroEditorApp.ts +3 -0
- package/src/components/RichTextEditor/index.tsx +20 -0
- package/src/components/RichTextEditor/types.ts +87 -0
- package/src/components/RichTextEditor/utils/events.ts +31 -0
- package/src/components/RichTextEditor/utils/rnWebView.tsx +30 -0
- package/src/components/Select/__tests__/__snapshots__/index.spec.tsx.snap +24 -2
- package/src/components/Select/index.tsx +11 -10
- package/src/components/TextInput/Group/__tests__/__snapshots__/index.spec.tsx.snap +3 -3
- package/src/components/TextInput/Group/index.tsx +6 -1
- package/src/components/TextInput/InputComponent.tsx +59 -18
- package/src/components/TextInput/InputRow.tsx +13 -7
- package/src/components/TextInput/StyledTextInput.tsx +3 -3
- package/src/components/TextInput/__tests__/__snapshots__/index.spec.tsx.snap +17 -17
- package/src/components/TextInput/index.tsx +22 -13
- package/src/components/TextInput/types.ts +30 -5
- package/src/index.ts +3 -1
- package/src/utils/hooks.ts +10 -0
- package/stats/1.5.0/rn-work-uikit-stats.html +4844 -0
- package/stats/1.3.1/rn-work-uikit-stats.html +0 -4844
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { within } from '@testing-library/react-native';
|
|
3
|
+
import theme from '../../../theme';
|
|
4
|
+
import TextInput from '../../TextInput';
|
|
5
|
+
import Select from '../../Select';
|
|
6
|
+
import FormGroup from '..';
|
|
7
|
+
import renderWithTheme from '../../../../testUtils/renderWithTheme';
|
|
8
|
+
import { noop } from '../../../utils/functions';
|
|
9
|
+
|
|
10
|
+
describe('FormGroup', () => {
|
|
11
|
+
it('should render', () => {
|
|
12
|
+
const { getByText, getByTestId, toJSON } = renderWithTheme(
|
|
13
|
+
<FormGroup>
|
|
14
|
+
<TextInput label="Text Input 1" value="Text Input 1" required />
|
|
15
|
+
<TextInput
|
|
16
|
+
label="Text Input 2"
|
|
17
|
+
value="Text Input 2"
|
|
18
|
+
error="This is an error"
|
|
19
|
+
testID="text-input-2"
|
|
20
|
+
/>
|
|
21
|
+
<TextInput label="Text Input 3" value="Text Input 3" required />
|
|
22
|
+
</FormGroup>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
expect(toJSON()).toMatchSnapshot('xxx');
|
|
26
|
+
|
|
27
|
+
expect(getByText('Text Input 1')).toBeVisible();
|
|
28
|
+
expect(getByText('Text Input 2', { exact: false })).toBeVisible();
|
|
29
|
+
expect(
|
|
30
|
+
within(getByTestId('text-input-2')).getByText('(Optional)', {
|
|
31
|
+
exact: false,
|
|
32
|
+
})
|
|
33
|
+
).toBeVisible();
|
|
34
|
+
expect(getByText('This is an error')).toBeVisible();
|
|
35
|
+
expect(getByText('Text Input 3')).toBeVisible();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renders with correct border styling', () => {
|
|
39
|
+
const { getByTestId } = renderWithTheme(
|
|
40
|
+
<FormGroup>
|
|
41
|
+
<TextInput value="Text Input 1" testID="text-input-1" />
|
|
42
|
+
<TextInput value="Text Input 2" testID="text-input-2" />
|
|
43
|
+
<TextInput value="Text Input 3" testID="text-input-3" />
|
|
44
|
+
</FormGroup>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(
|
|
48
|
+
within(getByTestId('text-input-1'))
|
|
49
|
+
.getByTestId('text-input-border')
|
|
50
|
+
.props.style.flat()
|
|
51
|
+
).toEqual(
|
|
52
|
+
expect.arrayContaining([
|
|
53
|
+
expect.objectContaining({
|
|
54
|
+
borderBottomLeftRadius: 0,
|
|
55
|
+
borderBottomRightRadius: 0,
|
|
56
|
+
}),
|
|
57
|
+
])
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
expect(
|
|
61
|
+
within(getByTestId('text-input-2'))
|
|
62
|
+
.getByTestId('text-input-border')
|
|
63
|
+
.props.style.flat()
|
|
64
|
+
).toEqual(
|
|
65
|
+
expect.arrayContaining([
|
|
66
|
+
expect.objectContaining({
|
|
67
|
+
borderRadius: 0,
|
|
68
|
+
}),
|
|
69
|
+
])
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
expect(
|
|
73
|
+
within(getByTestId('text-input-3'))
|
|
74
|
+
.getByTestId('text-input-border')
|
|
75
|
+
.props.style.flat()
|
|
76
|
+
).toEqual(
|
|
77
|
+
expect.arrayContaining([
|
|
78
|
+
expect.objectContaining({
|
|
79
|
+
borderTopLeftRadius: 0,
|
|
80
|
+
borderTopRightRadius: 0,
|
|
81
|
+
}),
|
|
82
|
+
])
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('merges with the children styles in correct order', () => {
|
|
87
|
+
const { getByTestId } = renderWithTheme(
|
|
88
|
+
<FormGroup>
|
|
89
|
+
<TextInput
|
|
90
|
+
value="Text Input 1"
|
|
91
|
+
testID="text-input-1"
|
|
92
|
+
textStyle={{
|
|
93
|
+
borderColor: '#ffffff',
|
|
94
|
+
borderWidth: 1,
|
|
95
|
+
}}
|
|
96
|
+
/>
|
|
97
|
+
<TextInput
|
|
98
|
+
value="Text Input 2"
|
|
99
|
+
testID="text-input-2"
|
|
100
|
+
style={{ width: 300 }}
|
|
101
|
+
/>
|
|
102
|
+
</FormGroup>
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Merging text styles
|
|
106
|
+
expect(
|
|
107
|
+
within(getByTestId('text-input-1'))
|
|
108
|
+
.getByTestId('text-input-border')
|
|
109
|
+
.props.style.flat()
|
|
110
|
+
).toEqual(
|
|
111
|
+
expect.arrayContaining([
|
|
112
|
+
expect.objectContaining({
|
|
113
|
+
// Passed style
|
|
114
|
+
borderWidth: 1,
|
|
115
|
+
borderColor: '#ffffff',
|
|
116
|
+
// Injected style
|
|
117
|
+
borderBottomLeftRadius: 0,
|
|
118
|
+
borderBottomRightRadius: 0,
|
|
119
|
+
}),
|
|
120
|
+
])
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Merging container styles
|
|
124
|
+
expect(getByTestId('text-input-2').props.style.flat()).toEqual(
|
|
125
|
+
expect.arrayContaining([
|
|
126
|
+
expect.objectContaining({
|
|
127
|
+
// Passed style
|
|
128
|
+
width: 300,
|
|
129
|
+
// Injected style
|
|
130
|
+
marginTop: -theme.__hd__.textInput.borderWidths.container.normal,
|
|
131
|
+
}),
|
|
132
|
+
])
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(
|
|
136
|
+
within(getByTestId('text-input-2'))
|
|
137
|
+
.getByTestId('text-input-border')
|
|
138
|
+
.props.style.flat()
|
|
139
|
+
).toEqual(
|
|
140
|
+
expect.arrayContaining([
|
|
141
|
+
expect.objectContaining({
|
|
142
|
+
// Injected style
|
|
143
|
+
borderTopLeftRadius: 0,
|
|
144
|
+
borderTopRightRadius: 0,
|
|
145
|
+
}),
|
|
146
|
+
])
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('does not alter the children styles if there is only one child', () => {
|
|
151
|
+
const { getByTestId } = renderWithTheme(
|
|
152
|
+
<FormGroup>
|
|
153
|
+
<TextInput
|
|
154
|
+
value="Text Input 1"
|
|
155
|
+
testID="text-input-1"
|
|
156
|
+
style={{
|
|
157
|
+
marginTop: theme.space.medium,
|
|
158
|
+
}}
|
|
159
|
+
/>
|
|
160
|
+
</FormGroup>
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
// Container style is not injected
|
|
164
|
+
expect(getByTestId('text-input-1').props.style.flat()).toEqual(
|
|
165
|
+
expect.arrayContaining([
|
|
166
|
+
expect.objectContaining({
|
|
167
|
+
// Passed style instead of injected style
|
|
168
|
+
marginTop: theme.space.medium,
|
|
169
|
+
}),
|
|
170
|
+
])
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// Border style is not injected
|
|
174
|
+
const internalBorderStyle =
|
|
175
|
+
getByTestId('text-input-border').props.style.flat();
|
|
176
|
+
const borderKeys = Object.keys(internalBorderStyle).filter((key) =>
|
|
177
|
+
key.startsWith('border')
|
|
178
|
+
);
|
|
179
|
+
expect(borderKeys).toHaveLength(0);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('renders enhanced TextInput/Select components with correct styles', () => {
|
|
183
|
+
const { getByTestId } = renderWithTheme(
|
|
184
|
+
<FormGroup>
|
|
185
|
+
<TextInput
|
|
186
|
+
label="Enhanced Text Input"
|
|
187
|
+
value="Enhanced Text Input"
|
|
188
|
+
testID="enhanced-text-input"
|
|
189
|
+
/>
|
|
190
|
+
<Select
|
|
191
|
+
label="Enhanced Select"
|
|
192
|
+
value="option1"
|
|
193
|
+
testID="enhanced-select"
|
|
194
|
+
onConfirm={noop}
|
|
195
|
+
options={[
|
|
196
|
+
{ text: 'Option 1', value: 'option1' },
|
|
197
|
+
{ text: 'Option 2', value: 'option2' },
|
|
198
|
+
]}
|
|
199
|
+
/>
|
|
200
|
+
</FormGroup>
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Enhanced TextInput should have correct border styling
|
|
204
|
+
expect(
|
|
205
|
+
within(getByTestId('enhanced-text-input'))
|
|
206
|
+
.getByTestId('text-input-border')
|
|
207
|
+
.props.style.flat()
|
|
208
|
+
).toEqual(
|
|
209
|
+
expect.arrayContaining([
|
|
210
|
+
expect.objectContaining({
|
|
211
|
+
borderBottomLeftRadius: 0,
|
|
212
|
+
borderBottomRightRadius: 0,
|
|
213
|
+
}),
|
|
214
|
+
])
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// Enhanced Select should have correct border styling
|
|
218
|
+
expect(
|
|
219
|
+
within(getByTestId('enhanced-select'))
|
|
220
|
+
.getByTestId('text-input-border')
|
|
221
|
+
.props.style.flat()
|
|
222
|
+
).toEqual(
|
|
223
|
+
expect.arrayContaining([
|
|
224
|
+
expect.objectContaining({
|
|
225
|
+
borderTopLeftRadius: 0,
|
|
226
|
+
borderTopRightRadius: 0,
|
|
227
|
+
}),
|
|
228
|
+
])
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
|
|
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>
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// TextInput should have top border radius removed
|
|
266
|
+
expect(
|
|
267
|
+
within(getByTestId('mixed-text-input'))
|
|
268
|
+
.getByTestId('text-input-border')
|
|
269
|
+
.props.style.flat()
|
|
270
|
+
).toEqual(
|
|
271
|
+
expect.arrayContaining([
|
|
272
|
+
expect.objectContaining({
|
|
273
|
+
borderBottomLeftRadius: 0,
|
|
274
|
+
borderBottomRightRadius: 0,
|
|
275
|
+
}),
|
|
276
|
+
])
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
// Select should have no border radius (middle component)
|
|
280
|
+
expect(
|
|
281
|
+
within(getByTestId('mixed-select'))
|
|
282
|
+
.getByTestId('text-input-border')
|
|
283
|
+
.props.style.flat()
|
|
284
|
+
).toEqual(
|
|
285
|
+
expect.arrayContaining([
|
|
286
|
+
expect.objectContaining({
|
|
287
|
+
borderRadius: 0,
|
|
288
|
+
}),
|
|
289
|
+
])
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Select.Multi should have bottom border radius removed
|
|
293
|
+
expect(
|
|
294
|
+
within(getByTestId('mixed-select-multi'))
|
|
295
|
+
.getByTestId('text-input-border')
|
|
296
|
+
.props.style.flat()
|
|
297
|
+
).toEqual(
|
|
298
|
+
expect.arrayContaining([
|
|
299
|
+
expect.objectContaining({
|
|
300
|
+
borderTopLeftRadius: 0,
|
|
301
|
+
borderTopRightRadius: 0,
|
|
302
|
+
}),
|
|
303
|
+
])
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import theme from '../../../theme';
|
|
2
|
+
import { generateBorderStyle, generateMarginStyle } from '../utils';
|
|
3
|
+
|
|
4
|
+
describe('utils', () => {
|
|
5
|
+
describe('generateBorderStyle', () => {
|
|
6
|
+
it('should generate the correct border style for the first child', () => {
|
|
7
|
+
const borderStyle = generateBorderStyle({ index: 0, length: 3 });
|
|
8
|
+
expect(borderStyle).toEqual({
|
|
9
|
+
borderBottomLeftRadius: 0,
|
|
10
|
+
borderBottomRightRadius: 0,
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should generate the correct border style for the last child', () => {
|
|
15
|
+
const borderStyle = generateBorderStyle({ index: 2, length: 3 });
|
|
16
|
+
expect(borderStyle).toEqual({
|
|
17
|
+
borderTopLeftRadius: 0,
|
|
18
|
+
borderTopRightRadius: 0,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should generate the correct border style for the middle child', () => {
|
|
23
|
+
const borderStyle = generateBorderStyle({ index: 1, length: 3 });
|
|
24
|
+
expect(borderStyle).toEqual({
|
|
25
|
+
borderRadius: 0,
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('generateMarginStyle', () => {
|
|
31
|
+
it('should generate the correct margin style for the first child', () => {
|
|
32
|
+
const marginStyle = generateMarginStyle({
|
|
33
|
+
index: 0,
|
|
34
|
+
length: 3,
|
|
35
|
+
theme,
|
|
36
|
+
});
|
|
37
|
+
expect(marginStyle).toEqual({
|
|
38
|
+
marginTop: 0,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should generate the correct margin style for the last child', () => {
|
|
43
|
+
const marginStyle = generateMarginStyle({
|
|
44
|
+
index: 2,
|
|
45
|
+
length: 3,
|
|
46
|
+
theme,
|
|
47
|
+
});
|
|
48
|
+
expect(marginStyle).toEqual({
|
|
49
|
+
marginTop: -theme.__hd__.textInput.borderWidths.container.normal,
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should generate the correct margin style for the middle child', () => {
|
|
54
|
+
const marginStyle = generateMarginStyle({
|
|
55
|
+
index: 1,
|
|
56
|
+
length: 3,
|
|
57
|
+
theme,
|
|
58
|
+
});
|
|
59
|
+
expect(marginStyle).toEqual({
|
|
60
|
+
marginTop: -theme.__hd__.textInput.borderWidths.container.normal,
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should generate the correct margin style for a single child', () => {
|
|
65
|
+
const marginStyle = generateMarginStyle({
|
|
66
|
+
index: 0,
|
|
67
|
+
length: 1,
|
|
68
|
+
theme,
|
|
69
|
+
});
|
|
70
|
+
expect(marginStyle).toEqual({});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React, { ReactElement, ReactNode, useMemo } from 'react';
|
|
2
|
+
import { StyleProp, StyleSheet, ViewProps, ViewStyle } from 'react-native';
|
|
3
|
+
import { Box, useTheme } from '@hero-design/rn';
|
|
4
|
+
import { generateBorderStyle, generateMarginStyle } from './utils';
|
|
5
|
+
|
|
6
|
+
export interface FormGroupProps extends ViewProps {
|
|
7
|
+
/**
|
|
8
|
+
* The children of the FormGroup. In order for the group styling to work,
|
|
9
|
+
* they must be either HD form components (TextInput, Select, Pickers,...) or enhanced HD input components
|
|
10
|
+
* that supports the corresponding interface.
|
|
11
|
+
*
|
|
12
|
+
* Example:
|
|
13
|
+
* const EnhancedTextInput = (props: TextInputProps) => {
|
|
14
|
+
* return <TextInput {...props} />;
|
|
15
|
+
* };
|
|
16
|
+
*
|
|
17
|
+
* <FormGroup>
|
|
18
|
+
* <Select ... />
|
|
19
|
+
* <EnhancedTextInput ... />
|
|
20
|
+
* </FormGroup>
|
|
21
|
+
*/
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* The style of the FormGroup.
|
|
25
|
+
*/
|
|
26
|
+
style?: StyleProp<ViewStyle>;
|
|
27
|
+
/**
|
|
28
|
+
* The testID of the FormGroup.
|
|
29
|
+
*/
|
|
30
|
+
testID?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const FormGroup = ({ children, style, testID, ...props }: FormGroupProps) => {
|
|
34
|
+
const theme = useTheme();
|
|
35
|
+
const childrenArray = React.Children.toArray(children).filter(
|
|
36
|
+
(child): child is ReactElement => React.isValidElement(child)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// If there are multiple children, inject styles to group them together.
|
|
40
|
+
const groupedChildren = useMemo(
|
|
41
|
+
() =>
|
|
42
|
+
childrenArray.map((child, index) => {
|
|
43
|
+
const rawChildStyle = child.props.style;
|
|
44
|
+
const rawChildTextStyle = child.props.textStyle;
|
|
45
|
+
|
|
46
|
+
// Handle array styles by flattening them first
|
|
47
|
+
const childStyle = StyleSheet.flatten(rawChildStyle);
|
|
48
|
+
const childTextStyle = StyleSheet.flatten(rawChildTextStyle);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Merge the child style with the group injected style.
|
|
52
|
+
* Order of precedence:
|
|
53
|
+
* 1. Child style.
|
|
54
|
+
* 2. Group injected style.
|
|
55
|
+
*/
|
|
56
|
+
const mergedStyle = {
|
|
57
|
+
...childStyle,
|
|
58
|
+
...generateMarginStyle({
|
|
59
|
+
index,
|
|
60
|
+
length: childrenArray.length,
|
|
61
|
+
theme,
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Merge the child text style with the group text style.
|
|
67
|
+
* Order of precedence:
|
|
68
|
+
* 1. Group text style through textStyle prop.
|
|
69
|
+
* 2. Child text style.
|
|
70
|
+
* 3. Group injected border style.
|
|
71
|
+
*/
|
|
72
|
+
const mergedTextStyle = {
|
|
73
|
+
...childTextStyle,
|
|
74
|
+
...generateBorderStyle({
|
|
75
|
+
index,
|
|
76
|
+
length: childrenArray.length,
|
|
77
|
+
}),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return React.cloneElement(child, {
|
|
81
|
+
style: mergedStyle,
|
|
82
|
+
textStyle: mergedTextStyle,
|
|
83
|
+
// Internal text input prop to allow for different styling
|
|
84
|
+
groupStyleEnabled: true,
|
|
85
|
+
|
|
86
|
+
// For HD components that uses FormGroup
|
|
87
|
+
inputProps: {
|
|
88
|
+
...child.props.inputProps,
|
|
89
|
+
textStyle: {
|
|
90
|
+
...child.props.inputProps?.textStyle,
|
|
91
|
+
...mergedTextStyle,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}),
|
|
96
|
+
[childrenArray, theme]
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<Box style={style} testID={testID} {...props}>
|
|
101
|
+
{groupedChildren}
|
|
102
|
+
</Box>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export default FormGroup;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { CSSProperties } from 'react';
|
|
2
|
+
import { Theme } from '@hero-design/rn';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates the border style for the TextInputGroup.
|
|
6
|
+
* @param index - The index of the TextInput.
|
|
7
|
+
* @param length - The length of the TextInputGroup.
|
|
8
|
+
* @returns The border style for the TextInputGroup.
|
|
9
|
+
*/
|
|
10
|
+
const generateBorderStyle = ({
|
|
11
|
+
index,
|
|
12
|
+
length,
|
|
13
|
+
}: {
|
|
14
|
+
index: number;
|
|
15
|
+
length: number;
|
|
16
|
+
}): CSSProperties => {
|
|
17
|
+
const isFirst = index === 0;
|
|
18
|
+
const isLast = index === length - 1;
|
|
19
|
+
|
|
20
|
+
if (length === 1) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (isFirst) {
|
|
25
|
+
return {
|
|
26
|
+
borderBottomLeftRadius: 0,
|
|
27
|
+
borderBottomRightRadius: 0,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isLast) {
|
|
32
|
+
return {
|
|
33
|
+
borderTopLeftRadius: 0,
|
|
34
|
+
borderTopRightRadius: 0,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
borderRadius: 0,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const generateMarginStyle = ({
|
|
44
|
+
index,
|
|
45
|
+
length,
|
|
46
|
+
theme,
|
|
47
|
+
}: {
|
|
48
|
+
index: number;
|
|
49
|
+
length: number;
|
|
50
|
+
theme: Theme;
|
|
51
|
+
}) => {
|
|
52
|
+
if (length === 1) {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (index === 0) {
|
|
57
|
+
return {
|
|
58
|
+
marginTop: 0,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
marginTop: -theme.__hd__.textInput.borderWidths.container.normal,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export { generateBorderStyle, generateMarginStyle };
|