diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.test.tsx index ced4d3af057f..f33eb3d515c1 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.test.tsx @@ -54,13 +54,6 @@ describe('OperationTypeSelect', function () { fireEvent.click(screen.getByTestId('operationTypeSelect')); - expect(setSeries).toHaveBeenCalledWith(0, { - operationType: 'median', - dataType: 'ux', - time: { from: 'now-15m', to: 'now' }, - name: 'performance-distribution', - }); - fireEvent.click(screen.getByText('95th Percentile')); expect(setSeries).toHaveBeenCalledWith(0, { operationType: '95th', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx index a223a74d74ae..5d949b91278f 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSuperSelect } from '@elastic/eui'; @@ -30,12 +30,6 @@ export function OperationTypeSelect({ setSeries(seriesId, { ...series, operationType: value }); }; - useEffect(() => { - setSeries(seriesId, { ...series, operationType: operationType || defaultOperationType }); - // We only want to call this when defaultOperationType changes - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [defaultOperationType]); - return ( { + const applyChanges = jest.fn(); + + const mockSeriesStorage = (allSeries: AllSeries, urlAllSeries: AllSeries) => { + jest.clearAllMocks(); + jest.spyOn(hooks, 'useSeriesStorage').mockReturnValue({ + ...jest.requireActual('../hooks/use_series_storage'), + allSeries, + applyChanges, + storage: { get: jest.fn().mockReturnValue(urlAllSeries) } as any, + }); + }; + + const assertApplyIsEnabled = async () => { + render(); + + const applyBtn = screen.getByText(/Apply changes/i); + + const btnComponent = screen.getByTestId('seriesChangesApplyButton'); + + expect(btnComponent.classList).not.toContain('euiButton-isDisabled'); + + fireEvent.click(applyBtn); + + await waitFor(() => { + expect(applyChanges).toBeCalledTimes(1); + }); + }; + + it('renders ViewActions', async () => { + mockSeriesStorage([], []); + render(); + + expect(screen.getByText(/Apply changes/i)).toBeInTheDocument(); + }); + + it('apply button is disabled when no changes', async () => { + mockSeriesStorage([], []); + + render(); + const applyBtn = screen.getByText(/Apply changes/i); + + const btnComponent = screen.getByTestId('seriesChangesApplyButton'); + + expect(btnComponent.classList).toContain('euiButton-isDisabled'); + + fireEvent.click(applyBtn); + + await waitFor(() => { + expect(applyChanges).toBeCalledTimes(0); + }); + }); + + it('should call apply changes when series length is different', async function () { + mockSeriesStorage([], [{ name: 'testSeries' } as any]); + + await assertApplyIsEnabled(); + }); + + it('should call apply changes when series content is different', async function () { + mockSeriesStorage([{ name: 'testSeriesChange' } as any], [{ name: 'testSeries' } as any]); + + await assertApplyIsEnabled(); + }); + + it('should call apply changes when series content is different as in undefined', async function () { + mockSeriesStorage( + [{ name: undefined } as any], + [{ name: 'testSeries', operationType: undefined } as any] + ); + + await assertApplyIsEnabled(); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/views/view_actions.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/views/view_actions.tsx index ee2668aa0c39..e85ce8ff40c6 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/views/view_actions.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/views/view_actions.tsx @@ -8,22 +8,41 @@ import React from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { isEqual } from 'lodash'; +import { isEqual, pickBy } from 'lodash'; import { allSeriesKey, convertAllShortSeries, useSeriesStorage } from '../hooks/use_series_storage'; interface Props { onApply?: () => void; } +export function removeUndefinedProps(obj: T): Partial { + return pickBy(obj, (value) => value !== undefined); +} + export function ViewActions({ onApply }: Props) { const { allSeries, storage, applyChanges } = useSeriesStorage(); - const noChanges = isEqual(allSeries, convertAllShortSeries(storage.get(allSeriesKey) ?? [])); + const urlAllSeries = convertAllShortSeries(storage.get(allSeriesKey) ?? []); + + let noChanges = allSeries.length === urlAllSeries.length; + + if (noChanges) { + noChanges = !allSeries.some( + (series, index) => + !isEqual(removeUndefinedProps(series), removeUndefinedProps(urlAllSeries[index])) + ); + } return ( - applyChanges(onApply)} isDisabled={noChanges} fill size="s"> + applyChanges(onApply)} + isDisabled={noChanges} + fill + size="s" + data-test-subj={'seriesChangesApplyButton'} + > {i18n.translate('xpack.observability.expView.seriesBuilder.apply', { defaultMessage: 'Apply changes', })}