From 8d9f5f542e44de6c0492029cd55f37510bdf5137 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Fri, 25 Oct 2024 10:40:23 -0700 Subject: [PATCH 01/10] adjusting service table and focus search Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 107 ++++++++++++++++-- 1 file changed, 97 insertions(+), 10 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 8edac99522..816ba3c103 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -5,7 +5,6 @@ import { EuiButtonGroup, - EuiCompressedFieldSearch, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -13,6 +12,10 @@ import { EuiSpacer, EuiSuperSelect, EuiSuperSelectOption, + EuiSelectable, + EuiSelectableOption, + EuiPopover, + EuiFieldSearch, } from '@elastic/eui'; import React, { useEffect, useState } from 'react'; // @ts-ignore @@ -79,6 +82,8 @@ export function ServiceMap({ const [ticks, setTicks] = useState([]); const [items, setItems] = useState({}); const [query, setQuery] = useState(''); + const [selectableOptions, setSelectableOptions] = useState([]); + const toggleButtons = [ { id: 'latency', @@ -97,6 +102,7 @@ export function ServiceMap({ const [selectedNodeDetails, setSelectedNodeDetails] = useState(null); const [selectableValue, setSelectableValue] = useState>>([]); + const [isPopoverOpen, setPopoverOpen] = useState(false); const onChangeSelectable = (value: React.SetStateAction>>) => { // if the change is changing for the first time then callback servicemap with metrics @@ -124,9 +130,16 @@ export function ServiceMap({ }, ]; + useEffect(() => { + const options = Object.keys(serviceMap).map((key) => ({ + label: serviceMap[key].serviceName, + value: serviceMap[key].serviceName, + })); + setSelectableOptions(options); + }, [serviceMap]); + const options = { layout: { - // hierarchical: true, hierarchical: { enabled: true, direction: 'UD', // UD, DU, LR, RL @@ -154,6 +167,8 @@ export function ServiceMap({ hover: true, tooltipDelay: 30, selectable: true, + zoomView: true, + zoomSpeed: 0.5, }, manipulation: { enabled: false, @@ -163,6 +178,28 @@ export function ServiceMap({ autoResize: true, }; + const setZoomLimits = (networkInstance) => { + let lastZoomLevel = 0.5; + const initialPosition = networkInstance.getViewPosition(); + + networkInstance.moveTo({ + scale: lastZoomLevel, + position: initialPosition, + }); + + networkInstance.on('zoom', (params) => { + const zoomLevel = params.scale; + + if (zoomLevel < 0.25 && zoomLevel < lastZoomLevel) { + networkInstance.moveTo({ scale: 0.25, position: initialPosition }); + } else if (zoomLevel > 1.75) { + networkInstance.moveTo({ scale: 1.75 }); + } + + lastZoomLevel = zoomLevel; + }); + }; + const addServiceFilter = (selectedServiceName: string) => { if (!addFilter) return; addFilter({ @@ -273,14 +310,63 @@ export function ServiceMap({ )} - setQuery(e.target.value)} - onSearch={(service) => onFocus(service)} - isInvalid={query.length > 0 && invalid} - /> + setPopoverOpen(!isPopoverOpen)} + onChange={(e) => { + setQuery(e.target.value); + if (!isPopoverOpen) { + setPopoverOpen(true); + } + }} + isInvalid={query.length > 0 && invalid} + aria-controls="service-select-dropdown" + /> + } + isOpen={isPopoverOpen} + closePopover={() => setPopoverOpen(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + repositionOnScroll + id="service-select-dropdown" + ownFocus={false} + > + setQuery(e.target.value), + isClearable: true, + autoFocus: true, + }} + options={selectableOptions.filter((option) => + option.label.toLowerCase().includes(query.toLowerCase()) + )} + singleSelection={true} + onChange={(newOptions) => { + const selectedOption = newOptions.find((option) => option.checked === 'on'); + if (selectedOption) { + setQuery(selectedOption.label); + onFocus(selectedOption.label as string); + setPopoverOpen(false); + setSelectableOptions( + selectableOptions.map((option) => ({ + ...option, + checked: undefined, + })) + ); + } + }} + listProps={{ bordered: true, style: { width: '300px' } }} + > + {(list) =>
{list}
} +
+
{page === 'traces' && ( @@ -307,6 +393,7 @@ export function ServiceMap({ events={events} getNetwork={(networkInstance: any) => { setNetwork(networkInstance); + setZoomLimits(networkInstance); if (currService) onFocus(currService, networkInstance); }} /> From 1fbf3505d18ad5e86c1b6286bcd39ae8424eac25 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Fri, 25 Oct 2024 14:25:12 -0700 Subject: [PATCH 02/10] revert starting zoom to default behavior Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 816ba3c103..9d7a00410e 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -179,14 +179,9 @@ export function ServiceMap({ }; const setZoomLimits = (networkInstance) => { - let lastZoomLevel = 0.5; + let lastZoomLevel = 1.0; const initialPosition = networkInstance.getViewPosition(); - networkInstance.moveTo({ - scale: lastZoomLevel, - position: initialPosition, - }); - networkInstance.on('zoom', (params) => { const zoomLevel = params.scale; From 5367b509112e0486aec5ee0e46ed29fb0539a2e1 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Fri, 25 Oct 2024 16:17:13 -0700 Subject: [PATCH 03/10] add reset button, and enlarge focused node Signed-off-by: Adam Tackett --- .../__snapshots__/create.test.tsx.snap | 1100 ++++++++++++----- .../service_config.test.tsx.snap | 560 ++++++--- .../__snapshots__/service_map.test.tsx.snap | 77 +- .../components/common/plots/service_map.tsx | 41 +- .../__snapshots__/services.test.tsx.snap | 560 ++++++--- 5 files changed, 1668 insertions(+), 670 deletions(-) diff --git a/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap index f59885d0f8..edd0739813 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap @@ -646,37 +646,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -1895,37 +1924,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -3083,37 +3141,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -4246,37 +4333,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -5502,37 +5618,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -6665,37 +6810,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -7848,37 +8022,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -8972,37 +9175,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -10158,37 +10390,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -11287,37 +11548,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -12507,37 +12797,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -13670,37 +13989,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -14858,37 +15206,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -16021,37 +16398,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -17247,37 +17653,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
@@ -18496,37 +18931,66 @@ Object { class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
-
-
- + Focus on + +
+ +
+ + + +
+
+
diff --git a/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap index e3a9c049fb..c4cbeeec4a 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap @@ -988,11 +988,9 @@ exports[`Service Config component renders empty service config 1`] = ` className="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" /> - +
- + } + aria-controls="service-select-dropdown" + compressed={true} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="service-select-dropdown" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + repositionOnScroll={true} > -
- - -
- - - - + } + aria-controls="service-select-dropdown" compressed={true} - icon="search" + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" > -
+ } + compressed={true} + fullWidth={false} + icon="search" + isLoading={false} + prepend="Focus on" > - - + + +
-
+ +
-
+ + + + + + + +
+ +
- - + +
@@ -2217,11 +2331,9 @@ exports[`Service Config component renders with one service selected 1`] = ` className="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" /> - +
- + } + aria-controls="service-select-dropdown" + compressed={true} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="service-select-dropdown" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + repositionOnScroll={true} > -
- - -
- - - - + } + aria-controls="service-select-dropdown" compressed={true} - icon="search" + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" > -
+ } + compressed={true} + fullWidth={false} + icon="search" + isLoading={false} + prepend="Focus on" > - - + + +
-
+ +
-
+ + + + + + + +
+ +
- - + +
diff --git a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap index 9af2c5d1ea..f3cdbbf3b8 100644 --- a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap +++ b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap @@ -34,25 +34,70 @@ exports[`Service map component renders service map 1`] = ` - + - + + } + aria-controls="service-select-dropdown" + compressed={true} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="service-select-dropdown" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + repositionOnScroll={true} + > + + + + diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 9d7a00410e..28b08c9e85 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -5,6 +5,7 @@ import { EuiButtonGroup, + EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -77,6 +78,7 @@ export function ServiceMap({ filterByCurrService?: boolean; includeMetricsCallback?: () => void; }) { + const [graphKey, setGraphKey] = useState(0); // adding key to allow for re-renders const [invalid, setInvalid] = useState(false); const [network, setNetwork] = useState(null); const [ticks, setTicks] = useState([]); @@ -236,9 +238,27 @@ export function ServiceMap({ const onFocus = (service: string, networkInstance?: any) => { if (service.length === 0) { + // Reset all nodes to the default size when no service is selected + const resetNodes = items.graph.nodes.map((node) => ({ ...node, size: 15 })); + setItems({ + ...items, + graph: { ...items.graph, nodes: resetNodes }, + }); + if (networkInstance) networkInstance.fit(); // Adjust the view if needed setInvalid(false); } else if (serviceMap[service]) { if (!networkInstance) networkInstance = network; + + // Enlarge the focused node and reset others + const updatedNodes = items.graph.nodes.map((node) => + node.label === service ? { ...node, size: 30 } : { ...node, size: 15 } + ); + + setItems({ + ...items, + graph: { ...items.graph, nodes: updatedNodes }, + }); + networkInstance.focus(serviceMap[service].id, { animation: true }); setInvalid(false); } else { @@ -303,7 +323,7 @@ export function ServiceMap({ )} - + setPopoverOpen(!isPopoverOpen)} onChange={(e) => { - setQuery(e.target.value); - if (!isPopoverOpen) { - setPopoverOpen(true); + const newValue = e.target.value; + setQuery(newValue); + if (newValue === '') { + onFocus(''); // Clear node focus when input is cleared } }} isInvalid={query.length > 0 && invalid} + append={ + { + setGraphKey((prevKey) => prevKey + 1); + setQuery(''); + onFocus(''); + }} + /> + } aria-controls="service-select-dropdown" /> } @@ -383,6 +415,7 @@ export function ServiceMap({
{items?.graph && ( - +
- + } + aria-controls="service-select-dropdown" + compressed={true} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="service-select-dropdown" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + repositionOnScroll={true} > -
- - -
- - - - + } + aria-controls="service-select-dropdown" compressed={true} - icon="search" + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" > -
+ } + compressed={true} + fullWidth={false} + icon="search" + isLoading={false} + prepend="Focus on" > - - -
+ +
- + + + + + + + +
+
+
- - + +
@@ -6105,11 +6219,9 @@ exports[`Services component renders services page 1`] = ` className="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" /> - +
- + } + aria-controls="service-select-dropdown" + compressed={true} + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} + isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" + /> + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="service-select-dropdown" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + repositionOnScroll={true} > -
- - -
- - - - + } + aria-controls="service-select-dropdown" compressed={true} - icon="search" + fullWidth={false} + incremental={false} + isClearable={true} + isInvalid={false} isLoading={false} + onChange={[Function]} + onClick={[Function]} + placeholder="Service name" + prepend="Focus on" + value="" > -
+ } + compressed={true} + fullWidth={false} + icon="search" + isLoading={false} + prepend="Focus on" > - - -
-
+ + + + + + + +
+ +
- - + +
From 51df028e5ea0dc59c901a7a531087feff7528c03 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Fri, 25 Oct 2024 16:55:54 -0700 Subject: [PATCH 04/10] adjust filter options, so only available are shown in flyout Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 28b08c9e85..8ee74a0d6c 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -133,12 +133,17 @@ export function ServiceMap({ ]; useEffect(() => { - const options = Object.keys(serviceMap).map((key) => ({ - label: serviceMap[key].serviceName, - value: serviceMap[key].serviceName, - })); - setSelectableOptions(options); - }, [serviceMap]); + if (items?.graph?.nodes) { + const visibleNodes = items.graph.nodes.map((node) => node.label); + const options = Object.keys(serviceMap) + .filter((key) => visibleNodes.includes(serviceMap[key].serviceName)) + .map((key) => ({ + label: serviceMap[key].serviceName, + value: serviceMap[key].serviceName, + })); + setSelectableOptions(options); + } + }, [items.graph, serviceMap]); const options = { layout: { From 6b9ce9a87095af5097f74607138acf4d96a6feef Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Tue, 29 Oct 2024 16:08:40 -0700 Subject: [PATCH 05/10] handle empty items Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 8ee74a0d6c..3f073df682 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -142,6 +142,8 @@ export function ServiceMap({ value: serviceMap[key].serviceName, })); setSelectableOptions(options); + } else { + setSelectableOptions([]); // Ensure options are empty if items.graph.nodes doesn't exist } }, [items.graph, serviceMap]); @@ -287,7 +289,10 @@ export function ServiceMap({ }, [items]); useEffect(() => { - if (Object.keys(serviceMap).length === 0) return; + if (!serviceMap || Object.keys(serviceMap).length === 0) { + setItems({}); + return; + } const values = Object.keys(serviceMap) .filter((service) => serviceMap[service][idSelected]) .map((service) => serviceMap[service][idSelected]!); From cbd00af33a3a694152f00f8f484b09407ffc2b9d Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Wed, 6 Nov 2024 14:15:53 -0800 Subject: [PATCH 06/10] loading state, borders, no movement, focus Signed-off-by: Adam Tackett --- .../__snapshots__/create.test.tsx.snap | 64 +++++-- .../service_config.test.tsx.snap | 10 +- .../components/common/helper_functions.tsx | 19 ++- .../__snapshots__/service_map.test.tsx.snap | 24 ++- .../components/common/plots/service_map.tsx | 160 +++++++++++++----- .../__snapshots__/services.test.tsx.snap | 12 +- .../components/services/services_content.tsx | 1 + test/constants.ts | 57 +++++-- 8 files changed, 265 insertions(+), 82 deletions(-) diff --git a/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap index edd0739813..c3b15e282b 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap @@ -563,7 +563,9 @@ Object { > + > + Select metric for service map display +
@@ -1841,7 +1843,9 @@ Object { > + > + Select metric for service map display +
@@ -3058,7 +3062,9 @@ Object { > + > + Select metric for service map display +
@@ -4250,7 +4256,9 @@ Object { > + > + Select metric for service map display +
@@ -5535,7 +5543,9 @@ Object { > + > + Select metric for service map display +
@@ -6727,7 +6737,9 @@ Object { > + > + Select metric for service map display +
@@ -7939,7 +7951,9 @@ Object { > + > + Select metric for service map display +
@@ -9092,7 +9106,9 @@ Object { > + > + Select metric for service map display +
@@ -10307,7 +10323,9 @@ Object { > + > + Select metric for service map display +
@@ -11465,7 +11483,9 @@ Object { > + > + Select metric for service map display +
@@ -12714,7 +12734,9 @@ Object { > + > + Select metric for service map display +
@@ -13906,7 +13928,9 @@ Object { > + > + Select metric for service map display +
@@ -15123,7 +15147,9 @@ Object { > + > + Select metric for service map display +
@@ -16315,7 +16341,9 @@ Object { > + > + Select metric for service map display +
@@ -17570,7 +17598,9 @@ Object { > + > + Select metric for service map display +
@@ -18848,7 +18878,9 @@ Object { > + > + Select metric for service map display +
diff --git a/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap index c4cbeeec4a..f8ad6ce5fa 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap @@ -705,6 +705,7 @@ exports[`Service Config component renders empty service config 1`] = ` buttonSize="s" color="text" idSelected="latency" + legend="Select metric for service map display" onChange={[Function]} options={ Array [ @@ -730,7 +731,9 @@ exports[`Service Config component renders empty service config 1`] = ` + > + Select metric for service map display +
+ > + Select metric for service map display +
= 0 ? `rgba(${color}, 1)` : `rgba(${color}, 0.2)`, + borderWidth: 3, + color: { + border: '#4A4A4A', + background: + relatedServices!.indexOf(service) >= 0 ? `rgba(${color}, 1)` : `rgba(${color}, 0.2)`, + }, font: { color: relatedServices!.indexOf(service) >= 0 @@ -194,10 +198,10 @@ export function getServiceMapGraph( }; } else { styleOptions = { - borderWidth: 1.0, + borderWidth: 3, chosen: false, color: { - border: '#DADADC', + border: '#4A4A4A', background: '#FFFFFF', }, }; @@ -540,9 +544,14 @@ interface JsonMapping { } export const extractAttributes = ( - mapping: AttributeMapping['properties'], + mapping: AttributeMapping['properties'] | undefined, prefix: string ): string[] => { + if (!mapping) { + console.warn('Mapping is missing or undefined.'); + return []; + } + let attributes: string[] = []; for (const [key, value] of Object.entries(mapping)) { diff --git a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap index f3cdbbf3b8..390230fd8c 100644 --- a/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap +++ b/public/components/trace_analytics/components/common/plots/__tests__/__snapshots__/service_map.test.tsx.snap @@ -13,6 +13,7 @@ exports[`Service map component renders service map 1`] = ` buttonSize="s" color="text" idSelected="latency" + legend="Select metric for service map display" onChange={[Function]} options={ Array [ @@ -112,7 +113,28 @@ exports[`Service map component renders service map 1`] = ` "position": "relative", } } - /> + > +
+ +
+
) => void; filterByCurrService?: boolean; includeMetricsCallback?: () => void; + mode?: string; }) { const [graphKey, setGraphKey] = useState(0); // adding key to allow for re-renders const [invalid, setInvalid] = useState(false); @@ -85,6 +88,8 @@ export function ServiceMap({ const [items, setItems] = useState({}); const [query, setQuery] = useState(''); const [selectableOptions, setSelectableOptions] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [filterChange, setIsFilterChange] = useState(false); const toggleButtons = [ { @@ -149,11 +154,26 @@ export function ServiceMap({ const options = { layout: { + randomSeed: 10, + improvedLayout: false, + clusterThreshold: 150, hierarchical: { + enabled: false, + }, + }, + physics: { + enabled: true, + stabilization: { enabled: true, - direction: 'UD', // UD, DU, LR, RL - sortMethod: 'directed', // hubsize, directed - shakeTowards: 'leaves', // roots, leaves + iterations: 1000, // Increase iterations for better layout stability + updateInterval: 25, + }, + solver: 'forceAtlas2Based', + forceAtlas2Based: { + gravitationalConstant: -100, // Adjust this for node repulsion + centralGravity: 0.005, + springLength: 200, // Increase to make nodes further apart + springConstant: 0.08, }, }, edges: { @@ -162,7 +182,7 @@ export function ServiceMap({ enabled: true, }, }, - physics: false, + physics: true, }, nodes: { shape: 'dot', @@ -194,8 +214,8 @@ export function ServiceMap({ networkInstance.on('zoom', (params) => { const zoomLevel = params.scale; - if (zoomLevel < 0.25 && zoomLevel < lastZoomLevel) { - networkInstance.moveTo({ scale: 0.25, position: initialPosition }); + if (zoomLevel < 0.1 && zoomLevel < lastZoomLevel) { + networkInstance.moveTo({ scale: 0.1, position: initialPosition }); } else if (zoomLevel > 1.75) { networkInstance.moveTo({ scale: 1.75 }); } @@ -206,6 +226,8 @@ export function ServiceMap({ const addServiceFilter = (selectedServiceName: string) => { if (!addFilter) return; + setGraphKey((prevKey) => prevKey + 1); + addFilter({ field: 'serviceName', operator: 'is', @@ -213,12 +235,24 @@ export function ServiceMap({ inverted: false, disabled: false, }); + setIsFilterChange(true); + if (!['appCreate', 'detailFlyout'].includes(page)) { window.scrollTo({ left: 0, top: 0, behavior: 'smooth' }); } }; const events = { + stabilizationProgress: () => { + setIsLoading(true); + }, + // Disable physics after rendering the tree + stabilizationIterationsDone: () => { + if (network) { + network.setOptions({ physics: { enabled: false } }); + } + setIsLoading(false); + }, select: (event) => { const { nodes } = event; if (!addFilter || !nodes) return; @@ -226,18 +260,17 @@ export function ServiceMap({ if (selectedNode) { const details = { label: selectedNode.label, - average_latency: selectedNode.average_latency, - error_rate: selectedNode.error_rate, - throughput: selectedNode.throughput, + average_latency: selectedNode.average_latency || '-', + error_rate: selectedNode.error_rate || '-', + throughput: selectedNode.throughput || '-', }; - // On traces page with custom sources - // When user clicks on empty graph, load metrics - if (selectableValue.length === 0) { - onChangeSelectable('latency'); + if (serviceMap[selectedNode.label]) { + setSelectedNodeDetails(details); + } else { + console.warn('Selected node details are missing in the new data source.'); + setSelectedNodeDetails(null); } - // Update the state to display node details - setSelectedNodeDetails(details); } }, hoverNode: (_event) => {}, @@ -245,28 +278,41 @@ export function ServiceMap({ const onFocus = (service: string, networkInstance?: any) => { if (service.length === 0) { - // Reset all nodes to the default size when no service is selected - const resetNodes = items.graph.nodes.map((node) => ({ ...node, size: 15 })); - setItems({ - ...items, - graph: { ...items.graph, nodes: resetNodes }, - }); - if (networkInstance) networkInstance.fit(); // Adjust the view if needed + // Reset all nodes to the default size and show the entire graph when no service is selected + const resetGraph = getServiceMapGraph( + serviceMap, + idSelected, + ticks, + undefined, + serviceMap[currService!]?.relatedServices, + false // Do not filter by the current service to show the entire graph + ); + setItems(resetGraph); + + if (networkInstance) networkInstance.fit(); setInvalid(false); } else if (serviceMap[service]) { if (!networkInstance) networkInstance = network; - // Enlarge the focused node and reset others - const updatedNodes = items.graph.nodes.map((node) => - node.label === service ? { ...node, size: 30 } : { ...node, size: 15 } + // Get a filtered graph showing only nodes connected to the focused service + const filteredGraph = getServiceMapGraph( + serviceMap, + idSelected, + ticks, + service, + serviceMap[service]?.relatedServices, + true // Enable filtering by the current service to show only connected nodes ); - setItems({ - ...items, - graph: { ...items.graph, nodes: updatedNodes }, - }); + setItems(filteredGraph); - networkInstance.focus(serviceMap[service].id, { animation: true }); + networkInstance.focus(serviceMap[service].id, { + scale: 0.75, // Higher scale for closer zoom + animation: { + duration: 1000, // Duration of the zoom-in animation in milliseconds + easingFunction: 'easeInOutQuad', + }, + }); setInvalid(false); } else { setInvalid(true); @@ -274,17 +320,30 @@ export function ServiceMap({ }; useEffect(() => { - if (selectedNodeDetails) { - const selectedNode = items?.graph.nodes.find( + setSelectedNodeDetails(null); + setQuery(''); + setItems({}); + + if (filterChange) { + setIsFilterChange(false); + } + }, [mode, filterChange]); + + useEffect(() => { + if (selectedNodeDetails && items?.graph?.nodes) { + const selectedNode = items.graph.nodes.find( (node) => node.label === selectedNodeDetails.label ); - const details = { - label: selectedNode.label, - average_latency: selectedNode.average_latency, - error_rate: selectedNode.error_rate, - throughput: selectedNode.throughput, - }; - setSelectedNodeDetails(details); + + if (selectedNode) { + const details = { + label: selectedNode.label, + average_latency: selectedNode.average_latency || '-', + error_rate: selectedNode.error_rate || '-', + throughput: selectedNode.throughput || '-', + }; + setSelectedNodeDetails(details); + } } }, [items]); @@ -329,6 +388,7 @@ export function ServiceMap({ onChange={(id) => setIdSelected(id as 'latency' | 'error_rate' | 'throughput')} buttonSize="s" color="text" + legend="Select metric for service map display" /> @@ -347,7 +407,9 @@ export function ServiceMap({ const newValue = e.target.value; setQuery(newValue); if (newValue === '') { - onFocus(''); // Clear node focus when input is cleared + setGraphKey((prevKey) => prevKey + 1); + setQuery(''); + onFocus(''); } }} isInvalid={query.length > 0 && invalid} @@ -436,6 +498,24 @@ export function ServiceMap({ }} /> )} + {isLoading && ( +
+ +
+ )} {selectedNodeDetails && (
+ > + Select metric for service map display +
+ > + Select metric for service map display +
) : (
diff --git a/test/constants.ts b/test/constants.ts index fed560878f..fc436f41c4 100644 --- a/test/constants.ts +++ b/test/constants.ts @@ -209,8 +209,11 @@ export const TEST_SERVICE_MAP_GRAPH = { label: 'order', size: 15, title: 'order\n\n Average duration: 90.1ms \n Error rate: 4.17% \n Request rate: 48', - borderWidth: 0, - color: 'rgba(158, 134, 192, 1)', + borderWidth: 3, + color: { + background: 'rgba(158, 134, 192, 1)', + border: '#4A4A4A', + }, font: { color: 'rgba(72, 122, 180, 1)', }, @@ -224,8 +227,11 @@ export const TEST_SERVICE_MAP_GRAPH = { size: 15, title: 'analytics-service\n\n Average duration: 12.99ms \n Error rate: 0% \n Request rate: 37', - borderWidth: 0, - color: 'rgba(210, 202, 224, 1)', + borderWidth: 3, + color: { + background: 'rgba(210, 202, 224, 1)', + border: '#4A4A4A', + }, font: { color: 'rgba(72, 122, 180, 1)', }, @@ -238,8 +244,11 @@ export const TEST_SERVICE_MAP_GRAPH = { label: 'database', size: 15, title: 'database\n\n Average duration: 49.54ms \n Error rate: 3.77% \n Request rate: 53', - borderWidth: 0, - color: 'rgba(187, 171, 212, 1)', + borderWidth: 3, + color: { + background: 'rgba(187, 171, 212, 1)', + border: '#4A4A4A', + }, font: { color: 'rgba(72, 122, 180, 1)', }, @@ -253,8 +262,11 @@ export const TEST_SERVICE_MAP_GRAPH = { size: 15, title: 'frontend-client\n\n Average duration: 207.71ms \n Error rate: 7.41% \n Request rate: 27', - borderWidth: 0, - color: 'rgba(78, 42, 122, 1)', + borderWidth: 3, + color: { + background: 'rgba(78, 42, 122, 1)', + border: '#4A4A4A', + }, font: { color: 'rgba(72, 122, 180, 1)', }, @@ -267,8 +279,11 @@ export const TEST_SERVICE_MAP_GRAPH = { label: 'inventory', size: 15, title: 'inventory\n\n Average duration: 183.52ms \n Error rate: 3.23% \n Request rate: 31', - borderWidth: 0, - color: 'rgba(95, 61, 138, 1)', + borderWidth: 3, + color: { + background: 'rgba(95, 61, 138, 1)', + border: '#4A4A4A', + }, font: { color: 'rgba(72, 122, 180, 1)', }, @@ -282,8 +297,11 @@ export const TEST_SERVICE_MAP_GRAPH = { size: 15, title: 'authentication\n\n Average duration: 139.09ms \n Error rate: 8.33% \n Request rate: 12', - borderWidth: 0, - color: 'rgba(125, 95, 166, 1)', + borderWidth: 3, + color: { + background: 'rgba(125, 95, 166, 1)', + border: '#4A4A4A', + }, font: { color: 'rgba(72, 122, 180, 1)', }, @@ -296,8 +314,11 @@ export const TEST_SERVICE_MAP_GRAPH = { label: 'payment', size: 15, title: 'payment\n\n Average duration: 134.36ms \n Error rate: 9.09% \n Request rate: 11', - borderWidth: 0, - color: 'rgba(129, 99, 169, 1)', + borderWidth: 3, + color: { + background: 'rgba(129, 99, 169, 1)', + border: '#4A4A4A', + }, font: { color: 'rgba(72, 122, 180, 1)', }, @@ -311,8 +332,11 @@ export const TEST_SERVICE_MAP_GRAPH = { size: 15, title: 'recommendation\n\n Average duration: 176.97ms \n Error rate: 6.25% \n Request rate: 16', - borderWidth: 0, - color: 'rgba(100, 66, 143, 1)', + borderWidth: 3, + color: { + background: 'rgba(100, 66, 143, 1)', + border: '#4A4A4A', + }, font: { color: 'rgba(72, 122, 180, 1)', }, @@ -390,6 +414,7 @@ export const TEST_SERVICE_MAP_GRAPH = { ], }, }; + export const TEST_SERVICE_MAP = { order: { serviceName: 'order', From 385c4633855ddcad7287538f32121eeeec2ed91c Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Wed, 6 Nov 2024 15:51:24 -0800 Subject: [PATCH 07/10] refresh on filter cleared Signed-off-by: Adam Tackett --- .../trace_analytics/components/common/plots/service_map.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index f1018952c3..8c09635199 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -327,7 +327,7 @@ export function ServiceMap({ if (filterChange) { setIsFilterChange(false); } - }, [mode, filterChange]); + }, [mode, filterChange, currService]); useEffect(() => { if (selectedNodeDetails && items?.graph?.nodes) { From 788049258a26cc132a9f239970c86aa848282884 Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Fri, 8 Nov 2024 09:56:57 -0800 Subject: [PATCH 08/10] update focus functionality to set filter, bug fixes Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 114 +++++++++++------- .../common/plots/service_map_node_details.tsx | 2 +- .../__snapshots__/service_view.test.tsx.snap | 1 + .../components/services/service_view.tsx | 1 + 4 files changed, 71 insertions(+), 47 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 8c09635199..86b90435a9 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -90,6 +90,7 @@ export function ServiceMap({ const [selectableOptions, setSelectableOptions] = useState([]); const [isLoading, setIsLoading] = useState(true); const [filterChange, setIsFilterChange] = useState(false); + const [focusedService, setFocusedService] = useState(null); const toggleButtons = [ { @@ -214,8 +215,8 @@ export function ServiceMap({ networkInstance.on('zoom', (params) => { const zoomLevel = params.scale; - if (zoomLevel < 0.1 && zoomLevel < lastZoomLevel) { - networkInstance.moveTo({ scale: 0.1, position: initialPosition }); + if (zoomLevel < 0.05 && zoomLevel < lastZoomLevel) { + networkInstance.moveTo({ scale: 0.05, position: initialPosition }); } else if (zoomLevel > 1.75) { networkInstance.moveTo({ scale: 1.75 }); } @@ -224,21 +225,30 @@ export function ServiceMap({ }); }; - const addServiceFilter = (selectedServiceName: string) => { - if (!addFilter) return; - setGraphKey((prevKey) => prevKey + 1); + const addServiceFilter = (selectedServiceName) => { + if (selectedServiceName === focusedService) return; - addFilter({ - field: 'serviceName', - operator: 'is', - value: selectedServiceName, - inverted: false, - disabled: false, - }); - setIsFilterChange(true); + if (!addFilter) return; - if (!['appCreate', 'detailFlyout'].includes(page)) { - window.scrollTo({ left: 0, top: 0, behavior: 'smooth' }); + if (selectedServiceName) { + setFocusedService(selectedServiceName); + addFilter({ + field: 'serviceName', + operator: 'is', + value: selectedServiceName, + inverted: false, + disabled: false, + }); + } else { + // Clear the filter by adding a disabled filter or an empty filter object + setFocusedService(null); + addFilter({ + field: 'serviceName', + operator: 'is', + value: '', + inverted: false, + disabled: true, + }); } }; @@ -276,25 +286,42 @@ export function ServiceMap({ hoverNode: (_event) => {}, }; - const onFocus = (service: string, networkInstance?: any) => { + const onFocus = (service: string) => { if (service.length === 0) { - // Reset all nodes to the default size and show the entire graph when no service is selected - const resetGraph = getServiceMapGraph( - serviceMap, - idSelected, - ticks, - undefined, - serviceMap[currService!]?.relatedServices, - false // Do not filter by the current service to show the entire graph + setFocusedService(null); + if (addFilter) { + addFilter({ + field: 'serviceName', + operator: 'is', + value: '', + inverted: false, + disabled: true, // Disable the filter to effectively clear it + }); + } + setItems( + getServiceMapGraph( + serviceMap, + idSelected, + ticks, + undefined, + serviceMap[currService!]?.relatedServices, + false // Do not filter by the current service to show the entire graph + ) ); - setItems(resetGraph); - - if (networkInstance) networkInstance.fit(); setInvalid(false); } else if (serviceMap[service]) { - if (!networkInstance) networkInstance = network; + // Focus on the specified service and add a filter + setFocusedService(service); + if (addFilter) { + addFilter({ + field: 'serviceName', + operator: 'is', + value: service, + inverted: false, + disabled: false, + }); + } - // Get a filtered graph showing only nodes connected to the focused service const filteredGraph = getServiceMapGraph( serviceMap, idSelected, @@ -303,16 +330,7 @@ export function ServiceMap({ serviceMap[service]?.relatedServices, true // Enable filtering by the current service to show only connected nodes ); - setItems(filteredGraph); - - networkInstance.focus(serviceMap[service].id, { - scale: 0.75, // Higher scale for closer zoom - animation: { - duration: 1000, // Duration of the zoom-in animation in milliseconds - easingFunction: 'easeInOutQuad', - }, - }); setInvalid(false); } else { setInvalid(true); @@ -323,6 +341,7 @@ export function ServiceMap({ setSelectedNodeDetails(null); setQuery(''); setItems({}); + setFocusedService(null); if (filterChange) { setIsFilterChange(false); @@ -348,10 +367,15 @@ export function ServiceMap({ }, [items]); useEffect(() => { + if (currService === focusedService) { + return; + } + if (!serviceMap || Object.keys(serviceMap).length === 0) { setItems({}); return; } + const values = Object.keys(serviceMap) .filter((service) => serviceMap[service][idSelected]) .map((service) => serviceMap[service][idSelected]!); @@ -401,7 +425,7 @@ export function ServiceMap({ compressed prepend="Focus on" placeholder="Service name" - value={query} + value={focusedService || query} onClick={() => setPopoverOpen(!isPopoverOpen)} onChange={(e) => { const newValue = e.target.value; @@ -450,15 +474,13 @@ export function ServiceMap({ onChange={(newOptions) => { const selectedOption = newOptions.find((option) => option.checked === 'on'); if (selectedOption) { + if (selectedOption.label === focusedService) { + setPopoverOpen(false); + return; + } setQuery(selectedOption.label); - onFocus(selectedOption.label as string); + onFocus(selectedOption.label); setPopoverOpen(false); - setSelectableOptions( - selectableOptions.map((option) => ({ - ...option, - checked: undefined, - })) - ); } }} listProps={{ bordered: true, style: { width: '300px' } }} diff --git a/public/components/trace_analytics/components/common/plots/service_map_node_details.tsx b/public/components/trace_analytics/components/common/plots/service_map_node_details.tsx index e061f1d78b..e059b98c8d 100644 --- a/public/components/trace_analytics/components/common/plots/service_map_node_details.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map_node_details.tsx @@ -18,7 +18,7 @@ import { ServiceNodeDetails } from '../../../../../../common/types/trace_analyti interface ServiceMapNodeDetailsProps { selectedNodeDetails: ServiceNodeDetails | null; setSelectedNodeDetails: React.Dispatch>; - addServiceFilter: (selectedServiceName: string) => void; + addServiceFilter: (selectedServiceName: string | null) => void; setCurrentSelectedService?: (value: React.SetStateAction) => void; } diff --git a/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap b/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap index eee380aed1..65e763370c 100644 --- a/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap +++ b/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap @@ -233,6 +233,7 @@ exports[`Service view component renders service view 1`] = ` currService="order" filterByCurrService={true} idSelected="latency" + mode="data_prepper" page="serviceView" serviceMap={Object {}} setIdSelected={[Function]} diff --git a/public/components/trace_analytics/components/services/service_view.tsx b/public/components/trace_analytics/components/services/service_view.tsx index 2dcb3cd613..b941c8653c 100644 --- a/public/components/trace_analytics/components/services/service_view.tsx +++ b/public/components/trace_analytics/components/services/service_view.tsx @@ -532,6 +532,7 @@ export function ServiceView(props: ServiceViewProps) { currService={props.serviceName} page="serviceView" filterByCurrService={true} + mode={mode} /> ) : ( From 9cd464a48ba901cc8414600f944ed7ce9939837d Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Fri, 8 Nov 2024 11:51:00 -0800 Subject: [PATCH 09/10] handle clearing focus Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 86b90435a9..1bf4734136 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -91,6 +91,7 @@ export function ServiceMap({ const [isLoading, setIsLoading] = useState(true); const [filterChange, setIsFilterChange] = useState(false); const [focusedService, setFocusedService] = useState(null); + const [clearFilterRequest, setClearFilterRequest] = useState(false); const toggleButtons = [ { @@ -138,6 +139,44 @@ export function ServiceMap({ }, ]; + const clearFilter = () => { + setFocusedService(null); + setClearFilterRequest(true); + }; + + useEffect(() => { + if (clearFilterRequest && focusedService === null) { + setClearFilterRequest(false); + + setQuery(''); + currService = ''; + + if (addFilter) { + addFilter({ + field: 'serviceName', + operator: 'is', + value: '', + inverted: false, + disabled: true, // Disable the filter to effectively clear it + }); + } + + // Reset the graph to show the full view + setItems( + getServiceMapGraph( + serviceMap, + idSelected, + ticks, + undefined, + serviceMap[currService!]?.relatedServices, + false // Do not filter by the current service to show the entire graph + ) + ); + + setInvalid(false); + } + }, [focusedService, clearFilterRequest]); + useEffect(() => { if (items?.graph?.nodes) { const visibleNodes = items.graph.nodes.map((node) => node.label); @@ -288,27 +327,7 @@ export function ServiceMap({ const onFocus = (service: string) => { if (service.length === 0) { - setFocusedService(null); - if (addFilter) { - addFilter({ - field: 'serviceName', - operator: 'is', - value: '', - inverted: false, - disabled: true, // Disable the filter to effectively clear it - }); - } - setItems( - getServiceMapGraph( - serviceMap, - idSelected, - ticks, - undefined, - serviceMap[currService!]?.relatedServices, - false // Do not filter by the current service to show the entire graph - ) - ); - setInvalid(false); + clearFilter(); } else if (serviceMap[service]) { // Focus on the specified service and add a filter setFocusedService(service); From 4ad83481dccbe3f7dc9f9f29e4aac3540ad1d0ad Mon Sep 17 00:00:00 2001 From: Adam Tackett Date: Mon, 11 Nov 2024 15:59:50 -0800 Subject: [PATCH 10/10] hide focus in the flyout Signed-off-by: Adam Tackett --- .../components/common/plots/service_map.tsx | 160 +++++++++--------- .../__snapshots__/service_view.test.tsx.snap | 1 + .../components/services/service_view.tsx | 1 + 3 files changed, 84 insertions(+), 78 deletions(-) diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index 1bf4734136..b764e21f6a 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -61,6 +61,7 @@ export function ServiceMap({ filterByCurrService, includeMetricsCallback, mode, + hideSearchBar = false, }: { serviceMap: ServiceObject; idSelected: 'latency' | 'error_rate' | 'throughput'; @@ -80,6 +81,7 @@ export function ServiceMap({ filterByCurrService?: boolean; includeMetricsCallback?: () => void; mode?: string; + hideSearchBar?: boolean; }) { const [graphKey, setGraphKey] = useState(0); // adding key to allow for re-renders const [invalid, setInvalid] = useState(false); @@ -436,90 +438,92 @@ export function ServiceMap({ )} - - - setPopoverOpen(!isPopoverOpen)} - onChange={(e) => { - const newValue = e.target.value; - setQuery(newValue); - if (newValue === '') { - setGraphKey((prevKey) => prevKey + 1); - setQuery(''); - onFocus(''); - } - }} - isInvalid={query.length > 0 && invalid} - append={ - { + {!hideSearchBar && ( + + + setPopoverOpen(!isPopoverOpen)} + onChange={(e) => { + const newValue = e.target.value; + setQuery(newValue); + if (newValue === '') { setGraphKey((prevKey) => prevKey + 1); setQuery(''); onFocus(''); - }} - /> - } - aria-controls="service-select-dropdown" - /> - } - isOpen={isPopoverOpen} - closePopover={() => setPopoverOpen(false)} - panelPaddingSize="none" - anchorPosition="downLeft" - repositionOnScroll - id="service-select-dropdown" - ownFocus={false} - > - setQuery(e.target.value), - isClearable: true, - autoFocus: true, - }} - options={selectableOptions.filter((option) => - option.label.toLowerCase().includes(query.toLowerCase()) - )} - singleSelection={true} - onChange={(newOptions) => { - const selectedOption = newOptions.find((option) => option.checked === 'on'); - if (selectedOption) { - if (selectedOption.label === focusedService) { - setPopoverOpen(false); - return; + } + }} + isInvalid={query.length > 0 && invalid} + append={ + { + setGraphKey((prevKey) => prevKey + 1); + setQuery(''); + onFocus(''); + }} + /> } - setQuery(selectedOption.label); - onFocus(selectedOption.label); - setPopoverOpen(false); - } - }} - listProps={{ bordered: true, style: { width: '300px' } }} + aria-controls="service-select-dropdown" + /> + } + isOpen={isPopoverOpen} + closePopover={() => setPopoverOpen(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + repositionOnScroll + id="service-select-dropdown" + ownFocus={false} > - {(list) =>
{list}
} -
-
-
- {page === 'traces' && ( - - onChangeSelectable(value)} - /> + setQuery(e.target.value), + isClearable: true, + autoFocus: true, + }} + options={selectableOptions.filter((option) => + option.label.toLowerCase().includes(query.toLowerCase()) + )} + singleSelection={true} + onChange={(newOptions) => { + const selectedOption = newOptions.find((option) => option.checked === 'on'); + if (selectedOption) { + if (selectedOption.label === focusedService) { + setPopoverOpen(false); + return; + } + setQuery(selectedOption.label); + onFocus(selectedOption.label); + setPopoverOpen(false); + } + }} + listProps={{ bordered: true, style: { width: '300px' } }} + > + {(list) =>
{list}
} +
+
- )} -
+ {page === 'traces' && ( + + onChangeSelectable(value)} + /> + + )} + + )} {Object.keys(serviceMap).length > 0 ? ( diff --git a/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap b/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap index 65e763370c..9bc8845a99 100644 --- a/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap +++ b/public/components/trace_analytics/components/services/__tests__/__snapshots__/service_view.test.tsx.snap @@ -232,6 +232,7 @@ exports[`Service view component renders service view 1`] = ` ) : (