diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Breakdowns/BreakdownFilter.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Breakdowns/BreakdownFilter.tsx index 7e5e7cdc53c55..12d8efdbd27f3 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/Breakdowns/BreakdownFilter.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Breakdowns/BreakdownFilter.tsx @@ -5,64 +5,85 @@ */ import React from 'react'; -import { BreakdownGroup } from './BreakdownGroup'; -import { BreakdownItem } from '../../../../../typings/ui_filters'; +import { EuiSuperSelect } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { CLIENT_GEO_COUNTRY_ISO_CODE, USER_AGENT_DEVICE, USER_AGENT_NAME, USER_AGENT_OS, } from '../../../../../common/elasticsearch_fieldnames'; +import { BreakdownItem } from '../../../../../typings/ui_filters'; interface Props { - id: string; - selectedBreakdowns: BreakdownItem[]; - onBreakdownChange: (values: BreakdownItem[]) => void; + selectedBreakdown: BreakdownItem | null; + onBreakdownChange: (value: BreakdownItem | null) => void; } export function BreakdownFilter({ - id, - selectedBreakdowns, + selectedBreakdown, onBreakdownChange, }: Props) { - const categories: BreakdownItem[] = [ + const NO_BREAKDOWN = 'noBreakdown'; + + const items: BreakdownItem[] = [ { - name: 'Browser', + name: i18n.translate('xpack.apm.csm.breakDownFilter.noBreakdown', { + defaultMessage: 'No breakdown', + }), + fieldName: NO_BREAKDOWN, type: 'category', - count: 0, - selected: selectedBreakdowns.some(({ name }) => name === 'Browser'), - fieldName: USER_AGENT_NAME, }, { - name: 'OS', + name: i18n.translate('xpack.apm.csm.breakdownFilter.browser', { + defaultMessage: 'Browser', + }), + fieldName: USER_AGENT_NAME, type: 'category', - count: 0, - selected: selectedBreakdowns.some(({ name }) => name === 'OS'), - fieldName: USER_AGENT_OS, }, { - name: 'Device', + name: i18n.translate('xpack.apm.csm.breakdownFilter.os', { + defaultMessage: 'OS', + }), + fieldName: USER_AGENT_OS, type: 'category', - count: 0, - selected: selectedBreakdowns.some(({ name }) => name === 'Device'), - fieldName: USER_AGENT_DEVICE, }, { - name: 'Location', + name: i18n.translate('xpack.apm.csm.breakdownFilter.device', { + defaultMessage: 'Device', + }), + fieldName: USER_AGENT_DEVICE, type: 'category', - count: 0, - selected: selectedBreakdowns.some(({ name }) => name === 'Location'), + }, + { + name: i18n.translate('xpack.apm.csm.breakdownFilter.location', { + defaultMessage: 'Location', + }), fieldName: CLIENT_GEO_COUNTRY_ISO_CODE, + type: 'category', }, ]; + const options = items.map(({ name, fieldName }) => ({ + inputDisplay: fieldName === NO_BREAKDOWN ? name : {name}, + value: fieldName, + dropdownDisplay: name, + })); + + const onOptionChange = (value: string) => { + if (value === NO_BREAKDOWN) { + onBreakdownChange(null); + } + onBreakdownChange(items.find(({ fieldName }) => fieldName === value)!); + }; + return ( - { - onBreakdownChange(selValues); - }} + onOptionChange(value)} /> ); } diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Breakdowns/BreakdownGroup.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Breakdowns/BreakdownGroup.tsx deleted file mode 100644 index d4f80667ce98b..0000000000000 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/Breakdowns/BreakdownGroup.tsx +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 { - EuiPopover, - EuiFilterButton, - EuiFilterGroup, - EuiPopoverTitle, - EuiFilterSelectItem, -} from '@elastic/eui'; -import React, { MouseEvent, useCallback, useEffect, useState } from 'react'; -import { BreakdownItem } from '../../../../../typings/ui_filters'; -import { I18LABELS } from '../translations'; - -export interface BreakdownGroupProps { - id: string; - disabled?: boolean; - items: BreakdownItem[]; - onChange: (values: BreakdownItem[]) => void; -} - -export function BreakdownGroup({ - id, - disabled, - onChange, - items, -}: BreakdownGroupProps) { - const [isOpen, setIsOpen] = useState(false); - - const [activeItems, setActiveItems] = useState(items); - - useEffect(() => { - setActiveItems(items); - }, [items]); - - const getSelItems = () => activeItems.filter((item) => item.selected); - - const onFilterItemClick = useCallback( - (name: string) => (_event: MouseEvent) => { - setActiveItems((prevItems) => - prevItems.map((item) => ({ - ...item, - selected: name === item.name ? !item.selected : item.selected, - })) - ); - }, - [] - ); - - return ( - - 0} - numFilters={activeItems.length} - numActiveFilters={getSelItems().length} - hasActiveFilters={getSelItems().length !== 0} - iconType="arrowDown" - onClick={() => { - setIsOpen(!isOpen); - }} - size="s" - > - {I18LABELS.breakdown} - - } - closePopover={() => { - setIsOpen(false); - onChange(getSelItems()); - }} - data-cy={`breakdown-popover_${id}`} - id={id} - isOpen={isOpen} - ownFocus={true} - withTitle - zIndex={10000} - > - {I18LABELS.selectBreakdown} -
- {activeItems.map(({ name, count, selected }) => ( - 0} - > - {name} - - ))} -
-
-
- ); -} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx index 33573052dbcbb..c832ec9fcc0d0 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/PageLoadDistChart.tsx @@ -43,7 +43,7 @@ interface PageLoadData { interface Props { onPercentileChange: (min: number, max: number) => void; data?: PageLoadData | null; - breakdowns: BreakdownItem[]; + breakdown: BreakdownItem | null; percentileRange: PercentileRange; loading: boolean; } @@ -57,7 +57,7 @@ const PageLoadChart = styled(Chart)` export function PageLoadDistChart({ onPercentileChange, data, - breakdowns, + breakdown, loading, percentileRange, }: Props) { @@ -122,17 +122,17 @@ export function PageLoadDistChart({ data={data?.pageLoadDistribution ?? []} curve={CurveType.CURVE_CATMULL_ROM} /> - {breakdowns.map(({ name, type }) => ( + {breakdown && ( { setBreakdownLoading(bLoading); }} /> - ))} + )} )} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx index 53f2d5ae238c5..3e35f15254937 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx @@ -34,7 +34,7 @@ export function PageLoadDistribution() { max: null, }); - const [breakdowns, setBreakdowns] = useState([]); + const [breakdown, setBreakdown] = useState(null); const { data, status } = useFetcher( (callApmApi) => { @@ -94,11 +94,10 @@ export function PageLoadDistribution() { {I18LABELS.resetZoom} - + @@ -107,7 +106,7 @@ export function PageLoadDistribution() { data={data} onPercentileChange={onPercentileChange} loading={status !== 'success'} - breakdowns={breakdowns} + breakdown={breakdown} percentileRange={{ max: percentileRange.max || data?.maxDuration, min: percentileRange.min || data?.minDuration, diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx index 0f43c0ddf540d..a67f6dd8e3cb5 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx @@ -18,7 +18,7 @@ export function PageViewsTrend() { const { start, end, serviceName } = urlParams; - const [breakdowns, setBreakdowns] = useState([]); + const [breakdown, setBreakdown] = useState(null); const { data, status } = useFetcher( (callApmApi) => { @@ -30,9 +30,9 @@ export function PageViewsTrend() { start, end, uiFilters: JSON.stringify(uiFilters), - ...(breakdowns.length > 0 + ...(breakdown ? { - breakdowns: JSON.stringify(breakdowns), + breakdowns: JSON.stringify(breakdown), } : {}), }, @@ -41,13 +41,9 @@ export function PageViewsTrend() { } return Promise.resolve(undefined); }, - [end, start, serviceName, uiFilters, breakdowns] + [end, start, serviceName, uiFilters, breakdown] ); - const onBreakdownChange = (values: BreakdownItem[]) => { - setBreakdowns(values); - }; - return (
@@ -56,11 +52,10 @@ export function PageViewsTrend() {

{I18LABELS.pageViews}

- +
diff --git a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap index 22b8c226e9026..2cb28d378e8fd 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap @@ -148,7 +148,7 @@ Object { "body": Object { "aggs": Object { "pageViews": Object { - "aggs": Object {}, + "aggs": undefined, "auto_date_histogram": Object { "buckets": 50, "field": "@timestamp", diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts index 23169ddaca534..114137e9fad17 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts @@ -11,7 +11,6 @@ import { SetupTimeRange, SetupUIFilters, } from '../helpers/setup_request'; -import { AggregationInputMap } from '../../../typings/elasticsearch/aggregations'; import { BreakdownItem } from '../../../typings/ui_filters'; export async function getPageViewTrends({ @@ -24,18 +23,9 @@ export async function getPageViewTrends({ const projection = getRumOverviewProjection({ setup, }); - const breakdownAggs: AggregationInputMap = {}; + let breakdownItem: BreakdownItem | null = null; if (breakdowns) { - const breakdownList: BreakdownItem[] = JSON.parse(breakdowns); - breakdownList.forEach(({ name, type, fieldName }) => { - breakdownAggs[name] = { - terms: { - field: fieldName, - size: 9, - missing: 'Other', - }, - }; - }); + breakdownItem = JSON.parse(breakdowns); } const params = mergeProjection(projection, { @@ -50,7 +40,17 @@ export async function getPageViewTrends({ field: '@timestamp', buckets: 50, }, - aggs: breakdownAggs, + aggs: breakdownItem + ? { + breakdown: { + terms: { + field: breakdownItem.fieldName, + size: 9, + missing: 'Other', + }, + }, + } + : undefined, }, }, }, @@ -68,19 +68,18 @@ export async function getPageViewTrends({ x: xVal, y: bCount, }; - - Object.keys(breakdownAggs).forEach((bKey) => { - const categoryBuckets = (bucket[bKey] as any).buckets; + if (breakdownItem) { + const categoryBuckets = (bucket.breakdown as any).buckets; categoryBuckets.forEach( ({ key, doc_count: docCount }: { key: string; doc_count: number }) => { if (key === 'Other') { - res[key + `(${bKey})`] = docCount; + res[key + `(${breakdownItem?.name})`] = docCount; } else { res[key] = docCount; } } ); - }); + } return res; }); diff --git a/x-pack/plugins/apm/typings/ui_filters.ts b/x-pack/plugins/apm/typings/ui_filters.ts index 2a727dda7241d..efba6919778bb 100644 --- a/x-pack/plugins/apm/typings/ui_filters.ts +++ b/x-pack/plugins/apm/typings/ui_filters.ts @@ -14,7 +14,6 @@ export type UIFilters = { export interface BreakdownItem { name: string; - count: number; type: string; fieldName: string; selected?: boolean;