Skip to content

Commit

Permalink
[perf] Reduce individual resize observer instantiations for each trun…
Browse files Browse the repository at this point in the history
…cated line

- by using `EuiAutoSizer` and attempting to guesstimate the default available width for options that don't have custom `append` or `prepend` nodes

[cleanup] Move several `options` based vars to state, to more explicitly limit the amount of times they fire/cause rerenders

+ fix `EuiAutoSizer` test mock to fire `onResize` on mount (same behavior as actual prod)
  • Loading branch information
cee-chen committed Nov 25, 2023
1 parent 5a1fe1b commit 0a58bd8
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 58 deletions.
5 changes: 4 additions & 1 deletion src-docs/src/views/selectable/selectable_truncation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ export default () => {
)}
</EuiFlexGroup>
<EuiSpacer />
<EuiPanel paddingSize="s" style={{ inlineSize: 400 }}>
<EuiPanel
paddingSize="s"
style={{ inlineSize: 400, resize: 'horizontal', overflow: 'auto' }}
>
<EuiSelectable
searchable={true}
options={options}
Expand Down
8 changes: 7 additions & 1 deletion src/components/auto_sizer/auto_sizer.testenv.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@
* Side Public License, v 1.
*/

import React from 'react';
import React, { useEffect } from 'react';

export const EuiAutoSizer = ({
children,
defaultHeight,
defaultWidth,
onResize,
}: any) => {
const childrenParams = {
height: defaultHeight ?? 600,
width: defaultWidth ?? 600,
};

useEffect(() => {
onResize?.(childrenParams);
}, [onResize, defaultHeight, defaultWidth]); // eslint-disable-line react-hooks/exhaustive-deps

return <div data-eui="EuiAutoSizer">{children(childrenParams)}</div>;
};
104 changes: 88 additions & 16 deletions src/components/selectable/selectable_list/selectable_list.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,22 +345,6 @@ describe('EuiSelectableListItem', () => {
expect(container.querySelector('.euiTextTruncate')).toBeInTheDocument();
});

it('defaults to CSS truncation if truncationProps is not passed', () => {
const { container } = render(
<EuiSelectableList
options={options}
{...selectableListRequiredProps}
/>
);

expect(
container.querySelector('.euiTextTruncate')
).not.toBeInTheDocument();
expect(
container.querySelector('.euiSelectableListItem__text--truncate')
).toBeInTheDocument();
});

it('allows setting `truncationProps` per-option', () => {
const { container } = render(
<EuiSelectableList
Expand All @@ -377,6 +361,94 @@ describe('EuiSelectableListItem', () => {
});
});

describe('truncation performance optimization', () => {
it('does not render EuiTextTruncate if not virtualized and text is wrapping', () => {
const { container } = render(
<EuiSelectableList
options={options}
{...selectableListRequiredProps}
isVirtualized={false}
textWrap="wrap"
/>
);

expect(
container.querySelector('.euiTextTruncate')
).not.toBeInTheDocument();
});

it('does not render EuiTextTruncate, and defaults to CSS truncation, if no truncationProps have been passed', () => {
const { container } = render(
<EuiSelectableList
options={options}
{...selectableListRequiredProps}
truncationProps={undefined}
/>
);

expect(
container.querySelector('.euiTextTruncate')
).not.toBeInTheDocument();
expect(
container.querySelector('.euiSelectableListItem__text--truncate')
).toBeInTheDocument();
});

it('attempts to use a default optimized option width calculated from the wrapping EuiAutoSizer', () => {
const { container } = render(
<EuiSelectableList
options={options}
{...selectableListRequiredProps}
isVirtualized={true}
searchable={true}
searchValue="searching"
/>
);

expect(container.querySelector('.euiTextTruncate')).toBeInTheDocument();
expect(
container.querySelector('[data-resize-observer]')
).not.toBeInTheDocument();
});

it('falls back to individual resize observers if options have append/prepend nodes', () => {
const { container } = render(
<EuiSelectableList
{...selectableListRequiredProps}
options={[
{ label: 'A', append: 'post' },
{ label: 'B' },
{ label: 'C', prepend: 'pre' },
]}
truncationProps={{ truncation: 'start' }}
/>
);

expect(container.querySelectorAll('.euiTextTruncate')).toHaveLength(3);
expect(container.querySelectorAll('[data-resize-observer]')).toHaveLength(
2
);
});

it('falls back to individual resize observers if individual options are truncated', () => {
const { container } = render(
<EuiSelectableList
{...selectableListRequiredProps}
options={[
{ label: 'A' },
{ label: 'B', truncationProps: { truncation: 'middle' } },
{ label: 'C', truncationProps: { truncation: 'startEnd' } },
]}
/>
);

expect(container.querySelectorAll('.euiTextTruncate')).toHaveLength(2);
expect(container.querySelectorAll('[data-resize-observer]')).toHaveLength(
2
);
});
});

describe('group labels', () => {
const optionsWithGroupLabels: EuiSelectableOption[] = [
{ label: 'Spaaaace' },
Expand Down
Loading

0 comments on commit 0a58bd8

Please sign in to comment.