Skip to content

Commit

Permalink
feat: adds auto expand functionality to <TextArea> (#1319)
Browse files Browse the repository at this point in the history
* feat: adds auto expand functionality to textarea

* fix: prettier on a file

* fix: update tests

* chore: adds a changeset

* fix: adds test to useAutoExpand

* chore: small post-review cleaning

* test: improve hook testing

* test: multiref utility

* test: a little bit further

* fix: Update typo

Co-authored-by: Robin Métral <robin.metral@sumup.com>

* fix: obsolete imports and rules

* fix: Adds descriptions for new props

Co-authored-by: Robin Métral <robin.metral@sumup.com>
  • Loading branch information
long-lazuli and Robin Métral authored Jan 31, 2022
1 parent 68c61fd commit 98d3f59
Show file tree
Hide file tree
Showing 10 changed files with 747 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changeset/perfect-jeans-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sumup/circuit-ui': minor
---

Added an auto expand behavior to the `TextArea` component.
16 changes: 13 additions & 3 deletions packages/circuit-ui/components/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@
* limitations under the License.
*/

import { forwardRef, Ref, FC, InputHTMLAttributes, ReactNode } from 'react';
import {
forwardRef,
Ref,
FC,
InputHTMLAttributes,
TextareaHTMLAttributes,
ReactNode,
} from 'react';
import { css, Interpolation } from '@emotion/react';
import { Theme } from '@sumup/design-tokens';

Expand All @@ -29,7 +36,10 @@ import ValidationHint from '../ValidationHint';
import { ReturnType } from '../../types/return-type';
import { deprecate } from '../../util/logger';

export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
export type HTMLCircuitInputElement = HTMLInputElement & HTMLTextAreaElement;
type CircuitInputHTMLAttributes = InputHTMLAttributes<HTMLInputElement> &
TextareaHTMLAttributes<HTMLTextAreaElement>;
export interface InputProps extends CircuitInputHTMLAttributes {
/**
* A clear and concise description of the input purpose.
*/
Expand Down Expand Up @@ -105,7 +115,7 @@ export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
/**
* The ref to the HTML DOM element
*/
ref?: Ref<HTMLInputElement & HTMLTextAreaElement>;
ref?: Ref<HTMLCircuitInputElement>;
}

const containerStyles = () => css`
Expand Down
14 changes: 14 additions & 0 deletions packages/circuit-ui/components/TextArea/TextArea.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,20 @@ describe('TextArea', () => {
expect(actual).toMatchSnapshot();
});

it('should render rows props when passed', () => {
const actual = create(<TextArea rows={3} />);
expect(actual).toMatchSnapshot();
});
it('should render without rows props when passed if rows is auto', () => {
const actual = create(<TextArea rows="auto" />);
expect(actual).toMatchSnapshot();
});

it('should render minRows props as rows when passed if rows is auto', () => {
const actual = create(<TextArea minRows={3} rows="auto" />);
expect(actual).toMatchSnapshot();
});

describe('business logic', () => {
/**
* Should accept a working ref
Expand Down
9 changes: 9 additions & 0 deletions packages/circuit-ui/components/TextArea/TextArea.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,12 @@ HiddenLabel.args = {
placeholder: 'Describe your issue',
hideLabel: true,
};

export const AutoExpand = (args: TextAreaProps) => <TextArea {...args} />;

AutoExpand.args = {
...baseArgs,
placeholder:
'Try writing a long text, textarea auto adapt its height to your text.',
rows: 'auto',
};
38 changes: 31 additions & 7 deletions packages/circuit-ui/components/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,27 @@
* limitations under the License.
*/

import { forwardRef } from 'react';
import { forwardRef, useRef } from 'react';
import { css } from '@emotion/react';

import Input from '../Input';
import { InputProps } from '../Input/Input';
import { HTMLCircuitInputElement, InputProps } from '../Input/Input';
import { multiRefs } from '../../util/multiRefs';

export type TextAreaProps = InputProps;
import { useAutoExpand } from './useAutoExpand';

export type TextAreaProps = Omit<InputProps, 'rows'> & {
/**
* The number of visible text lines for the control.
* If set to `auto`, the control will auto-expand vertically to show the whole value.
*/
rows?: InputProps['rows'] | 'auto';
/**
* Define the minimum number of visible text lines for the control.
* Works only when `rows` is set to `auto`.
*/
minRows?: InputProps['rows'];
};

const textAreaStyles = css`
overflow: auto;
Expand All @@ -29,10 +43,20 @@ const textAreaStyles = css`
/**
* TextArea component for forms.
*/
export const TextArea = forwardRef(
(props: TextAreaProps, ref: TextAreaProps['ref']) => (
<Input {...props} inputStyles={textAreaStyles} as="textarea" ref={ref} />
),
export const TextArea = forwardRef<HTMLCircuitInputElement, TextAreaProps>(
(props, passedRef) => {
const localRef = useRef<HTMLCircuitInputElement>(null);
const modifiedProps = useAutoExpand(localRef, props);

return (
<Input
{...modifiedProps}
inputStyles={textAreaStyles}
as="textarea"
ref={multiRefs(localRef, passedRef)}
/>
);
},
);

TextArea.displayName = 'TextArea';
Loading

1 comment on commit 98d3f59

@vercel
Copy link

@vercel vercel bot commented on 98d3f59 Jan 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.