Skip to content

Commit

Permalink
feat(textarea): add value counter (#695)
Browse files Browse the repository at this point in the history
* feat(textarea): add value counter

* feat(textarea): remove pluralize from counter text
  • Loading branch information
dmitrsavk committed Jun 22, 2021
1 parent b5e91ed commit cbc6bd3
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 63 deletions.
59 changes: 0 additions & 59 deletions packages/textarea/src/Component.stories.mdx

This file was deleted.

21 changes: 21 additions & 0 deletions packages/textarea/src/Component.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,27 @@ describe('Textarea', () => {

expect(container).toMatchSnapshot();
});

it('should match snapshot with default counter', () => {
const { container } = render(
<Textarea value='value' showCounter={true} maxLength={500} />,
);

expect(container).toMatchSnapshot();
});

it('should match snapshot with custom counter', () => {
const { container } = render(
<Textarea
value='value'
showCounter={true}
maxLength={500}
getCounterText={() => 'Custom counter'}
/>,
);

expect(container).toMatchSnapshot();
});
});

it('should forward ref to textarea', () => {
Expand Down
47 changes: 44 additions & 3 deletions packages/textarea/src/Component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, {
useRef,
TextareaHTMLAttributes,
ChangeEvent,
forwardRef,
} from 'react';
import cn from 'classnames';
import TextareaAutosize from 'react-textarea-autosize';
Expand All @@ -14,8 +15,10 @@ import { FormControl } from '@alfalab/core-components-form-control';

import styles from './index.module.css';

type NativeProps = TextareaHTMLAttributes<HTMLTextAreaElement>;

export type TextareaProps = Omit<
TextareaHTMLAttributes<HTMLTextAreaElement>,
NativeProps,
'size' | 'style' | 'value' | 'defaultValue' | 'onChange'
> & {
/**
Expand Down Expand Up @@ -122,9 +125,27 @@ export type TextareaProps = Omit<
* Идентификатор для систем автоматизированного тестирования
*/
dataTestId?: string;

/**
* Максимальное количество символов (native prop)
*/
maxLength?: number;

/**
* Показывать счетчик введенных символов
*/
showCounter?: boolean;

/**
* Функция, возвращающая текст для счетчика
*/
getCounterText?: (textLength: number, maxLength?: number) => string;
};

export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
export const getDefaultCounterText = (textLength: number, maxLength = 0): string =>
`${textLength}/${maxLength} символов`;

export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
(
{
autoComplete = 'on',
Expand Down Expand Up @@ -153,6 +174,9 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
value,
defaultValue,
rows = autosize ? 1 : 3,
showCounter = false,
getCounterText = getDefaultCounterText,
maxLength,
...restProps
},
ref,
Expand Down Expand Up @@ -210,6 +234,22 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
[onChange, uncontrolled],
);

const getValueLength = (): number => {
if (uncontrolled) {
return stateValue.length;
}

return (value as string).length;
};

const getHint = () => {
if (showCounter) {
return getCounterText(getValueLength(), maxLength);
}

return hint;
};

const textareaProps = {
...restProps,
className: cn(
Expand All @@ -231,6 +271,7 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
rows,
ref: mergeRefs([ref, textareaRef]),
'data-test-id': dataTestId,
maxLength,
};

return (
Expand All @@ -246,7 +287,7 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
focused={focused}
error={error}
label={label}
hint={hint}
hint={getHint()}
leftAddons={leftAddons}
rightAddons={rightAddons}
bottomAddons={bottomAddons}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions packages/textarea/src/__snapshots__/Component.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,73 @@ exports[`Textarea Snapshots tests should match snapshot 1`] = `
</div>
</div>
`;

exports[`Textarea Snapshots tests should match snapshot with custom counter 1`] = `
<div>
<div
class="component s"
>
<div
class="inner filled"
>
<div
class="inputWrapper"
>
<div
class="input"
>
<textarea
autocomplete="on"
class="textarea s filled"
maxlength="500"
rows="1"
>
value
</textarea>
</div>
</div>
</div>
<span
class="sub hint"
>
Custom counter
</span>
</div>
</div>
`;

exports[`Textarea Snapshots tests should match snapshot with default counter 1`] = `
<div>
<div
class="component s"
>
<div
class="inner filled"
>
<div
class="inputWrapper"
>
<div
class="input"
>
<textarea
autocomplete="on"
class="textarea s filled"
maxlength="500"
rows="1"
>
value
</textarea>
</div>
</div>
</div>
<span
class="sub hint"
>
5/500 символов
</span>
</div>
</div>
`;
13 changes: 13 additions & 0 deletions packages/textarea/src/component.screenshots.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ describe('Textarea | sprite', () => {
size: { width: 240, height: 100 },
}),
],
[
`${theme} - counter`,
createSpriteStorybookUrl({
componentName: 'Textarea',
knobs: {
value: 'Компонент текстового поля ввода.',
block: true,
showCounter: true,
maxLength: 500,
},
size: { width: 240, height: 100 },
}),
],
],
screenshotOpts: {
fullPage: true,
Expand Down
66 changes: 66 additions & 0 deletions packages/textarea/src/docs/component.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Meta, Story, Props, Preview } from '@storybook/addon-docs/blocks';
import { text, select, boolean, number } from '@storybook/addon-knobs';
import { CssVars, ComponentHeader, Tabs } from 'storybook/blocks';
import Icon from '@alfalab/icons-glyph/StarMIcon';

import { Textarea } from '..';
import { name, version } from '../../package.json';

import styles from '!!raw-loader!../index.module.css';
import Description from './description.mdx';

<Meta
title='Компоненты'
component={Textarea}
parameters={{ 'theme-switcher': { themes: ['click'] } }}
/>

<!-- Canvas -->

<Story name='Textarea'>
{React.createElement(() => {
const showCounter = boolean('showCounter', false);
return (
<Textarea
autosize={boolean('autosize', true)}
block={boolean('block', false)}
size={select('size', ['s', 'm', 'l', 'xl'], 's')}
disabled={boolean('disabled', false)}
placeholder={text('placeholder', '')}
resize={select('resize', ['none', 'vertical'], 'none')}
minRows={number('minRows', undefined)}
maxRows={number('maxRows', undefined)}
maxHeight={number('maxHeight', undefined)}
label={text('label', '')}
hint={text('hint', '')}
error={text('error', '')}
rightAddons={boolean('rightAddons', false) && !text('error') && <Icon />}
leftAddons={boolean('leftAddons', false) && <Icon />}
bottomAddons={boolean('bottomAddons', false) && <span>bottom text</span>}
readOnly={boolean('readOnly', false)}
showCounter={showCounter}
maxLength={showCounter && 500}
/>
);
})}
</Story>

<!-- Docs -->

<ComponentHeader
name='Textarea'
version={version}
package='@alfalab/core-components-textarea'
stage={2}
design='https://www.figma.com/file/hqSP3L6qu8UcL3sf18Su1M/Web-Components?node-id=49%3A0'
/>

```tsx
import { Textarea } from '@alfalab/core-components/textarea';
```

<Tabs
description={<Description />}
prpos={<Props of={Textarea} />}
cssVars={<CssVars css={styles} />}
/>
13 changes: 13 additions & 0 deletions packages/textarea/src/docs/description.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Textarea } from '..';

Компонент текстового поля ввода.

```tsx live
<Textarea />
```

Со счетчиком:

```tsx live
<Textarea showCounter={true} maxLength={500} />
```
8 changes: 7 additions & 1 deletion packages/textarea/src/index.module.css
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
@import '../../themes/src/default.css';

:root {
/* Local variables, not used in theming */
/* min-height */
--textarea-s-min-height: var(--form-control-s-min-height);
--textarea-m-min-height: var(--form-control-m-min-height);
--textarea-l-min-height: var(--form-control-l-min-height);
--textarea-xl-min-height: var(--form-control-xl-min-height);

/* padding-top */
--textarea-s-padding-top: 14px;
--textarea-m-padding-top: 18px;
--textarea-l-padding-top: 22px;
--textarea-xl-padding-top: 24px;

/* filled margin-top */
--textarea-s-filled-margin-top: 23px;
--textarea-m-filled-margin-top: 28px;
--textarea-l-filled-margin-top: 32px;
--textarea-xl-filled-margin-top: 34px;

/* filled min-height */
--textarea-s-filled-min-height: calc(
var(--textarea-s-min-height) - var(--textarea-s-filled-margin-top)
);
Expand Down

0 comments on commit cbc6bd3

Please sign in to comment.