diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx
new file mode 100644
index 000000000000..62b5f7834d3a
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx
@@ -0,0 +1,37 @@
+/*
+ * 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 { fireEvent, render } from '@testing-library/react';
+import React from 'react';
+import { expectTextsInDocument } from '../../../../../utils/testHelpers';
+import { ErrorCount } from '../ErrorCount';
+
+describe('ErrorCount', () => {
+ it('shows singular error message', () => {
+ const component = render(
);
+ expectTextsInDocument(component, ['1 Error']);
+ });
+ it('shows plural error message', () => {
+ const component = render(
);
+ expectTextsInDocument(component, ['2 Errors']);
+ });
+ it('prevents click propagation', () => {
+ const mock = jest.fn();
+ const { getByText } = render(
+
+ );
+ fireEvent(
+ getByText('1 Error'),
+ new MouseEvent('click', {
+ bubbles: true,
+ cancelable: true
+ })
+ );
+ expect(mock).not.toHaveBeenCalled();
+ });
+});
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx
index b56370a59c8e..6dcab6c6b97c 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx
@@ -5,30 +5,29 @@
*/
import {
+ EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
+ EuiPagination,
EuiPanel,
EuiSpacer,
- EuiEmptyPrompt,
- EuiTitle,
- EuiPagination
+ EuiTitle
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Location } from 'history';
-import React, { useState, useEffect } from 'react';
-import { sum } from 'lodash';
+import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
-import { IUrlParams } from '../../../../context/UrlParamsContext/types';
-import { TransactionActionMenu } from '../../../shared/TransactionActionMenu/TransactionActionMenu';
-import { TransactionTabs } from './TransactionTabs';
-import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
-import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt';
-import { TransactionSummary } from '../../../shared/Summary/TransactionSummary';
import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform';
+import { IUrlParams } from '../../../../context/UrlParamsContext/types';
+import { px, units } from '../../../../style/variables';
import { history } from '../../../../utils/history';
import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
+import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt';
+import { TransactionSummary } from '../../../shared/Summary/TransactionSummary';
+import { TransactionActionMenu } from '../../../shared/TransactionActionMenu/TransactionActionMenu';
import { MaybeViewTraceLink } from './MaybeViewTraceLink';
-import { units, px } from '../../../../style/variables';
+import { TransactionTabs } from './TransactionTabs';
+import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
const PaginationContainer = styled.div`
margin-left: ${px(units.quarter)};
@@ -140,8 +139,8 @@ export const WaterfallWithSummmary: React.FC
= ({
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx
index 0312e94d7ee1..eba59f6e3ce4 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx
@@ -18,7 +18,7 @@ interface Props extends EuiLinkAnchorProps {
children?: React.ReactNode;
}
-export type APMLinkExtendProps = Omit;
+export type APMLinkExtendProps = Omit;
export const PERSISTENT_APM_PARAMS = [
'kuery',
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItem.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx
similarity index 51%
rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItem.tsx
rename to x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx
index 964debbedb2e..7558f002c0af 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItem.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx
@@ -6,28 +6,25 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
+import { EuiBadge } from '@elastic/eui';
+import euiThemeLight from '@elastic/eui/dist/eui_theme_light.json';
import { px } from '../../../../public/style/variables';
-import { ErrorCountBadge } from '../../app/TransactionDetails/WaterfallWithSummmary/ErrorCountBadge';
import { units } from '../../../style/variables';
interface Props {
count: number;
}
-const Badge = styled(ErrorCountBadge)`
+const Badge = styled(EuiBadge)`
margin-top: ${px(units.eighth)};
`;
-const ErrorCountSummaryItem = ({ count }: Props) => {
- return (
-
- {i18n.translate('xpack.apm.transactionDetails.errorCount', {
- defaultMessage:
- '{errorCount, number} {errorCount, plural, one {Error} other {Errors}}',
- values: { errorCount: count }
- })}
-
- );
-};
-
-export { ErrorCountSummaryItem };
+export const ErrorCountSummaryItemBadge = ({ count }: Props) => (
+
+ {i18n.translate('xpack.apm.transactionDetails.errorCount', {
+ defaultMessage:
+ '{errorCount, number} {errorCount, plural, one {Error} other {Errors}}',
+ values: { errorCount: count }
+ })}
+
+);
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx
index 8b7380a18edc..51da61cd7c1a 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx
@@ -8,7 +8,7 @@ import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
import { Summary } from './';
import { TimestampTooltip } from '../TimestampTooltip';
import { DurationSummaryItem } from './DurationSummaryItem';
-import { ErrorCountSummaryItem } from './ErrorCountSummaryItem';
+import { ErrorCountSummaryItemBadge } from './ErrorCountSummaryItemBadge';
import { isRumAgentName } from '../../../../common/agent_name';
import { HttpInfoSummaryItem } from './HttpInfoSummaryItem';
import { TransactionResultSummaryItem } from './TransactionResultSummaryItem';
@@ -54,7 +54,7 @@ const TransactionSummary = ({
parentType="trace"
/>,
getTransactionResultSummaryItem(transaction),
- errorCount ? : null,
+ errorCount ? : null,
transaction.user_agent ? (
) : null
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx
new file mode 100644
index 000000000000..33f5752b6389
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx
@@ -0,0 +1,21 @@
+/*
+ * 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 React from 'react';
+import { ErrorCountSummaryItemBadge } from '../ErrorCountSummaryItemBadge';
+import { render } from '@testing-library/react';
+import { expectTextsInDocument } from '../../../../utils/testHelpers';
+
+describe('ErrorCountSummaryItemBadge', () => {
+ it('shows singular error message', () => {
+ const component = render();
+ expectTextsInDocument(component, ['1 Error']);
+ });
+ it('shows plural error message', () => {
+ const component = render();
+ expectTextsInDocument(component, ['2 Errors']);
+ });
+});
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js
index 848c975942ff..99eb17386f84 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js
@@ -7,7 +7,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import styled from 'styled-components';
-import Legend from '../Legend';
+import { Legend } from '../Legend';
import {
unit,
units,
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap
index c46cbbbcccc0..557751a0f022 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap
@@ -2725,11 +2725,14 @@ Array [
@@ -2763,11 +2766,14 @@ Array [
@@ -2794,11 +2800,14 @@ Array [
@@ -5167,11 +5176,14 @@ Array [
Avg.
@@ -5210,11 +5222,14 @@ Array [
95th
@@ -5253,11 +5268,14 @@ Array [
99th
@@ -5886,11 +5904,14 @@ Array [
@@ -5924,11 +5945,14 @@ Array [
@@ -5955,11 +5979,14 @@ Array [
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.js
deleted file mode 100644
index 601482430b00..000000000000
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.js
+++ /dev/null
@@ -1,56 +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 React, { PureComponent } from 'react';
-import styled from 'styled-components';
-import { units, px, fontSizes } from '../../../../style/variables';
-import theme from '@elastic/eui/dist/eui_theme_light.json';
-
-const Container = styled.div`
- display: flex;
- align-items: center;
- font-size: ${props => props.fontSize};
- color: ${theme.euiColorDarkShade};
- cursor: ${props => (props.clickable ? 'pointer' : 'initial')};
- opacity: ${props => (props.disabled ? 0.4 : 1)};
- user-select: none;
-`;
-
-export const Indicator = styled.span`
- width: ${props => px(props.radius)};
- height: ${props => px(props.radius)};
- margin-right: ${props => px(props.radius / 2)};
- background: ${props => props.color};
- border-radius: 100%;
-`;
-
-export default class Legend extends PureComponent {
- render() {
- const {
- onClick,
- text,
- color = theme.euiColorVis1,
- fontSize = fontSizes.small,
- radius = units.minus - 1,
- disabled = false,
- clickable = false,
- indicator,
- ...rest
- } = this.props;
- return (
-
- {indicator ? indicator() : }
- {text}
-
- );
- }
-}
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.tsx
new file mode 100644
index 000000000000..436b020bc9eb
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.tsx
@@ -0,0 +1,94 @@
+/*
+ * 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 theme from '@elastic/eui/dist/eui_theme_light.json';
+import React from 'react';
+import styled from 'styled-components';
+import { fontSizes, px, units } from '../../../../style/variables';
+
+export enum Shape {
+ circle = 'circle',
+ square = 'square'
+}
+
+interface ContainerProps {
+ onClick: (e: Event) => void;
+ fontSize?: string;
+ clickable: boolean;
+ disabled: boolean;
+}
+const Container = styled.div`
+ display: flex;
+ align-items: center;
+ font-size: ${props => props.fontSize};
+ color: ${theme.euiColorDarkShade};
+ cursor: ${props => (props.clickable ? 'pointer' : 'initial')};
+ opacity: ${props => (props.disabled ? 0.4 : 1)};
+ user-select: none;
+`;
+
+interface IndicatorProps {
+ radius: number;
+ color: string;
+ shape: Shape;
+ withMargin: boolean;
+}
+export const Indicator = styled.span`
+ width: ${props => px(props.radius)};
+ height: ${props => px(props.radius)};
+ margin-right: ${props => (props.withMargin ? px(props.radius / 2) : 0)};
+ background: ${props => props.color};
+ border-radius: ${props => {
+ return props.shape === Shape.circle ? '100%' : '0';
+ }};
+`;
+
+interface Props {
+ onClick?: any;
+ text?: string;
+ color?: string;
+ fontSize?: string;
+ radius?: number;
+ disabled?: boolean;
+ clickable?: boolean;
+ shape?: Shape;
+ indicator?: () => React.ReactNode;
+}
+
+export const Legend: React.FC = ({
+ onClick,
+ text,
+ color = theme.euiColorVis1,
+ fontSize = fontSizes.small,
+ radius = units.minus - 1,
+ disabled = false,
+ clickable = false,
+ shape = Shape.circle,
+ indicator,
+ ...rest
+}) => {
+ return (
+
+ {indicator ? (
+ indicator()
+ ) : (
+
+ )}
+ {text}
+
+ );
+};
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/AgentMarker.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx
similarity index 56%
rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/AgentMarker.js
rename to x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx
index 8ee23d61fe0e..ffdbfe6cce7e 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/AgentMarker.js
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx
@@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React from 'react';
-import PropTypes from 'prop-types';
import { EuiToolTip } from '@elastic/eui';
-import Legend from '../Legend';
-import { units, px } from '../../../../style/variables';
-import styled from 'styled-components';
-import { asDuration } from '../../../../utils/formatters';
import theme from '@elastic/eui/dist/eui_theme_light.json';
+import React from 'react';
+import styled from 'styled-components';
+import { px, units } from '../../../../../style/variables';
+import { asDuration } from '../../../../../utils/formatters';
+import { Legend } from '../../Legend';
+import { AgentMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks';
const NameContainer = styled.div`
border-bottom: 1px solid ${theme.euiColorMediumShade};
@@ -23,33 +23,25 @@ const TimeContainer = styled.div`
padding-top: ${px(units.half)};
`;
-export default function AgentMarker({ agentMark, x }) {
- const legendWidth = 11;
+interface Props {
+ mark: AgentMark;
+}
+
+export const AgentMarker: React.FC = ({ mark }) => {
return (
-
+ <>
- {agentMark.name}
- {asDuration(agentMark.us)}
+ {mark.id}
+ {asDuration(mark.offset)}
}
>
-
+ >
);
-}
-
-AgentMarker.propTypes = {
- agentMark: PropTypes.object.isRequired,
- x: PropTypes.number.isRequired
};
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx
new file mode 100644
index 000000000000..51368a4fb946
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx
@@ -0,0 +1,103 @@
+/*
+ * 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, EuiText } from '@elastic/eui';
+import theme from '@elastic/eui/dist/eui_theme_light.json';
+import React, { useState } from 'react';
+import styled from 'styled-components';
+import {
+ TRACE_ID,
+ TRANSACTION_ID
+} from '../../../../../../common/elasticsearch_fieldnames';
+import { useUrlParams } from '../../../../../hooks/useUrlParams';
+import { px, unit, units } from '../../../../../style/variables';
+import { asDuration } from '../../../../../utils/formatters';
+import { ErrorMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks';
+import { ErrorDetailLink } from '../../../Links/apm/ErrorDetailLink';
+import { Legend, Shape } from '../../Legend';
+
+interface Props {
+ mark: ErrorMark;
+}
+
+const Popover = styled.div`
+ max-width: ${px(280)};
+`;
+
+const TimeLegend = styled(Legend)`
+ margin-bottom: ${px(unit)};
+`;
+
+const ErrorLink = styled(ErrorDetailLink)`
+ display: block;
+ margin: ${px(units.half)} 0 ${px(units.half)} 0;
+`;
+
+const Button = styled(Legend)`
+ height: 20px;
+ display: flex;
+ align-items: flex-end;
+`;
+
+export const ErrorMarker: React.FC