Skip to content
This repository has been archived by the owner on Mar 31, 2021. It is now read-only.

Add Textarea #18

Merged
merged 2 commits into from
Nov 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions src/Textarea/Textarea.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React, { type Node } from 'react';

import type { Size } from '../types';
import _Textarea, { LoadingSpinner } from './styled';
import { InlineBlock } from '../primitives';

export type Props = {
/** An accessible identifier for the textarea */
a11yId?: string,
/** An accessible label for the textarea */
a11yLabel?: string,
as?: any,
autoComplete?: string,
/** Automatically focus on the textarea */
autoFocus?: boolean,
children: Node,
className?: string,
/** Default value of the textarea */
defaultValue?: string,
/** Disables the textrea */
disabled?: boolean,
/** Makes the textarea span full width */
isFullWidth?: boolean,
/** Adds a cute loading indicator to the textarea */
isLoading?: boolean,
/** Makes the texrea required and sets aria-invalid to true */
isRequired?: boolean,
/** Name of the textarea */
name?: string,
/** Alters the size of the textarea. Can be "small", "medium" or "large" */
size?: Size,
/** If the value of the type attribute is text, email, search, password, tel, or url, this attribute specifies the maximum number of characters (in UTF-16 code units) that the user can enter. For other control types, it is ignored. */
maxLength?: number,
/** If the value of the type attribute is text, email, search, password, tel, or url, this attribute specifies the minimum number of characters (in UTF-16 code points) that the user can enter. For other control types, it is ignored. */
minLength?: number,
/** Regex pattern to apply to the textarea */
pattern?: string,
/** Hint text to display */
placeholder?: string,
/** This prop prevents the user from modifying the value of the textarea. It is ignored if the value of the type attribute is hidden, range, color, checkbox, radio, file, or a button type (such as button or submit). */
readOnly?: boolean,
/** Setting the value of this attribute to true indicates that the element needs to have its spelling and grammar checked. The value default indicates that the element is to act according to a default behavior, possibly based on the parent element's own spellcheck value. The value false indicates that the element should not be checked. */
spellCheck?: boolean,
/** State of the input. Can be any color in the palette. */
state?: string,
/** Value of the input */
value?: string,
/** Function to invoke when focus is lost */
onBlur?: Function,
/** Function to invoke when input has changed */
onChange?: Function,
/** Function to invoke when input is focused */
onFocus?: Function
};

const Textarea = ({
a11yId,
a11yLabel,
autoComplete,
autoFocus,
className,
defaultValue,
disabled,
isFullWidth,
isLoading,
isRequired,
maxLength,
minLength,
onBlur,
onChange,
onFocus,
name,
pattern,
placeholder,
readOnly,
size,
spellCheck,
state,
value,
...props
}: Props) => (
<InlineBlock relative width={isFullWidth ? '100%' : undefined} {...props}>
<_Textarea
aria-invalid={state === 'danger'}
aria-label={a11yLabel}
aria-required={isRequired}
autoComplete={autoComplete}
autoFocus={autoFocus}
defaultValue={defaultValue}
disabled={disabled}
id={a11yId}
isFullWidth={isFullWidth}
maxLength={maxLength}
minLength={minLength}
onBlur={onBlur}
onChange={onChange}
onFocus={onFocus}
name={name}
readOnly={readOnly}
pattern={pattern}
placeholder={placeholder}
size={size}
spellCheck={spellCheck}
state={state}
value={value}
/>
{isLoading && <LoadingSpinner color="text" />}
</InlineBlock>
);

Textarea.defaultProps = {
a11yId: undefined,
a11yLabel: undefined,
as: undefined,
autoComplete: undefined,
autoFocus: false,
className: undefined,
defaultValue: undefined,
disabled: false,
isFullWidth: false,
isLoading: false,
isRequired: false,
maxLength: undefined,
minLength: undefined,
name: undefined,
onBlur: undefined,
onChange: undefined,
onFocus: undefined,
pattern: undefined,
placeholder: undefined,
readOnly: undefined,
spellCheck: undefined,
size: 'default',
state: undefined,
value: undefined
};

export default Textarea;
104 changes: 104 additions & 0 deletions src/Textarea/Textarea.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
name: Textarea
route: /form/textarea
menu: Form
---

import { Playground, PropsTable } from 'docz';
import { Box } from '../primitives/index';
import Textarea from './index';
import Component from '@reactions/component';

# Textarea

## Import

`import { Textarea } from 'fannypack'`

## Basic usage
<Playground>
<Textarea />
</Playground>

## Default values

For uncontrolled textareas, set a default value using the `defaultValue` prop.

<Playground>
<Textarea defaultValue="Fannypacks rock!" />
</Playground>

## Disabled

Make the textarea disabled with the `disabled` prop.

<Playground>
<Textarea disabled defaultValue="Disabled field" />
</Playground>

## Full width

To make the textarea span full width, add the `isFullWidth` prop.

<Playground>
<Textarea isFullWidth />
</Playground>

## Sizes

A textarea can come in different sizes - `small`, `medium`, `large`, or the default.

<Playground>
<Textarea size="small" /><br/>
<Textarea marginTop="xxsmall" /><br/>
<Textarea size="medium" marginTop="xxsmall" /><br/>
<Textarea size="large" marginTop="xxsmall" />
</Playground>

## States

A textarea can use different states (as per palette) such as `danger`, `success` and `warning`.

<Playground>
<Textarea state="danger" /><br/>
<Textarea state="success" marginTop="xxsmall" /><br/>
<Textarea state="warning" marginTop="xxsmall" /><br/>
<Textarea state="primary" marginTop="xxsmall" />
</Playground>


## Controlled Usage

The previous examples are examples of uncontrolled usage. To control the value yourself, use a combination of `onChange` and `value`.

Note: Do not use `defaultValue` with controlled textareas!

<Playground>
<Component initialState={{ value: 'Fannypacks rock!' }}>
{({ state, setState }) => (
<Textarea onChange={e => setState({ value: e.target.value })} value={state.value} />
)}
</Component>
</Playground>

## Accessibility

Using an accessibility label (`a11yLabel`), will enable screen readers to read the contents of the label.

<Playground>
<Textarea a11yLabel="name" placeholder="Jake Moxey" />
</Playground>

## `<Textarea>` Props

<PropsTable of={Textarea} />

## Theming

### Schema

```jsx
{
base: string | Object
}
```
47 changes: 47 additions & 0 deletions src/Textarea/__tests__/Textarea.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import render from '../../_utils/tests/render';
import Textarea from '../Textarea';
import 'jest-styled-components';

it('renders correctly for a basic textarea', () => {
const { container } = render(<Textarea />);
expect(container.firstChild).toMatchSnapshot();
});

it('renders correctly for a textarea with a placeholder', () => {
const { container } = render(<Textarea placeholder="Awesome placeholder" />);
expect(container.firstChild).toMatchSnapshot();
});

it('renders correctly for a disabled textarea', () => {
const { container } = render(<Textarea disabled />);
expect(container.firstChild).toMatchSnapshot();
});

it('renders correctly for an input with a default value', () => {
const { container } = render(<Textarea defaultValue="Awesome value" />);
expect(container.firstChild).toMatchSnapshot();
});

it('renders correctly for a full width textarea', () => {
const { container } = render(<Textarea isFullWidth />);
expect(container.firstChild).toMatchSnapshot();
});

describe('sizes', () => {
['small', 'medium', 'large'].forEach(size => {
it(`renders correctly for a textarea with size ${size}`, () => {
const { container } = render(<Textarea size={size} />);
expect(container.firstChild).toMatchSnapshot();
});
});
});

describe('states', () => {
['danger', 'success', 'warning', 'primary'].forEach(state => {
it(`renders correctly for a textarea with state ${state}`, () => {
const { container } = render(<Textarea state={state} />);
expect(container.firstChild).toMatchSnapshot();
});
});
});
2 changes: 2 additions & 0 deletions src/Textarea/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Textarea } from './Textarea';
export { default } from './Textarea';
Loading