Skip to content

Commit

Permalink
[UX] Add percentile selector (#78562) (#79058)
Browse files Browse the repository at this point in the history
  • Loading branch information
shahzad31 authored Oct 1, 2020
1 parent ea018b3 commit 9775119
Show file tree
Hide file tree
Showing 41 changed files with 504 additions and 242 deletions.
98 changes: 98 additions & 0 deletions x-pack/plugins/apm/common/ui_filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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 { i18n } from '@kbn/i18n';
import {
AGENT_NAME,
CLIENT_GEO_COUNTRY_ISO_CODE,
CONTAINER_ID,
HOST_NAME,
POD_NAME,
SERVICE_NAME,
SERVICE_VERSION,
TRANSACTION_RESULT,
TRANSACTION_URL,
USER_AGENT_DEVICE,
USER_AGENT_NAME,
USER_AGENT_OS,
} from './elasticsearch_fieldnames';

export const filtersByName = {
host: {
title: i18n.translate('xpack.apm.localFilters.titles.host', {
defaultMessage: 'Host',
}),
fieldName: HOST_NAME,
},
agentName: {
title: i18n.translate('xpack.apm.localFilters.titles.agentName', {
defaultMessage: 'Agent name',
}),
fieldName: AGENT_NAME,
},
containerId: {
title: i18n.translate('xpack.apm.localFilters.titles.containerId', {
defaultMessage: 'Container ID',
}),
fieldName: CONTAINER_ID,
},
podName: {
title: i18n.translate('xpack.apm.localFilters.titles.podName', {
defaultMessage: 'Kubernetes pod',
}),
fieldName: POD_NAME,
},
transactionResult: {
title: i18n.translate('xpack.apm.localFilters.titles.transactionResult', {
defaultMessage: 'Transaction result',
}),
fieldName: TRANSACTION_RESULT,
},
serviceVersion: {
title: i18n.translate('xpack.apm.localFilters.titles.serviceVersion', {
defaultMessage: 'Service version',
}),
fieldName: SERVICE_VERSION,
},
transactionUrl: {
title: i18n.translate('xpack.apm.localFilters.titles.transactionUrl', {
defaultMessage: 'Url',
}),
fieldName: TRANSACTION_URL,
},
browser: {
title: i18n.translate('xpack.apm.localFilters.titles.browser', {
defaultMessage: 'Browser',
}),
fieldName: USER_AGENT_NAME,
},
device: {
title: i18n.translate('xpack.apm.localFilters.titles.device', {
defaultMessage: 'Device',
}),
fieldName: USER_AGENT_DEVICE,
},
location: {
title: i18n.translate('xpack.apm.localFilters.titles.location', {
defaultMessage: 'Location',
}),
fieldName: CLIENT_GEO_COUNTRY_ISO_CODE,
},
os: {
title: i18n.translate('xpack.apm.localFilters.titles.os', {
defaultMessage: 'OS',
}),
fieldName: USER_AGENT_OS,
},
serviceName: {
title: i18n.translate('xpack.apm.localFilters.titles.serviceName', {
defaultMessage: 'Service name',
}),
fieldName: SERVICE_NAME,
},
};

export type LocalUIFilterName = keyof typeof filtersByName;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Feature: CSM Dashboard
When a user browses the APM UI application for RUM Data
Then should have correct client metrics

Scenario: Percentile select
When the user changes the selected percentile
Then it displays client metric related to that percentile

Scenario Outline: CSM page filters
When the user filters by "<filterName>"
Then it filters the client metrics "<filterName>"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { When, Then } from 'cypress-cucumber-preprocessor/steps';
import { verifyClientMetrics } from './client_metrics_helper';
import { getDataTestSubj } from './utils';

When('the user changes the selected percentile', () => {
// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');

getDataTestSubj('uxPercentileSelect').click();

getDataTestSubj('p95Percentile').click();
});

Then(`it displays client metric related to that percentile`, () => {
const metrics = ['14 ms', '0.13 s', '55 '];

verifyClientMetrics(metrics, false);

// reset to median
getDataTestSubj('uxPercentileSelect').click();

getDataTestSubj('p50Percentile').click();
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Then(`it displays top pages in the suggestion popover`, () => {
listOfUrls.should('have.length', 5);

const actualUrlsText = [
'http://opbeans-node:3000/dashboardPage views: 17Page load duration: 109 ms ',
'http://opbeans-node:3000/dashboardPage views: 17Page load duration: 109 ms',
'http://opbeans-node:3000/ordersPage views: 14Page load duration: 72 ms',
];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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 { DEFAULT_TIMEOUT } from './csm_dashboard';

export function getDataTestSubj(dataTestSubj: string) {
// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');

return cy.get(`[data-test-subj=${dataTestSubj}]`, DEFAULT_TIMEOUT);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import styled from 'styled-components';
import { useContext, useEffect } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiToolTip } from '@elastic/eui';
import { useFetcher } from '../../../../hooks/useFetcher';
import { useUrlParams } from '../../../../hooks/useUrlParams';
import { I18LABELS } from '../translations';
import { useUxQuery } from '../hooks/useUxQuery';
import { CsmSharedContext } from '../CsmSharedContext';

const ClFlexGroup = styled(EuiFlexGroup)`
Expand All @@ -22,29 +22,23 @@ const ClFlexGroup = styled(EuiFlexGroup)`
`;

export function ClientMetrics() {
const { urlParams, uiFilters } = useUrlParams();

const { start, end, searchTerm } = urlParams;
const uxQuery = useUxQuery();

const { data, status } = useFetcher(
(callApmApi) => {
const { serviceName } = uiFilters;
if (start && end && serviceName) {
if (uxQuery) {
return callApmApi({
pathname: '/api/apm/rum/client-metrics',
params: {
query: {
start,
end,
uiFilters: JSON.stringify(uiFilters),
urlQuery: searchTerm,
...uxQuery,
},
},
});
}
return Promise.resolve(null);
},
[start, end, uiFilters, searchTerm]
[uxQuery]
);

const { setSharedData } = useContext(CsmSharedContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ export function PageLoadDistribution() {

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end) {
const { serviceName } = uiFilters;

if (start && end && serviceName) {
return callApmApi({
pathname: '/api/apm/rum-client/page-load-distribution',
params: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export function PageViewsTrend() {

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end) {
const { serviceName } = uiFilters;

if (start && end && serviceName) {
return callApmApi({
pathname: '/api/apm/rum-client/page-view-trends',
params: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { RumOverview } from '../RumDashboard';
import { RumHeader } from './RumHeader';
import { UserPercentile } from './UserPercentile';
import { CsmSharedContextProvider } from './CsmSharedContext';

export const UX_LABEL = i18n.translate('xpack.apm.ux.title', {
Expand All @@ -21,11 +22,14 @@ export function RumHome() {
<CsmSharedContextProvider>
<RumHeader>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiFlexItem grow={true}>
<EuiTitle size="l">
<h1>{UX_LABEL}</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<UserPercentile />
</EuiFlexItem>
</EuiFlexGroup>
</RumHeader>
<RumOverview />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { FormEvent, useRef, useState } from 'react';
import React, { FormEvent, SetStateAction, useRef, useState } from 'react';
import {
EuiButtonEmpty,
EuiFlexGroup,
Expand Down Expand Up @@ -33,6 +33,8 @@ interface Props {
onChange: (updatedOptions: UrlOption[]) => void;
searchValue: string;
onClose: () => void;
popoverIsOpen: boolean;
setPopoverIsOpen: React.Dispatch<SetStateAction<boolean>>;
}

export function SelectableUrlList({
Expand All @@ -43,8 +45,9 @@ export function SelectableUrlList({
onChange,
searchValue,
onClose,
popoverIsOpen,
setPopoverIsOpen,
}: Props) {
const [popoverIsOpen, setPopoverIsOpen] = useState(false);
const [popoverRef, setPopoverRef] = useState<HTMLElement | null>(null);
const [searchRef, setSearchRef] = useState<HTMLInputElement | null>(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { fromQuery, toQuery } from '../../../../shared/Links/url_helpers';
import { formatToSec } from '../../UXMetrics/KeyUXMetrics';
import { SelectableUrlList } from './SelectableUrlList';
import { UrlOption } from './RenderOption';
import { useUxQuery } from '../../hooks/useUxQuery';

interface Props {
onChange: (value: string[]) => void;
Expand All @@ -23,9 +24,10 @@ interface Props {
export function URLSearch({ onChange: onFilterChange }: Props) {
const history = useHistory();

const { urlParams, uiFilters } = useUrlParams();
const { uiFilters } = useUrlParams();

const [popoverIsOpen, setPopoverIsOpen] = useState(false);

const { start, end, serviceName } = urlParams;
const [searchValue, setSearchValue] = useState('');

const [debouncedValue, setDebouncedValue] = useState('');
Expand Down Expand Up @@ -54,17 +56,18 @@ export function URLSearch({ onChange: onFilterChange }: Props) {

const [checkedUrls, setCheckedUrls] = useState<string[]>([]);

const uxQuery = useUxQuery();

const { data, status } = useFetcher(
(callApmApi) => {
if (start && end && serviceName) {
if (uxQuery && popoverIsOpen) {
const { transactionUrl, ...restFilters } = uiFilters;

return callApmApi({
pathname: '/api/apm/rum-client/url-search',
params: {
query: {
start,
end,
...uxQuery,
uiFilters: JSON.stringify(restFilters),
urlQuery: searchValue,
},
Expand All @@ -73,7 +76,8 @@ export function URLSearch({ onChange: onFilterChange }: Props) {
}
return Promise.resolve(null);
},
[start, end, serviceName, uiFilters, searchValue]
// eslint-disable-next-line react-hooks/exhaustive-deps
[uxQuery, searchValue, popoverIsOpen]
);

useEffect(() => {
Expand Down Expand Up @@ -110,7 +114,9 @@ export function URLSearch({ onChange: onFilterChange }: Props) {
};

const onClose = () => {
onFilterChange(checkedUrls);
if (uiFilters.transactionUrl || checkedUrls.length > 0) {
onFilterChange(checkedUrls);
}
};

return (
Expand All @@ -126,6 +132,8 @@ export function URLSearch({ onChange: onFilterChange }: Props) {
onChange={onChange}
onClose={onClose}
searchValue={searchValue}
popoverIsOpen={popoverIsOpen}
setPopoverIsOpen={setPopoverIsOpen}
/>
</>
);
Expand Down
Loading

0 comments on commit 9775119

Please sign in to comment.