-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #347 from dcos-labs/mp/feat/DCOS-54882-textarea-co…
…mponent DCOS-54882: add textarea component
- Loading branch information
Showing
8 changed files
with
498 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Textarea | ||
|
||
A Textarea is used to input a large amount of text data in a form field. They're very similar to a regular text input, but they support multiple lines of text. | ||
|
||
By default, the Textarea height is big enough to hold 3 lines of text. Adjust this using the `rows` prop to give users a hint of how much content the field expects. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import * as React from "react"; | ||
import { InputAppearance } from "../../shared/types/inputAppearance"; | ||
import FormFieldWrapper from "../../shared/components/FormFieldWrapper"; | ||
import { cx } from "emotion"; | ||
import { | ||
inputReset, | ||
tintText, | ||
visuallyHidden | ||
} from "../../shared/styles/styleUtils"; | ||
import { | ||
getInputAppearanceStyle, | ||
inputContainer, | ||
getLabelStyle | ||
} from "../../shared/styles/formStyles"; | ||
import { textarea } from "../style"; | ||
import { themeError } from "../../design-tokens/build/js/designTokens"; | ||
|
||
export interface TextareaProps extends React.HTMLProps<HTMLTextAreaElement> { | ||
/** | ||
* Unique identifier used for the form textarea element | ||
*/ | ||
id: string; | ||
/** | ||
* Sets the current appearance of the component. This defaults to InputAppearance.Standard, but supports `InputAppearance.Error` & `InputAppearance.Success` appearances as well. | ||
*/ | ||
appearance: InputAppearance; | ||
/** | ||
* Sets the contents of the label. This can be a `string` or any `ReactNode`. | ||
*/ | ||
inputLabel: React.ReactNode; | ||
/** | ||
* Defaults to `true`, but can be set to `false` to visibly hide the `Textarea`'s label. The `inputLabel` should still be set even when hidden for accessibility support. | ||
*/ | ||
showInputLabel: boolean; | ||
/** | ||
* Text or a ReactNode that is displayed directly under the textarea with additional information about the expected input. | ||
*/ | ||
hintContent?: React.ReactNode; | ||
/** | ||
* Sets the contents for validation errors. This will be displayed below the textarea element. Errors are only visible when the `Textarea` appearance is also set to `InputAppearance.Error`. | ||
*/ | ||
errors?: React.ReactNode[]; | ||
} | ||
|
||
class Textarea extends React.PureComponent<TextareaProps, {}> { | ||
public static defaultProps: Partial<TextareaProps> = { | ||
appearance: InputAppearance.Standard, | ||
showInputLabel: true, | ||
rows: 3 | ||
}; | ||
|
||
public render() { | ||
const { | ||
appearance, | ||
errors, | ||
hintContent, | ||
id, | ||
inputLabel, | ||
required, | ||
showInputLabel, | ||
value, | ||
...other | ||
} = this.props; | ||
const hasError = appearance === InputAppearance.Error; | ||
let { onChange } = other; | ||
if (onChange == null && value != null) { | ||
onChange = Function.prototype as ( | ||
event: React.FormEvent<HTMLTextAreaElement> | ||
) => void; | ||
} | ||
|
||
return ( | ||
<FormFieldWrapper id={id} errors={errors} hintContent={hintContent}> | ||
{({ getValidationErrors, getHintContent, isValid, describedByIds }) => ( | ||
<div> | ||
<label | ||
className={cx(getLabelStyle(hasError), { | ||
[visuallyHidden]: !showInputLabel | ||
})} | ||
htmlFor={id} | ||
> | ||
{inputLabel} | ||
{required ? ( | ||
<span className={cx(tintText(themeError))}> *</span> | ||
) : null} | ||
</label> | ||
<textarea | ||
aria-invalid={!isValid} | ||
aria-describedby={describedByIds} | ||
value={value} | ||
id={id} | ||
className={cx( | ||
inputReset, | ||
inputContainer, | ||
getInputAppearanceStyle(this.getInputAppearance()), | ||
textarea | ||
)} | ||
required={required} | ||
{...{ ...other, onChange }} | ||
/> | ||
{getHintContent} | ||
{hasError && getValidationErrors} | ||
</div> | ||
)} | ||
</FormFieldWrapper> | ||
); | ||
} | ||
|
||
private getInputAppearance(): string { | ||
return this.props.disabled ? "disabled" : this.props.appearance; | ||
} | ||
} | ||
|
||
export default Textarea; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as Textarea } from "./components/Textarea"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import * as React from "react"; | ||
import { storiesOf } from "@storybook/react"; | ||
import { action } from "@storybook/addon-actions"; | ||
import { withReadme } from "storybook-readme"; | ||
import { Textarea } from "../index"; | ||
import styled from "react-emotion"; | ||
import { InputAppearance } from "../../shared/types/inputAppearance"; | ||
|
||
const readme = require("../README.md"); | ||
|
||
const InputStoryWrapper = styled("div")` | ||
max-width: 300px; | ||
& > div { | ||
margin-bottom: 1.5em; | ||
} | ||
`; | ||
|
||
storiesOf("Forms/Textarea", module) | ||
.addDecorator(withReadme([readme])) | ||
.add("default", () => ( | ||
<InputStoryWrapper> | ||
<div> | ||
<Textarea | ||
id="standard" | ||
inputLabel="Standard" | ||
placeholder="Placeholder" | ||
/> | ||
</div> | ||
<div> | ||
<Textarea | ||
appearance={InputAppearance.Error} | ||
id="error" | ||
inputLabel="Error" | ||
placeholder="Placeholder" | ||
/> | ||
</div> | ||
<div> | ||
<Textarea | ||
appearance={InputAppearance.Success} | ||
id="success" | ||
inputLabel="Success" | ||
placeholder="Placeholder" | ||
/> | ||
</div> | ||
<div> | ||
<Textarea | ||
id="value" | ||
inputLabel="With Value" | ||
placeholder="Placeholder" | ||
value="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book." | ||
/> | ||
</div> | ||
<div> | ||
<Textarea | ||
id="disabled" | ||
inputLabel="Disabled" | ||
placeholder="Placeholder" | ||
disabled={true} | ||
/> | ||
</div> | ||
<div> | ||
<Textarea | ||
id="disabledValue" | ||
inputLabel="Disabled w/ Value" | ||
value="This is Disabled" | ||
disabled={true} | ||
/> | ||
</div> | ||
</InputStoryWrapper> | ||
)) | ||
.add("more rows", () => ( | ||
<InputStoryWrapper> | ||
<Textarea | ||
id="tenRows" | ||
inputLabel="Standard" | ||
placeholder="Placeholder" | ||
rows={10} | ||
/> | ||
</InputStoryWrapper> | ||
)) | ||
.add("required", () => ( | ||
<InputStoryWrapper> | ||
<div> | ||
<Textarea | ||
id="required" | ||
inputLabel="Required" | ||
placeholder="Placeholder" | ||
required={true} | ||
/> | ||
</div> | ||
<div> | ||
<Textarea | ||
appearance={InputAppearance.Error} | ||
id="required" | ||
inputLabel="Required" | ||
placeholder="Placeholder" | ||
errors={["Please enter a value"]} | ||
required={true} | ||
/> | ||
</div> | ||
</InputStoryWrapper> | ||
)) | ||
.add("with hint", () => ( | ||
<InputStoryWrapper> | ||
<Textarea | ||
id="hint" | ||
inputLabel="Standard" | ||
placeholder="Placeholder" | ||
hintContent="Enter a body of text here" | ||
/> | ||
</InputStoryWrapper> | ||
)) | ||
.add("error with message", () => ( | ||
<InputStoryWrapper> | ||
<Textarea | ||
appearance={InputAppearance.Error} | ||
id="error" | ||
inputLabel="Error" | ||
placeholder="Placeholder" | ||
errors={["Something is wrong here"]} | ||
/> | ||
</InputStoryWrapper> | ||
)) | ||
.add("error with messages", () => ( | ||
<InputStoryWrapper> | ||
<Textarea | ||
appearance={InputAppearance.Error} | ||
id="error" | ||
inputLabel="Error" | ||
placeholder="Placeholder" | ||
errors={["Something is wrong here", "Another error message"]} | ||
/> | ||
</InputStoryWrapper> | ||
)) | ||
.add("hidden label", () => ( | ||
<InputStoryWrapper> | ||
<Textarea | ||
id="hiddenlabel" | ||
inputLabel="Standard" | ||
placeholder="Placeholder" | ||
showInputLabel={false} | ||
/> | ||
</InputStoryWrapper> | ||
)) | ||
.add("with onChange", () => ( | ||
<InputStoryWrapper> | ||
<Textarea | ||
id="onChange" | ||
inputLabel="Standard" | ||
placeholder="Placeholder" | ||
onChange={action("onChange happened")} | ||
/> | ||
</InputStoryWrapper> | ||
)) | ||
.add("with onChange delegated", () => ( | ||
<InputStoryWrapper> | ||
<form onChange={action("onChange delegated")}> | ||
<Textarea | ||
id="onChangeDelegated" | ||
inputLabel="Standard" | ||
placeholder="Placeholder" | ||
/> | ||
</form> | ||
</InputStoryWrapper> | ||
)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { css } from "emotion"; | ||
import { spaceM } from "../design-tokens/build/js/designTokens"; | ||
|
||
export const textarea = css` | ||
box-sizing: border-box; | ||
height: unset; | ||
padding: ${spaceM}; | ||
width: 100%; | ||
`; |
Oops, something went wrong.