From 9aaa75ec63baae2a5b3670d9d5ee6870bc86f0d8 Mon Sep 17 00:00:00 2001 From: Kelvin Tan <141756464+kelvtanv@users.noreply.github.com> Date: Tue, 14 Apr 2026 09:05:29 -0400 Subject: [PATCH] [Security Solution][Alert KPI] Fix white space bug in alert KPIs (#260803) ## Summary [See issue](https://github.com/elastic/kibana/issues/225838) - Happens on both the new attacks page and alerts page for any kpi that has an arbitrary list of items - Caused by content overflowing parent container - For example within the `Alerts by name` kpi, if there are many rows, the content is hidden and scrollable but still (invisibly) takes up space within the parent container - Fix the tree map kpi to be bounded within the parent container (so border does not overlap with content) and only make the graph portion scrollable ### To test 1. Setup lots of rules (enough to overflow the kpi) 2. Generate a bunch of alerts from those rules 3. Alternatively, generate a bunch of alerts with unique hostnames to overflow the `Top alerts by` kpi when sorting by `host.name` 4. Go to the summary section in alerts kpi and notice the large scrollable white space 5. Go to the tree map and make sure the graph is large, notice the graph overlaps the border of the bounding container and scrolling scrolls the entire bounding container Before: https://github.com/user-attachments/assets/be6683c7-8590-495f-bc89-fce7182af135 After: https://github.com/user-attachments/assets/7b4d53c7-cfd4-406f-8955-80217b5dd09e ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] 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. (cherry picked from commit 58479ea7a8c04967d6805239f5f7e192f1d9b584) # Conflicts: # x-pack/solutions/security/plugins/security_solution/public/detections/components/attacks/kpis/attacks_summary_panel.tsx --- .../alerts_summary_charts_panel/index.tsx | 14 ++--------- .../alerts_treemap_panel/index.test.tsx | 14 ++--------- .../alerts_treemap_panel/index.tsx | 25 ++++++++++++------- .../alerts_kpis/chart_panels/index.tsx | 2 +- .../alerts_kpis/common/components.tsx | 14 +---------- 5 files changed, 22 insertions(+), 47 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx index 0a5e2f3610541..5ee527d5ddf8a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx @@ -64,12 +64,7 @@ export const AlertsSummaryChartsPanel: React.FC = ({ ); return ( - + = ({ toggleQuery={toggleQuery} /> {isExpanded && ( - + { ); }); - it('renders the panel with the expected class to style the overflow-y scroll bar', async () => { - render( - - - - ); - - await waitFor(() => expect(screen.getByTestId('treemapPanel')).toHaveClass('eui-yScroll')); - }); - - it('renders the panel with an auto overflow-y to allow vertical scrolling when necessary when the panel is expanded', async () => { + it('renders the panel with a hidden overflow-y when the panel is expanded, delegating scrolling to the inner content', async () => { render( @@ -184,7 +174,7 @@ describe('AlertsTreemapPanel', () => { ); await waitFor(() => - expect(screen.getByTestId('treemapPanel')).toHaveStyleRule('overflow-y', 'auto') + expect(screen.getByTestId('treemapPanel')).toHaveStyleRule('overflow-y', 'hidden') ); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/index.tsx index 043e931d79a9f..5ad6443567958 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/index.tsx @@ -8,6 +8,7 @@ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { EuiComboBox } from '@elastic/eui'; import { EuiProgress } from '@elastic/eui'; +import styled from '@emotion/styled'; import type { Filter, Query } from '@kbn/es-query'; import { buildEsQuery } from '@kbn/es-query'; import { getEsQueryConfig } from '@kbn/data-plugin/common'; @@ -29,6 +30,12 @@ import { useKibana } from '../../../../common/lib/kibana'; const DEFAULT_HEIGHT = DEFAULT_MIN_CHART_HEIGHT + 134; // px +const ScrollableContent = styled.div` + flex: 1; + min-height: 0; + overflow-y: auto; +`; + const COLLAPSED_HEIGHT = 64; // px const ALERTS_TREEMAP_ID = 'alerts-treemap'; @@ -162,11 +169,9 @@ const AlertsTreemapPanelComponent: React.FC = ({ return ( = ({ ) : ( <> {alertsData != null && isPanelExpanded && ( - + + + )} )} diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/index.tsx index 80e7527e6df07..e8e9552e04aea 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/index.tsx @@ -172,7 +172,7 @@ const ChartPanelsComponent: React.FC = ({ ]); return ( -
+
{alertViewSelection === 'trend' && ( {isLoadingIndexPattern ? ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/common/components.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/common/components.tsx index 3a0dccc492475..65bf387748fc9 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/common/components.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/common/components.tsx @@ -10,7 +10,6 @@ import { EuiPanel, EuiComboBox } from '@elastic/eui'; import styled from 'styled-components'; import type { LegacyRef } from 'react'; import React, { useCallback, useMemo } from 'react'; -import { PANEL_HEIGHT, MOBILE_PANEL_HEIGHT } from './config'; import { useStackByFields } from './hooks'; import * as i18n from './translations'; @@ -37,18 +36,7 @@ export const KpiPanel = styled(EuiPanel)<{ position: relative; overflow-x: hidden; overflow-y: ${({ $overflowY }) => $overflowY ?? 'hidden'}; - @media only screen and (min-width: ${(props) => props.theme.eui.euiBreakpoints.m}) { - ${({ height, $toggleStatus }) => - $toggleStatus && - ` - height: ${height != null ? height : PANEL_HEIGHT}px; - `} - } - ${({ $toggleStatus }) => - $toggleStatus && - ` - height: ${MOBILE_PANEL_HEIGHT}px; - `} + ${({ height }) => height != null && `height: ${height}px;`} `; interface StackedBySelectProps { 'aria-label'?: string;