diff --git a/public/components/metrics/helpers/__tests__/utils.test.tsx b/public/components/metrics/helpers/__tests__/utils.test.tsx index fa599cc0a9..42d099a356 100644 --- a/public/components/metrics/helpers/__tests__/utils.test.tsx +++ b/public/components/metrics/helpers/__tests__/utils.test.tsx @@ -34,26 +34,6 @@ import _ from 'lodash'; describe('Utils helper functions', () => { configure({ adapter: new Adapter() }); - it('validates onTimeChange function', () => { - const setRecentlyUsedRanges = jest.fn((x) => x); - const setStart = jest.fn(); - const setEnd = jest.fn(); - const recentlyUsedRanges: DurationRange[] = []; - onTimeChange( - '2022-01-30T18:44:40.577Z', - '2022-02-25T19:18:33.075Z', - recentlyUsedRanges, - setRecentlyUsedRanges, - setStart, - setEnd - ); - expect(setRecentlyUsedRanges).toHaveBeenCalledWith([ - { start: '2022-01-30T18:44:40.577Z', end: '2022-02-25T19:18:33.075Z' }, - ]); - expect(setStart).toHaveBeenCalledWith('2022-01-30T18:44:40.577Z'); - expect(setEnd).toHaveBeenCalledWith('2022-02-25T19:18:33.075Z'); - }); - it('validates getNewVizDimensions function', () => { expect(getNewVizDimensions([])).toMatchObject({ x: 0, diff --git a/public/components/metrics/helpers/utils.tsx b/public/components/metrics/helpers/utils.tsx index 96f7fe4772..2736a04679 100644 --- a/public/components/metrics/helpers/utils.tsx +++ b/public/components/metrics/helpers/utils.tsx @@ -22,24 +22,6 @@ import { VisualizationType } from '../../../../common/types/custom_panels'; import { DEFAULT_METRIC_HEIGHT, DEFAULT_METRIC_WIDTH } from '../../../../common/constants/metrics'; import { updateQuerySpanInterval } from '../../custom_panels/helpers/utils'; -export const onTimeChange = ( - start: ShortDate, - end: ShortDate, - recentlyUsedRanges: DurationRange[], - setRecentlyUsedRanges: React.Dispatch>, - setStart: React.Dispatch>, - setEnd: React.Dispatch> -) => { - const recentlyUsedRange = recentlyUsedRanges.filter((recentlyUsedRange) => { - const isDuplicate = recentlyUsedRange.start === start && recentlyUsedRange.end === end; - return !isDuplicate; - }); - recentlyUsedRange.unshift({ start, end }); - setStart(start); - setEnd(end); - setRecentlyUsedRanges(recentlyUsedRange.slice(0, 9)); -}; - // PPL Service requestor export const pplServiceRequestor = (pplService: PPLService, finalQuery: string) => { return pplService.fetch({ query: finalQuery, format: VISUALIZATION }).catch((error: Error) => { @@ -58,21 +40,21 @@ export const getVisualizations = (http: CoreStart['http']) => { }); }; -interface boxType { +interface BoxType { x1: number; y1: number; x2: number; y2: number; } -const calculatOverlapArea = (bb1: boxType, bb2: boxType) => { - const x_left = Math.max(bb1.x1, bb2.x1); - const y_top = Math.max(bb1.y1, bb2.y1); - const x_right = Math.min(bb1.x2, bb2.x2); - const y_bottom = Math.min(bb1.y2, bb2.y2); +const calculatOverlapArea = (bb1: BoxType, bb2: BoxType) => { + const xLeft = Math.max(bb1.x1, bb2.x1); + const yTop = Math.max(bb1.y1, bb2.y1); + const xRight = Math.min(bb1.x2, bb2.x2); + const yBottom = Math.min(bb1.y2, bb2.y2); - if (x_right < x_left || y_bottom < y_top) return 0; - return (x_right - x_left) * (y_bottom - y_top); + if (xRight < xLeft || yBottom < yTop) return 0; + return (xRight - xLeft) * (yBottom - yTop); }; const getTotalOverlapArea = (panelVisualizations: MetricType[]) => { @@ -149,7 +131,7 @@ export const mergeLayoutAndMetrics = ( for (let i = 0; i < newVisualizationList.length; i++) { for (let j = 0; j < layout.length; j++) { - if (newVisualizationList[i].id == layout[j].i) { + if (newVisualizationList[i].id === layout[j].i) { newPanelVisualizations.push({ ...newVisualizationList[i], x: layout[j].x, diff --git a/public/components/metrics/index.tsx b/public/components/metrics/index.tsx index ecac571428..168f68fe4b 100644 --- a/public/components/metrics/index.tsx +++ b/public/components/metrics/index.tsx @@ -13,13 +13,11 @@ import { OnTimeChangeProps, ShortDate, } from '@elastic/eui'; -import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; import React, { ReactChild, useEffect, useState } from 'react'; import { HashRouter, Route, RouteComponentProps } from 'react-router-dom'; import { StaticContext } from 'react-router-dom'; import { useSelector } from 'react-redux'; import { ChromeBreadcrumb, Toast } from '../../../../../src/core/public'; -import { onTimeChange } from './helpers/utils'; import { Sidebar } from './sidebar/sidebar'; import { EmptyMetricsView } from './view/empty_view'; import PPLService from '../../services/requests/ppl'; @@ -44,19 +42,10 @@ export const Home = ({ chrome, parentBreadcrumb }: MetricsProps) => { const selectedMetrics = useSelector(selectedMetricsSelector); const metricsLayout = useSelector(metricsLayoutSelector); - // Date picker constants - const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]); - const [startTime, setStartTime] = useState('now-1d'); - const [endTime, setEndTime] = useState('now'); - // Top panel const [IsTopPanelDisabled, setIsTopPanelDisabled] = useState(false); const [editMode, setEditMode] = useState(false); - const [onRefresh, setOnRefresh] = useState(false); const [editActionType, setEditActionType] = useState(''); - const [resolutionValue, setResolutionValue] = useState(resolutionOptions[2].value); - const [spanValue, setSpanValue] = useState(1); - const resolutionSelectId = htmlIdGenerator('resolutionSelect')(); const [toasts, setToasts] = useState([]); const [toastRightSide, setToastRightSide] = useState(true); @@ -69,26 +58,6 @@ export const Home = ({ chrome, parentBreadcrumb }: MetricsProps) => { setToasts([...toasts, { id: new Date().toISOString(), title, text, color } as Toast]); }; - const onRefreshFilters = () => { - if (spanValue < 1) { - setToast('Please add a valid span interval', 'danger'); - return; - } - setOnRefresh(!onRefresh); - }; - - const onDatePickerChange = (props: OnTimeChangeProps) => { - onTimeChange( - props.start, - props.end, - recentlyUsedRanges, - setRecentlyUsedRanges, - setStartTime, - setEndTime - ); - onRefreshFilters(); - }; - const onEditClick = (savedVisualizationId: string) => { window.location.assign(`${observabilityLogsID}#/explorer/${savedVisualizationId}`); }; @@ -135,20 +104,11 @@ export const Home = ({ chrome, parentBreadcrumb }: MetricsProps) => {
@@ -168,13 +128,9 @@ export const Home = ({ chrome, parentBreadcrumb }: MetricsProps) => { panelVisualizations={panelVisualizations} setPanelVisualizations={setPanelVisualizations} editMode={editMode} - startTime={startTime} - endTime={endTime} moveToEvents={onEditClick} - onRefresh={onRefresh} editActionType={editActionType} setEditActionType={setEditActionType} - spanParam={spanValue + resolutionValue} /> ) : ( diff --git a/public/components/metrics/redux/slices/metrics_slice.ts b/public/components/metrics/redux/slices/metrics_slice.ts index e323ba628d..93e1ff26b9 100644 --- a/public/components/metrics/redux/slices/metrics_slice.ts +++ b/public/components/metrics/redux/slices/metrics_slice.ts @@ -44,6 +44,14 @@ const initialState = { dataSources: [OBSERVABILITY_CUSTOM_METRIC], dataSourceTitles: ['Observability Custom Metrics'], dataSourceIcons: coloredIconsFrom([OBSERVABILITY_CUSTOM_METRIC]), + dateSpanFilter: { + start: 'now-1d', + end: 'now', + span: 1, + resolution: 'h', + recentlyUsedRanges: [], + }, + refresh: 0, // set to new Date() to trigger }; export const loadMetrics = () => async (dispatch) => { @@ -168,20 +176,30 @@ export const metricSlice = createSlice({ setDataSourceIcons: (state, { payload }) => { state.dataSourceIcons = payload; }, + setDateSpan: (state, { payload }) => { + state.dateSpanFilter = { ...state.dateSpanFilter, ...payload }; + }, + setRefresh: (state) => { + state.refresh = Date.now(); + }, }, }); export const { - setMetrics, deSelectMetric, selectMetric, - updateMetricsLayout, - setSearch, - setDataSources, - setDataSourceTitles, setDataSourceIcons, + setDataSourceTitles, + setDataSources, + setMetrics, + setRefresh, + setSearch, + updateMetricsLayout, } = metricSlice.actions; +/** private actions */ +const { setDateSpan } = metricSlice.actions; + export const availableMetricsSelector = (state) => state.metrics.metrics .filter((metric) => !state.metrics.selected.includes(metric.id)) @@ -190,6 +208,22 @@ export const availableMetricsSelector = (state) => state.metrics.search === '' || metric.name.match(new RegExp(state.metrics.search, 'i')) ); +export const updateStartEndDate = ({ start, end }) => (dispatch, getState) => { + const currentDateSpanFilter = getState().metrics.dateSpanFilter; + const recentlyUsedRange = currentDateSpanFilter.recentlyUsedRanges.filter((r) => { + const isDuplicate = r.start === start && r.end === end; + return !isDuplicate; + }); + recentlyUsedRange.unshift({ start, end }); + + dispatch(setDateSpan({ start, end, recentlyUsedRanges: recentlyUsedRange.slice(0, 9) })); + dispatch(setRefresh()); +}; + +export const updateDateSpan = (props: { span?: string; resolution?: string }) => (dispatch) => { + dispatch(setDateSpan(props)); // specifically use props variable to partial update +}; + export const selectedMetricsSelector = (state) => state.metrics.selected.map((id) => state.metrics.metrics.find((metric) => metric.id === id)); @@ -201,4 +235,8 @@ export const metricsLayoutSelector = (state) => state.metrics.metricsLayout; export const dataSourcesSelector = (state) => state.metrics.dataSources; +export const dateSpanFilterSelector = (state) => state.metrics.dateSpanFilter; + +export const refreshSelector = (state) => state.metrics.refresh; + export const metricsReducers = metricSlice.reducer; diff --git a/public/components/metrics/top_menu/__tests__/__snapshots__/top_menu.test.tsx.snap b/public/components/metrics/top_menu/__tests__/__snapshots__/top_menu.test.tsx.snap index bdd596ab6e..dc2ff27603 100644 --- a/public/components/metrics/top_menu/__tests__/__snapshots__/top_menu.test.tsx.snap +++ b/public/components/metrics/top_menu/__tests__/__snapshots__/top_menu.test.tsx.snap @@ -88,7 +88,6 @@ exports[`Metrics Top Menu Component renders Top Menu Component when disabled in aria-label="resolutionSelect" className="resolutionSelectOption" data-test-subj="metrics__spanResolutionSelect" - id="select_123" onChange={[Function]} options={ Array [ @@ -132,7 +131,6 @@ exports[`Metrics Top Menu Component renders Top Menu Component when disabled in aria-label="resolutionSelect" className="resolutionSelectOption" data-test-subj="metrics__spanResolutionSelect" - id="select_123" onChange={[Function]} options={ Array [ @@ -198,7 +196,6 @@ exports[`Metrics Top Menu Component renders Top Menu Component when disabled in aria-label="resolutionSelect" className="euiFormControlLayout__append resolutionSelectOption" data-test-subj="metrics__spanResolutionSelect" - id="select_123" key="0/.0" onChange={[Function]} options={ @@ -236,7 +233,6 @@ exports[`Metrics Top Menu Component renders Top Menu Component when disabled in "type": "arrowDown", } } - inputId="select_123" isLoading={false} >
void; - recentlyUsedRanges: DurationRange[]; editMode: boolean; setEditMode: React.Dispatch>; setEditActionType: React.Dispatch>; panelVisualizations: MetricType[]; setPanelVisualizations: React.Dispatch>; - resolutionValue: string; - setResolutionValue: React.Dispatch>; - spanValue: number; - setSpanValue: React.Dispatch>; - resolutionSelectId: string; setToast: (title: string, color?: string, text?: any, side?: string) => void; } export const TopMenu = ({ IsTopPanelDisabled, - startTime, - endTime, - onDatePickerChange, - recentlyUsedRanges, editMode, setEditActionType, setEditMode, panelVisualizations, setPanelVisualizations, - resolutionValue, - setResolutionValue, - spanValue, - setSpanValue, - resolutionSelectId, setToast, }: TopMenuProps) => { // Redux tools const dispatch = useDispatch(); const metricsLayout = useSelector(metricsLayoutSelector); const sortedMetricsLayout = sortMetricLayout([...metricsLayout]); + const dateSpanFilter = useSelector(dateSpanFilterSelector); const [visualizationsMetaData, setVisualizationsMetaData] = useState([]); const [originalPanelVisualizations, setOriginalPanelVisualizations] = useState([]); @@ -107,10 +96,6 @@ export const TopMenu = ({ setEditActionType(editType); }; - const onResolutionChange = (e) => { - setResolutionValue(e.target.value); - }; - const cancelButton = ( editPanel('cancel')}> Cancel @@ -162,9 +147,9 @@ export const TopMenu = ({ sortedMetricsLayout.map(async (metricLayout, index) => { const updatedMetric = updateMetricsWithSelections( visualizationsMetaData[index], - startTime, - endTime, - spanValue + resolutionValue + dateSpanFilter.start, + dateSpanFilter.end, + dateSpanFilter.span + dateSpanFilter.resolution ); if (metricLayout.metricType === 'prometheusMetric') { @@ -212,17 +197,16 @@ export const TopMenu = ({ setSpanValue(e.target.value)} + value={dateSpanFilter.span} + isInvalid={dateSpanFilter.span < 1} + onChange={(e) => dispatch(updateDateSpan({ span: e.target.value }))} data-test-subj="metrics__spanValue" append={ onResolutionChange(e)} + value={dateSpanFilter.resolution} + onChange={(e) => dispatch(updateDateSpan({ resolution: e.target.value }))} aria-label="resolutionSelect" data-test-subj="metrics__spanResolutionSelect" /> @@ -235,10 +219,10 @@ export const TopMenu = ({ dispatch(updateStartEndDate(e))} + recentlyUsedRanges={dateSpanFilter.recentlyUsedRanges} isDisabled={IsTopPanelDisabled} /> diff --git a/public/components/metrics/view/__tests__/__snapshots__/metrics_grid.test.tsx.snap b/public/components/metrics/view/__tests__/__snapshots__/metrics_grid.test.tsx.snap index 36eb2a7fa9..1fac9b7f19 100644 --- a/public/components/metrics/view/__tests__/__snapshots__/metrics_grid.test.tsx.snap +++ b/public/components/metrics/view/__tests__/__snapshots__/metrics_grid.test.tsx.snap @@ -733,10 +733,10 @@ exports[`Metrics Grid Component renders Metrics Grid Component 1`] = ` > >; editMode: boolean; - startTime: string; - endTime: string; moveToEvents: (savedVisualizationId: string) => any; - onRefresh: boolean; editActionType: string; setEditActionType: React.Dispatch>; - spanParam: string; } export const MetricsGrid = ({ @@ -39,16 +40,15 @@ export const MetricsGrid = ({ panelVisualizations, setPanelVisualizations, editMode, - startTime, - endTime, moveToEvents, - onRefresh, editActionType, setEditActionType, - spanParam, }: MetricsGridProps) => { // Redux tools const dispatch = useDispatch(); + const dateSpanFilter = useSelector(dateSpanFilterSelector); + const refresh = useSelector(refreshSelector); + const updateLayout = (metric: any) => dispatch(updateMetricsLayout(metric)); const handleRemoveMetric = (metric: any) => { dispatch(deSelectMetric(metric)); @@ -73,9 +73,9 @@ export const MetricsGrid = ({ editMode={editMode} visualizationId={panelVisualization.id} savedVisualizationId={panelVisualization.savedVisualizationId} - fromTime={startTime} - toTime={endTime} - onRefresh={onRefresh} + fromTime={dateSpanFilter.start} + toTime={dateSpanFilter.end} + onRefresh={refresh} onEditClick={moveToEvents} usedInNotebooks={true} pplFilterValue="" @@ -83,7 +83,7 @@ export const MetricsGrid = ({ catalogVisualization={ panelVisualization.metricType === 'savedCustomMetric' ? undefined : true } - spanParam={spanParam} + spanParam={`${dateSpanFilter.span}${dateSpanFilter.resolution}`} /> )); setGridData(gridDataComps); @@ -149,7 +149,7 @@ export const MetricsGrid = ({ useEffect(() => { loadVizComponents(); - }, [onRefresh]); + }, [refresh]); useEffect(() => { loadVizComponents();