Skip to content

Commit

Permalink
feat(TextArea|TextInput): add note prop into TextArea and TextInput (#…
Browse files Browse the repository at this point in the history
…895)

Co-authored-by: Sergey Zveroboev <[email protected]>
Co-authored-by: Mr.Dr.Professor Patrick <[email protected]>
  • Loading branch information
3 people authored Aug 17, 2023
1 parent e191c0b commit ad959e4
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 38 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.idea
.vscode
.history
.DS_Store
*.log

Expand Down
1 change: 1 addition & 0 deletions src/components/controls/TextArea/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@
| type | `string` | `-` | The control's type |
| value | `string` | `-` | The control's value |
| view | `'normal' \| 'clear'` | `'normal'` | The control's view. `'normal'` by default |
| note | `React.ReactNode` | `-` | An optional element displayed under the lower right corner of the control and sharing the place with the error container |
18 changes: 18 additions & 0 deletions src/components/controls/TextArea/TextArea.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,28 @@ $block: '.#{variables.$ns-new}text-area';
}
}

&__outer-additional-content {
display: flex;
justify-content: space-between;
vertical-align: top;
}

&__note {
margin-left: auto;
}

&__error {
@include mixins.text-body-1();

color: var(--g-color-text-danger);

&:not(:last-child) {
margin-right: var(--g-spacing-2);
}
}

&__note,
&__error {
margin-top: 2px;
}

Expand Down
10 changes: 9 additions & 1 deletion src/components/controls/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export type TextAreaProps = BaseInputControlProps<HTMLTextAreaElement> & {
minRows?: number;
/** The number of maximum visible text lines for the control. Ignored if `rows` is specified */
maxRows?: number;
/** An optional element displayed under the lower right corner of the control and sharing the place with the error container */
note?: React.ReactNode;
};
export type TextAreaPin = InputControlPin;
export type TextAreaSize = InputControlSize;
Expand Down Expand Up @@ -54,6 +56,7 @@ export const TextArea = React.forwardRef<HTMLSpanElement, TextAreaProps>(functio
className,
qa,
controlProps,
note,
onUpdate,
onChange,
} = props;
Expand Down Expand Up @@ -156,7 +159,12 @@ export const TextArea = React.forwardRef<HTMLSpanElement, TextAreaProps>(functio
/>
)}
</span>
{isErrorMsgVisible && <div className={b('error')}>{error}</div>}
{(isErrorMsgVisible || note) && (
<div className={b('outer-additional-content')}>
{isErrorMsgVisible && <div className={b('error')}>{error}</div>}
{note && <div className={b('note')}>{note}</div>}
</div>
)}
</span>
);
});
16 changes: 16 additions & 0 deletions src/components/controls/TextArea/__stories__/TextAreaShowcase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import block from 'bem-cn-lite';

import {Checkbox} from '../../../Checkbox';
import {Text} from '../../../Text';
import {TextArea} from '../TextArea';
import type {TextAreaProps} from '../TextArea';

Expand Down Expand Up @@ -64,6 +65,21 @@ export function TextAreaShowcase() {
controlProps={{style: {resize: 'vertical'}}}
rows={4}
/>
<TextArea
{...textAreaProps}
placeholder="with note"
rows={4}
note={<Text color="secondary">Additional</Text>}
/>
<TextArea
{...textAreaProps}
placeholder="with counter and long error message"
rows={4}
note={<Text color="secondary">Additional</Text>}
error={
'It happened a very very very very very very very very very very very very very very very very very very very very very long validation error'
}
/>
</div>
</div>
</div>
Expand Down
10 changes: 7 additions & 3 deletions src/components/controls/TextArea/__tests__/TextArea.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ describe('TextArea', () => {
});

test('render error message with error prop', () => {
const {container} = render(<TextArea error="Some Error" />);
render(<TextArea error="Some Error" />);

// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
expect(container.querySelector('.g-text-area__error')).toBeInTheDocument();
expect(screen.getByText('Some Error')).toBeVisible();
});

test('render note container with note prop', () => {
render(<TextArea error="Some Error" note={<div>Additional</div>} />);

expect(screen.getByText('Additional')).toBeVisible();
});

test('do not show error without error prop', () => {
const {container} = render(<TextArea />);

Expand Down
61 changes: 31 additions & 30 deletions src/components/controls/TextInput/README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
| Property | Type | Default | Description |
| :----------- | :-------------------------------------------- | :-------------- | :--------------------------------------------------------------------------------------------------- |
| autoComplete | `boolean \| string` | `-` | The control's `autocomplete` attribute |
| autoFocus | `boolean` | `-` | The control's `autofocus` attribute |
| className | `string` | `-` | The control's wrapper class name |
| controlProps | `React.InputHTMLAttributes<HTMLInputElement>` | `-` | The control's html attributes |
| controlRef | `React.Ref<HTMLInputElement>` | `-` | React ref provided to the control |
| defaultValue | `string` | `-` | The control's default value. Use when the component is not controlled |
| disabled | `boolean` | `false` | Indicates that the user cannot interact with the control |
| error | `boolean \| string` | `-` | Shows error state and optional message if property identified as a string |
| hasClear | `boolean` | `false` | Shows icon for clearing control's value |
| id | `string` | `-` | The control's `id` attribute |
| label | `string` | `-` | Help text rendered to the left of the input node |
| leftContent | `React.ReactNode` | `-` | User`s node rendered before label and input |
| name | `string` | `-` | The control's `name` attribute. Will be autogenerated if not specified |
| onBlur | `function` | `-` | Fires when the control lost focus. Provides focus event as an callback's argument |
| onChange | `function` | `-` | Fires when the input’s value is changed by the user. Provides change event as an callback's argument |
| onFocus | `function` | `-` | Fires when the control gets focus. Provides focus event as an callback's argument |
| onKeyDown | `function` | `-` | Fires when a key is pressed. Provides keyboard event as an callback's argument |
| onKeyUp | `function` | `-` | Fires when a key is released. Provides keyboard event as an callback's argument |
| onUpdate | `function` | `-` | Fires when the input’s value is changed by the user. Provides new value as an callback's argument |
| pin | `string` | `'round-round'` | The control's border view. `'round-round'` by default |
| placeholder | `string` | `-` | Text that appears in the control when it has no value set |
| qa | `string` | `-` | Test id attribute (`data-qa`) |
| rightContent | `React.ReactNode` | `-` | User`s node rendered after input node and clear button |
| size | `'s' \| 'm' \| 'l' \| 'xl'` | `'m'` | The control's size. `'m'` by default |
| tabIndex | `string` | `-` | The control's `tabindex` attribute |
| type | `string` | `-` | The control's type |
| value | `string` | `-` | The control's value |
| view | `'normal' \| 'clear'` | `'normal'` | The control's view. `'normal'` by default |
| Property | Type | Default | Description |
| :----------- | :-------------------------------------------- | :-------------- | :----------------------------------------------------------------------------------------------------------------------- |
| autoComplete | `boolean \| string` | `-` | The control's `autocomplete` attribute |
| autoFocus | `boolean` | `-` | The control's `autofocus` attribute |
| className | `string` | `-` | The control's wrapper class name |
| controlProps | `React.InputHTMLAttributes<HTMLInputElement>` | `-` | The control's html attributes |
| controlRef | `React.Ref<HTMLInputElement>` | `-` | React ref provided to the control |
| defaultValue | `string` | `-` | The control's default value. Use when the component is not controlled |
| disabled | `boolean` | `false` | Indicates that the user cannot interact with the control |
| error | `boolean \| string` | `-` | Shows error state and optional message if property identified as a string |
| hasClear | `boolean` | `false` | Shows icon for clearing control's value |
| id | `string` | `-` | The control's `id` attribute |
| label | `string` | `-` | Help text rendered to the left of the input node |
| leftContent | `React.ReactNode` | `-` | User`s node rendered before label and input |
| name | `string` | `-` | The control's `name` attribute. Will be autogenerated if not specified |
| onBlur | `function` | `-` | Fires when the control lost focus. Provides focus event as an callback's argument |
| onChange | `function` | `-` | Fires when the input’s value is changed by the user. Provides change event as an callback's argument |
| onFocus | `function` | `-` | Fires when the control gets focus. Provides focus event as an callback's argument |
| onKeyDown | `function` | `-` | Fires when a key is pressed. Provides keyboard event as an callback's argument |
| onKeyUp | `function` | `-` | Fires when a key is released. Provides keyboard event as an callback's argument |
| onUpdate | `function` | `-` | Fires when the input’s value is changed by the user. Provides new value as an callback's argument |
| pin | `string` | `'round-round'` | The control's border view. `'round-round'` by default |
| placeholder | `string` | `-` | Text that appears in the control when it has no value set |
| qa | `string` | `-` | Test id attribute (`data-qa`) |
| rightContent | `React.ReactNode` | `-` | User`s node rendered after input node and clear button |
| size | `'s' \| 'm' \| 'l' \| 'xl'` | `'m'` | The control's size. `'m'` by default |
| tabIndex | `string` | `-` | The control's `tabindex` attribute |
| type | `string` | `-` | The control's type |
| value | `string` | `-` | The control's value |
| view | `'normal' \| 'clear'` | `'normal'` | The control's view. `'normal'` by default |
| note | `React.ReactNode` | `-` | An optional element displayed under the lower right corner of the control and sharing the place with the error container |
18 changes: 18 additions & 0 deletions src/components/controls/TextInput/TextInput.scss
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,28 @@ $block: '.#{variables.$ns}text-input';
flex-shrink: 0;
}

&__outer-additional-content {
display: flex;
justify-content: space-between;
vertical-align: top;
}

&__note {
margin-left: auto;
}

&__error {
@include mixins.text-body-1();

color: var(--g-color-text-danger);

&:not(:last-child) {
margin-right: var(--g-spacing-2);
}
}

&__note,
&__error {
margin-top: 2px;
}

Expand Down
10 changes: 9 additions & 1 deletion src/components/controls/TextInput/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export type TextInputProps = BaseInputControlProps<HTMLInputElement> & {
leftContent?: React.ReactNode;
/** User`s node rendered after input node and clear button */
rightContent?: React.ReactNode;
/** An optional element displayed under the lower right corner of the control and sharing the place with the error container */
note?: React.ReactNode;
};
export type TextInputPin = InputControlPin;
export type TextInputSize = InputControlSize;
Expand Down Expand Up @@ -61,6 +63,7 @@ export const TextInput = React.forwardRef<HTMLSpanElement, TextInputProps>(funct
controlProps: originalControlProps,
leftContent,
rightContent,
note,
onUpdate,
onChange,
} = props;
Expand Down Expand Up @@ -206,7 +209,12 @@ export const TextInput = React.forwardRef<HTMLSpanElement, TextInputProps>(funct
</AdditionalContent>
)}
</span>
{isErrorMsgVisible && <div className={b('error')}>{error}</div>}
{(isErrorMsgVisible || note) && (
<div className={b('outer-additional-content')}>
{isErrorMsgVisible && <div className={b('error')}>{error}</div>}
{note && <div className={b('note')}>{note}</div>}
</div>
)}
</span>
);
});
Loading

0 comments on commit ad959e4

Please sign in to comment.