Skip to content

Commit 7242344

Browse files
[Autocomplete] Clarify value and inputValue link
1 parent e4f66b0 commit 7242344

File tree

17 files changed

+51
-104
lines changed

17 files changed

+51
-104
lines changed

docs/src/pages/components/autocomplete/autocomplete.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ Choose one of the 248 countries.
3232

3333
{{"demo": "pages/components/autocomplete/CountrySelect.js"}}
3434

35+
### Controllable states
36+
37+
The component has two states that can be controlled:
38+
39+
1. the "value" state with the `value`/`onChange` props combination.
40+
2. the "input value" state with the `inputValue`/`onInputChange` props combination.
41+
42+
> ⚠️ These two state are isolated, they should be controlled independently.
43+
3544
## Free solo
3645

3746
Set `freeSolo` to true so the textbox can contain any arbitrary value. The prop is designed to cover the primary use case of a search box with suggestions, e.g. Google search.

packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,13 +1101,6 @@ describe('<Autocomplete />', () => {
11011101
});
11021102

11031103
describe('controlled', () => {
1104-
beforeEach(() => {
1105-
consoleWarnMock.spy();
1106-
});
1107-
1108-
afterEach(() => {
1109-
consoleWarnMock.reset();
1110-
});
11111104
it('controls the input value', () => {
11121105
const handleChange = spy();
11131106
function MyComponent() {
@@ -1151,49 +1144,6 @@ describe('<Autocomplete />', () => {
11511144
fireEvent.keyDown(document.activeElement, { key: 'Enter' });
11521145
expect(handleInputChange.calledBefore(handleChange)).to.equal(true);
11531146
});
1154-
1155-
it('should log warning if undefined passed as input value', () => {
1156-
function MyComponent() {
1157-
const [value, setInputValue] = React.useState('foo');
1158-
const handleInputChange = () => {
1159-
setInputValue(undefined);
1160-
};
1161-
return (
1162-
<Autocomplete
1163-
{...defaultProps}
1164-
inputValue={value}
1165-
onInputChange={handleInputChange}
1166-
renderInput={(params) => <TextField {...params} autoFocus />}
1167-
/>
1168-
);
1169-
}
1170-
render(<MyComponent />);
1171-
expect(
1172-
consoleWarnMock
1173-
.getSpy()
1174-
.calledWithExactly(
1175-
'inputValue provided was null, defaulting to empty string. Check that the inputValue property gets passed a valid string value',
1176-
),
1177-
).to.equal(true);
1178-
});
1179-
it('should treat undefined as empty string when passed in as input value', () => {
1180-
function MyComponent() {
1181-
const [value, setInputValue] = React.useState('foo');
1182-
const handleInputChange = () => {
1183-
setInputValue(undefined);
1184-
};
1185-
return (
1186-
<Autocomplete
1187-
{...defaultProps}
1188-
inputValue={value}
1189-
onInputChange={handleInputChange}
1190-
renderInput={(params) => <TextField {...params} autoFocus />}
1191-
/>
1192-
);
1193-
}
1194-
render(<MyComponent />);
1195-
expect(document.activeElement.value).to.equal('');
1196-
});
11971147
});
11981148

11991149
describe('prop: filterOptions', () => {

packages/material-ui-lab/src/Pagination/usePagination.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default function usePagination(props = {}) {
2525
controlled: pageProp,
2626
default: defaultPage,
2727
name: componentName,
28+
state: 'page',
2829
});
2930

3031
const handleClick = (event, value) => {

packages/material-ui-lab/src/TreeView/TreeView.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,14 @@ const TreeView = React.forwardRef(function TreeView(props, ref) {
6767
controlled: expandedProp,
6868
default: defaultExpanded,
6969
name: 'TreeView',
70+
state: 'expanded',
7071
});
7172

7273
const [selected, setSelectedState] = useControlled({
7374
controlled: selectedProp,
7475
default: defaultSelected,
7576
name: 'TreeView',
77+
state: 'selected',
7678
});
7779

7880
/*

packages/material-ui-lab/src/TreeView/TreeView.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('<TreeView />', () => {
4646

4747
setProps({ expanded: undefined });
4848
expect(consoleErrorMock.messages()[0]).to.include(
49-
'A component is changing a controlled TreeView to be uncontrolled',
49+
'Material-UI: a component is changing the controlled expanded state of TreeView to be uncontrolled.',
5050
);
5151
});
5252

@@ -59,7 +59,7 @@ describe('<TreeView />', () => {
5959

6060
setProps({ selected: undefined });
6161
expect(consoleErrorMock.messages()[0]).to.include(
62-
'A component is changing a controlled TreeView to be uncontrolled',
62+
'Material-UI: a component is changing the controlled selected state of TreeView to be uncontrolled.',
6363
);
6464
});
6565
});

packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,6 @@ const defaultFilterOptions = createFilterOptions();
6767
// Number of options to jump in list box when pageup and pagedown keys are used.
6868
const pageSize = 5;
6969

70-
const getInputValue = (inputValue) => {
71-
if (inputValue == null) {
72-
console.warn(
73-
'inputValue provided was null, defaulting to empty string. Check that the inputValue property gets passed a valid string value',
74-
);
75-
return '';
76-
}
77-
return inputValue;
78-
};
7970
export default function useAutocomplete(props) {
8071
const {
8172
autoComplete = false,
@@ -195,10 +186,12 @@ export default function useAutocomplete(props) {
195186
default: defaultValue,
196187
name: componentName,
197188
});
198-
199-
const { current: isInputValueControlled } = React.useRef(inputValueProp != null);
200-
const [inputValueState, setInputValue] = React.useState('');
201-
const inputValue = isInputValueControlled ? getInputValue(inputValueProp) : inputValueState;
189+
const [inputValue, setInputValue] = useControlled({
190+
controlled: inputValueProp,
191+
default: '',
192+
name: componentName,
193+
state: 'inputValue',
194+
});
202195

203196
const [focused, setFocused] = React.useState(false);
204197

packages/material-ui/src/ExpansionPanel/ExpansionPanel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ const ExpansionPanel = React.forwardRef(function ExpansionPanel(props, ref) {
9999
controlled: expandedProp,
100100
default: defaultExpanded,
101101
name: 'ExpansionPanel',
102+
state: 'expanded',
102103
});
103104

104105
const handleChange = React.useCallback(

packages/material-ui/src/ExpansionPanel/ExpansionPanel.test.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import PropTypes from 'prop-types';
3-
import { assert } from 'chai';
3+
import { assert, expect } from 'chai';
44
import { spy } from 'sinon';
55
import { createMount, getClasses, findOutermostIntrinsic } from '@material-ui/core/test-utils';
66
import describeConformance from '../test-utils/describeConformance';
@@ -193,9 +193,8 @@ describe('<ExpansionPanel />', () => {
193193
const wrapper = mount(<ExpansionPanel expanded>{minimalChildren}</ExpansionPanel>);
194194

195195
wrapper.setProps({ expanded: undefined });
196-
assert.include(
197-
consoleErrorMock.messages()[0],
198-
'A component is changing a controlled ExpansionPanel to be uncontrolled.',
196+
expect(consoleErrorMock.messages()[0]).to.include(
197+
'Material-UI: a component is changing the controlled expanded state of ExpansionPanel to be uncontrolled.',
199198
);
200199
});
201200

@@ -205,7 +204,7 @@ describe('<ExpansionPanel />', () => {
205204
wrapper.setProps({ expanded: true });
206205
assert.include(
207206
consoleErrorMock.messages()[0],
208-
'A component is changing an uncontrolled ExpansionPanel to be controlled.',
207+
'Material-UI: a component is changing the uncontrolled expanded state of ExpansionPanel to be controlled.',
209208
);
210209
});
211210
});

packages/material-ui/src/RadioGroup/RadioGroup.test.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,8 @@ describe('<RadioGroup />', () => {
348348
);
349349

350350
wrapper.setProps({ value: undefined });
351-
assert.include(
352-
consoleErrorMock.messages()[0],
353-
'A component is changing a controlled RadioGroup to be uncontrolled.',
351+
expect(consoleErrorMock.messages()[0]).to.include(
352+
'Material-UI: a component is changing the controlled value state of RadioGroup to be uncontrolled.',
354353
);
355354
});
356355

@@ -362,9 +361,8 @@ describe('<RadioGroup />', () => {
362361
);
363362

364363
wrapper.setProps({ value: 'foo' });
365-
assert.include(
366-
consoleErrorMock.messages()[0],
367-
'A component is changing an uncontrolled RadioGroup to be controlled.',
364+
expect(consoleErrorMock.messages()[0]).to.include(
365+
'Material-UI: a component is changing the uncontrolled value state of RadioGroup to be controlled.',
368366
);
369367
});
370368
});

packages/material-ui/src/Slider/Slider.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ describe('<Slider />', () => {
515515

516516
setProps({ value: undefined });
517517
expect(consoleErrorMock.messages()[0]).to.include(
518-
'A component is changing a controlled Slider to be uncontrolled.',
518+
'Material-UI: a component is changing the controlled value state of Slider to be uncontrolled.',
519519
);
520520
});
521521

@@ -524,7 +524,7 @@ describe('<Slider />', () => {
524524

525525
setProps({ value: [20, 50] });
526526
expect(consoleErrorMock.messages()[0]).to.include(
527-
'A component is changing an uncontrolled Slider to be controlled.',
527+
'Material-UI: a component is changing the uncontrolled value state of Slider to be controlled.',
528528
);
529529
});
530530
});

0 commit comments

Comments
 (0)