@instructure/ui-simple-select 10.2.3-snapshot-6 → 10.2.3-snapshot-7
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +5 -2
- package/es/SimpleSelect/__new-tests__/SimpleSelect.test.js +49 -1
- package/es/SimpleSelect/index.js +18 -0
- package/lib/SimpleSelect/__new-tests__/SimpleSelect.test.js +48 -0
- package/lib/SimpleSelect/index.js +18 -0
- package/package.json +15 -15
- package/src/SimpleSelect/__new-tests__/SimpleSelect.test.tsx +61 -1
- package/src/SimpleSelect/index.tsx +26 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/SimpleSelect/index.d.ts +1 -0
- package/types/SimpleSelect/index.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
@@ -3,9 +3,12 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
5
5
|
|
6
|
-
## [10.2.3-snapshot-
|
6
|
+
## [10.2.3-snapshot-7](https://github.com/instructure/instructure-ui/compare/v10.2.2...v10.2.3-snapshot-7) (2024-09-27)
|
7
7
|
|
8
|
-
|
8
|
+
|
9
|
+
### Bug Fixes
|
10
|
+
|
11
|
+
* **ui-simple-select:** ensure input value updates correctly when options change ([4dc7cb2](https://github.com/instructure/instructure-ui/commit/4dc7cb2cfde69d28baaaced32a5e63aea9a48ee1))
|
9
12
|
|
10
13
|
|
11
14
|
|
@@ -22,7 +22,7 @@
|
|
22
22
|
* SOFTWARE.
|
23
23
|
*/
|
24
24
|
import React from 'react';
|
25
|
-
import { render } from '@testing-library/react';
|
25
|
+
import { render, fireEvent, screen } from '@testing-library/react';
|
26
26
|
import { vi } from 'vitest';
|
27
27
|
import '@testing-library/jest-dom';
|
28
28
|
import SimpleSelect from '../index';
|
@@ -63,4 +63,52 @@ describe('<SimpleSelect />', () => {
|
|
63
63
|
const input = container.querySelector('input');
|
64
64
|
expect(input).toHaveAttribute('role', 'combobox');
|
65
65
|
});
|
66
|
+
describe('children', () => {
|
67
|
+
const initialOptions = ['foo', 'bar'];
|
68
|
+
const updatedOptions = ['bar', 'baz'];
|
69
|
+
const getOptions = options => options.map(opt => /*#__PURE__*/React.createElement(SimpleSelect.Option, {
|
70
|
+
id: opt,
|
71
|
+
key: opt,
|
72
|
+
value: opt
|
73
|
+
}, opt));
|
74
|
+
const renderSimpleSelect = options => {
|
75
|
+
return render( /*#__PURE__*/React.createElement(SimpleSelect, {
|
76
|
+
renderLabel: "Choose an option"
|
77
|
+
}, getOptions(options)));
|
78
|
+
};
|
79
|
+
it('should clear selection if selected option does not exist in updated options', () => {
|
80
|
+
const _renderSimpleSelect = renderSimpleSelect(initialOptions),
|
81
|
+
rerender = _renderSimpleSelect.rerender;
|
82
|
+
const input = screen.getByRole('combobox', {
|
83
|
+
name: 'Choose an option'
|
84
|
+
});
|
85
|
+
fireEvent.click(input);
|
86
|
+
const fooOption = screen.getByRole('option', {
|
87
|
+
name: 'foo'
|
88
|
+
});
|
89
|
+
fireEvent.click(fooOption);
|
90
|
+
expect(input).toHaveValue('foo');
|
91
|
+
rerender( /*#__PURE__*/React.createElement(SimpleSelect, {
|
92
|
+
renderLabel: "Choose an option"
|
93
|
+
}, getOptions(updatedOptions)));
|
94
|
+
expect(input).toHaveValue('');
|
95
|
+
});
|
96
|
+
it('should persist selected option if it exists in updated options', () => {
|
97
|
+
const _renderSimpleSelect2 = renderSimpleSelect(initialOptions),
|
98
|
+
rerender = _renderSimpleSelect2.rerender;
|
99
|
+
const input = screen.getByRole('combobox', {
|
100
|
+
name: 'Choose an option'
|
101
|
+
});
|
102
|
+
fireEvent.click(input);
|
103
|
+
const barOption = screen.getByRole('option', {
|
104
|
+
name: 'bar'
|
105
|
+
});
|
106
|
+
fireEvent.click(barOption);
|
107
|
+
expect(input).toHaveValue('bar');
|
108
|
+
rerender( /*#__PURE__*/React.createElement(SimpleSelect, {
|
109
|
+
renderLabel: "Choose an option"
|
110
|
+
}, getOptions(updatedOptions)));
|
111
|
+
expect(input).toHaveValue('bar');
|
112
|
+
});
|
113
|
+
});
|
66
114
|
});
|
package/es/SimpleSelect/index.js
CHANGED
@@ -176,7 +176,25 @@ let SimpleSelect = (_dec = withDeterministicId(), _dec2 = testable(), _dec(_clas
|
|
176
176
|
props: this.props
|
177
177
|
});
|
178
178
|
}
|
179
|
+
hasOptionsChanged(prevChildren, currentChildren) {
|
180
|
+
const getValues = children => React.Children.map(children, child => {
|
181
|
+
if ( /*#__PURE__*/React.isValidElement(child)) {
|
182
|
+
return child.props.value;
|
183
|
+
}
|
184
|
+
return null;
|
185
|
+
});
|
186
|
+
const prevValues = getValues(prevChildren);
|
187
|
+
const currentValues = getValues(currentChildren);
|
188
|
+
return JSON.stringify(prevValues) !== JSON.stringify(currentValues);
|
189
|
+
}
|
179
190
|
componentDidUpdate(prevProps) {
|
191
|
+
if (this.hasOptionsChanged(prevProps.children, this.props.children)) {
|
192
|
+
const option = this.getOption('value', this.state.inputValue);
|
193
|
+
this.setState({
|
194
|
+
inputValue: option ? option.props.children : void 0,
|
195
|
+
selectedOptionId: option ? option.props.id : ''
|
196
|
+
});
|
197
|
+
}
|
180
198
|
if (this.props.value !== prevProps.value) {
|
181
199
|
let option = this.getOption('value', this.props.value);
|
182
200
|
if (typeof this.props.value === 'undefined') {
|
@@ -68,4 +68,52 @@ describe('<SimpleSelect />', () => {
|
|
68
68
|
const input = container.querySelector('input');
|
69
69
|
expect(input).toHaveAttribute('role', 'combobox');
|
70
70
|
});
|
71
|
+
describe('children', () => {
|
72
|
+
const initialOptions = ['foo', 'bar'];
|
73
|
+
const updatedOptions = ['bar', 'baz'];
|
74
|
+
const getOptions = options => options.map(opt => /*#__PURE__*/_react.default.createElement(_index.default.Option, {
|
75
|
+
id: opt,
|
76
|
+
key: opt,
|
77
|
+
value: opt
|
78
|
+
}, opt));
|
79
|
+
const renderSimpleSelect = options => {
|
80
|
+
return (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, {
|
81
|
+
renderLabel: "Choose an option"
|
82
|
+
}, getOptions(options)));
|
83
|
+
};
|
84
|
+
it('should clear selection if selected option does not exist in updated options', () => {
|
85
|
+
const _renderSimpleSelect = renderSimpleSelect(initialOptions),
|
86
|
+
rerender = _renderSimpleSelect.rerender;
|
87
|
+
const input = _react2.screen.getByRole('combobox', {
|
88
|
+
name: 'Choose an option'
|
89
|
+
});
|
90
|
+
_react2.fireEvent.click(input);
|
91
|
+
const fooOption = _react2.screen.getByRole('option', {
|
92
|
+
name: 'foo'
|
93
|
+
});
|
94
|
+
_react2.fireEvent.click(fooOption);
|
95
|
+
expect(input).toHaveValue('foo');
|
96
|
+
rerender( /*#__PURE__*/_react.default.createElement(_index.default, {
|
97
|
+
renderLabel: "Choose an option"
|
98
|
+
}, getOptions(updatedOptions)));
|
99
|
+
expect(input).toHaveValue('');
|
100
|
+
});
|
101
|
+
it('should persist selected option if it exists in updated options', () => {
|
102
|
+
const _renderSimpleSelect2 = renderSimpleSelect(initialOptions),
|
103
|
+
rerender = _renderSimpleSelect2.rerender;
|
104
|
+
const input = _react2.screen.getByRole('combobox', {
|
105
|
+
name: 'Choose an option'
|
106
|
+
});
|
107
|
+
_react2.fireEvent.click(input);
|
108
|
+
const barOption = _react2.screen.getByRole('option', {
|
109
|
+
name: 'bar'
|
110
|
+
});
|
111
|
+
_react2.fireEvent.click(barOption);
|
112
|
+
expect(input).toHaveValue('bar');
|
113
|
+
rerender( /*#__PURE__*/_react.default.createElement(_index.default, {
|
114
|
+
renderLabel: "Choose an option"
|
115
|
+
}, getOptions(updatedOptions)));
|
116
|
+
expect(input).toHaveValue('bar');
|
117
|
+
});
|
118
|
+
});
|
71
119
|
});
|
@@ -187,7 +187,25 @@ let SimpleSelect = exports.SimpleSelect = (_dec = (0, _withDeterministicId.withD
|
|
187
187
|
props: this.props
|
188
188
|
});
|
189
189
|
}
|
190
|
+
hasOptionsChanged(prevChildren, currentChildren) {
|
191
|
+
const getValues = children => _react.default.Children.map(children, child => {
|
192
|
+
if ( /*#__PURE__*/_react.default.isValidElement(child)) {
|
193
|
+
return child.props.value;
|
194
|
+
}
|
195
|
+
return null;
|
196
|
+
});
|
197
|
+
const prevValues = getValues(prevChildren);
|
198
|
+
const currentValues = getValues(currentChildren);
|
199
|
+
return JSON.stringify(prevValues) !== JSON.stringify(currentValues);
|
200
|
+
}
|
190
201
|
componentDidUpdate(prevProps) {
|
202
|
+
if (this.hasOptionsChanged(prevProps.children, this.props.children)) {
|
203
|
+
const option = this.getOption('value', this.state.inputValue);
|
204
|
+
this.setState({
|
205
|
+
inputValue: option ? option.props.children : void 0,
|
206
|
+
selectedOptionId: option ? option.props.id : ''
|
207
|
+
});
|
208
|
+
}
|
191
209
|
if (this.props.value !== prevProps.value) {
|
192
210
|
let option = this.getOption('value', this.props.value);
|
193
211
|
if (typeof this.props.value === 'undefined') {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@instructure/ui-simple-select",
|
3
|
-
"version": "10.2.3-snapshot-
|
3
|
+
"version": "10.2.3-snapshot-7",
|
4
4
|
"description": "A component for standard select element behavior.",
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
6
6
|
"module": "./es/index.js",
|
@@ -24,23 +24,23 @@
|
|
24
24
|
"license": "MIT",
|
25
25
|
"dependencies": {
|
26
26
|
"@babel/runtime": "^7.24.5",
|
27
|
-
"@instructure/console": "10.2.3-snapshot-
|
28
|
-
"@instructure/shared-types": "10.2.3-snapshot-
|
29
|
-
"@instructure/ui-form-field": "10.2.3-snapshot-
|
30
|
-
"@instructure/ui-position": "10.2.3-snapshot-
|
31
|
-
"@instructure/ui-prop-types": "10.2.3-snapshot-
|
32
|
-
"@instructure/ui-react-utils": "10.2.3-snapshot-
|
33
|
-
"@instructure/ui-select": "10.2.3-snapshot-
|
34
|
-
"@instructure/ui-testable": "10.2.3-snapshot-
|
27
|
+
"@instructure/console": "10.2.3-snapshot-7",
|
28
|
+
"@instructure/shared-types": "10.2.3-snapshot-7",
|
29
|
+
"@instructure/ui-form-field": "10.2.3-snapshot-7",
|
30
|
+
"@instructure/ui-position": "10.2.3-snapshot-7",
|
31
|
+
"@instructure/ui-prop-types": "10.2.3-snapshot-7",
|
32
|
+
"@instructure/ui-react-utils": "10.2.3-snapshot-7",
|
33
|
+
"@instructure/ui-select": "10.2.3-snapshot-7",
|
34
|
+
"@instructure/ui-testable": "10.2.3-snapshot-7",
|
35
35
|
"prop-types": "^15.8.1"
|
36
36
|
},
|
37
37
|
"devDependencies": {
|
38
|
-
"@instructure/ui-babel-preset": "10.2.3-snapshot-
|
39
|
-
"@instructure/ui-color-utils": "10.2.3-snapshot-
|
40
|
-
"@instructure/ui-icons": "10.2.3-snapshot-
|
41
|
-
"@instructure/ui-test-locator": "10.2.3-snapshot-
|
42
|
-
"@instructure/ui-test-utils": "10.2.3-snapshot-
|
43
|
-
"@instructure/ui-utils": "10.2.3-snapshot-
|
38
|
+
"@instructure/ui-babel-preset": "10.2.3-snapshot-7",
|
39
|
+
"@instructure/ui-color-utils": "10.2.3-snapshot-7",
|
40
|
+
"@instructure/ui-icons": "10.2.3-snapshot-7",
|
41
|
+
"@instructure/ui-test-locator": "10.2.3-snapshot-7",
|
42
|
+
"@instructure/ui-test-utils": "10.2.3-snapshot-7",
|
43
|
+
"@instructure/ui-utils": "10.2.3-snapshot-7",
|
44
44
|
"@testing-library/jest-dom": "^6.4.6",
|
45
45
|
"@testing-library/react": "^15.0.7",
|
46
46
|
"vitest": "^2.0.2"
|
@@ -22,7 +22,7 @@
|
|
22
22
|
* SOFTWARE.
|
23
23
|
*/
|
24
24
|
import React from 'react'
|
25
|
-
import { render } from '@testing-library/react'
|
25
|
+
import { render, fireEvent, screen } from '@testing-library/react'
|
26
26
|
import { vi } from 'vitest'
|
27
27
|
import '@testing-library/jest-dom'
|
28
28
|
import SimpleSelect from '../index'
|
@@ -73,4 +73,64 @@ describe('<SimpleSelect />', () => {
|
|
73
73
|
const input = container.querySelector('input')
|
74
74
|
expect(input).toHaveAttribute('role', 'combobox')
|
75
75
|
})
|
76
|
+
|
77
|
+
describe('children', () => {
|
78
|
+
const initialOptions: ExampleOption[] = ['foo', 'bar']
|
79
|
+
const updatedOptions: ExampleOption[] = ['bar', 'baz']
|
80
|
+
|
81
|
+
const getOptions = (options: string[]) =>
|
82
|
+
options.map((opt) => (
|
83
|
+
<SimpleSelect.Option id={opt} key={opt} value={opt}>
|
84
|
+
{opt}
|
85
|
+
</SimpleSelect.Option>
|
86
|
+
))
|
87
|
+
|
88
|
+
const renderSimpleSelect = (options: ExampleOption[]) => {
|
89
|
+
return render(
|
90
|
+
<SimpleSelect renderLabel="Choose an option">
|
91
|
+
{getOptions(options)}
|
92
|
+
</SimpleSelect>
|
93
|
+
)
|
94
|
+
}
|
95
|
+
|
96
|
+
it('should clear selection if selected option does not exist in updated options', () => {
|
97
|
+
const { rerender } = renderSimpleSelect(initialOptions)
|
98
|
+
|
99
|
+
const input = screen.getByRole('combobox', { name: 'Choose an option' })
|
100
|
+
fireEvent.click(input)
|
101
|
+
|
102
|
+
const fooOption = screen.getByRole('option', { name: 'foo' })
|
103
|
+
fireEvent.click(fooOption)
|
104
|
+
|
105
|
+
expect(input).toHaveValue('foo')
|
106
|
+
|
107
|
+
rerender(
|
108
|
+
<SimpleSelect renderLabel="Choose an option">
|
109
|
+
{getOptions(updatedOptions)}
|
110
|
+
</SimpleSelect>
|
111
|
+
)
|
112
|
+
|
113
|
+
expect(input).toHaveValue('')
|
114
|
+
})
|
115
|
+
|
116
|
+
it('should persist selected option if it exists in updated options', () => {
|
117
|
+
const { rerender } = renderSimpleSelect(initialOptions)
|
118
|
+
|
119
|
+
const input = screen.getByRole('combobox', { name: 'Choose an option' })
|
120
|
+
fireEvent.click(input)
|
121
|
+
|
122
|
+
const barOption = screen.getByRole('option', { name: 'bar' })
|
123
|
+
fireEvent.click(barOption)
|
124
|
+
|
125
|
+
expect(input).toHaveValue('bar')
|
126
|
+
|
127
|
+
rerender(
|
128
|
+
<SimpleSelect renderLabel="Choose an option">
|
129
|
+
{getOptions(updatedOptions)}
|
130
|
+
</SimpleSelect>
|
131
|
+
)
|
132
|
+
|
133
|
+
expect(input).toHaveValue('bar')
|
134
|
+
})
|
135
|
+
})
|
76
136
|
})
|
@@ -130,7 +130,33 @@ class SimpleSelect extends Component<SimpleSelectProps, SimpleSelectState> {
|
|
130
130
|
return getInteraction({ props: this.props })
|
131
131
|
}
|
132
132
|
|
133
|
+
hasOptionsChanged(
|
134
|
+
prevChildren: SimpleSelectProps['children'],
|
135
|
+
currentChildren: SimpleSelectProps['children']
|
136
|
+
) {
|
137
|
+
const getValues = (children: SimpleSelectProps['children']) =>
|
138
|
+
React.Children.map(children, (child) => {
|
139
|
+
if (React.isValidElement(child)) {
|
140
|
+
return child.props.value
|
141
|
+
}
|
142
|
+
return null
|
143
|
+
})
|
144
|
+
|
145
|
+
const prevValues = getValues(prevChildren)
|
146
|
+
const currentValues = getValues(currentChildren)
|
147
|
+
|
148
|
+
return JSON.stringify(prevValues) !== JSON.stringify(currentValues)
|
149
|
+
}
|
150
|
+
|
133
151
|
componentDidUpdate(prevProps: SimpleSelectProps) {
|
152
|
+
if (this.hasOptionsChanged(prevProps.children, this.props.children)) {
|
153
|
+
const option = this.getOption('value', this.state.inputValue)
|
154
|
+
this.setState({
|
155
|
+
inputValue: option ? option.props.children : undefined,
|
156
|
+
selectedOptionId: option ? option.props.id : ''
|
157
|
+
})
|
158
|
+
}
|
159
|
+
|
134
160
|
if (this.props.value !== prevProps.value) {
|
135
161
|
let option = this.getOption('value', this.props.value)
|
136
162
|
if (typeof this.props.value === 'undefined') {
|