Skip to content

Commit

Permalink
Add option to grow text area with input
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Roitgrund committed Mar 7, 2019
1 parent a919718 commit c08d6b0
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 4 deletions.
4 changes: 3 additions & 1 deletion packages/core/src/components/forms/text-inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ field.

@### Props

The `InputGroup` React component supports one non-interactive icon on the left
The `InputGroup` React component supports one non-interactive icon on the left
side and one arbitrary element on the right side. Unlike the CSS approach,
`InputGroup` supports _content of any length_ on the right side (not just
icon buttons) because it is able to measure the content and ensure there is
Expand Down Expand Up @@ -45,6 +45,7 @@ the parent input.

Conversely, the [`InputGroup`](#core/components/text-inputs.input-group) React
component _does_ support arbitrary content in its right element.

</div>

@css input-group
Expand All @@ -63,6 +64,7 @@ Apply `Classes.INPUT` on a `<textarea>`, or use the `TextArea` React component.

```tsx
<TextArea
growVertically={true}
large={true}
intent={Intent.PRIMARY}
onChange={this.handleChange}
Expand Down
42 changes: 39 additions & 3 deletions packages/core/src/components/forms/textArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export interface ITextAreaProps extends IIntentProps, IProps, React.TextareaHTML
*/
small?: boolean;

/**
* Whether the text area should automatically grow vertically to accomodate content.
*/
growVertically?: boolean;

/**
* Ref handler that receives HTML `<textarea>` element backing this component.
*/
Expand All @@ -33,11 +38,19 @@ export interface ITextAreaProps extends IIntentProps, IProps, React.TextareaHTML

// this component is simple enough that tests would be purely tautological.
/* istanbul ignore next */
export class TextArea extends React.PureComponent<ITextAreaProps, {}> {
export class TextArea extends React.PureComponent<ITextAreaProps, { height?: number }> {
public static displayName = `${DISPLAYNAME_PREFIX}.TextArea`;

constructor(props: ITextAreaProps) {
super(props);

this.state = {
height: null,
};
}

public render() {
const { className, fill, inputRef, intent, large, small, ...htmlProps } = this.props;
const { className, fill, inputRef, intent, large, small, growVertically, ...htmlProps } = this.props;

const rootClasses = classNames(
Classes.INPUT,
Expand All @@ -50,6 +63,29 @@ export class TextArea extends React.PureComponent<ITextAreaProps, {}> {
className,
);

return <textarea {...htmlProps} className={rootClasses} ref={inputRef} />;
const styleProps =
this.props.growVertically && this.state.height != null
? {
style: {
height: `${this.state.height}px`,
},
}
: {};

return (
<textarea {...htmlProps} {...styleProps} className={rootClasses} ref={inputRef} onChange={this.onChange} />
);
}

private onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
if (this.props.growVertically) {
this.setState({
height: e.target.scrollHeight,
});
}

if (this.props.onChange != null) {
this.props.onChange(e);
}
};
}
39 changes: 39 additions & 0 deletions packages/core/test/forms/textAreaTests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2017 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the terms of the LICENSE file distributed with this project.
*/

import { assert } from "chai";
import { mount } from "enzyme";
import * as React from "react";

import { TextArea } from "../../src/index";

describe("<TextArea>", () => {
it("can resize automatically", () => {
const wrapper = mount(<TextArea growVertically={true} />);
const textarea = wrapper.find("textarea");

textarea.simulate("change", {
target: {
scrollHeight: 500,
},
});

assert.equal((textarea.getDOMNode() as HTMLElement).style.height, "500px");
});

it("doesn't resize by default", () => {
const wrapper = mount(<TextArea />);
const textarea = wrapper.find("textarea");

textarea.simulate("change", {
target: {
scrollHeight: textarea.getDOMNode().scrollHeight,
},
});

assert.equal((textarea.getDOMNode() as HTMLElement).style.height, "");
});
});
1 change: 1 addition & 0 deletions packages/core/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import "./drawer/drawerTests";
import "./editable-text/editableTextTests";
import "./forms/fileInputTests";
import "./forms/formGroupTests";
import "./forms/textAreaTests";
import "./hotkeys/hotkeysTests";
import "./html-select/htmlSelectTests";
import "./icon/iconTests";
Expand Down

0 comments on commit c08d6b0

Please sign in to comment.