diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts index 95ec298e2e34..7fae1fc74d65 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts @@ -13,3 +13,6 @@ export const SIDEBAR_GROW_SIZE = 2; // Axis height // NOTE: This isn't a perfect solution - changes in font size etc within charts could change the ideal height here. export const FIXED_AXIS_HEIGHT = 32; + +// number of items to display in canvas, since canvas can only have limited size +export const CANVAS_MAX_ITEMS = 150; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx index 8479ee5cae19..592e49740b5f 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx @@ -7,6 +7,7 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; import { EuiScreenReaderOnly, EuiToolTip } from '@elastic/eui'; +import { FIXED_AXIS_HEIGHT } from './constants'; const OuterContainer = styled.div` width: 100%; @@ -29,10 +30,12 @@ const FirstChunk = styled.span` text-overflow: ellipsis; white-space: nowrap; overflow: hidden; + line-height: ${FIXED_AXIS_HEIGHT}px; `; const LastChunk = styled.span` flex-shrink: 0; + line-height: ${FIXED_AXIS_HEIGHT}px; `; export const getChunks = (text: string) => { diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx index c551561d5ad4..7493cfef8b70 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { EuiFlexItem } from '@elastic/eui'; -import { SIDEBAR_GROW_SIZE } from './constants'; +import { FIXED_AXIS_HEIGHT, SIDEBAR_GROW_SIZE } from './constants'; import { IWaterfallContext } from '../context/waterfall_chart'; import { WaterfallChartSidebarContainer, @@ -18,14 +18,13 @@ import { WaterfallChartProps } from './waterfall_chart'; interface SidebarProps { items: Required['sidebarItems']; - height: number; render: Required['renderSidebarItem']; } -export const Sidebar: React.FC = ({ items, height, render }) => { +export const Sidebar: React.FC = ({ items, render }) => { return ( - + props.theme.eui.euiZLevel4}; `; interface WaterfallChartSidebarContainer { @@ -54,7 +55,7 @@ interface WaterfallChartSidebarContainer { } export const WaterfallChartSidebarContainer = euiStyled.div` - height: ${(props) => `${props.height - FIXED_AXIS_HEIGHT}px`}; + height: ${(props) => `${props.height}px`}; overflow-y: hidden; `; @@ -76,12 +77,14 @@ export const WaterfallChartSidebarFlexItem = euiStyled(EuiFlexItem)` interface WaterfallChartChartContainer { height: number; + chartIndex: number; } export const WaterfallChartChartContainer = euiStyled.div` width: 100%; - height: ${(props) => `${props.height}px`}; - margin-top: -${FIXED_AXIS_HEIGHT}px; + height: ${(props) => `${props.height + FIXED_AXIS_HEIGHT - 4}px`}; + margin-top: -${FIXED_AXIS_HEIGHT - 4}px; + z-index: ${(props) => Math.round(props.theme.eui.euiZLevel3 / (props.chartIndex + 1))}; `; export const WaterfallChartLegendContainer = euiStyled.div` diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/use_bar_charts.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/use_bar_charts.test.tsx new file mode 100644 index 000000000000..28b74c5affbd --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/use_bar_charts.test.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useBarCharts } from './use_bar_charts'; +import { renderHook } from '@testing-library/react-hooks'; +import { IWaterfallContext } from '../context/waterfall_chart'; +import { CANVAS_MAX_ITEMS } from './constants'; + +const generateTestData = (): IWaterfallContext['data'] => { + const numberOfItems = 1000; + + const data: IWaterfallContext['data'] = []; + const testItem = { + x: 0, + y0: 0, + y: 4.345000023022294, + config: { + colour: '#b9a888', + showTooltip: true, + tooltipProps: { value: 'Queued / Blocked: 4.345ms', colour: '#b9a888' }, + }, + }; + + for (let i = 0; i < numberOfItems; i++) { + data.push( + { + ...testItem, + x: i, + }, + { + ...testItem, + x: i, + y0: 7, + y: 25, + } + ); + } + + return data; +}; + +describe('useBarChartsHooks', () => { + it('returns result as expected', () => { + const { result, rerender } = renderHook((props) => useBarCharts(props), { + initialProps: { data: [] as IWaterfallContext['data'] }, + }); + + expect(result.current).toHaveLength(0); + const newData = generateTestData(); + + rerender({ data: newData }); + + // Thousands items will result in 7 Canvas + expect(result.current.length).toBe(7); + + const firstChartItems = result.current[0]; + const lastChartItems = result.current[4]; + + // first chart items last item should be x 199, since we only display 150 items + expect(firstChartItems[firstChartItems.length - 1].x).toBe(CANVAS_MAX_ITEMS - 1); + + // since here are 5 charts, last chart first item should be x 800 + expect(lastChartItems[0].x).toBe(CANVAS_MAX_ITEMS * 4); + expect(lastChartItems[lastChartItems.length - 1].x).toBe(CANVAS_MAX_ITEMS * 5 - 1); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/use_bar_charts.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/use_bar_charts.ts new file mode 100644 index 000000000000..3345b30f5239 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/use_bar_charts.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useState } from 'react'; +import { IWaterfallContext } from '../context/waterfall_chart'; +import { CANVAS_MAX_ITEMS } from './constants'; + +export interface UseBarHookProps { + data: IWaterfallContext['data']; +} + +export const useBarCharts = ({ data = [] }: UseBarHookProps) => { + const [charts, setCharts] = useState>([]); + + useEffect(() => { + if (data.length > 0) { + let chartIndex = 1; + + const firstCanvasItems = data.filter((item) => item.x <= CANVAS_MAX_ITEMS); + + const chartsN: Array = [firstCanvasItems]; + + data.forEach((item) => { + // Subtract 1 to account for x value starting from 0 + if (item.x === CANVAS_MAX_ITEMS * chartIndex && !chartsN[item.x / CANVAS_MAX_ITEMS]) { + chartsN.push([]); + chartIndex++; + } + chartsN[chartIndex - 1].push(item); + }); + + setCharts(chartsN); + } + }, [data]); + + return charts; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx index 73aaacb6fab5..e937c3d35ec0 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx @@ -33,9 +33,10 @@ import { WaterfallChartTooltip, } from './styles'; import { WaterfallData } from '../types'; -import { BAR_HEIGHT, MAIN_GROW_SIZE, SIDEBAR_GROW_SIZE, FIXED_AXIS_HEIGHT } from './constants'; +import { BAR_HEIGHT, CANVAS_MAX_ITEMS, MAIN_GROW_SIZE, SIDEBAR_GROW_SIZE } from './constants'; import { Sidebar } from './sidebar'; import { Legend } from './legend'; +import { useBarCharts } from './use_bar_charts'; const Tooltip = (tooltipInfo: TooltipInfo) => { const { data, renderTooltipItem } = useWaterfallContext(); @@ -69,20 +70,11 @@ export interface WaterfallChartProps { fullHeight?: boolean; } -const getUniqueBars = (data: WaterfallData) => { - return (data ?? []).reduce>((acc, item) => { - if (!acc.has(item.x)) { - acc.add(item.x); - return acc; - } else { - return acc; - } - }, new Set()); +const getChartHeight = (data: WaterfallData, ind: number): number => { + // We get the last item x(number of bars) and adds 1 to cater for 0 index + return (data[data.length - 1]?.x + 1 - ind * CANVAS_MAX_ITEMS) * BAR_HEIGHT; }; -const getChartHeight = (data: WaterfallData): number => - getUniqueBars(data).size * BAR_HEIGHT + FIXED_AXIS_HEIGHT; - export const WaterfallChart = ({ tickFormat, domain, @@ -94,10 +86,6 @@ export const WaterfallChart = ({ }: WaterfallChartProps) => { const { data, sidebarItems, legendItems } = useWaterfallContext(); - const generatedHeight = useMemo(() => { - return getChartHeight(data); - }, [data]); - const [darkMode] = useUiSetting$('theme:darkMode'); const theme = useMemo(() => { @@ -108,10 +96,8 @@ export const WaterfallChart = ({ const [height, setHeight] = useState(maxHeight); - const shouldRenderSidebar = - sidebarItems && sidebarItems.length > 0 && renderSidebarItem ? true : false; - const shouldRenderLegend = - legendItems && legendItems.length > 0 && renderLegendItem ? true : false; + const shouldRenderSidebar = !!(sidebarItems && sidebarItems.length > 0 && renderSidebarItem); + const shouldRenderLegend = !!(legendItems && legendItems.length > 0 && renderLegendItem); useEffect(() => { if (fullHeight && chartWrapperDivRef.current) { @@ -120,6 +106,8 @@ export const WaterfallChart = ({ } }, [chartWrapperDivRef, fullHeight]); + const chartsToDisplay = useBarCharts({ data }); + return ( <> @@ -174,44 +162,48 @@ export const WaterfallChart = ({ style={{ paddingTop: '10px' }} ref={chartWrapperDivRef} > - {shouldRenderSidebar && ( - - )} + {shouldRenderSidebar && } - - - + {chartsToDisplay.map((chartData, ind) => ( + + + - + - - - + + + + ))} {shouldRenderLegend && }