Skip to content
This repository has been archived by the owner on Oct 6, 2020. It is now read-only.

Commit

Permalink
fix(Input): Use parentNode.removeChild to teardown textarea shadow no…
Browse files Browse the repository at this point in the history
…de in support of older browsers (#15)
  • Loading branch information
kylealwyn authored Jan 17, 2019
1 parent cc3fcc0 commit 6169126
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 36 deletions.
83 changes: 49 additions & 34 deletions src/Form/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ const StyledInput = createComponent({

const StyledTextArea = StyledInput.withComponent('textarea');

const AutogrowShadow = createComponent({
name: 'AutogrowShadow',
tag: 'textarea',
style: css`
position: absolute;
left: -9999px;
`,
});

const validateValueProp = (props, propName, componentName) => {
if (props.type === 'number' && typeof props[propName] !== 'number') {
return new Error(`Invalid prop ${propName} supplied to ${componentName} with type="number", expected Number`);
Expand All @@ -77,6 +86,7 @@ export default class Input extends Component {
rows: PropTypes.number,
maxRows: PropTypes.number,
rowHeight: PropTypes.number,
lineHeight: PropTypes.number,
autogrow: PropTypes.bool,
size: PropTypes.string,
floating: PropTypes.bool,
Expand All @@ -89,7 +99,8 @@ export default class Input extends Component {
minRows: 2,
rows: 3,
maxRows: 6,
rowHeight: 14 * 1.5,
rowHeight: 14,
lineHeight: 1.5,
autogrow: false,
disabled: false,
size: 'md',
Expand All @@ -99,8 +110,16 @@ export default class Input extends Component {
floating: false,
};

static getDerivedStateFromProps(props, state) {
if (props.value !== undefined && props.value !== state.value) {
return {
value: props.value,
};
}
return null;
}

state = {
value: this.props.value || '',
focused: false,
};

Expand All @@ -111,33 +130,27 @@ export default class Input extends Component {
}

componentDidMount() {
if (this.props.autogrow) {
this.createShadowElement();
this.autogrow();
}

if (this.props.autofocus) {
if (this.ref.current) {
this.ref.current.focus();
}
if (this.props.autofocus && this.ref.current) {
this.ref.current.focus();
}
}

componentWillReceiveProps(props) {
if (props.value !== this.props.value) {
this.setState({ value: props.value });
if (this.props.multiline) {
/* eslint-disable-next-line react/no-did-mount-set-state */
this.setState({
height: this.props.rows * this.props.rowHeight * this.props.lineHeight,
});
}
}

componentDidUpdate(prevProps) {
if (prevProps.value !== this.props.value) {
componentDidUpdate(oldProps, oldState) {
if (oldState.value !== this.state.value) {
this.autogrow();
}
}

componentWillUnmount() {
if (this.shadow) {
this.shadow.remove();
if (this.autogrowShadowNode && this.autogrowShadowNode.parentNode) {
this.autogrowShadowNode.parentNode.removeChild(this.autogrowShadowNode);
}
}

Expand All @@ -163,30 +176,30 @@ export default class Input extends Component {
};

onChange = e => {
this.autogrow();
this.setState({ value: e.target.value });
this.props.onChange(e.target.name, e.target.value);
};

createShadowElement() {
this.shadow = document.createElement('textarea');
this.shadow.style.position = 'absolute';
this.shadow.style.left = '-9000px';
document.body.appendChild(this.shadow);
}
handleAutogrowRef = node => {
this.autogrowShadowNode = node;
this.autogrow();
};

autogrow() {
const { multiline, autogrow, minRows, maxRows, rowHeight } = this.props;
if (!multiline || !autogrow) {
if (!this.props.autogrow || !this.autogrowShadowNode) {
return;
}

const minHeight = minRows * rowHeight;
const maxHeight = maxRows * rowHeight;
const { minRows, maxRows, rowHeight, lineHeight } = this.props;

const minHeight = minRows * rowHeight * lineHeight;
const maxHeight = maxRows * rowHeight * lineHeight;

this.autogrowShadowNode.style.width = `${this.ref.current.clientWidth}px`;
this.autogrowShadowNode.value = this.state.value;

let height = this.autogrowShadowNode.scrollHeight + rowHeight * lineHeight;

this.shadow.style.width = `${this.ref.current.clientWidth}px`;
this.shadow.value = this.props.value || this.state.value;
let height = this.shadow.scrollHeight + 14;
if (height < minHeight) {
height = minHeight;
} else if (height > maxHeight) {
Expand Down Expand Up @@ -235,7 +248,7 @@ export default class Input extends Component {
onChange: this.onChange,
onFocus: this.onFocus,
onBlur: this.onBlur,
style: autogrow ? { ...style, height: this.state.height } : style,
style: multiline ? { ...style, height: this.state.height } : style,
placeholder,
isFloatable: floating,
isFloating,
Expand All @@ -257,6 +270,8 @@ export default class Input extends Component {
{multiline ? <StyledTextArea {...inputProps} /> : <StyledInput {...inputProps} />}
</InputContainer>

{autogrow && <AutogrowShadow ref={this.handleAutogrowRef} />}

{!this.state.focused && error ? <FormError styles={rest.styles}>{error}</FormError> : null}
</Field>
);
Expand Down
4 changes: 2 additions & 2 deletions src/Form/Input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ Form input component. Different sizes are available.

### Multiline Input
<Playground>
<Input multiline placeholder="Example" label="Example" />
<Input multiline placeholder="Example" label="Example" value="I already have a value! If provided a value, I become controlled, so I won't update when you type here..." />
</Playground>

### Autogrow Multiline Input
<Playground>
<Input autogrow multiline placeholder="Example" label="Example" />
<Input multiline autogrow placeholder="Example" label="Example" />
</Playground>

### Floating Input
Expand Down

0 comments on commit 6169126

Please sign in to comment.