Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[APM] Service overview: Introduce time-series comparison #88665

Merged
merged 14 commits into from
Jan 21, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function TransactionDetails({
<h1>{transactionName}</h1>
</EuiTitle>
</ApmHeader>
<SearchBar />
<SearchBar showTimeComparison />
<EuiPage>
<EuiFlexGroup>
<EuiFlexItem grow={1}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export function ServiceInventory() {

return (
<>
<SearchBar />
<SearchBar showTimeComparison />
<EuiPage>
<EuiFlexGroup>
<EuiFlexItem grow={1}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ function wrapper({ children }: { children?: ReactNode }) {
rangeTo: 'now',
start: 'mystart',
end: 'myend',
comparisonEnabled: true,
comparisonType: 'yesterday',
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function ServiceOverview({
return (
<AnnotationsContextProvider>
<ChartPointerEventContextProvider>
<SearchBar prepend={transactionTypeLabel} />
<SearchBar prepend={transactionTypeLabel} showTimeComparison />
<EuiPage>
<EuiFlexGroup direction="column" gutterSize="s">
{isRumAgent && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export function TransactionOverview({ serviceName }: TransactionOverviewProps) {

return (
<>
<SearchBar />
<SearchBar showTimeComparison />

<EuiPage>
<EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ describe('TransactionOverview', () => {
});

expect(history.location.search).toEqual(
'?transactionType=secondType&rangeFrom=now-15m&rangeTo=now'
'?transactionType=secondType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=yesterday'
);
expect(getByText(container, 'firstType')).toBeInTheDocument();
expect(getByText(container, 'secondType')).toBeInTheDocument();
Expand All @@ -139,7 +139,7 @@ describe('TransactionOverview', () => {

expect(history.push).toHaveBeenCalled();
expect(history.location.search).toEqual(
'?transactionType=firstType&rangeFrom=now-15m&rangeTo=now'
'?transactionType=firstType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=yesterday'
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ export type APMQueryParams = {
searchTerm?: string;
percentile?: 50 | 75 | 90 | 95 | 99;
latencyAggregationType?: string;
comparisonEnabled?: boolean;
comparisonType?: string;
} & { [key in LocalUIFilterName]?: string };

// forces every value of T[K] to be type: string
Expand Down
38 changes: 32 additions & 6 deletions x-pack/plugins/apm/public/components/shared/search_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,48 @@
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { px } from '../../style/variables';
import { DatePicker } from './DatePicker';
import { KueryBar } from './KueryBar';
import { TimeComparison } from './time_comparison';
import { useBreakPoints } from '../app/RumDashboard/hooks/useBreakPoints';

const SearchBarFlexGroup = styled(EuiFlexGroup)`
margin: ${({ theme }) =>
`${theme.eui.euiSizeM} ${theme.eui.euiSizeM} -${theme.eui.gutterTypes.gutterMedium} ${theme.eui.euiSizeM}`};
`;

export function SearchBar(props: { prepend?: React.ReactNode | string }) {
interface Props {
prepend?: React.ReactNode | string;
showTimeComparison?: boolean;
}

function getRowDirection(showColumn: boolean) {
return showColumn ? 'column' : 'row';
}

export function SearchBar({ prepend, showTimeComparison = false }: Props) {
const { isMedium, isLarge } = useBreakPoints();
return (
<SearchBarFlexGroup alignItems="flexStart" gutterSize="s">
<EuiFlexItem grow={3}>
<KueryBar prepend={props.prepend} />
<SearchBarFlexGroup gutterSize="s" direction={getRowDirection(isLarge)}>
<EuiFlexItem>
<KueryBar prepend={prepend} />
</EuiFlexItem>
<EuiFlexItem grow={1}>
<DatePicker />
<EuiFlexItem grow={false}>
<EuiFlexGroup
justifyContent="flexEnd"
gutterSize="s"
direction={getRowDirection(isMedium)}
>
{showTimeComparison && (
<EuiFlexItem style={{ minWidth: px(300) }}>
<TimeComparison />
</EuiFlexItem>
)}
<EuiFlexItem>
<DatePicker />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</SearchBarFlexGroup>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* 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 { render } from '@testing-library/react';
import React, { ReactNode } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { EuiThemeProvider } from '../../../../../observability/public';
import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider';
import { IUrlParams } from '../../../context/url_params_context/types';
import {
expectTextsInDocument,
expectTextsNotInDocument,
} from '../../../utils/testHelpers';
import { TimeComparison } from './';
import * as urlHelpers from '../../shared/Links/url_helpers';

function getWrapper(params?: IUrlParams) {
return ({ children }: { children?: ReactNode }) => {
return (
<MemoryRouter>
<MockUrlParamsContextProvider params={params}>
<EuiThemeProvider>{children}</EuiThemeProvider>
</MockUrlParamsContextProvider>
</MemoryRouter>
);
};
}

describe('TimeComparison', () => {
const spy = jest.spyOn(urlHelpers, 'replace');
beforeEach(() => {
jest.resetAllMocks();
});
describe('Time range is between 0 - 24 hours', () => {
it('sets default values', () => {
const Wrapper = getWrapper({
start: '2021-01-28T14:45:00.000Z',
end: '2021-01-28T15:00:00.000Z',
});
render(<TimeComparison />, {
wrapper: Wrapper,
});
expect(spy).toHaveBeenCalledWith(expect.anything(), {
query: {
comparisonEnabled: 'true',
comparisonType: 'yesterday',
},
});
});
it('selects yesterday and enables comparison', () => {
const Wrapper = getWrapper({
start: '2021-01-28T14:45:00.000Z',
end: '2021-01-28T15:00:00.000Z',
comparisonEnabled: true,
comparisonType: 'yesterday',
});
const component = render(<TimeComparison />, {
wrapper: Wrapper,
});
expectTextsInDocument(component, ['Yesterday', 'A week ago']);
expect(
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
.selectedIndex
).toEqual(0);
});
});

describe('Time range is between 24 hours - 1 week', () => {
it('sets default values', () => {
const Wrapper = getWrapper({
start: '2021-01-26T15:00:00.000Z',
end: '2021-01-28T15:00:00.000Z',
});
render(<TimeComparison />, {
wrapper: Wrapper,
});
expect(spy).toHaveBeenCalledWith(expect.anything(), {
query: {
comparisonEnabled: 'true',
comparisonType: 'week',
},
});
});
it('selects week and enables comparison', () => {
const Wrapper = getWrapper({
start: '2021-01-26T15:00:00.000Z',
end: '2021-01-28T15:00:00.000Z',
comparisonEnabled: true,
comparisonType: 'week',
});
const component = render(<TimeComparison />, {
wrapper: Wrapper,
});
expectTextsNotInDocument(component, ['Yesterday']);
expectTextsInDocument(component, ['A week ago']);
expect(
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
.selectedIndex
).toEqual(0);
});
});

describe('Time range is greater than 7 days', () => {
it('Shows absolute times without year when within the same year', () => {
const Wrapper = getWrapper({
start: '2021-01-20T15:00:00.000Z',
end: '2021-01-28T15:00:00.000Z',
comparisonEnabled: true,
comparisonType: 'previousPeriod',
});
const component = render(<TimeComparison />, {
wrapper: Wrapper,
});
expect(spy).not.toHaveBeenCalled();
expectTextsInDocument(component, ['20/01 - 28/01']);
expect(
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
.selectedIndex
).toEqual(0);
});

it('Shows absolute times with year when on different year', () => {
const Wrapper = getWrapper({
start: '2020-12-20T15:00:00.000Z',
end: '2021-01-28T15:00:00.000Z',
comparisonEnabled: true,
comparisonType: 'previousPeriod',
});
const component = render(<TimeComparison />, {
wrapper: Wrapper,
});
expect(spy).not.toHaveBeenCalled();
expectTextsInDocument(component, ['20/12/20 - 28/01/21']);
expect(
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
.selectedIndex
).toEqual(0);
});
});
});
Loading