Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Textarea To Components #3292

Merged
merged 17 commits into from Jan 8, 2020
Merged
14 changes: 10 additions & 4 deletions packages/components/.storybook/config.tsx
@@ -1,4 +1,4 @@
import React, { Fragment } from 'react';
import React from 'react';

import { withKnobs } from '@storybook/addon-knobs';
import { withA11y } from '@storybook/addon-a11y';
Expand Down Expand Up @@ -32,18 +32,24 @@ const viewports = {
// using sidebar as the styles for body for now 🤷
const GlobalStyle = createGlobalStyle`
${global};
body {
html body {
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
font-family: 'Inter', sans-serif;
background-color: ${theme.colors.sideBar.background};
color: ${theme.colors.sideBar.foreground};
margin: 0;
padding: 20px;

* {
box-sizing: border-box;
}
}
`;

export const withGlobal = cb => (
<Fragment>
<>
<GlobalStyle />
{cb()}
</Fragment>
</>
);

addDecorator(withA11y);
Expand Down
15 changes: 0 additions & 15 deletions packages/components/src/components/Example/index.tsx

This file was deleted.

1 change: 0 additions & 1 deletion packages/components/src/components/Input/index.stories.tsx
Expand Up @@ -8,7 +8,6 @@ export default {

// replace the text inside with Text variants when available
export const Basic = () => <Input />;
export const InvisibleLabel = () => <Input invisibleLabel="Fill your name" />;
export const Placeholder = () => <Input placeholder="Your name" />;
export const Label = () => (
<Input label="Your full name" placeholder="John Doe" />
Expand Down
11 changes: 6 additions & 5 deletions packages/components/src/components/Input/index.tsx
Expand Up @@ -3,6 +3,7 @@ import styled from 'styled-components';
import css from '@styled-system/css';
import VisuallyHidden from '@reach/visually-hidden';
import { uniqueId } from 'lodash-es';
import { Text } from '../Text';

const placeholderStyles = {
color: 'input.placeholderForeground',
Expand All @@ -25,11 +26,9 @@ export const InputComponent = styled.input(
})
);

const Label = styled.label(
const Label = styled(Text)(
css({
fontSize: 2,
paddingBottom: 2,
color: 'sidebar.foreground',
marginBottom: 2,
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
display: 'block',
})
);
Expand All @@ -52,7 +51,9 @@ export const Input: React.FC<IInputProps> = ({
<label htmlFor={id}>{props.placeholder}</label>
</VisuallyHidden>
) : null}
{label ? <Label htmlFor={id}>{label}</Label> : null}
<Label size={2} as="label" htmlFor={id}>
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
{label}
</Label>
<InputComponent id={id} {...props} />
</>
);
Expand Down
32 changes: 32 additions & 0 deletions packages/components/src/components/Textarea/index.stories.tsx
@@ -0,0 +1,32 @@
import React from 'react';
import { Textarea } from '.';

export default {
title: 'components/Textarea',
component: Textarea,
};

const Wrapper = ({ children }) => <div style={{ width: 400 }}>{children}</div>;

// replace the text inside with Text variants when available
export const Basic = () => (
<Wrapper>
<Textarea />
</Wrapper>
);
export const Placeholder = () => (
<Wrapper>
<Textarea placeholder="Your name" />
</Wrapper>
);
export const Label = () => (
<Wrapper>
<Textarea label="Your full name" placeholder="John Doe" />
</Wrapper>
);

export const MaxLength = () => (
<Wrapper>
<Textarea maxLength={10} label="Your full name" placeholder="John Doe" />
</Wrapper>
);
120 changes: 120 additions & 0 deletions packages/components/src/components/Textarea/index.tsx
@@ -0,0 +1,120 @@
import React, { useState, useCallback } from 'react';
import styled from 'styled-components';
import css from '@styled-system/css';
import { VisuallyHidden } from 'reakit/VisuallyHidden';
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
import { uniqueId } from 'lodash-es';
import { Text } from '../Text';
import { Stack } from '../Stack';

const placeholderStyles = {
color: 'input.placeholderForeground',
fontSize: 13,
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
};

export const TextareaComponent = styled.textarea(
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
css({
height: 64,
width: '100%',
padding: 2,
fontSize: 3,
resize: 'none',
backgroundColor: 'input.background',
borderBottom: '1px solid',
borderColor: 'input.border',
color: 'input.foreground',
borderRadius: 'small',
'::-webkit-input-placeholder': placeholderStyles,
'::-ms-input-placeholder': placeholderStyles,
'::placeholder': placeholderStyles,
})
);

const Label = styled(Text)(
css({
marginBottom: 2,
display: 'block',
})
);

const Count = styled.div<{ limit: boolean }>(({ limit }) =>
css({
fontSize: 11,
paddingTop: 1,
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
color: limit ? 'errorForeground' : 'input.placeholderForeground',
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
alignSelf: 'flex-end',
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
})
);

interface ITextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
label?: string;
maxLength?: number;
}

export const Textarea: React.FC<ITextareaProps> = ({
label,
maxLength,
...props
}) => {
const id = props.id || uniqueId('form_');
SaraVieira marked this conversation as resolved.
Show resolved Hide resolved
const [wordCount, setWordCount] = useState(0);
const [value, setValue] = useState('');

const Wrapper = useCallback(
({ children }) =>
maxLength ? <Stack direction="vertical">{children}</Stack> : children,
[maxLength]
);

// eslint-disable-next-line consistent-return
Copy link
Contributor

Choose a reason for hiding this comment

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

freaking hate that rule

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can turn it off in this package, what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

yaaas!

const update = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
if (props.onChange) props.onChange(e);
if (maxLength) {
const trimmedText = e.target.value.substring(0, maxLength);
setValue(trimmedText);
setWordCount(trimmedText.length);
} else {
setValue(e.target.value);
}
};

const keyPress = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (props.onKeyPress) props.onKeyPress(e);
if (maxLength) {
if (maxLength <= wordCount) {
return false;
}
}

return true;
};

return (
<>
{props.placeholder && !label ? (
<VisuallyHidden>
<label htmlFor={id}>{props.placeholder}</label>
</VisuallyHidden>
) : null}
{label ? (
<Label size={2} as="label" htmlFor={id}>
{label}
</Label>
) : null}
<Wrapper>
<TextareaComponent
value={value}
onChange={update}
onKeyPress={keyPress}
id={id}
{...props}
/>
{maxLength ? (
<Count limit={maxLength <= wordCount}>
{wordCount}/{maxLength}
</Count>
) : null}
</Wrapper>
</>
);
};