Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EuiSelect] allow select's value to be reset with an undefined value #4428

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Added a `size` prop to `EuiContextMenu` and added a smaller size ([#4409](https://github.com/elastic/eui/pull/4409))
- Added a `textSize` prop to `EuiHealth` ([#4420](https://github.com/elastic/eui/pull/4420))
- Removed selected item of `EuiSelect` when `hasNoInitialSelection=true` and value reset to `undefined` ([#4428](https://github.com/elastic/eui/pull/4428))

## [`31.2.0`](https://github.com/elastic/eui/tree/v31.2.0)

Expand Down
81 changes: 73 additions & 8 deletions src/components/form/select/select.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
/* eslint-disable no-irregular-whitespace */

import React from 'react';
import { render } from 'enzyme';
import { render, mount } from 'enzyme';
import { requiredProps } from '../../../test/required_props';

import { EuiSelect } from './select';
Expand All @@ -31,7 +32,7 @@ jest.mock('../validatable_control', () => ({
}));

describe('EuiSelect', () => {
test('is rendered', () => {
it('is rendered', () => {
const component = render(
<EuiSelect id="id" name="name" {...requiredProps} />
);
Expand All @@ -40,7 +41,7 @@ describe('EuiSelect', () => {
});

describe('props', () => {
test('options are rendered', () => {
it('options are rendered', () => {
const component = render(
<EuiSelect
options={[
Expand All @@ -53,25 +54,25 @@ describe('EuiSelect', () => {
expect(component).toMatchSnapshot();
});

test('isInvalid is rendered', () => {
it('isInvalid is rendered', () => {
const component = render(<EuiSelect isInvalid />);

expect(component).toMatchSnapshot();
});

test('fullWidth is rendered', () => {
it('fullWidth is rendered', () => {
const component = render(<EuiSelect fullWidth />);

expect(component).toMatchSnapshot();
});

test('isLoading is rendered', () => {
it('isLoading is rendered', () => {
const component = render(<EuiSelect isLoading />);

expect(component).toMatchSnapshot();
});

test('disabled options are rendered', () => {
it('disabled options are rendered', () => {
const component = render(
<EuiSelect
options={[
Expand All @@ -84,7 +85,7 @@ describe('EuiSelect', () => {
expect(component).toMatchSnapshot();
});

test('value option is rendered', () => {
it('value option is rendered', () => {
const component = render(
<EuiSelect
options={[
Expand All @@ -99,4 +100,68 @@ describe('EuiSelect', () => {
expect(component).toMatchSnapshot();
});
});

describe('hasNoInitialSelection', () => {
it('renders with an extra option at the top', () => {
const component = mount(
<EuiSelect
hasNoInitialSelection
options={[
{ value: '1', text: 'Option #1' },
{ value: '2', text: 'Option #2' },
]}
onChange={() => {}}
/>
);

expect(component.find('option').length).toBe(3);
expect(component.find('option').at(0)).toMatchInlineSnapshot(`
<option
disabled={true}
hidden={true}
style={
Object {
"display": "none",
}
}
value=""
>

</option>
`);
});

it('can be reset to an empty initial selection', () => {
const component = mount(
<EuiSelect
hasNoInitialSelection
value="1"
options={[
{ value: '1', text: 'Option #1' },
{ value: '2', text: 'Option #2' },
]}
onChange={() => {}}
/>
);

expect(
component.find('select').getDOMNode<HTMLSelectElement>().value
).toBe('1');

component.setProps({ value: '' });
expect(
component.find('select').getDOMNode<HTMLSelectElement>().value
).toBe('');

component.setProps({ value: '1' });
expect(
component.find('select').getDOMNode<HTMLSelectElement>().value
).toBe('1');

component.setProps({ value: undefined });
expect(
component.find('select').getDOMNode<HTMLSelectElement>().value
).toBe('');
});
});
});
6 changes: 5 additions & 1 deletion src/components/form/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,16 @@ export const EuiSelect: FunctionComponent<EuiSelectProps> = ({
hasNoInitialSelection = false,
defaultValue,
compressed = false,
value,
value: _value,
prepend,
append,
onMouseUp,
...rest
}) => {
// if this is injecting an empty option for `hasNoInitialSelection` then
// value needs to fallback to an empty string to interact properly with `defaultValue`
const value = hasNoInitialSelection ? _value ?? '' : _value;

const handleMouseUp = (e: React.MouseEvent<HTMLSelectElement>) => {
// Normalizes cross-browser mouse eventing by preventing propagation,
// notably for use in conjunction with EuiOutsideClickDetector.
Expand Down