|
1 |
| -import React from 'react'; |
2 |
| -import PropTypes from 'prop-types'; |
3 |
| -import requiredIf from 'react-required-if'; |
| 1 | +import React, { FocusEventHandler, ChangeEventHandler } from 'react'; |
4 | 2 | import { useTheme } from '@emotion/react';
|
5 |
| -import { filterDataAttributes } from '@commercetools-uikit/utils'; |
| 3 | +import { filterDataAttributes, warning } from '@commercetools-uikit/utils'; |
6 | 4 | import Constraints from '@commercetools-uikit/constraints';
|
7 | 5 | import { getInputStyles } from '@commercetools-uikit/input-utils';
|
8 | 6 |
|
9 |
| -const TextInput = (props) => { |
10 |
| - const theme = useTheme(); |
11 |
| - return ( |
12 |
| - <Constraints.Horizontal max={props.horizontalConstraint}> |
13 |
| - <input |
14 |
| - id={props.id} |
15 |
| - name={props.name} |
16 |
| - type="text" |
17 |
| - value={props.value} |
18 |
| - onChange={props.onChange} |
19 |
| - onBlur={props.onBlur} |
20 |
| - onFocus={props.onFocus} |
21 |
| - disabled={props.isDisabled} |
22 |
| - placeholder={props.placeholder} |
23 |
| - readOnly={props.isReadOnly} |
24 |
| - autoFocus={props.isAutofocussed} |
25 |
| - autoComplete={props.autoComplete} |
26 |
| - css={getInputStyles(props, theme)} |
27 |
| - // Allow to override the styles by passing a `className` prop. |
28 |
| - // Custom styles can also be passed using the `css` prop from emotion. |
29 |
| - // https://emotion.sh/docs/css-prop#style-precedence |
30 |
| - className={props.className} |
31 |
| - {...filterDataAttributes(props)} |
32 |
| - /* ARIA */ |
33 |
| - aria-readonly={props.isReadOnly} |
34 |
| - role="textbox" |
35 |
| - contentEditable={!props.isReadOnly} |
36 |
| - /> |
37 |
| - </Constraints.Horizontal> |
38 |
| - ); |
39 |
| -}; |
40 |
| - |
41 |
| -TextInput.displayName = 'TextInput'; |
42 |
| - |
43 |
| -TextInput.propTypes = { |
| 7 | +type TTextInputProps = { |
44 | 8 | /**
|
45 | 9 | * Used as HTML id property. An id is auto-generated when it is not specified.
|
46 | 10 | */
|
47 |
| - id: PropTypes.string, |
| 11 | + id?: string; |
48 | 12 | /**
|
49 | 13 | * Used as HTML autocomplete property
|
50 | 14 | */
|
51 |
| - autoComplete: PropTypes.string, |
52 |
| - className: PropTypes.string, |
| 15 | + autoComplete?: string; |
| 16 | + /** |
| 17 | + * `className` forwarded to the underlying `<input />`. |
| 18 | + */ |
| 19 | + className?: string; |
53 | 20 | /**
|
54 | 21 | * Used as HTML name of the input component. property
|
55 | 22 | */
|
56 |
| - name: PropTypes.string, |
| 23 | + name?: string; |
57 | 24 | /**
|
58 | 25 | * Value of the input component.
|
59 | 26 | */
|
60 |
| - value: PropTypes.string.isRequired, |
| 27 | + value: string; |
61 | 28 | /**
|
62 | 29 | * Called with an event containing the new value. Required when input is not read only. Parent should pass it back as value.
|
63 | 30 | * <br />
|
64 |
| - * Signature: `(event) => void` |
65 | 31 | */
|
66 |
| - onChange: requiredIf(PropTypes.func, (props) => !props.isReadOnly), |
| 32 | + onChange: ChangeEventHandler; |
67 | 33 | /**
|
68 | 34 | * Called when input is blurred
|
69 |
| - * Signature: `(event) => void` |
70 | 35 | */
|
71 |
| - onBlur: PropTypes.func, |
| 36 | + onBlur?: FocusEventHandler; |
72 | 37 | /**
|
73 | 38 | * Called when input is focused
|
74 |
| - * Signature: `(event) => void` |
75 | 39 | */
|
76 |
| - onFocus: PropTypes.func, |
| 40 | + onFocus?: FocusEventHandler; |
77 | 41 | /**
|
78 | 42 | * Focus the input on initial render
|
79 | 43 | */
|
80 |
| - isAutofocussed: PropTypes.bool, |
| 44 | + isAutofocussed?: boolean; |
81 | 45 | /**
|
82 | 46 | * Indicates that the input cannot be modified (e.g not authorized, or changes currently saving).
|
83 | 47 | */
|
84 |
| - isDisabled: PropTypes.bool, |
| 48 | + isDisabled?: boolean; |
85 | 49 | /**
|
86 | 50 | * Indicates that the field is displaying read-only content
|
87 | 51 | */
|
88 |
| - isReadOnly: PropTypes.bool, |
| 52 | + isReadOnly?: boolean; |
89 | 53 | /**
|
90 | 54 | * Indicates if the input has invalid values
|
91 | 55 | */
|
92 |
| - hasError: PropTypes.bool, |
93 |
| - hasWarning: PropTypes.bool, |
| 56 | + hasError?: boolean; |
| 57 | + hasWarning?: boolean; |
94 | 58 | /**
|
95 | 59 | * Placeholder text for the input
|
96 | 60 | */
|
97 |
| - placeholder: PropTypes.string, |
| 61 | + placeholder?: string; |
98 | 62 | /**
|
99 | 63 | * Horizontal size limit of the input fields.
|
100 | 64 | */
|
101 |
| - horizontalConstraint: PropTypes.oneOf([ |
102 |
| - 3, |
103 |
| - 4, |
104 |
| - 5, |
105 |
| - 6, |
106 |
| - 7, |
107 |
| - 8, |
108 |
| - 9, |
109 |
| - 10, |
110 |
| - 11, |
111 |
| - 12, |
112 |
| - 13, |
113 |
| - 14, |
114 |
| - 15, |
115 |
| - 16, |
116 |
| - 'scale', |
117 |
| - 'auto', |
118 |
| - ]), |
| 65 | + horizontalConstraint: |
| 66 | + | 3 |
| 67 | + | 4 |
| 68 | + | 5 |
| 69 | + | 6 |
| 70 | + | 7 |
| 71 | + | 8 |
| 72 | + | 9 |
| 73 | + | 10 |
| 74 | + | 11 |
| 75 | + | 12 |
| 76 | + | 13 |
| 77 | + | 14 |
| 78 | + | 15 |
| 79 | + | 16 |
| 80 | + | 'scale' |
| 81 | + | 'auto'; |
119 | 82 | };
|
120 | 83 |
|
121 |
| -TextInput.defaultProps = { |
| 84 | +const defaultProps: Pick<TTextInputProps, 'horizontalConstraint'> = { |
122 | 85 | horizontalConstraint: 'scale',
|
123 | 86 | };
|
124 | 87 |
|
125 |
| -TextInput.isEmpty = (value) => !value || value.trim().length === 0; |
| 88 | +const TextInput = (props: TTextInputProps) => { |
| 89 | + const theme = useTheme(); |
| 90 | + if (!props.isReadOnly) { |
| 91 | + warning( |
| 92 | + Boolean(props.onChange), |
| 93 | + 'TextInput: `onChange` is required when is not read only.' |
| 94 | + ); |
| 95 | + } |
| 96 | + return ( |
| 97 | + <Constraints.Horizontal max={props.horizontalConstraint}> |
| 98 | + <input |
| 99 | + id={props.id} |
| 100 | + name={props.name} |
| 101 | + type="text" |
| 102 | + value={props.value} |
| 103 | + onChange={props.onChange} |
| 104 | + onBlur={props.onBlur} |
| 105 | + onFocus={props.onFocus} |
| 106 | + disabled={props.isDisabled} |
| 107 | + placeholder={props.placeholder} |
| 108 | + readOnly={props.isReadOnly} |
| 109 | + autoFocus={props.isAutofocussed} |
| 110 | + autoComplete={props.autoComplete} |
| 111 | + css={getInputStyles(props, theme)} |
| 112 | + // Allow to override the styles by passing a `className` prop. |
| 113 | + // Custom styles can also be passed using the `css` prop from emotion. |
| 114 | + // https://emotion.sh/docs/css-prop#style-precedence |
| 115 | + className={props.className} |
| 116 | + {...filterDataAttributes(props)} |
| 117 | + /* ARIA */ |
| 118 | + aria-readonly={props.isReadOnly} |
| 119 | + role="textbox" |
| 120 | + contentEditable={!props.isReadOnly} |
| 121 | + /> |
| 122 | + </Constraints.Horizontal> |
| 123 | + ); |
| 124 | +}; |
| 125 | + |
| 126 | +TextInput.displayName = 'TextInput'; |
| 127 | +TextInput.defaultProps = defaultProps; |
| 128 | +TextInput.isEmpty = (value: TTextInputProps['value']) => |
| 129 | + !value || value.trim().length === 0; |
126 | 130 |
|
127 | 131 | export default TextInput;
|
0 commit comments