@hero-design/rn-work-uikit 1.3.0 → 1.4.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 +23 -0
- package/lib/index.js +48742 -29649
- package/package.json +5 -4
- package/src/__tests__/index-export.spec.ts +64 -0
- package/src/components/DatePicker/__tests__/__snapshots__/index.spec.tsx.snap +1602 -0
- package/src/components/DatePicker/__tests__/index.spec.tsx +56 -0
- package/src/components/DatePicker/index.tsx +12 -0
- package/src/components/FormGroup/__tests__/__snapshots__/index.spec.tsx.snap +880 -0
- package/src/components/FormGroup/__tests__/index.spec.tsx +179 -0
- package/src/components/FormGroup/__tests__/utils.spec.ts +73 -0
- package/src/components/FormGroup/index.tsx +97 -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 +286 -0
- package/src/components/RichTextEditor/StyledRichTextEditor.tsx +18 -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 +1293 -0
- package/src/components/Select/__tests__/index.spec.tsx +43 -0
- package/src/components/TextInput/Group/__tests__/__snapshots__/index.spec.tsx.snap +0 -3
- package/src/components/TextInput/InputComponent.tsx +59 -18
- package/src/components/TextInput/InputRow.tsx +13 -7
- package/src/components/TextInput/StyledTextInput.tsx +0 -1
- package/src/components/TextInput/__tests__/__snapshots__/index.spec.tsx.snap +0 -17
- package/src/components/TextInput/index.tsx +20 -11
- package/src/components/TextInput/types.ts +29 -4
- package/src/components/TimePicker/__tests__/index.spec.tsx +34 -0
- package/src/components/TimePicker/index.tsx +12 -0
- package/src/index.ts +4 -1
- package/src/utils/functions.ts +2 -0
- package/stats/1.3.0/rn-work-uikit-stats.html +1 -1
- package/stats/1.4.0/rn-work-uikit-stats.html +4844 -0
- package/src/__tests__/theme-export-override.spec.ts +0 -96
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Select from '..';
|
|
3
|
+
import renderWithTheme from '../../../../testUtils/renderWithTheme';
|
|
4
|
+
import { noop } from '../../../utils/functions';
|
|
5
|
+
|
|
6
|
+
const options = [
|
|
7
|
+
{ text: 'Monday', value: 'mon' },
|
|
8
|
+
{ text: 'Tuesday', value: 'tue' },
|
|
9
|
+
{ text: 'Wednesday', value: 'wed' },
|
|
10
|
+
{ text: 'Thursday', value: 'thu' },
|
|
11
|
+
{ text: 'Friday', value: 'fri' },
|
|
12
|
+
{ text: 'Saturday', value: 'sat' },
|
|
13
|
+
{ text: 'Sunday', value: 'sun', disabled: true },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
describe('Select', () => {
|
|
17
|
+
it('renders correctly (snapshot)', () => {
|
|
18
|
+
const { toJSON } = renderWithTheme(
|
|
19
|
+
<Select
|
|
20
|
+
value="mon"
|
|
21
|
+
onConfirm={noop}
|
|
22
|
+
options={options}
|
|
23
|
+
label="Select Label"
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
expect(toJSON()).toMatchSnapshot();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('MultiSelect', () => {
|
|
31
|
+
it('renders correctly (snapshot)', () => {
|
|
32
|
+
const { toJSON } = renderWithTheme(
|
|
33
|
+
<Select.Multi
|
|
34
|
+
value={['tue', 'wed']}
|
|
35
|
+
onConfirm={noop}
|
|
36
|
+
options={options}
|
|
37
|
+
label="Select Label"
|
|
38
|
+
footerLabel="Confirm"
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
expect(toJSON()).toMatchSnapshot();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -91,7 +91,6 @@ exports[`TextInputGroup should render: xxx 1`] = `
|
|
|
91
91
|
{
|
|
92
92
|
"alignItems": "center",
|
|
93
93
|
"backgroundColor": "transparent",
|
|
94
|
-
"flex": 1,
|
|
95
94
|
"flexDirection": "row",
|
|
96
95
|
},
|
|
97
96
|
],
|
|
@@ -344,7 +343,6 @@ exports[`TextInputGroup should render: xxx 1`] = `
|
|
|
344
343
|
{
|
|
345
344
|
"alignItems": "center",
|
|
346
345
|
"backgroundColor": "transparent",
|
|
347
|
-
"flex": 1,
|
|
348
346
|
"flexDirection": "row",
|
|
349
347
|
},
|
|
350
348
|
],
|
|
@@ -706,7 +704,6 @@ exports[`TextInputGroup should render: xxx 1`] = `
|
|
|
706
704
|
{
|
|
707
705
|
"alignItems": "center",
|
|
708
706
|
"backgroundColor": "transparent",
|
|
709
|
-
"flex": 1,
|
|
710
707
|
"flexDirection": "row",
|
|
711
708
|
},
|
|
712
709
|
],
|
|
@@ -1,9 +1,52 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import React, { useImperativeHandle, useRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
TextInput as RNTextInput,
|
|
4
|
+
TextInputProps as RNTextInputProps,
|
|
5
|
+
} from 'react-native';
|
|
4
6
|
import { StyledTextInput } from './StyledTextInput';
|
|
5
7
|
import { useTheme } from '../../theme';
|
|
6
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
NativeTextInputProps,
|
|
10
|
+
TextInputRef,
|
|
11
|
+
TextInputVariant,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
interface DefaultTextInputProps {
|
|
15
|
+
variant: TextInputVariant;
|
|
16
|
+
/** Native TextInput props */
|
|
17
|
+
nativeInputProps: NativeTextInputProps;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DefaultTextInput = React.forwardRef<TextInputRef, DefaultTextInputProps>(
|
|
21
|
+
({ variant, nativeInputProps }, ref) => {
|
|
22
|
+
const theme = useTheme();
|
|
23
|
+
const inputRef = useRef<RNTextInput>(null);
|
|
24
|
+
useImperativeHandle(
|
|
25
|
+
ref,
|
|
26
|
+
() => ({
|
|
27
|
+
focus: () => inputRef.current?.focus(),
|
|
28
|
+
blur: () => inputRef.current?.blur(),
|
|
29
|
+
clear: () => inputRef.current?.clear(),
|
|
30
|
+
isFocused: () => inputRef.current?.isFocused() ?? false,
|
|
31
|
+
setNativeProps: (props: RNTextInputProps) => {
|
|
32
|
+
inputRef.current?.setNativeProps(props);
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
[]
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Default styled input with theme and variant support
|
|
39
|
+
return (
|
|
40
|
+
<StyledTextInput
|
|
41
|
+
{...nativeInputProps}
|
|
42
|
+
themeVariant={variant}
|
|
43
|
+
multiline={variant === 'textarea' || nativeInputProps.multiline}
|
|
44
|
+
ref={inputRef}
|
|
45
|
+
placeholderTextColor={theme.__hd__.textInput.colors.placeholder}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
);
|
|
7
50
|
|
|
8
51
|
export interface InputComponentProps {
|
|
9
52
|
/** Input type ('text' or 'textarea') */
|
|
@@ -11,7 +54,10 @@ export interface InputComponentProps {
|
|
|
11
54
|
/** All props passed to the underlying TextInput */
|
|
12
55
|
nativeInputProps: NativeTextInputProps;
|
|
13
56
|
/** Optional custom input renderer function */
|
|
14
|
-
renderInputValue?: (
|
|
57
|
+
renderInputValue?: (
|
|
58
|
+
inputProps: NativeTextInputProps,
|
|
59
|
+
ref?: React.ForwardedRef<TextInputRef>
|
|
60
|
+
) => React.ReactNode;
|
|
15
61
|
}
|
|
16
62
|
|
|
17
63
|
/**
|
|
@@ -36,21 +82,16 @@ export interface InputComponentProps {
|
|
|
36
82
|
*
|
|
37
83
|
* @param props - The component props (see InputComponentProps interface for details)
|
|
38
84
|
*/
|
|
39
|
-
const InputComponent = React.forwardRef<
|
|
85
|
+
const InputComponent = React.forwardRef<TextInputRef, InputComponentProps>(
|
|
40
86
|
({ variant, nativeInputProps, renderInputValue }, ref) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<StyledTextInput
|
|
49
|
-
{...nativeInputProps}
|
|
50
|
-
themeVariant={variant}
|
|
51
|
-
multiline={variant === 'textarea' || nativeInputProps.multiline}
|
|
87
|
+
if (renderInputValue) {
|
|
88
|
+
return <>{renderInputValue({ ...nativeInputProps }, ref)}</>;
|
|
89
|
+
}
|
|
90
|
+
return (
|
|
91
|
+
<DefaultTextInput
|
|
92
|
+
variant={variant}
|
|
93
|
+
nativeInputProps={nativeInputProps}
|
|
52
94
|
ref={ref}
|
|
53
|
-
placeholderTextColor={theme.__hd__.textInput.colors.placeholder}
|
|
54
95
|
/>
|
|
55
96
|
);
|
|
56
97
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { View } from 'react-native';
|
|
3
3
|
import type {
|
|
4
4
|
NativeSyntheticEvent,
|
|
5
|
-
TextInputProps as NativeTextInputProps,
|
|
6
5
|
TextInputFocusEventData,
|
|
7
6
|
} from 'react-native';
|
|
8
7
|
import { IconName } from '@hero-design/rn';
|
|
@@ -10,7 +9,11 @@ import { StyledInputRow } from './StyledTextInput';
|
|
|
10
9
|
import PrefixComponent from './PrefixComponent';
|
|
11
10
|
import InputComponent from './InputComponent';
|
|
12
11
|
import type { State } from './StyledTextInput';
|
|
13
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
NativeTextInputProps,
|
|
14
|
+
TextInputRef,
|
|
15
|
+
TextInputVariant,
|
|
16
|
+
} from './types';
|
|
14
17
|
|
|
15
18
|
interface InputRowProps {
|
|
16
19
|
/** Current state of the input (focused, error, disabled, etc.) */
|
|
@@ -22,7 +25,10 @@ interface InputRowProps {
|
|
|
22
25
|
/** Native TextInput props passed to the input component */
|
|
23
26
|
nativeInputProps: NativeTextInputProps;
|
|
24
27
|
/** Optional custom render function for input value */
|
|
25
|
-
renderInputValue?: (
|
|
28
|
+
renderInputValue?: (
|
|
29
|
+
inputProps: NativeTextInputProps,
|
|
30
|
+
ref?: React.ForwardedRef<TextInputRef>
|
|
31
|
+
) => React.ReactNode;
|
|
26
32
|
/** Whether the input value is empty */
|
|
27
33
|
isEmptyValue: boolean;
|
|
28
34
|
/** Whether the input should be visible when not focused and empty. Defaults to true. */
|
|
@@ -52,7 +58,7 @@ interface InputRowProps {
|
|
|
52
58
|
* 1. Conditionally visible prefix component (icon or custom element)
|
|
53
59
|
* 2. Conditionally visible input component (TextInput or custom rendered input)
|
|
54
60
|
*/
|
|
55
|
-
const InputRow = React.forwardRef<
|
|
61
|
+
const InputRow = React.forwardRef<TextInputRef, InputRowProps>(
|
|
56
62
|
(
|
|
57
63
|
{
|
|
58
64
|
state,
|
|
@@ -75,7 +81,7 @@ const InputRow = React.forwardRef<RNTextInput, InputRowProps>(
|
|
|
75
81
|
|
|
76
82
|
// Simplified callback functions (removed unnecessary memoization for simple cases)
|
|
77
83
|
const handleFocus = useCallback(
|
|
78
|
-
(event
|
|
84
|
+
(event?: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
79
85
|
setIsFocused(true);
|
|
80
86
|
nativeInputProps.onFocus?.(event);
|
|
81
87
|
},
|
|
@@ -83,7 +89,7 @@ const InputRow = React.forwardRef<RNTextInput, InputRowProps>(
|
|
|
83
89
|
);
|
|
84
90
|
|
|
85
91
|
const handleBlur = useCallback(
|
|
86
|
-
(event
|
|
92
|
+
(event?: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
87
93
|
setIsFocused(false);
|
|
88
94
|
nativeInputProps.onBlur?.(event);
|
|
89
95
|
},
|
|
@@ -42,7 +42,6 @@ const StyledFloatingLabelContainer = styled(Animated.View)<{
|
|
|
42
42
|
themeVariant: Variant;
|
|
43
43
|
}>(({ themeVariant }) => ({
|
|
44
44
|
flexDirection: 'row',
|
|
45
|
-
flex: 1,
|
|
46
45
|
alignItems: themeVariant === 'text' ? 'center' : 'flex-start',
|
|
47
46
|
backgroundColor: 'transparent',
|
|
48
47
|
}));
|
|
@@ -75,7 +75,6 @@ exports[`TextInput when user applies custom styling should respect user-provided
|
|
|
75
75
|
{
|
|
76
76
|
"alignItems": "center",
|
|
77
77
|
"backgroundColor": "transparent",
|
|
78
|
-
"flex": 1,
|
|
79
78
|
"flexDirection": "row",
|
|
80
79
|
},
|
|
81
80
|
],
|
|
@@ -432,7 +431,6 @@ exports[`TextInput when user chooses textarea variant should provide multiline t
|
|
|
432
431
|
{
|
|
433
432
|
"alignItems": "flex-start",
|
|
434
433
|
"backgroundColor": "transparent",
|
|
435
|
-
"flex": 1,
|
|
436
434
|
"flexDirection": "row",
|
|
437
435
|
},
|
|
438
436
|
],
|
|
@@ -712,7 +710,6 @@ exports[`TextInput when user encounters a disabled field should display content
|
|
|
712
710
|
{
|
|
713
711
|
"alignItems": "center",
|
|
714
712
|
"backgroundColor": "transparent",
|
|
715
|
-
"flex": 1,
|
|
716
713
|
"flexDirection": "row",
|
|
717
714
|
},
|
|
718
715
|
],
|
|
@@ -991,7 +988,6 @@ exports[`TextInput when user encounters a read-only field should display content
|
|
|
991
988
|
{
|
|
992
989
|
"alignItems": "center",
|
|
993
990
|
"backgroundColor": "transparent",
|
|
994
|
-
"flex": 1,
|
|
995
991
|
"flexDirection": "row",
|
|
996
992
|
},
|
|
997
993
|
],
|
|
@@ -1354,7 +1350,6 @@ exports[`TextInput when user has entered text should show the input content and
|
|
|
1354
1350
|
{
|
|
1355
1351
|
"alignItems": "center",
|
|
1356
1352
|
"backgroundColor": "transparent",
|
|
1357
|
-
"flex": 1,
|
|
1358
1353
|
"flexDirection": "row",
|
|
1359
1354
|
},
|
|
1360
1355
|
],
|
|
@@ -1717,7 +1712,6 @@ exports[`TextInput when user interacts with placeholder text starting from empty
|
|
|
1717
1712
|
{
|
|
1718
1713
|
"alignItems": "center",
|
|
1719
1714
|
"backgroundColor": "transparent",
|
|
1720
|
-
"flex": 1,
|
|
1721
1715
|
"flexDirection": "row",
|
|
1722
1716
|
},
|
|
1723
1717
|
],
|
|
@@ -2037,7 +2031,6 @@ exports[`TextInput when user needs programmatic control should provide ref metho
|
|
|
2037
2031
|
{
|
|
2038
2032
|
"alignItems": "center",
|
|
2039
2033
|
"backgroundColor": "transparent",
|
|
2040
|
-
"flex": 1,
|
|
2041
2034
|
"flexDirection": "row",
|
|
2042
2035
|
},
|
|
2043
2036
|
],
|
|
@@ -2316,7 +2309,6 @@ exports[`TextInput when user provides default values starting with pre-filled co
|
|
|
2316
2309
|
{
|
|
2317
2310
|
"alignItems": "center",
|
|
2318
2311
|
"backgroundColor": "transparent",
|
|
2319
|
-
"flex": 1,
|
|
2320
2312
|
"flexDirection": "row",
|
|
2321
2313
|
},
|
|
2322
2314
|
],
|
|
@@ -2672,7 +2664,6 @@ exports[`TextInput when user provides default values when both default and contr
|
|
|
2672
2664
|
{
|
|
2673
2665
|
"alignItems": "center",
|
|
2674
2666
|
"backgroundColor": "transparent",
|
|
2675
|
-
"flex": 1,
|
|
2676
2667
|
"flexDirection": "row",
|
|
2677
2668
|
},
|
|
2678
2669
|
],
|
|
@@ -3029,7 +3020,6 @@ exports[`TextInput when user sees a loading state should show loading indicator
|
|
|
3029
3020
|
{
|
|
3030
3021
|
"alignItems": "center",
|
|
3031
3022
|
"backgroundColor": "transparent",
|
|
3032
|
-
"flex": 1,
|
|
3033
3023
|
"flexDirection": "row",
|
|
3034
3024
|
},
|
|
3035
3025
|
],
|
|
@@ -3411,7 +3401,6 @@ exports[`TextInput when user sees a required field should indicate the field is
|
|
|
3411
3401
|
{
|
|
3412
3402
|
"alignItems": "center",
|
|
3413
3403
|
"backgroundColor": "transparent",
|
|
3414
|
-
"flex": 1,
|
|
3415
3404
|
"flexDirection": "row",
|
|
3416
3405
|
},
|
|
3417
3406
|
],
|
|
@@ -3744,7 +3733,6 @@ exports[`TextInput when user sees a textarea with character count should display
|
|
|
3744
3733
|
{
|
|
3745
3734
|
"alignItems": "flex-start",
|
|
3746
3735
|
"backgroundColor": "transparent",
|
|
3747
|
-
"flex": 1,
|
|
3748
3736
|
"flexDirection": "row",
|
|
3749
3737
|
},
|
|
3750
3738
|
],
|
|
@@ -4144,7 +4132,6 @@ exports[`TextInput when user sees a textarea with character count should hide ch
|
|
|
4144
4132
|
{
|
|
4145
4133
|
"alignItems": "flex-start",
|
|
4146
4134
|
"backgroundColor": "transparent",
|
|
4147
|
-
"flex": 1,
|
|
4148
4135
|
"flexDirection": "row",
|
|
4149
4136
|
},
|
|
4150
4137
|
],
|
|
@@ -4511,7 +4498,6 @@ exports[`TextInput when user sees an empty input field should display label and
|
|
|
4511
4498
|
{
|
|
4512
4499
|
"alignItems": "center",
|
|
4513
4500
|
"backgroundColor": "transparent",
|
|
4514
|
-
"flex": 1,
|
|
4515
4501
|
"flexDirection": "row",
|
|
4516
4502
|
},
|
|
4517
4503
|
],
|
|
@@ -4873,7 +4859,6 @@ exports[`TextInput when user sees an error state should display error message to
|
|
|
4873
4859
|
{
|
|
4874
4860
|
"alignItems": "center",
|
|
4875
4861
|
"backgroundColor": "transparent",
|
|
4876
|
-
"flex": 1,
|
|
4877
4862
|
"flexDirection": "row",
|
|
4878
4863
|
},
|
|
4879
4864
|
],
|
|
@@ -5251,7 +5236,6 @@ exports[`TextInput when user sees helper text should display guidance text to as
|
|
|
5251
5236
|
{
|
|
5252
5237
|
"alignItems": "center",
|
|
5253
5238
|
"backgroundColor": "transparent",
|
|
5254
|
-
"flex": 1,
|
|
5255
5239
|
"flexDirection": "row",
|
|
5256
5240
|
},
|
|
5257
5241
|
],
|
|
@@ -5571,7 +5555,6 @@ exports[`TextInput when user sees input with custom prefix and suffix elements s
|
|
|
5571
5555
|
{
|
|
5572
5556
|
"alignItems": "center",
|
|
5573
5557
|
"backgroundColor": "transparent",
|
|
5574
|
-
"flex": 1,
|
|
5575
5558
|
"flexDirection": "row",
|
|
5576
5559
|
},
|
|
5577
5560
|
],
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback } from 'react';
|
|
2
|
-
import { StyleSheet
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
3
|
import type {
|
|
4
|
-
TextInputProps as NativeTextInputProps,
|
|
5
4
|
StyleProp,
|
|
6
5
|
ViewStyle,
|
|
7
6
|
NativeSyntheticEvent,
|
|
8
7
|
TextInputFocusEventData,
|
|
8
|
+
ColorValue,
|
|
9
9
|
} from 'react-native';
|
|
10
10
|
import {
|
|
11
11
|
StyledInputWrapper,
|
|
@@ -23,8 +23,10 @@ import FloatingLabel from './FloatingLabel';
|
|
|
23
23
|
import InputRow from './InputRow';
|
|
24
24
|
import type {
|
|
25
25
|
InternalTextInputProps,
|
|
26
|
+
NativeTextInputProps,
|
|
26
27
|
TextInputHandles,
|
|
27
28
|
TextInputProps,
|
|
29
|
+
TextInputRef,
|
|
28
30
|
} from './types';
|
|
29
31
|
import Group from './Group';
|
|
30
32
|
|
|
@@ -89,6 +91,17 @@ const extractBorderStyles = (style: StyleProp<ViewStyle>) => {
|
|
|
89
91
|
return borderStyles;
|
|
90
92
|
};
|
|
91
93
|
|
|
94
|
+
// Create container style without background color using a helper function
|
|
95
|
+
const getContainerStyle = (
|
|
96
|
+
style: StyleProp<ViewStyle>,
|
|
97
|
+
customBackgroundColor?: ColorValue
|
|
98
|
+
) => {
|
|
99
|
+
const flattenedStyle = style ? { ...StyleSheet.flatten(style) } : {};
|
|
100
|
+
if (customBackgroundColor) {
|
|
101
|
+
delete flattenedStyle.backgroundColor;
|
|
102
|
+
}
|
|
103
|
+
return flattenedStyle;
|
|
104
|
+
};
|
|
92
105
|
/**
|
|
93
106
|
* TextInput Layout Structure:
|
|
94
107
|
*
|
|
@@ -171,7 +184,7 @@ export const InternalTextInput = forwardRef<
|
|
|
171
184
|
|
|
172
185
|
const theme = useTheme();
|
|
173
186
|
|
|
174
|
-
const innerTextInput = React.useRef<
|
|
187
|
+
const innerTextInput = React.useRef<TextInputRef | null>(null);
|
|
175
188
|
React.useImperativeHandle(
|
|
176
189
|
ref,
|
|
177
190
|
() => ({
|
|
@@ -182,7 +195,7 @@ export const InternalTextInput = forwardRef<
|
|
|
182
195
|
},
|
|
183
196
|
clear: () => innerTextInput.current?.clear(),
|
|
184
197
|
setNativeProps: (args: NativeTextInputProps) =>
|
|
185
|
-
innerTextInput.current?.setNativeProps(args),
|
|
198
|
+
innerTextInput.current?.setNativeProps?.(args),
|
|
186
199
|
isFocused: () => innerTextInput.current?.isFocused() || false,
|
|
187
200
|
blur: () => innerTextInput.current?.blur(),
|
|
188
201
|
}),
|
|
@@ -198,7 +211,7 @@ export const InternalTextInput = forwardRef<
|
|
|
198
211
|
|
|
199
212
|
// Simplified callback functions (removed unnecessary memoization for simple cases)
|
|
200
213
|
const handleFocus = useCallback(
|
|
201
|
-
(event
|
|
214
|
+
(event?: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
202
215
|
setIsFocused(true);
|
|
203
216
|
onFocus?.(event);
|
|
204
217
|
},
|
|
@@ -206,7 +219,7 @@ export const InternalTextInput = forwardRef<
|
|
|
206
219
|
);
|
|
207
220
|
|
|
208
221
|
const handleBlur = useCallback(
|
|
209
|
-
(event
|
|
222
|
+
(event?: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
210
223
|
setIsFocused(false);
|
|
211
224
|
onBlur?.(event);
|
|
212
225
|
},
|
|
@@ -265,11 +278,7 @@ export const InternalTextInput = forwardRef<
|
|
|
265
278
|
accessibilityLabel,
|
|
266
279
|
};
|
|
267
280
|
|
|
268
|
-
|
|
269
|
-
const containerStyle = style ? { ...StyleSheet.flatten(style) } : {};
|
|
270
|
-
if (customBackgroundColor) {
|
|
271
|
-
delete containerStyle.backgroundColor;
|
|
272
|
-
}
|
|
281
|
+
const containerStyle = getContainerStyle(style, customBackgroundColor);
|
|
273
282
|
|
|
274
283
|
const isDisabledOrReadonly = state === 'disabled' || state === 'readonly';
|
|
275
284
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
TextInputProps as
|
|
2
|
+
TextInputProps as RNTextInputProps,
|
|
3
3
|
StyleProp,
|
|
4
4
|
ViewStyle,
|
|
5
5
|
TextStyle,
|
|
6
6
|
TextInput as RNTextInput,
|
|
7
|
+
NativeSyntheticEvent,
|
|
8
|
+
TextInputFocusEventData,
|
|
7
9
|
} from 'react-native';
|
|
8
10
|
import type { IconName } from '@hero-design/rn';
|
|
9
11
|
|
|
@@ -14,7 +16,27 @@ export type TextInputHandles = Pick<
|
|
|
14
16
|
|
|
15
17
|
export type TextInputVariant = 'text' | 'textarea';
|
|
16
18
|
|
|
17
|
-
export
|
|
19
|
+
export type NativeTextInputProps = Omit<
|
|
20
|
+
RNTextInputProps,
|
|
21
|
+
'onFocus' | 'onBlur'
|
|
22
|
+
> & {
|
|
23
|
+
onFocus?: (
|
|
24
|
+
event?: NativeSyntheticEvent<TextInputFocusEventData>
|
|
25
|
+
) => void | undefined;
|
|
26
|
+
onBlur?: (
|
|
27
|
+
event?: NativeSyntheticEvent<TextInputFocusEventData>
|
|
28
|
+
) => void | undefined;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export interface TextInputRef {
|
|
32
|
+
focus: () => void;
|
|
33
|
+
blur: () => void;
|
|
34
|
+
clear: () => void;
|
|
35
|
+
isFocused: () => boolean;
|
|
36
|
+
setNativeProps?: (props: RNTextInputProps) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type TextInputProps = NativeTextInputProps & {
|
|
18
40
|
/**
|
|
19
41
|
* Field label.
|
|
20
42
|
*/
|
|
@@ -83,7 +105,10 @@ export interface TextInputProps extends NativeTextInputProps {
|
|
|
83
105
|
/**
|
|
84
106
|
* Customise input value renderer
|
|
85
107
|
*/
|
|
86
|
-
renderInputValue?: (
|
|
108
|
+
renderInputValue?: (
|
|
109
|
+
inputProps: NativeTextInputProps,
|
|
110
|
+
ref?: React.ForwardedRef<TextInputRef>
|
|
111
|
+
) => React.ReactNode;
|
|
87
112
|
/**
|
|
88
113
|
* Component ref.
|
|
89
114
|
*/
|
|
@@ -92,7 +117,7 @@ export interface TextInputProps extends NativeTextInputProps {
|
|
|
92
117
|
* Component variant.
|
|
93
118
|
*/
|
|
94
119
|
variant?: TextInputVariant;
|
|
95
|
-
}
|
|
120
|
+
};
|
|
96
121
|
|
|
97
122
|
export interface InternalTextInputProps extends TextInputProps {
|
|
98
123
|
/**
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import TimePicker from '..';
|
|
4
|
+
import renderWithTheme from '../../../../testUtils/renderWithTheme';
|
|
5
|
+
|
|
6
|
+
describe('TimePicker', () => {
|
|
7
|
+
it('renders TimePickerIOS when OS is iOS', () => {
|
|
8
|
+
Platform.OS = 'ios';
|
|
9
|
+
const { getByTestId } = renderWithTheme(
|
|
10
|
+
<TimePicker
|
|
11
|
+
label="Start time"
|
|
12
|
+
value={new Date('December 17, 1995 03:24:00')}
|
|
13
|
+
confirmLabel="Confirm"
|
|
14
|
+
onChange={jest.fn()}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
expect(getByTestId('timePickerInputIOS')).toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('renders TimePickerAndroid when OS is android', () => {
|
|
22
|
+
Platform.OS = 'android';
|
|
23
|
+
const { getByTestId } = renderWithTheme(
|
|
24
|
+
<TimePicker
|
|
25
|
+
label="Start time"
|
|
26
|
+
value={new Date('December 17, 1995 03:24:00')}
|
|
27
|
+
confirmLabel="Confirm"
|
|
28
|
+
onChange={jest.fn()}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(getByTestId('timePickerInputAndroid')).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
TimePicker as InternalTimePicker,
|
|
4
|
+
TimePickerProps,
|
|
5
|
+
} from '@hero-design/rn';
|
|
6
|
+
import TextInput from '../TextInput';
|
|
7
|
+
|
|
8
|
+
const TimePicker = (props: TimePickerProps) => {
|
|
9
|
+
return <InternalTimePicker {...props} TextInputComponent={TextInput} />;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default TimePicker;
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// Re-export everything from @hero-design/rn except theme exports we want to override
|
|
2
2
|
import TextInput from './components/TextInput';
|
|
3
3
|
import Select from './components/Select';
|
|
4
|
+
import DatePicker from './components/DatePicker';
|
|
5
|
+
import TimePicker from './components/TimePicker';
|
|
6
|
+
import RichTextEditor from './components/RichTextEditor';
|
|
4
7
|
|
|
5
8
|
export * from '@hero-design/rn';
|
|
6
9
|
|
|
@@ -15,4 +18,4 @@ export {
|
|
|
15
18
|
} from './theme';
|
|
16
19
|
|
|
17
20
|
export { default as theme } from './theme';
|
|
18
|
-
export { TextInput, Select };
|
|
21
|
+
export { TextInput, Select, DatePicker, TimePicker, RichTextEditor };
|