Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.

Commit ea04dc6

Browse files
author
Matt Goo
authored
fix(text-field): added reference to input element (#414)
1 parent 90adbbe commit ea04dc6

File tree

5 files changed

+78
-11
lines changed

5 files changed

+78
-11
lines changed

packages/text-field/Input.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import classnames from 'classnames';
2727
import {VALIDATION_ATTR_WHITELIST} from '@material/textfield/constants';
2828

2929
export default class Input extends React.Component {
30-
inputElement = React.createRef();
30+
inputElement_ = React.createRef();
3131
state = {wasUserTriggeredChange: false};
3232

3333
componentDidMount() {
@@ -85,6 +85,11 @@ export default class Input extends React.Component {
8585
return classnames('mdc-text-field__input', this.props.className);
8686
}
8787

88+
get inputElement() {
89+
const element = this.inputElement_.current;
90+
return element ? element : null;
91+
}
92+
8893
handleFocus = (e) => {
8994
const {foundation, handleFocusChange, onFocus} = this.props;
9095
foundation.activateFocus();
@@ -141,12 +146,12 @@ export default class Input extends React.Component {
141146
});
142147
}
143148

144-
isBadInput = () => this.inputElement.current.validity.badInput;
149+
isBadInput = () => this.inputElement_.current.validity.badInput;
145150
isValid = () => {
146151
if (this.props.isValid !== undefined) {
147152
return this.props.isValid;
148153
}
149-
return this.inputElement.current.validity.valid;
154+
return this.inputElement_.current.validity.valid;
150155
}
151156

152157
render() {
@@ -170,6 +175,7 @@ export default class Input extends React.Component {
170175
/* eslint-enable no-unused-vars */
171176
...otherProps
172177
} = this.props;
178+
173179
const InputComponent = inputType;
174180
return (
175181
<InputComponent
@@ -180,7 +186,7 @@ export default class Input extends React.Component {
180186
onChange={this.handleChange}
181187
disabled={disabled}
182188
value={value}
183-
ref={this.inputElement}
189+
ref={this.inputElement_}
184190
className={this.classes}
185191
{...otherProps}
186192
/>

packages/text-field/README.md

+40
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,49 @@ setDisabled | Function | Callback function that is called when the `disabled` pr
9494
setInputId | Function | Callback function that is called when the `id` attribute updates.
9595
handleFocusChange | Function | Callback function that is called when `focus` or `blur` events occur
9696
value | Number/String | Value of the input.
97+
ref | Function(input: ReactElement) => void | On mount of component, will call passed function with the instance of the `<Input />`.
9798

9899
>NOTE: the `<Input>` component will receive all properties that a standard `<input>` accepts.
99100
101+
### Accessing the Native Input element
102+
103+
There will be times when you need to access the native <input />. For example if you need to focus the text field, you can add a ref callback method to the `<Input />` element and access the `<input />`. The `ref` will accept a callback method and on mount and will pass the instance of the input component. Here is an example of how to programatically focus the `<input />`:
104+
105+
```js
106+
import React from 'react';
107+
import TextField, {Input} from '@material/react-text-field';
108+
import Button from '@material/react-button';
109+
110+
class MyApp extends React.Component {
111+
input = null;
112+
state = {value: 'Woof'};
113+
114+
focusTextField = () => {
115+
if (!this.input) return;
116+
const inputElement = this.input.inputElement;
117+
if (inputElement) {
118+
inputElement.focus();
119+
}
120+
}
121+
122+
render() {
123+
return (
124+
<div>
125+
<div>
126+
<Button onClick={this.focusTextField}>Focus Text Field</Button>
127+
</div>
128+
<TextField label='Dog'>
129+
<Input
130+
value={this.state.value}
131+
ref={input => this.input = input}
132+
onChange={(e) => this.setState({value: e.target.value})}/>
133+
</TextField>
134+
</div>
135+
);
136+
}
137+
}
138+
```
139+
100140

101141
### Sass Mixins
102142

packages/text-field/index.js

+13-7
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class TextField extends React.Component {
3636
constructor(props) {
3737
super(props);
3838
this.floatingLabelElement = React.createRef();
39-
this.inputElement_ = React.createRef();
39+
this.inputComponent_ = null;
4040

4141
this.state = {
4242
// root state
@@ -163,9 +163,9 @@ class TextField extends React.Component {
163163
getNativeInput: () => {
164164
let badInput;
165165
let valid;
166-
if (this.inputElement_ && this.inputElement_.current) {
167-
badInput = this.inputElement_.current.isBadInput();
168-
valid = this.inputElement_.current.isValid();
166+
if (this.inputComponent_) {
167+
badInput = this.inputComponent_.isBadInput();
168+
valid = this.inputComponent_.isValid();
169169
}
170170
const input = {
171171
validity: {badInput, valid},
@@ -223,14 +223,20 @@ class TextField extends React.Component {
223223
};
224224
}
225225

226-
inputProps(props) {
226+
inputProps(child) {
227+
const {props, ref} = child;
227228
return Object.assign({}, props, {
228229
foundation: this.state.foundation,
229230
handleFocusChange: (isFocused) => this.setState({isFocused}),
230231
handleValueChange: (value, cb) => this.setState({value}, cb),
231232
setDisabled: (disabled) => this.setState({disabled}),
232233
setInputId: (id) => this.setState({inputId: id}),
233-
ref: this.inputElement_,
234+
ref: (input) => {
235+
if (typeof ref === 'function') {
236+
ref(input);
237+
}
238+
this.inputComponent_ = input;
239+
},
234240
inputType: this.props.textarea ? 'textarea' : 'input',
235241
});
236242
}
@@ -277,7 +283,7 @@ class TextField extends React.Component {
277283

278284
renderInput() {
279285
const child = React.Children.only(this.props.children);
280-
const props = this.inputProps(child.props);
286+
const props = this.inputProps(child);
281287
return React.cloneElement(child, props);
282288
}
283289

test/unit/text-field/Input.test.js

+7
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,10 @@ test('#event.onChange calls props.onChange()', () => {
298298
wrapper.simulate('change', event);
299299
td.verify(onChange(event), {times: 1});
300300
});
301+
302+
test('#inputElement should return the native input', () => {
303+
const wrapper = mount(<Input />);
304+
const inputElement = wrapper.instance().inputElement;
305+
assert.equal(inputElement.tagName.toLowerCase(), 'input');
306+
assert.isTrue(inputElement instanceof HTMLInputElement);
307+
});

test/unit/text-field/index.test.js

+8
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,14 @@ test('#inputProps.setInputId updates state.disabled', () => {
394394
assert.equal(wrapper.state().inputId, 'my-id');
395395
});
396396

397+
test('passing a ref to the <Input /> should return the instance of the Input', () => {
398+
let inputInstance = null;
399+
const wrapper = mount(<TextField label='my label'>
400+
<Input ref={(input) => inputInstance = input}/>
401+
</TextField>);
402+
assert.equal(wrapper.childAt(0).childAt(0).instance(), inputInstance);
403+
});
404+
397405
test('#componentWillUnmount destroys foundation', () => {
398406
const wrapper = shallow(<TextField label='my label'><Input /></TextField>);
399407
const foundation = wrapper.state().foundation;

0 commit comments

Comments
 (0)