Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: elastic/eui
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: fa7133ac297044f6c365880ef44f32dde652b079
Choose a base ref
..
head repository: elastic/eui
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: b400a4df9e76dafc93a2627f1522f93c01d1aaaa
Choose a head ref
Showing with 36 additions and 32 deletions.
  1. +0 −4 changelogs/upcoming/7683.md
  2. +21 −4 src/components/selectable/selectable.tsx
  3. +15 −24 src/components/selectable/selectable_list/selectable_list.tsx
4 changes: 0 additions & 4 deletions changelogs/upcoming/7683.md
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
- Updated `EuiSelectable`'s `isPreFiltered` prop to allow passing a configuration object, which allows disabling search highlighting in addition to search filtering

**Bug fixes**

- Fixed unvirtualized single selection `EuiSelectable`s not scrolling the selected option into view on mount
25 changes: 21 additions & 4 deletions src/components/selectable/selectable.tsx
Original file line number Diff line number Diff line change
@@ -207,6 +207,7 @@ export class EuiSelectable<T = {}> extends Component<
private inputRef: HTMLInputElement | null = null;
private containerRef = createRef<HTMLDivElement>();
private optionsListRef = createRef<EuiSelectableList<T>>();
private preventOnFocus = false;
rootId: (suffix?: string) => string;
messageContentId: string;
listId: string;
@@ -233,10 +234,13 @@ export class EuiSelectable<T = {}> extends Component<
searchProps?.onChange?.(initialSearchValue, visibleOptions);

// ensure that the currently selected single option is active if it is in the visibleOptions
let activeOptionIndex = singleSelection
? visibleOptions.findIndex((option) => option.checked)
: undefined;
if (activeOptionIndex === -1) activeOptionIndex = undefined;
const selectedOptions = options.filter((option) => option.checked);
let activeOptionIndex;
if (singleSelection && selectedOptions.length === 1) {
if (visibleOptions.includes(selectedOptions[0])) {
activeOptionIndex = visibleOptions.indexOf(selectedOptions[0]);
}
}

this.state = {
activeOptionIndex,
@@ -302,7 +306,19 @@ export class EuiSelectable<T = {}> extends Component<
return searchHasFocus || listBoxHasFocus;
};

onMouseDown = () => {
// Bypass onFocus when a click event originates from this.containerRef.
// Prevents onFocus from scrolling away from a clicked option and negating the selection event.
// https://github.com/elastic/eui/issues/4147
this.preventOnFocus = true;
};

onFocus = (event?: FocusEvent) => {
if (this.preventOnFocus) {
this.preventOnFocus = false;
return;
}

if (
!this.state.visibleOptions.length ||
this.state.activeOptionIndex != null
@@ -805,6 +821,7 @@ export class EuiSelectable<T = {}> extends Component<
onKeyDown={this.onKeyDown}
onBlur={this.onContainerBlur}
onFocus={this.onFocus}
onMouseDown={this.onMouseDown}
{...rest}
>
{children && children(list, search)}
39 changes: 15 additions & 24 deletions src/components/selectable/selectable_list/selectable_list.tsx
Original file line number Diff line number Diff line change
@@ -248,7 +248,8 @@ export class EuiSelectableList<T> extends Component<
};

componentDidUpdate(prevProps: EuiSelectableListProps<T>) {
const { activeOptionIndex, visibleOptions, options } = this.props;
const { isVirtualized, activeOptionIndex, visibleOptions, options } =
this.props;

if (prevProps.activeOptionIndex !== activeOptionIndex) {
const { makeOptionId } = this.props;
@@ -260,7 +261,19 @@ export class EuiSelectableList<T> extends Component<
);
}

this.scrollActiveOptionIntoView();
if (typeof activeOptionIndex !== 'undefined') {
if (isVirtualized) {
this.listRef?.scrollToItem(activeOptionIndex, 'auto');
} else {
const activeOptionId = `#${makeOptionId(activeOptionIndex)}`;
const activeOptionEl = this.listBoxRef?.querySelector(activeOptionId);
if (activeOptionEl) {
// TODO: we can remove scrollIntoView's conditional chaining once jsdom stubs it
// @see https://github.com/jsdom/jsdom/issues/1695
activeOptionEl.scrollIntoView?.({ block: 'nearest' });
}
}
}
}

if (
@@ -276,28 +289,6 @@ export class EuiSelectableList<T> extends Component<
}
}

componentDidMount() {
requestAnimationFrame(this.scrollActiveOptionIntoView);
}

scrollActiveOptionIntoView = () => {
const { isVirtualized, activeOptionIndex, makeOptionId } = this.props;

if (typeof activeOptionIndex !== 'undefined') {
if (isVirtualized) {
this.listRef?.scrollToItem(activeOptionIndex, 'auto');
} else {
const activeOptionId = `#${makeOptionId(activeOptionIndex)}`;
const activeOptionEl = this.listBoxRef?.querySelector(activeOptionId);
if (activeOptionEl) {
// TODO: we can remove scrollIntoView's conditional chaining once jsdom stubs it
// @see https://github.com/jsdom/jsdom/issues/1695
activeOptionEl.scrollIntoView?.({ block: 'nearest' });
}
}
}
};

// This utility is necessary to exclude group labels from the aria set count
calculateAriaSetAttrs = (optionArray: State<T>['optionArray']) => {
const ariaPosInSetMap: State<T>['ariaPosInSetMap'] = {};