@true-engineering/true-react-common-ui-kit 4.0.0-alpha21 → 4.0.0-alpha22
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/dist/components/TextArea/TextArea.d.ts +2 -0
- package/dist/components/TextArea/TextArea.styles.d.ts +4 -2
- package/dist/components/TextArea/types.d.ts +4 -0
- package/dist/true-react-common-ui-kit.js +50 -38
- package/dist/true-react-common-ui-kit.js.map +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs +50 -38
- package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/src/components/TextArea/TextArea.stories.tsx +14 -1
- package/src/components/TextArea/TextArea.styles.ts +11 -1
- package/src/components/TextArea/TextArea.tsx +65 -55
- package/src/components/TextArea/types.ts +6 -0
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { ComponentStory } from '@storybook/react';
|
|
3
|
+
import { IExtendableProps } from '../../types';
|
|
3
4
|
import { TextArea } from './TextArea';
|
|
4
5
|
|
|
6
|
+
const COUNTER_POSITIONS = ['default', 'top'] as const;
|
|
7
|
+
|
|
5
8
|
export default {
|
|
6
9
|
title: 'Inputs/TextArea',
|
|
7
10
|
component: TextArea,
|
|
@@ -12,9 +15,14 @@ export default {
|
|
|
12
15
|
},
|
|
13
16
|
};
|
|
14
17
|
|
|
18
|
+
declare module './types' {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
20
|
+
export interface ITextAreaCounterPositions extends IExtendableProps<typeof COUNTER_POSITIONS> {}
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
const Template: ComponentStory<typeof TextArea> = (args) => {
|
|
16
24
|
const [value, setValue] = useState('');
|
|
17
|
-
return <TextArea {...args} value={value} onChange={
|
|
25
|
+
return <TextArea {...args} value={value} onChange={setValue} />;
|
|
18
26
|
};
|
|
19
27
|
|
|
20
28
|
export const Default = Template.bind({});
|
|
@@ -26,6 +34,7 @@ Default.args = {
|
|
|
26
34
|
rows: 5,
|
|
27
35
|
maxLength: 500,
|
|
28
36
|
isInvalid: false,
|
|
37
|
+
counterPosition: 'default',
|
|
29
38
|
errorMessage: 'Error Text',
|
|
30
39
|
isActive: false,
|
|
31
40
|
isDisabled: false,
|
|
@@ -33,3 +42,7 @@ Default.args = {
|
|
|
33
42
|
isAutoSized: true,
|
|
34
43
|
shouldAlwaysShowPlaceholder: false,
|
|
35
44
|
};
|
|
45
|
+
|
|
46
|
+
Default.argTypes = {
|
|
47
|
+
counterPosition: { options: [undefined, ...COUNTER_POSITIONS], control: 'select' },
|
|
48
|
+
};
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { animations, createThemedStyles, helpers, ITweakStyles } from '../../theme';
|
|
2
|
+
import { IWithPrefix } from '../../types';
|
|
2
3
|
import { IControlWrapperStyles } from '../ControlWrapper';
|
|
3
4
|
import { IWithMessagesStyles } from '../WithMessages';
|
|
5
|
+
import { ITextAreaCounterPositions } from './types';
|
|
4
6
|
|
|
5
7
|
export const useStyles = createThemedStyles('TextArea', {
|
|
8
|
+
root: {
|
|
9
|
+
width: '100%',
|
|
10
|
+
},
|
|
11
|
+
|
|
6
12
|
textarea: {
|
|
7
13
|
...helpers.withScrollBar,
|
|
8
14
|
width: '100%',
|
|
@@ -66,6 +72,10 @@ export const useStyles = createThemedStyles('TextArea', {
|
|
|
66
72
|
color: 'red',
|
|
67
73
|
},
|
|
68
74
|
|
|
75
|
+
symbolsCountActive: {},
|
|
76
|
+
|
|
77
|
+
'counter-default': {},
|
|
78
|
+
|
|
69
79
|
withLabel: {},
|
|
70
80
|
});
|
|
71
81
|
|
|
@@ -74,5 +84,5 @@ export type ITextAreaStyles = ITweakStyles<
|
|
|
74
84
|
{
|
|
75
85
|
tweakWithMessages: IWithMessagesStyles;
|
|
76
86
|
tweakControlWrapper: IControlWrapperStyles;
|
|
77
|
-
}
|
|
87
|
+
} & IWithPrefix<ITextAreaCounterPositions, 'counter-'>
|
|
78
88
|
>;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
TextareaHTMLAttributes,
|
|
7
7
|
useState,
|
|
8
8
|
} from 'react';
|
|
9
|
+
import { Styles } from 'react-jss';
|
|
9
10
|
import clsx from 'clsx';
|
|
10
11
|
import {
|
|
11
12
|
addDataAttributes,
|
|
@@ -19,6 +20,7 @@ import { useTweakStyles } from '../../hooks';
|
|
|
19
20
|
import { ICommonProps } from '../../types';
|
|
20
21
|
import { ControlWrapper, IControlWrapperProps } from '../ControlWrapper';
|
|
21
22
|
import { IWithMessagesProps, WithMessages } from '../WithMessages';
|
|
23
|
+
import { ITextAreaCounterPosition } from './types';
|
|
22
24
|
import { ITextAreaStyles, useStyles } from './TextArea.styles';
|
|
23
25
|
|
|
24
26
|
export interface ITextAreaProps
|
|
@@ -31,6 +33,7 @@ export interface ITextAreaProps
|
|
|
31
33
|
Pick<IWithMessagesProps, 'infoMessage' | 'errorMessage'> {
|
|
32
34
|
value?: string;
|
|
33
35
|
placeholder?: string;
|
|
36
|
+
counterPosition?: ITextAreaCounterPosition;
|
|
34
37
|
/** @default false */
|
|
35
38
|
isActive?: boolean;
|
|
36
39
|
/**
|
|
@@ -49,14 +52,13 @@ export interface ITextAreaProps
|
|
|
49
52
|
onChange: (value: string, event?: FormEvent<HTMLTextAreaElement>) => void;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
const DEFAULT_VALUE = '';
|
|
53
|
-
|
|
54
55
|
export const TextArea = forwardRef<HTMLTextAreaElement, ITextAreaProps>(
|
|
55
56
|
(
|
|
56
57
|
{
|
|
57
|
-
value =
|
|
58
|
+
value = '',
|
|
58
59
|
placeholder,
|
|
59
60
|
name,
|
|
61
|
+
counterPosition = 'default',
|
|
60
62
|
shouldFocusOnMount,
|
|
61
63
|
hasCounter = true,
|
|
62
64
|
shouldTrimAfterMaxLength,
|
|
@@ -88,6 +90,9 @@ export const TextArea = forwardRef<HTMLTextAreaElement, ITextAreaProps>(
|
|
|
88
90
|
) => {
|
|
89
91
|
const classes = useStyles({ theme: tweakStyles });
|
|
90
92
|
|
|
93
|
+
const shouldShowCounter = hasCounter && isNotEmpty(maxLength);
|
|
94
|
+
const counterString = `${value.length} / ${maxLength}`;
|
|
95
|
+
|
|
91
96
|
const tweakWithMessagesStyles = useTweakStyles({
|
|
92
97
|
tweakStyles,
|
|
93
98
|
className: 'tweakWithMessages',
|
|
@@ -123,60 +128,65 @@ export const TextArea = forwardRef<HTMLTextAreaElement, ITextAreaProps>(
|
|
|
123
128
|
};
|
|
124
129
|
|
|
125
130
|
return (
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<ControlWrapper
|
|
133
|
-
label={label}
|
|
134
|
-
tweakStyles={tweakControlWrapperStyles}
|
|
135
|
-
isFocused={hasFocus}
|
|
136
|
-
isDisabled={isDisabled}
|
|
137
|
-
hasValue={hasValue}
|
|
138
|
-
isInvalid={isInvalid}
|
|
139
|
-
isRequired={isRequired}
|
|
140
|
-
groupPlacement={groupPlacement}
|
|
141
|
-
size={size}
|
|
142
|
-
isFullWidth
|
|
131
|
+
<div className={classes.root} style={{ '--counter-length': counterString.length } as Styles}>
|
|
132
|
+
<WithMessages
|
|
133
|
+
errorMessage={errorMessage}
|
|
134
|
+
infoMessage={infoMessage}
|
|
135
|
+
tweakStyles={tweakWithMessagesStyles}
|
|
136
|
+
testId={getTestId(testId, 'wrapper')}
|
|
143
137
|
>
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
{
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
disabled={isDisabled}
|
|
156
|
-
placeholder={hasPlaceholder ? placeholder : undefined}
|
|
157
|
-
maxLength={shouldTrimAfterMaxLength ? maxLength : undefined}
|
|
158
|
-
name={name}
|
|
159
|
-
rows={rows}
|
|
160
|
-
onPaste={onPaste}
|
|
161
|
-
onFocus={handleFocus}
|
|
162
|
-
onBlur={handleBlur}
|
|
163
|
-
onChange={handleChange}
|
|
164
|
-
autoFocus={shouldFocusOnMount}
|
|
165
|
-
{...addDataAttributes(data, testId)}
|
|
166
|
-
{...textAreaProps}
|
|
167
|
-
/>
|
|
168
|
-
</div>
|
|
169
|
-
</ControlWrapper>
|
|
170
|
-
{hasCounter && isNotEmpty(maxLength) && (
|
|
171
|
-
<span
|
|
172
|
-
className={clsx(classes.symbolsCount, {
|
|
173
|
-
[classes.symbolsCountError]: value.length > maxLength,
|
|
174
|
-
})}
|
|
138
|
+
<ControlWrapper
|
|
139
|
+
label={label}
|
|
140
|
+
tweakStyles={tweakControlWrapperStyles}
|
|
141
|
+
isFocused={hasFocus}
|
|
142
|
+
isDisabled={isDisabled}
|
|
143
|
+
hasValue={hasValue}
|
|
144
|
+
isInvalid={isInvalid}
|
|
145
|
+
isRequired={isRequired}
|
|
146
|
+
groupPlacement={groupPlacement}
|
|
147
|
+
size={size}
|
|
148
|
+
isFullWidth
|
|
175
149
|
>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
150
|
+
<div
|
|
151
|
+
className={clsx(classes.wrapper, isNotEmpty(size) && classes[size], {
|
|
152
|
+
[classes.autoSized]: isAutoSized,
|
|
153
|
+
[classes.withLabel]: hasLabel,
|
|
154
|
+
})}
|
|
155
|
+
// Не менять на addDataAttributes
|
|
156
|
+
{...(isAutoSized && { 'data-value': value })}
|
|
157
|
+
>
|
|
158
|
+
<textarea
|
|
159
|
+
ref={ref}
|
|
160
|
+
className={classes.textarea}
|
|
161
|
+
value={value}
|
|
162
|
+
disabled={isDisabled}
|
|
163
|
+
placeholder={hasPlaceholder ? placeholder : undefined}
|
|
164
|
+
maxLength={shouldTrimAfterMaxLength ? maxLength : undefined}
|
|
165
|
+
name={name}
|
|
166
|
+
rows={rows}
|
|
167
|
+
onPaste={onPaste}
|
|
168
|
+
onFocus={handleFocus}
|
|
169
|
+
onBlur={handleBlur}
|
|
170
|
+
onChange={handleChange}
|
|
171
|
+
autoFocus={shouldFocusOnMount}
|
|
172
|
+
{...addDataAttributes(data, testId)}
|
|
173
|
+
{...textAreaProps}
|
|
174
|
+
/>
|
|
175
|
+
{shouldShowCounter && (
|
|
176
|
+
<span
|
|
177
|
+
className={clsx(classes.symbolsCount, classes[`counter-${counterPosition}`], {
|
|
178
|
+
[classes.symbolsCountError]: value.length > maxLength,
|
|
179
|
+
[classes.symbolsCountActive]: hasFocus || hasValue,
|
|
180
|
+
})}
|
|
181
|
+
{...addDataAttributes({ counterPosition })}
|
|
182
|
+
>
|
|
183
|
+
{counterString}
|
|
184
|
+
</span>
|
|
185
|
+
)}
|
|
186
|
+
</div>
|
|
187
|
+
</ControlWrapper>
|
|
188
|
+
</WithMessages>
|
|
189
|
+
</div>
|
|
180
190
|
);
|
|
181
191
|
},
|
|
182
192
|
);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
2
|
+
import { IExtendableProps } from '../../types';
|
|
3
|
+
|
|
4
|
+
export interface ITextAreaCounterPositions extends IExtendableProps<'default'> {}
|
|
5
|
+
|
|
6
|
+
export type ITextAreaCounterPosition = keyof ITextAreaCounterPositions;
|