Skip to content

Patch/cascade component perf#256037

Closed
eokoneyo wants to merge 5 commits intoelastic:mainfrom
eokoneyo:patch/cascade-component-perf
Closed

Patch/cascade component perf#256037
eokoneyo wants to merge 5 commits intoelastic:mainfrom
eokoneyo:patch/cascade-component-perf

Conversation

@eokoneyo
Copy link
Copy Markdown
Contributor

@eokoneyo eokoneyo commented Mar 4, 2026

Summary

Fixes resulting from cascade performance investigation (#255745);

  • Apply the contain CSS property to rows and cells, to reduce style calculations on each subsequnt render [Cascade] Specify strict layout guidance for rows and cell #256630
  • Removed the previous API exposed from the virtualizer for manually signally when to opt out of scroll adjustments, we do this automatically without any manual intervention
  • Changes to cascade store, so all data is initialised up front, and changes that reset the cascade state are defined only in the provider. Rework cascade internal store initialization to be data-driven from the provider #257220
  • Provide a mechanism to read leaf data from an external source, this is mostly for the discover use case.
  • Adopt improved useCascadeVirtualizer within discover, that controls it's nested virtualizer, handles removing the render coordination responsibility from the consumer. [Cascade] Virtualizer Improvements #256699
  • Removes the unnecessary use of the EUI Truncate text component, this component creates a canvas for every row instance with it's own resize observers that also get called on width change, the functionality provided here has been replaced with a simple line-clamp property which widely supported except for in IE and Opera Mini, see here [Cascade] remove unnecessary use of EUITruncateText #256254
  • Adjustments to storybook; all cascade specific stories are now split into their own individual file with the multiple stats story now rendering 100K rows
  • Modifies the scroll sync util to use one single resize-observer across entire cascade element, said observer will be attached to only the current row being hover on, and will remain observing said till another row is hovered on since anyways we can only show the scroll button on single hover rows, there's no need to spawn all those observers. [Cascade] Improve scroll sync implementation #256511

This PR is still a Work In Progress, created it so I can evaluate the experience for a production build in a la carte.

@eokoneyo eokoneyo self-assigned this Mar 4, 2026
@eokoneyo eokoneyo added the Team:SharedUX Platform AppEx-SharedUX (formerly Global Experience) t// label Mar 4, 2026
@elasticmachine
Copy link
Copy Markdown
Contributor

elasticmachine commented Mar 4, 2026

🤖 Jobs for this PR can be triggered through checkboxes. 🚧

ℹ️ To trigger the CI, please tick the checkbox below 👇

  • Click to trigger kibana-pull-request for this PR!
  • Click to trigger kibana-deploy-project-from-pr for this PR!
  • Click to trigger kibana-deploy-cloud-from-pr for this PR!
  • Click to trigger kibana-entity-store-performance-from-pr for this PR!
  • Click to trigger kibana-storybooks-from-pr for this PR!

@eokoneyo eokoneyo force-pushed the patch/cascade-component-perf branch from c489a84 to 0934461 Compare March 5, 2026 11:33
@eokoneyo
Copy link
Copy Markdown
Contributor Author

eokoneyo commented Mar 5, 2026

Test out the cascade in storybook with complex render with the multiple state story rendering exactly 100K rows here

@eokoneyo eokoneyo force-pushed the patch/cascade-component-perf branch from bb83bcb to 74314b7 Compare March 5, 2026 21:41
eokoneyo added a commit that referenced this pull request Mar 6, 2026
## Summary

Part of #255745, 
Culled from #256037.

This PR removes the usage of the the eui component `TruncateText` for
titles in the discover cascade experience, in replacement for the much
simpler
[`line-clamp`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/line-clamp)
property. line clamp is supported in most browsers besides IE and Opera
Mini [see here](https://caniuse.com/?search=line-clamp).

### Why are we doing this? 

The component `TruncateText` uses a canvas to measure text to determine
how text can be displayed in addition it also adds a resizer so it can
make adjustments for the amount for the text to display to the user. For
our use case this is an overkill given we were already virtualising
items, so this resulted in a large chunk of unnecessary work being done.

See trace profile below;

<img width="2558" height="1092" alt="Screenshot 2026-03-05 at 15 02 01"
src="https://github.com/user-attachments/assets/a4675691-36f4-41fe-9895-d11c0c576871"
/>

_Figure: Trace profile with eui truncate text_

<img width="2558" height="1142" alt="Screenshot 2026-03-05 at 15 15 26"
src="https://github.com/user-attachments/assets/a52c3224-f68e-4ba6-9926-7cc7a7f7d0fa"
/>

_Figure: Trace profile without eui truncate text_


### Performance improvements (Chrome DevTools Trace comparison analysis
by cursor)

| Metric | Before (with `EuiTextTruncate`) | After (CSS clamp) |
Improvement |
|---|---|---|---|
| **UpdateLayoutTree (style recalc)** | **2,328 ms** across 150 events |
**528 ms** across 69 events | **~77% reduction** (1,800 ms saved) |
| **Total rendering cost** | **2,391 ms** | **632 ms** | **~74%
reduction** |
| **Long tasks > 50 ms (count)** | 6 | 18 | More tasks, but far smaller
(see below) |
| **Worst long task** | 2,374 ms | 2,256 ms | ~5% shorter |
| **Mean long task duration** | 1,010 ms | 299 ms | **~70% reduction** |
| **Long tasks > 1 s** | 3 | 1 | Eliminated 2 multi-second jank frames |

### Key takeaways

- **Style recalculation is the headline win.** The before trace shows
150 `UpdateLayoutTree` events — all in the 10–31 ms range — totalling
2.3 s. After the change there are only 69 events (37 of which are sub-1
ms), totalling 528 ms. This is because `EuiTextTruncate` forces the
browser to synchronously recalculate styles to measure text width on
every render pass.

- **Long task severity drops dramatically.** Before the change, 3 long
tasks exceeded 1 second (up to 2.4 s), and the mean long task was over 1
s. After the change only 1 long task exceeds 1 s (the initial
pointer-out handler, which is unrelated to truncation), and 9 of the 18
long tasks fall in the 100–200 ms range — well below the "noticeable
jank" threshold.

- **Scroll is significantly smoother.** The before trace processed only
7 scroll events during the capture window; the after trace processed 43
scroll events in roughly the same window. This means the browser was
able to dispatch and paint far more scroll frames, resulting in visibly
smoother scrolling.

- **The rendering pipeline is no longer the bottleneck.** Total
rendering cost dropped from 2.4 s to 0.6 s, meaning the main thread
spends ~74% less time in layout/style work during scroll.

### What changed

Replaced `<EuiTextTruncate>` (JS-measured truncation with a render
callback) with a CSS `-webkit-line-clamp: 2` approach on the `<h4>`
element, which lets the browser handle truncation natively without
requiring layout measurement. The visual result is equivalent — text is
truncated with an ellipsis — but with no JavaScript overhead.

That been said there's still couple of improvements to be made.

### Visuals

<img width="993" height="758" alt="Screenshot 2026-03-05 at 15 38 36"
src="https://github.com/user-attachments/assets/3c799132-4280-45c0-95f9-d3dc33d3c67e"
/>

_Figure: Cascade experience with Truncate Text, note text ellipsis shows
at one line_

<img width="1152" height="901" alt="Screenshot 2026-03-05 at 15 37 42"
src="https://github.com/user-attachments/assets/5da7666c-cfd3-4d07-905b-7e3988037f94"
/>


_Figure: Cascade experience with line clamp, note text ellipsis shows at
2 lines to match CATEGORIZE pattern behaviour_

<!--
### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...


-->
kapral18 pushed a commit to kapral18/kibana that referenced this pull request Mar 9, 2026
## Summary

Part of elastic#255745, 
Culled from elastic#256037.

This PR removes the usage of the the eui component `TruncateText` for
titles in the discover cascade experience, in replacement for the much
simpler
[`line-clamp`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/line-clamp)
property. line clamp is supported in most browsers besides IE and Opera
Mini [see here](https://caniuse.com/?search=line-clamp).

### Why are we doing this? 

The component `TruncateText` uses a canvas to measure text to determine
how text can be displayed in addition it also adds a resizer so it can
make adjustments for the amount for the text to display to the user. For
our use case this is an overkill given we were already virtualising
items, so this resulted in a large chunk of unnecessary work being done.

See trace profile below;

<img width="2558" height="1092" alt="Screenshot 2026-03-05 at 15 02 01"
src="https://github.com/user-attachments/assets/a4675691-36f4-41fe-9895-d11c0c576871"
/>

_Figure: Trace profile with eui truncate text_

<img width="2558" height="1142" alt="Screenshot 2026-03-05 at 15 15 26"
src="https://github.com/user-attachments/assets/a52c3224-f68e-4ba6-9926-7cc7a7f7d0fa"
/>

_Figure: Trace profile without eui truncate text_


### Performance improvements (Chrome DevTools Trace comparison analysis
by cursor)

| Metric | Before (with `EuiTextTruncate`) | After (CSS clamp) |
Improvement |
|---|---|---|---|
| **UpdateLayoutTree (style recalc)** | **2,328 ms** across 150 events |
**528 ms** across 69 events | **~77% reduction** (1,800 ms saved) |
| **Total rendering cost** | **2,391 ms** | **632 ms** | **~74%
reduction** |
| **Long tasks > 50 ms (count)** | 6 | 18 | More tasks, but far smaller
(see below) |
| **Worst long task** | 2,374 ms | 2,256 ms | ~5% shorter |
| **Mean long task duration** | 1,010 ms | 299 ms | **~70% reduction** |
| **Long tasks > 1 s** | 3 | 1 | Eliminated 2 multi-second jank frames |

### Key takeaways

- **Style recalculation is the headline win.** The before trace shows
150 `UpdateLayoutTree` events — all in the 10–31 ms range — totalling
2.3 s. After the change there are only 69 events (37 of which are sub-1
ms), totalling 528 ms. This is because `EuiTextTruncate` forces the
browser to synchronously recalculate styles to measure text width on
every render pass.

- **Long task severity drops dramatically.** Before the change, 3 long
tasks exceeded 1 second (up to 2.4 s), and the mean long task was over 1
s. After the change only 1 long task exceeds 1 s (the initial
pointer-out handler, which is unrelated to truncation), and 9 of the 18
long tasks fall in the 100–200 ms range — well below the "noticeable
jank" threshold.

- **Scroll is significantly smoother.** The before trace processed only
7 scroll events during the capture window; the after trace processed 43
scroll events in roughly the same window. This means the browser was
able to dispatch and paint far more scroll frames, resulting in visibly
smoother scrolling.

- **The rendering pipeline is no longer the bottleneck.** Total
rendering cost dropped from 2.4 s to 0.6 s, meaning the main thread
spends ~74% less time in layout/style work during scroll.

### What changed

Replaced `<EuiTextTruncate>` (JS-measured truncation with a render
callback) with a CSS `-webkit-line-clamp: 2` approach on the `<h4>`
element, which lets the browser handle truncation natively without
requiring layout measurement. The visual result is equivalent — text is
truncated with an ellipsis — but with no JavaScript overhead.

That been said there's still couple of improvements to be made.

### Visuals

<img width="993" height="758" alt="Screenshot 2026-03-05 at 15 38 36"
src="https://github.com/user-attachments/assets/3c799132-4280-45c0-95f9-d3dc33d3c67e"
/>

_Figure: Cascade experience with Truncate Text, note text ellipsis shows
at one line_

<img width="1152" height="901" alt="Screenshot 2026-03-05 at 15 37 42"
src="https://github.com/user-attachments/assets/5da7666c-cfd3-4d07-905b-7e3988037f94"
/>


_Figure: Cascade experience with line clamp, note text ellipsis shows at
2 lines to match CATEGORIZE pattern behaviour_

<!--
### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...


-->
@eokoneyo eokoneyo force-pushed the patch/cascade-component-perf branch from 74314b7 to 90b2af5 Compare March 9, 2026 09:16
@eokoneyo eokoneyo force-pushed the patch/cascade-component-perf branch from 90b2af5 to ea34f5e Compare March 9, 2026 13:53
eokoneyo added a commit that referenced this pull request Mar 9, 2026
## Summary
This PR is part of the cascade performance improvement
work(#255745), culled from
#256037.

Adds CSS `contain: layout style` and `overflowAnchor: none` to the
DataCascade virtualized scroll container and row wrappers, scoping
layout/style invalidation so that row DOM mutations during scrolling
cannot trigger full-document relayouts.

## Changes

- **`data_cascade_impl.styles.tsx`** (`cascadeTreeGridBlock`): add
`contain: 'layout style'` and `overflowAnchor: 'none'`
- **`cascade_row.styles.ts`** (`rowWrapper`): add `contain: 'layout
style'`

## Performance impact (Generated by Cursor)

Compared using Chrome DevTools trace profiles (baseline vs this PR)
during scroll interaction:

| Metric (per-second normalized) | Baseline | PR | Change |
|---|---|---|---|
| Layout total time | 3,737 us/s | 1,686 us/s | **-55%** |
| Full (non-partial) layouts | 9 | **0** | **-100%** |
| UpdateLayoutTree time | 63,821 us/s | 38,009 us/s | **-40%** |
| Paint time | 4,392 us/s | 1,025 us/s | **-77%** |
| Layout invalidations | 13.3/s | 6.7/s | **-50%** |
| Style recalc triggers | 20.6/s | 9.6/s | **-54%** |
| Long task (>50ms) time | 459,137 us/s | 237,431 us/s | **-48%** |
| Avg frame time | 5.9 ms | 3.4 ms | **-42%** |
| Worst-case frame | 203 ms | 73 ms | **-64%** |
| Max dirty objects/layout | 864 | 414 | **-52%** |
| Layout shifts | 4 | 2 | **-50%** |

The headline result is **complete elimination of full document layouts**
during scroll. The two nested containment boundaries (container + rows)
ensure layout invalidation from virtualizer DOM mutations stays confined
to the contained subtree, turning expensive full-document relayouts into
cheap scoped partial layouts. `overflowAnchor: none` prevents additional
layout passes from scroll anchoring adjustments during DOM recycling.

<!--
### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

-->
kibanamachine pushed a commit to kibanamachine/kibana that referenced this pull request Mar 9, 2026
…56630)

## Summary
This PR is part of the cascade performance improvement
work(elastic#255745), culled from
elastic#256037.

Adds CSS `contain: layout style` and `overflowAnchor: none` to the
DataCascade virtualized scroll container and row wrappers, scoping
layout/style invalidation so that row DOM mutations during scrolling
cannot trigger full-document relayouts.

## Changes

- **`data_cascade_impl.styles.tsx`** (`cascadeTreeGridBlock`): add
`contain: 'layout style'` and `overflowAnchor: 'none'`
- **`cascade_row.styles.ts`** (`rowWrapper`): add `contain: 'layout
style'`

## Performance impact (Generated by Cursor)

Compared using Chrome DevTools trace profiles (baseline vs this PR)
during scroll interaction:

| Metric (per-second normalized) | Baseline | PR | Change |
|---|---|---|---|
| Layout total time | 3,737 us/s | 1,686 us/s | **-55%** |
| Full (non-partial) layouts | 9 | **0** | **-100%** |
| UpdateLayoutTree time | 63,821 us/s | 38,009 us/s | **-40%** |
| Paint time | 4,392 us/s | 1,025 us/s | **-77%** |
| Layout invalidations | 13.3/s | 6.7/s | **-50%** |
| Style recalc triggers | 20.6/s | 9.6/s | **-54%** |
| Long task (>50ms) time | 459,137 us/s | 237,431 us/s | **-48%** |
| Avg frame time | 5.9 ms | 3.4 ms | **-42%** |
| Worst-case frame | 203 ms | 73 ms | **-64%** |
| Max dirty objects/layout | 864 | 414 | **-52%** |
| Layout shifts | 4 | 2 | **-50%** |

The headline result is **complete elimination of full document layouts**
during scroll. The two nested containment boundaries (container + rows)
ensure layout invalidation from virtualizer DOM mutations stays confined
to the contained subtree, turning expensive full-document relayouts into
cheap scoped partial layouts. `overflowAnchor: none` prevents additional
layout passes from scroll anchoring adjustments during DOM recycling.

<!--
### Checklist

Check the PR satisfies following conditions.

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

-->

(cherry picked from commit 52968c7)
kibanamachine added a commit that referenced this pull request Mar 9, 2026
…6630) (#256800)

# Backport

This will backport the following commits from `main` to `9.3`:
- [[Cascade] Specify strict layout guidance for rows and cell
(#256630)](#256630)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Eyo O.
Eyo","email":"7893459+eokoneyo@users.noreply.github.com"},"sourceCommit":{"committedDate":"2026-03-09T21:34:44Z","message":"[Cascade]
Specify strict layout guidance for rows and cell (#256630)\n\n##
Summary\nThis PR is part of the cascade performance
improvement\nwork(#255745),
culled from\nhttps://github.com//pull/256037.\n\nAdds CSS
`contain: layout style` and `overflowAnchor: none` to the\nDataCascade
virtualized scroll container and row wrappers, scoping\nlayout/style
invalidation so that row DOM mutations during scrolling\ncannot trigger
full-document relayouts.\n\n## Changes\n\n-
**`data_cascade_impl.styles.tsx`** (`cascadeTreeGridBlock`):
add\n`contain: 'layout style'` and `overflowAnchor: 'none'`\n-
**`cascade_row.styles.ts`** (`rowWrapper`): add `contain:
'layout\nstyle'`\n\n## Performance impact (Generated by
Cursor)\n\nCompared using Chrome DevTools trace profiles (baseline vs
this PR)\nduring scroll interaction:\n\n| Metric (per-second normalized)
| Baseline | PR | Change |\n|---|---|---|---|\n| Layout total time |
3,737 us/s | 1,686 us/s | **-55%** |\n| Full (non-partial) layouts | 9 |
**0** | **-100%** |\n| UpdateLayoutTree time | 63,821 us/s | 38,009 us/s
| **-40%** |\n| Paint time | 4,392 us/s | 1,025 us/s | **-77%** |\n|
Layout invalidations | 13.3/s | 6.7/s | **-50%** |\n| Style recalc
triggers | 20.6/s | 9.6/s | **-54%** |\n| Long task (>50ms) time |
459,137 us/s | 237,431 us/s | **-48%** |\n| Avg frame time | 5.9 ms |
3.4 ms | **-42%** |\n| Worst-case frame | 203 ms | 73 ms | **-64%** |\n|
Max dirty objects/layout | 864 | 414 | **-52%** |\n| Layout shifts | 4 |
2 | **-50%** |\n\nThe headline result is **complete elimination of full
document layouts**\nduring scroll. The two nested containment boundaries
(container + rows)\nensure layout invalidation from virtualizer DOM
mutations stays confined\nto the contained subtree, turning expensive
full-document relayouts into\ncheap scoped partial layouts.
`overflowAnchor: none` prevents additional\nlayout passes from scroll
anchoring adjustments during DOM
recycling.\n\n","sha":"52968c7bcc3bb75f83fa57c3bbb8fcffaa260454","branchLabelMapping":{"^v9.4.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:SharedUX","backport:version","v9.4.0","v9.3.2"],"title":"[Cascade]
Specify strict layout guidance for rows and
cell","number":256630,"url":"https://github.com/elastic/kibana/pull/256630","mergeCommit":{"message":"[Cascade]
Specify strict layout guidance for rows and cell (#256630)\n\n##
Summary\nThis PR is part of the cascade performance
improvement\nwork(#255745),
culled from\nhttps://github.com//pull/256037.\n\nAdds CSS
`contain: layout style` and `overflowAnchor: none` to the\nDataCascade
virtualized scroll container and row wrappers, scoping\nlayout/style
invalidation so that row DOM mutations during scrolling\ncannot trigger
full-document relayouts.\n\n## Changes\n\n-
**`data_cascade_impl.styles.tsx`** (`cascadeTreeGridBlock`):
add\n`contain: 'layout style'` and `overflowAnchor: 'none'`\n-
**`cascade_row.styles.ts`** (`rowWrapper`): add `contain:
'layout\nstyle'`\n\n## Performance impact (Generated by
Cursor)\n\nCompared using Chrome DevTools trace profiles (baseline vs
this PR)\nduring scroll interaction:\n\n| Metric (per-second normalized)
| Baseline | PR | Change |\n|---|---|---|---|\n| Layout total time |
3,737 us/s | 1,686 us/s | **-55%** |\n| Full (non-partial) layouts | 9 |
**0** | **-100%** |\n| UpdateLayoutTree time | 63,821 us/s | 38,009 us/s
| **-40%** |\n| Paint time | 4,392 us/s | 1,025 us/s | **-77%** |\n|
Layout invalidations | 13.3/s | 6.7/s | **-50%** |\n| Style recalc
triggers | 20.6/s | 9.6/s | **-54%** |\n| Long task (>50ms) time |
459,137 us/s | 237,431 us/s | **-48%** |\n| Avg frame time | 5.9 ms |
3.4 ms | **-42%** |\n| Worst-case frame | 203 ms | 73 ms | **-64%** |\n|
Max dirty objects/layout | 864 | 414 | **-52%** |\n| Layout shifts | 4 |
2 | **-50%** |\n\nThe headline result is **complete elimination of full
document layouts**\nduring scroll. The two nested containment boundaries
(container + rows)\nensure layout invalidation from virtualizer DOM
mutations stays confined\nto the contained subtree, turning expensive
full-document relayouts into\ncheap scoped partial layouts.
`overflowAnchor: none` prevents additional\nlayout passes from scroll
anchoring adjustments during DOM
recycling.\n\n","sha":"52968c7bcc3bb75f83fa57c3bbb8fcffaa260454"}},"sourceBranch":"main","suggestedTargetBranches":["9.3"],"targetPullRequestStates":[{"branch":"main","label":"v9.4.0","branchLabelMappingKey":"^v9.4.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/256630","number":256630,"mergeCommit":{"message":"[Cascade]
Specify strict layout guidance for rows and cell (#256630)\n\n##
Summary\nThis PR is part of the cascade performance
improvement\nwork(#255745),
culled from\nhttps://github.com//pull/256037.\n\nAdds CSS
`contain: layout style` and `overflowAnchor: none` to the\nDataCascade
virtualized scroll container and row wrappers, scoping\nlayout/style
invalidation so that row DOM mutations during scrolling\ncannot trigger
full-document relayouts.\n\n## Changes\n\n-
**`data_cascade_impl.styles.tsx`** (`cascadeTreeGridBlock`):
add\n`contain: 'layout style'` and `overflowAnchor: 'none'`\n-
**`cascade_row.styles.ts`** (`rowWrapper`): add `contain:
'layout\nstyle'`\n\n## Performance impact (Generated by
Cursor)\n\nCompared using Chrome DevTools trace profiles (baseline vs
this PR)\nduring scroll interaction:\n\n| Metric (per-second normalized)
| Baseline | PR | Change |\n|---|---|---|---|\n| Layout total time |
3,737 us/s | 1,686 us/s | **-55%** |\n| Full (non-partial) layouts | 9 |
**0** | **-100%** |\n| UpdateLayoutTree time | 63,821 us/s | 38,009 us/s
| **-40%** |\n| Paint time | 4,392 us/s | 1,025 us/s | **-77%** |\n|
Layout invalidations | 13.3/s | 6.7/s | **-50%** |\n| Style recalc
triggers | 20.6/s | 9.6/s | **-54%** |\n| Long task (>50ms) time |
459,137 us/s | 237,431 us/s | **-48%** |\n| Avg frame time | 5.9 ms |
3.4 ms | **-42%** |\n| Worst-case frame | 203 ms | 73 ms | **-64%** |\n|
Max dirty objects/layout | 864 | 414 | **-52%** |\n| Layout shifts | 4 |
2 | **-50%** |\n\nThe headline result is **complete elimination of full
document layouts**\nduring scroll. The two nested containment boundaries
(container + rows)\nensure layout invalidation from virtualizer DOM
mutations stays confined\nto the contained subtree, turning expensive
full-document relayouts into\ncheap scoped partial layouts.
`overflowAnchor: none` prevents additional\nlayout passes from scroll
anchoring adjustments during DOM
recycling.\n\n","sha":"52968c7bcc3bb75f83fa57c3bbb8fcffaa260454"}},{"branch":"9.3","label":"v9.3.2","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Eyo O. Eyo <7893459+eokoneyo@users.noreply.github.com>
@eokoneyo eokoneyo force-pushed the patch/cascade-component-perf branch from ea34f5e to 07ccdfb Compare March 10, 2026 09:59
@kibanamachine
Copy link
Copy Markdown
Contributor

eokoneyo added a commit that referenced this pull request Mar 10, 2026
## Summary

This PR is part of the cascade performance improvement
work(#255745), culled from
#256037.

Refactors the `ScrollSyncProvider` to eliminate per-row scroll overhead
by centralizing scroll coordination into a single capture-phase listener
with an allocation-free hot path.

### What changed

- **Single capture-phase scroll listener**: Replaced N individual resize
observers and `onScroll` React handlers (one per row) with a single
native `scroll` listener attached at the provider's wrapper element
using `{ capture: true, passive: true }`. This bypasses React's
synthetic event system entirely for programmatic scroll events and uses
a scroll-leader pattern to prevent the O(N^2) feedback cascade where
each `scrollTo` write re-triggers handlers on all other rows.

- **`useSyncExternalStore` for scroll state**: Replaced per-row
`useState` + `onScroll` with a centralized pub/sub store. The provider
computes three boolean flags (`isScrollable`, `canScrollLeft`,
`canScrollRight`) and only emits to subscribers when the booleans
actually change — not on every pixel of scroll. Consumers call
`useSyncExternalStore(subscribe, getSnapshot)` for batched, stable
re-renders.

- **Cached layout dimensions**: `scrollWidth` and `clientWidth` are
cached in the provider and only refreshed via `ResizeObserver` or on
hover. The scroll hot path never reads these layout-forcing properties
from the DOM.

- **Zero-allocation scroll hot path**: All intermediate objects are
mutated in place to minimize GC pressure:
- Scroll state booleans are compared inline; a new snapshot object is
only allocated when they change (~2-3 times per session, not per event).
- A single reusable `ScrollToOptions` ref is shared across all
`scrollTo` calls instead of allocating `{ left, behavior: 'instant' }`
per container per event.
- `cachedDimensions` are updated via property assignment, not object
replacement.
  - `for...of` loops replace `.forEach` to avoid closure allocation.
- The scroll-leader timeout callback is hoisted to a stable
`useCallback` ref.

- **Instant programmatic scrolling**: All programmatic `scrollTo` calls
use `{ behavior: 'instant' }` to override the CSS `scroll-behavior:
smooth` on scroll containers, ensuring synchronized rows update in the
same frame as the leader. User-initiated smooth scroll (e.g. arrow
button clicks) is unaffected since those rely on the CSS property
directly.

### Performance results

Measured via Chrome DevTools traces on a data cascade with ~40 visible
rows, comparing the optimized implementation against the original
baseline:

| Category | Metric | Baseline | Optimized | Change |
|---|---|---|---|---|
| **GC** | Events | 4,949 | 3,818 | **-23%** |
| | Time | 2,121 ms | 2,099 ms | **-1%** (parity) |
| **Layout** | Style recalc time | 722 ms | 668 ms | **-7%** |
| | Layout time | 47 ms | 40 ms | **-15%** |
| **Frames** | P95 inter-frame gap | 111 ms | 111 ms | parity |
| | P50 FPS | 59.9 | 59.8 | parity |

**Key wins**: GC events are 23% below baseline, layout/style costs are
reduced, and the P95 inter-frame gap and median FPS are at parity. The
architectural shift from N independent scroll handlers to a single
coordinated listener also reduces the total number of scroll dispatch
events by 26%.

Most importantly we are giving back rendering time to consumers of the
component.

How to test

- Verify that scroll sync works;
- Navigate to the storybook link that show cases the cascade rendering
multiple stats, on resizing the viewport to the extent where the stats
get clipped, hovering over any one of the rows should reveal the scroll
button
- Clicking the scroll button should move all rows in the specified
direction

<!--

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...


-->

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
@eokoneyo eokoneyo force-pushed the patch/cascade-component-perf branch 2 times, most recently from 34b9117 to 2a95539 Compare March 11, 2026 10:59
qn895 pushed a commit to qn895/kibana that referenced this pull request Mar 11, 2026
## Summary

Part of elastic#255745, 
Culled from elastic#256037.

This PR removes the usage of the the eui component `TruncateText` for
titles in the discover cascade experience, in replacement for the much
simpler
[`line-clamp`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/line-clamp)
property. line clamp is supported in most browsers besides IE and Opera
Mini [see here](https://caniuse.com/?search=line-clamp).

### Why are we doing this? 

The component `TruncateText` uses a canvas to measure text to determine
how text can be displayed in addition it also adds a resizer so it can
make adjustments for the amount for the text to display to the user. For
our use case this is an overkill given we were already virtualising
items, so this resulted in a large chunk of unnecessary work being done.

See trace profile below;

<img width="2558" height="1092" alt="Screenshot 2026-03-05 at 15 02 01"
src="https://github.com/user-attachments/assets/a4675691-36f4-41fe-9895-d11c0c576871"
/>

_Figure: Trace profile with eui truncate text_

<img width="2558" height="1142" alt="Screenshot 2026-03-05 at 15 15 26"
src="https://github.com/user-attachments/assets/a52c3224-f68e-4ba6-9926-7cc7a7f7d0fa"
/>

_Figure: Trace profile without eui truncate text_


### Performance improvements (Chrome DevTools Trace comparison analysis
by cursor)

| Metric | Before (with `EuiTextTruncate`) | After (CSS clamp) |
Improvement |
|---|---|---|---|
| **UpdateLayoutTree (style recalc)** | **2,328 ms** across 150 events |
**528 ms** across 69 events | **~77% reduction** (1,800 ms saved) |
| **Total rendering cost** | **2,391 ms** | **632 ms** | **~74%
reduction** |
| **Long tasks > 50 ms (count)** | 6 | 18 | More tasks, but far smaller
(see below) |
| **Worst long task** | 2,374 ms | 2,256 ms | ~5% shorter |
| **Mean long task duration** | 1,010 ms | 299 ms | **~70% reduction** |
| **Long tasks > 1 s** | 3 | 1 | Eliminated 2 multi-second jank frames |

### Key takeaways

- **Style recalculation is the headline win.** The before trace shows
150 `UpdateLayoutTree` events — all in the 10–31 ms range — totalling
2.3 s. After the change there are only 69 events (37 of which are sub-1
ms), totalling 528 ms. This is because `EuiTextTruncate` forces the
browser to synchronously recalculate styles to measure text width on
every render pass.

- **Long task severity drops dramatically.** Before the change, 3 long
tasks exceeded 1 second (up to 2.4 s), and the mean long task was over 1
s. After the change only 1 long task exceeds 1 s (the initial
pointer-out handler, which is unrelated to truncation), and 9 of the 18
long tasks fall in the 100–200 ms range — well below the "noticeable
jank" threshold.

- **Scroll is significantly smoother.** The before trace processed only
7 scroll events during the capture window; the after trace processed 43
scroll events in roughly the same window. This means the browser was
able to dispatch and paint far more scroll frames, resulting in visibly
smoother scrolling.

- **The rendering pipeline is no longer the bottleneck.** Total
rendering cost dropped from 2.4 s to 0.6 s, meaning the main thread
spends ~74% less time in layout/style work during scroll.

### What changed

Replaced `<EuiTextTruncate>` (JS-measured truncation with a render
callback) with a CSS `-webkit-line-clamp: 2` approach on the `<h4>`
element, which lets the browser handle truncation natively without
requiring layout measurement. The visual result is equivalent — text is
truncated with an ellipsis — but with no JavaScript overhead.

That been said there's still couple of improvements to be made.

### Visuals

<img width="993" height="758" alt="Screenshot 2026-03-05 at 15 38 36"
src="https://github.com/user-attachments/assets/3c799132-4280-45c0-95f9-d3dc33d3c67e"
/>

_Figure: Cascade experience with Truncate Text, note text ellipsis shows
at one line_

<img width="1152" height="901" alt="Screenshot 2026-03-05 at 15 37 42"
src="https://github.com/user-attachments/assets/5da7666c-cfd3-4d07-905b-7e3988037f94"
/>


_Figure: Cascade experience with line clamp, note text ellipsis shows at
2 lines to match CATEGORIZE pattern behaviour_

<!--
### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...


-->
qn895 pushed a commit to qn895/kibana that referenced this pull request Mar 11, 2026
…56630)

## Summary
This PR is part of the cascade performance improvement
work(elastic#255745), culled from
elastic#256037.

Adds CSS `contain: layout style` and `overflowAnchor: none` to the
DataCascade virtualized scroll container and row wrappers, scoping
layout/style invalidation so that row DOM mutations during scrolling
cannot trigger full-document relayouts.

## Changes

- **`data_cascade_impl.styles.tsx`** (`cascadeTreeGridBlock`): add
`contain: 'layout style'` and `overflowAnchor: 'none'`
- **`cascade_row.styles.ts`** (`rowWrapper`): add `contain: 'layout
style'`

## Performance impact (Generated by Cursor)

Compared using Chrome DevTools trace profiles (baseline vs this PR)
during scroll interaction:

| Metric (per-second normalized) | Baseline | PR | Change |
|---|---|---|---|
| Layout total time | 3,737 us/s | 1,686 us/s | **-55%** |
| Full (non-partial) layouts | 9 | **0** | **-100%** |
| UpdateLayoutTree time | 63,821 us/s | 38,009 us/s | **-40%** |
| Paint time | 4,392 us/s | 1,025 us/s | **-77%** |
| Layout invalidations | 13.3/s | 6.7/s | **-50%** |
| Style recalc triggers | 20.6/s | 9.6/s | **-54%** |
| Long task (>50ms) time | 459,137 us/s | 237,431 us/s | **-48%** |
| Avg frame time | 5.9 ms | 3.4 ms | **-42%** |
| Worst-case frame | 203 ms | 73 ms | **-64%** |
| Max dirty objects/layout | 864 | 414 | **-52%** |
| Layout shifts | 4 | 2 | **-50%** |

The headline result is **complete elimination of full document layouts**
during scroll. The two nested containment boundaries (container + rows)
ensure layout invalidation from virtualizer DOM mutations stays confined
to the contained subtree, turning expensive full-document relayouts into
cheap scoped partial layouts. `overflowAnchor: none` prevents additional
layout passes from scroll anchoring adjustments during DOM recycling.

<!--
### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

-->
qn895 pushed a commit to qn895/kibana that referenced this pull request Mar 11, 2026
## Summary

This PR is part of the cascade performance improvement
work(elastic#255745), culled from
elastic#256037.

Refactors the `ScrollSyncProvider` to eliminate per-row scroll overhead
by centralizing scroll coordination into a single capture-phase listener
with an allocation-free hot path.

### What changed

- **Single capture-phase scroll listener**: Replaced N individual resize
observers and `onScroll` React handlers (one per row) with a single
native `scroll` listener attached at the provider's wrapper element
using `{ capture: true, passive: true }`. This bypasses React's
synthetic event system entirely for programmatic scroll events and uses
a scroll-leader pattern to prevent the O(N^2) feedback cascade where
each `scrollTo` write re-triggers handlers on all other rows.

- **`useSyncExternalStore` for scroll state**: Replaced per-row
`useState` + `onScroll` with a centralized pub/sub store. The provider
computes three boolean flags (`isScrollable`, `canScrollLeft`,
`canScrollRight`) and only emits to subscribers when the booleans
actually change — not on every pixel of scroll. Consumers call
`useSyncExternalStore(subscribe, getSnapshot)` for batched, stable
re-renders.

- **Cached layout dimensions**: `scrollWidth` and `clientWidth` are
cached in the provider and only refreshed via `ResizeObserver` or on
hover. The scroll hot path never reads these layout-forcing properties
from the DOM.

- **Zero-allocation scroll hot path**: All intermediate objects are
mutated in place to minimize GC pressure:
- Scroll state booleans are compared inline; a new snapshot object is
only allocated when they change (~2-3 times per session, not per event).
- A single reusable `ScrollToOptions` ref is shared across all
`scrollTo` calls instead of allocating `{ left, behavior: 'instant' }`
per container per event.
- `cachedDimensions` are updated via property assignment, not object
replacement.
  - `for...of` loops replace `.forEach` to avoid closure allocation.
- The scroll-leader timeout callback is hoisted to a stable
`useCallback` ref.

- **Instant programmatic scrolling**: All programmatic `scrollTo` calls
use `{ behavior: 'instant' }` to override the CSS `scroll-behavior:
smooth` on scroll containers, ensuring synchronized rows update in the
same frame as the leader. User-initiated smooth scroll (e.g. arrow
button clicks) is unaffected since those rely on the CSS property
directly.

### Performance results

Measured via Chrome DevTools traces on a data cascade with ~40 visible
rows, comparing the optimized implementation against the original
baseline:

| Category | Metric | Baseline | Optimized | Change |
|---|---|---|---|---|
| **GC** | Events | 4,949 | 3,818 | **-23%** |
| | Time | 2,121 ms | 2,099 ms | **-1%** (parity) |
| **Layout** | Style recalc time | 722 ms | 668 ms | **-7%** |
| | Layout time | 47 ms | 40 ms | **-15%** |
| **Frames** | P95 inter-frame gap | 111 ms | 111 ms | parity |
| | P50 FPS | 59.9 | 59.8 | parity |

**Key wins**: GC events are 23% below baseline, layout/style costs are
reduced, and the P95 inter-frame gap and median FPS are at parity. The
architectural shift from N independent scroll handlers to a single
coordinated listener also reduces the total number of scroll dispatch
events by 26%.

Most importantly we are giving back rendering time to consumers of the
component.

How to test

- Verify that scroll sync works;
- Navigate to the storybook link that show cases the cascade rendering
multiple stats, on resizing the viewport to the extent where the stats
get clipped, hovering over any one of the rows should reveal the scroll
button
- Clicking the scroll button should move all rows in the specified
direction

<!--

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...


-->

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
- provide escape hatch for fetch all persisted data if it exists
@eokoneyo eokoneyo force-pushed the patch/cascade-component-perf branch from 2a95539 to ad2c235 Compare March 12, 2026 09:33
- structural changes to virtualizer implementation
- favor anchor item index over scroll offset for scroll restoration
- use cascade virtualizer for nesting in discover
- deferred mounting for nested rows
- add foundation for controlled child virtualizer
- add orchestration for nested virtualized children
- adjustments so returning expanded rows do not show loading skeleton
- configure cascade multiple stats story to render 100K rows

- split data cascade stories into managable chunks
@eokoneyo eokoneyo force-pushed the patch/cascade-component-perf branch from ad2c235 to 8e98b10 Compare March 12, 2026 11:41
sorenlouv pushed a commit that referenced this pull request Mar 17, 2026
## Summary
This PR is part of the cascade performance improvement
work(#255745), culled from
#256037.

Adds CSS `contain: layout style` and `overflowAnchor: none` to the
DataCascade virtualized scroll container and row wrappers, scoping
layout/style invalidation so that row DOM mutations during scrolling
cannot trigger full-document relayouts.

## Changes

- **`data_cascade_impl.styles.tsx`** (`cascadeTreeGridBlock`): add
`contain: 'layout style'` and `overflowAnchor: 'none'`
- **`cascade_row.styles.ts`** (`rowWrapper`): add `contain: 'layout
style'`

## Performance impact (Generated by Cursor)

Compared using Chrome DevTools trace profiles (baseline vs this PR)
during scroll interaction:

| Metric (per-second normalized) | Baseline | PR | Change |
|---|---|---|---|
| Layout total time | 3,737 us/s | 1,686 us/s | **-55%** |
| Full (non-partial) layouts | 9 | **0** | **-100%** |
| UpdateLayoutTree time | 63,821 us/s | 38,009 us/s | **-40%** |
| Paint time | 4,392 us/s | 1,025 us/s | **-77%** |
| Layout invalidations | 13.3/s | 6.7/s | **-50%** |
| Style recalc triggers | 20.6/s | 9.6/s | **-54%** |
| Long task (>50ms) time | 459,137 us/s | 237,431 us/s | **-48%** |
| Avg frame time | 5.9 ms | 3.4 ms | **-42%** |
| Worst-case frame | 203 ms | 73 ms | **-64%** |
| Max dirty objects/layout | 864 | 414 | **-52%** |
| Layout shifts | 4 | 2 | **-50%** |

The headline result is **complete elimination of full document layouts**
during scroll. The two nested containment boundaries (container + rows)
ensure layout invalidation from virtualizer DOM mutations stays confined
to the contained subtree, turning expensive full-document relayouts into
cheap scoped partial layouts. `overflowAnchor: none` prevents additional
layout passes from scroll anchoring adjustments during DOM recycling.

<!--
### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

-->
sorenlouv pushed a commit that referenced this pull request Mar 17, 2026
## Summary

This PR is part of the cascade performance improvement
work(#255745), culled from
#256037.

Refactors the `ScrollSyncProvider` to eliminate per-row scroll overhead
by centralizing scroll coordination into a single capture-phase listener
with an allocation-free hot path.

### What changed

- **Single capture-phase scroll listener**: Replaced N individual resize
observers and `onScroll` React handlers (one per row) with a single
native `scroll` listener attached at the provider's wrapper element
using `{ capture: true, passive: true }`. This bypasses React's
synthetic event system entirely for programmatic scroll events and uses
a scroll-leader pattern to prevent the O(N^2) feedback cascade where
each `scrollTo` write re-triggers handlers on all other rows.

- **`useSyncExternalStore` for scroll state**: Replaced per-row
`useState` + `onScroll` with a centralized pub/sub store. The provider
computes three boolean flags (`isScrollable`, `canScrollLeft`,
`canScrollRight`) and only emits to subscribers when the booleans
actually change — not on every pixel of scroll. Consumers call
`useSyncExternalStore(subscribe, getSnapshot)` for batched, stable
re-renders.

- **Cached layout dimensions**: `scrollWidth` and `clientWidth` are
cached in the provider and only refreshed via `ResizeObserver` or on
hover. The scroll hot path never reads these layout-forcing properties
from the DOM.

- **Zero-allocation scroll hot path**: All intermediate objects are
mutated in place to minimize GC pressure:
- Scroll state booleans are compared inline; a new snapshot object is
only allocated when they change (~2-3 times per session, not per event).
- A single reusable `ScrollToOptions` ref is shared across all
`scrollTo` calls instead of allocating `{ left, behavior: 'instant' }`
per container per event.
- `cachedDimensions` are updated via property assignment, not object
replacement.
  - `for...of` loops replace `.forEach` to avoid closure allocation.
- The scroll-leader timeout callback is hoisted to a stable
`useCallback` ref.

- **Instant programmatic scrolling**: All programmatic `scrollTo` calls
use `{ behavior: 'instant' }` to override the CSS `scroll-behavior:
smooth` on scroll containers, ensuring synchronized rows update in the
same frame as the leader. User-initiated smooth scroll (e.g. arrow
button clicks) is unaffected since those rely on the CSS property
directly.

### Performance results

Measured via Chrome DevTools traces on a data cascade with ~40 visible
rows, comparing the optimized implementation against the original
baseline:

| Category | Metric | Baseline | Optimized | Change |
|---|---|---|---|---|
| **GC** | Events | 4,949 | 3,818 | **-23%** |
| | Time | 2,121 ms | 2,099 ms | **-1%** (parity) |
| **Layout** | Style recalc time | 722 ms | 668 ms | **-7%** |
| | Layout time | 47 ms | 40 ms | **-15%** |
| **Frames** | P95 inter-frame gap | 111 ms | 111 ms | parity |
| | P50 FPS | 59.9 | 59.8 | parity |

**Key wins**: GC events are 23% below baseline, layout/style costs are
reduced, and the P95 inter-frame gap and median FPS are at parity. The
architectural shift from N independent scroll handlers to a single
coordinated listener also reduces the total number of scroll dispatch
events by 26%.

Most importantly we are giving back rendering time to consumers of the
component.

How to test

- Verify that scroll sync works;
- Navigate to the storybook link that show cases the cascade rendering
multiple stats, on resizing the viewport to the extent where the stats
get clipped, hovering over any one of the rows should reveal the scroll
button
- Clicking the scroll button should move all rows in the specified
direction

<!--

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...


-->

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
eokoneyo added a commit that referenced this pull request Mar 30, 2026
## Summary

This PR is part of the cascade performance improvement
work(#255745), culled from
#256037.

Also resolves #255745

In this PR the virtualizer implementation has been reworked, notable
amongst the implementation change;

- Introduces a ChildVirtualizerController that orchestrates nested
virtualized lists within the data cascade, replacing the previous
approach where each leaf cell independently managed its own
useVirtualizer instance and manually wired scroll elements, margins, and
offsets via individual props
- Adds staggered activation for child virtualizers so expanded rows
mount their heavy content (e.g. UnifiedDataTable) progressively rather
than all at once, preventing main-thread stalls
- introduces item-index-based anchoring (initialAnchorItemIndex), which
survives row resizes and remeasurements more reliably than a raw pixel
offset
- Defers initial overscan to zero on mount and applies the configured
value after the first paint, reducing DOM node count and layout
thrashing during initial render
- Exposes connectedChildren state and scrollAnchorItemIndex through the
public API snapshot, enabling external consumers to observe nested
virtualizer state.
- Row measurements are now cached by the row key, this enables stable
reference for previously measured rows reducing the style recalculation
that needs to be performed when a new row gets rendered into the DOM on
scroll
- Lifts the skeleton/content gate from the consumer
(ESQLDataCascadeLeafCell) into the library (CascadeRowCellPrimitive), so
the skeleton is coordinated by the controller's activation signal rather
than being opt-in per consumer

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.
<!--
- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials -->
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

<!--
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

-->

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Matthias Polman <matthias.wilhelm@elastic.co>
@eokoneyo
Copy link
Copy Markdown
Contributor Author

Closing this PR as all the work that came out from the investigation into the performance of cascade in discover has been merged in.

@eokoneyo eokoneyo closed this Mar 31, 2026
jeramysoucy pushed a commit to jeramysoucy/kibana that referenced this pull request Apr 1, 2026
## Summary

This PR is part of the cascade performance improvement
work(elastic#255745), culled from
elastic#256037.

Also resolves elastic#255745

In this PR the virtualizer implementation has been reworked, notable
amongst the implementation change;

- Introduces a ChildVirtualizerController that orchestrates nested
virtualized lists within the data cascade, replacing the previous
approach where each leaf cell independently managed its own
useVirtualizer instance and manually wired scroll elements, margins, and
offsets via individual props
- Adds staggered activation for child virtualizers so expanded rows
mount their heavy content (e.g. UnifiedDataTable) progressively rather
than all at once, preventing main-thread stalls
- introduces item-index-based anchoring (initialAnchorItemIndex), which
survives row resizes and remeasurements more reliably than a raw pixel
offset
- Defers initial overscan to zero on mount and applies the configured
value after the first paint, reducing DOM node count and layout
thrashing during initial render
- Exposes connectedChildren state and scrollAnchorItemIndex through the
public API snapshot, enabling external consumers to observe nested
virtualizer state.
- Row measurements are now cached by the row key, this enables stable
reference for previously measured rows reducing the style recalculation
that needs to be performed when a new row gets rendered into the DOM on
scroll
- Lifts the skeleton/content gate from the consumer
(ESQLDataCascadeLeafCell) into the library (CascadeRowCellPrimitive), so
the skeleton is coordinated by the controller's activation signal rather
than being opt-in per consumer

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.
<!--
- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials -->
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

<!--
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

-->

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Matthias Polman <matthias.wilhelm@elastic.co>
paulinashakirova pushed a commit to paulinashakirova/kibana that referenced this pull request Apr 2, 2026
## Summary

This PR is part of the cascade performance improvement
work(elastic#255745), culled from
elastic#256037.

Also resolves elastic#255745

In this PR the virtualizer implementation has been reworked, notable
amongst the implementation change;

- Introduces a ChildVirtualizerController that orchestrates nested
virtualized lists within the data cascade, replacing the previous
approach where each leaf cell independently managed its own
useVirtualizer instance and manually wired scroll elements, margins, and
offsets via individual props
- Adds staggered activation for child virtualizers so expanded rows
mount their heavy content (e.g. UnifiedDataTable) progressively rather
than all at once, preventing main-thread stalls
- introduces item-index-based anchoring (initialAnchorItemIndex), which
survives row resizes and remeasurements more reliably than a raw pixel
offset
- Defers initial overscan to zero on mount and applies the configured
value after the first paint, reducing DOM node count and layout
thrashing during initial render
- Exposes connectedChildren state and scrollAnchorItemIndex through the
public API snapshot, enabling external consumers to observe nested
virtualizer state.
- Row measurements are now cached by the row key, this enables stable
reference for previously measured rows reducing the style recalculation
that needs to be performed when a new row gets rendered into the DOM on
scroll
- Lifts the skeleton/content gate from the consumer
(ESQLDataCascadeLeafCell) into the library (CascadeRowCellPrimitive), so
the skeleton is coordinated by the controller's activation signal rather
than being opt-in per consumer

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.
<!--
- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials -->
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

<!--
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

-->

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Matthias Polman <matthias.wilhelm@elastic.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Team:SharedUX Platform AppEx-SharedUX (formerly Global Experience) t//

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants