diff --git a/CHANGELOG.md b/CHANGELOG.md
index f26e8a6b796..5d68860295d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
- Update Elastic-Charts to version 13.0.0 and updated the theme object accordingly ([#2381](https://github.com/elastic/eui/pull/2381))
+**Bug fixes**
+
+- Fix `EuiSelectable` to accept programmatic updates to its `options` prop ([#2390](https://github.com/elastic/eui/pull/2390))
+
## [`14.4.0`](https://github.com/elastic/eui/tree/v14.4.0)
- Migrate `EuiEmptyPrompt`and `EuiCard` to TS ([#2387](https://github.com/elastic/eui/pull/2387))
diff --git a/src-docs/src/views/selectable/selectable_search.tsx b/src-docs/src/views/selectable/selectable_search.tsx
index 0d780caa96f..e94c755e1c3 100644
--- a/src-docs/src/views/selectable/selectable_search.tsx
+++ b/src-docs/src/views/selectable/selectable_search.tsx
@@ -1,5 +1,7 @@
import React, { Component, Fragment } from 'react';
+import { EuiButtonEmpty } from '../../../../src/components/button';
+
import { EuiSelectable } from '../../../../src/components/selectable';
import { Option } from '../../../../src/components/selectable/types';
import { Options } from './data';
@@ -23,20 +25,33 @@ export default class extends Component<{}, { options: Option[] }> {
const { options } = this.state;
return (
-
- {(list, search) => (
-
- {search}
- {list}
-
- )}
-
+
+ {
+ this.setState({
+ options: Options.map(option => ({
+ ...option,
+ checked: undefined,
+ })),
+ });
+ }}>
+ Deselect all
+
+
+ {(list, search) => (
+
+ {search}
+ {list}
+
+ )}
+
+
);
}
}
diff --git a/src/components/selectable/selectable.tsx b/src/components/selectable/selectable.tsx
index 7eb1d8354ca..86ba38155c9 100644
--- a/src/components/selectable/selectable.tsx
+++ b/src/components/selectable/selectable.tsx
@@ -148,6 +148,27 @@ export class EuiSelectable extends Component<
};
}
+ static getDerivedStateFromProps(
+ nextProps: EuiSelectableProps,
+ prevState: EuiSelectableState
+ ) {
+ const { options } = nextProps;
+ const { activeOptionIndex, searchValue } = prevState;
+
+ const matchingOptions = getMatchingOptions(options, searchValue);
+
+ const stateUpdate = { visibleOptions: matchingOptions, activeOptionIndex };
+
+ if (
+ activeOptionIndex != null &&
+ activeOptionIndex >= matchingOptions.length
+ ) {
+ stateUpdate.activeOptionIndex = -1;
+ }
+
+ return stateUpdate;
+ }
+
hasActiveOption = () => {
return this.state.activeOptionIndex != null;
};