Skip to content

Commit a07fe4a

Browse files
authored
Merge pull request #297 from rvsia/refreshOptionsPF4Select
fix(pf4): allow to update options, loadOptions props
2 parents 7f0cb80 + a8fc960 commit a07fe4a

File tree

2 files changed

+134
-11
lines changed

2 files changed

+134
-11
lines changed

packages/pf4-component-mapper/src/form-fields/select/select.js

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import ReactSelect from 'react-select';
44
import CreatableSelect from 'react-select/creatable';
5+
import isEqual from 'lodash/isEqual';
6+
7+
import fnToString from '@data-driven-forms/common/src/utils/fn-to-string';
58

69
import MultiValueContainer from './multi-value-container';
710
import ValueContainer from './value-container';
@@ -25,19 +28,41 @@ export class Select extends React.Component {
2528
};
2629

2730
componentDidMount() {
28-
const { loadOptions } = this.props;
29-
if (!loadOptions) {
30-
this.setState({
31-
isLoading: false,
32-
});
31+
if (!this.props.loadOptions) {
32+
this.setState({ isLoading: false });
3333
} else {
34-
return loadOptions()
35-
.then((data) => {
36-
return this.setState({
37-
allOptions: data,
38-
isLoading: false,
39-
});});
34+
return this.updateOptions();
35+
}
36+
}
37+
38+
componentDidUpdate(prevProps) {
39+
if (!isEqual(this.props.options, prevProps.options)) {
40+
if (!this.props.options.map(({ value }) => value).includes(this.props.value)) {
41+
this.props.onChange(undefined);
42+
}
43+
44+
this.setState({ allOptions: this.props.options });
4045
}
46+
47+
if (this.props.loadOptions && fnToString(this.props.loadOptions) !== fnToString(prevProps.loadOptions)){
48+
return this.updateOptions();
49+
}
50+
}
51+
52+
updateOptions = () => {
53+
this.setState({ isLoading: true });
54+
55+
return this.props.loadOptions().then(data => {
56+
if (!data.map(({ value }) => value).includes(this.props.value)) {
57+
this.props.onChange(undefined);
58+
}
59+
60+
return this.setState({
61+
allOptions: data,
62+
isLoading: false,
63+
promises: {},
64+
});
65+
});
4166
}
4267

4368
componentWillUnmount() {

packages/pf4-component-mapper/src/tests/select/select.test.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react';
22
import { mount } from 'enzyme';
33
import { components } from 'react-select';
44
import ReactSelect from 'react-select';
5+
import isEqual from 'lodash/isEqual';
6+
57
import DataDrivenSelect, { Select } from '../../form-fields/select/select';
68

79
describe('<Select />', () => {
@@ -166,4 +168,100 @@ describe('<Select />', () => {
166168
});
167169
});
168170
});
171+
172+
describe('reloading props', () => {
173+
const NEW_OPTIONS = [{ label: 'Different label', value: 2 }];
174+
let asyncLoading;
175+
let asyncLoadingNew;
176+
177+
beforeEach(() => {
178+
asyncLoading = () => Promise.resolve(initialProps.options);
179+
asyncLoadingNew = () => Promise.resolve(NEW_OPTIONS);
180+
});
181+
182+
it('should change the options when options prop is changed', () => {
183+
const wrapper = mount(<Select { ...initialProps } />);
184+
185+
let innerSelectProps = wrapper.find(ReactSelect).props().options;
186+
187+
expect(isEqual(innerSelectProps, initialProps.options)).toEqual(true);
188+
189+
wrapper.setProps({ options: NEW_OPTIONS });
190+
wrapper.update();
191+
innerSelectProps = wrapper.find(ReactSelect).props().options;
192+
193+
expect(innerSelectProps).toEqual(NEW_OPTIONS);
194+
});
195+
196+
it('should change the options when loadOptions prop is changed', (done) => {
197+
const wrapper = mount(<Select { ...initialProps } loadOptions={ asyncLoading }/>);
198+
199+
setImmediate(() => {
200+
wrapper.update();
201+
let innerSelectProps = wrapper.find(ReactSelect).props().options;
202+
203+
expect(isEqual(innerSelectProps, initialProps.options)).toEqual(true);
204+
205+
wrapper.setProps({ loadOptions: asyncLoadingNew });
206+
207+
setImmediate(() => {
208+
wrapper.update();
209+
innerSelectProps = wrapper.find(ReactSelect).props().options;
210+
211+
expect(isEqual(innerSelectProps, NEW_OPTIONS)).toEqual(true);
212+
done();
213+
});
214+
});
215+
});
216+
217+
it('should change the value when new options do not include it', () => {
218+
const wrapper = mount(<Select { ...initialProps } value={ 1 }/>);
219+
220+
wrapper.setProps({ options: NEW_OPTIONS });
221+
wrapper.update();
222+
223+
expect(onChange).toHaveBeenCalledWith(undefined);
224+
});
225+
226+
it('not should change the value when new options include it', () => {
227+
const wrapper = mount(<Select { ...initialProps } value={ 2 }/>);
228+
229+
wrapper.setProps({ options: NEW_OPTIONS });
230+
wrapper.update();
231+
232+
expect(onChange).not.toHaveBeenCalled();
233+
});
234+
235+
it('should reset the value when loadOptions prop is changed and new options do not include the value', (done) => {
236+
const wrapper = mount(<Select { ...initialProps } loadOptions={ asyncLoading } value={ 1 }/>);
237+
238+
setImmediate(() => {
239+
wrapper.update();
240+
wrapper.setProps({ loadOptions: asyncLoadingNew });
241+
242+
setImmediate(() => {
243+
wrapper.update();
244+
245+
expect(onChange).toHaveBeenCalledWith(undefined);
246+
done();
247+
});
248+
});
249+
});
250+
251+
it('should not reset the value when loadOptions prop is changed and new options includes the value', (done) => {
252+
const wrapper = mount(<Select { ...initialProps } loadOptions={ asyncLoading } value={ 2 }/>);
253+
254+
setImmediate(() => {
255+
wrapper.update();
256+
wrapper.setProps({ loadOptions: asyncLoadingNew });
257+
258+
setImmediate(() => {
259+
wrapper.update();
260+
261+
expect(onChange).not.toHaveBeenCalled();
262+
done();
263+
});
264+
});
265+
});
266+
});
169267
});

0 commit comments

Comments
 (0)