diff --git a/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx
index 2fb500f3c9916..042752ef62f53 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx
@@ -15,7 +15,7 @@ import React, { useMemo } from 'react';
import { useServiceMetricCharts } from '../../../hooks/useServiceMetricCharts';
import { MetricsChart } from '../../shared/charts/MetricsChart';
import { useUrlParams } from '../../../hooks/useUrlParams';
-import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
+import { LegacyChartsSyncContextProvider as ChartsSyncContextProvider } from '../../../context/charts_sync_context';
import { Projection } from '../../../../common/projections';
import { LocalUIFilters } from '../../shared/LocalUIFilters';
diff --git a/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx
index 84a1920d17fa8..566585c67e212 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx
@@ -22,7 +22,7 @@ import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import styled from 'styled-components';
import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes';
-import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
+import { LegacyChartsSyncContextProvider as ChartsSyncContextProvider } from '../../../context/charts_sync_context';
import { useAgentName } from '../../../hooks/useAgentName';
import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher';
import { useServiceMetricCharts } from '../../../hooks/useServiceMetricCharts';
diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx
index b79186a90cd1d..efdd7b1f34221 100644
--- a/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx
@@ -24,7 +24,7 @@ import { TransactionCharts } from '../../shared/charts/TransactionCharts';
import { TransactionDistribution } from './Distribution';
import { WaterfallWithSummmary } from './WaterfallWithSummmary';
import { FETCH_STATUS } from '../../../hooks/useFetcher';
-import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
+import { LegacyChartsSyncContextProvider as ChartsSyncContextProvider } from '../../../context/charts_sync_context';
import { useTrackPageview } from '../../../../../observability/public';
import { Projection } from '../../../../common/projections';
import { fromQuery, toQuery } from '../../shared/Links/url_helpers';
diff --git a/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx
index 003df632d11b3..5444d2d521f37 100644
--- a/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx
@@ -22,7 +22,7 @@ import React, { useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { useTrackPageview } from '../../../../../observability/public';
import { Projection } from '../../../../common/projections';
-import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
+import { LegacyChartsSyncContextProvider as ChartsSyncContextProvider } from '../../../context/charts_sync_context';
import { IUrlParams } from '../../../context/UrlParamsContext/types';
import { useServiceTransactionTypes } from '../../../hooks/useServiceTransactionTypes';
import { useTransactionCharts } from '../../../hooks/useTransactionCharts';
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
index 81f23b6427508..8f14571ff946e 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
@@ -9,13 +9,16 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import styled from 'styled-components';
import { useTrackPageview } from '../../../../../observability/public';
+import { ChartsSyncContextProvider } from '../../../context/charts_sync_context';
+import { ErroneousTransactionsRateChart } from '../../shared/charts/erroneous_transactions_rate_chart';
import { ErrorOverviewLink } from '../../shared/Links/apm/ErrorOverviewLink';
import { ServiceMapLink } from '../../shared/Links/apm/ServiceMapLink';
import { TransactionOverviewLink } from '../../shared/Links/apm/TransactionOverviewLink';
-const rowHeight = 310;
-const latencyChartRowHeight = 230;
-
+// const rowHeight = 310;
+// const latencyChartRowHeight = 230;
+const rowHeight = 0;
+const latencyChartRowHeight = 0;
const Row = styled(EuiFlexItem)`
height: ${rowHeight}px;
`;
@@ -39,208 +42,211 @@ export function ServiceOverview({ serviceName }: ServiceOverviewProps) {
useTrackPageview({ app: 'apm', path: 'service_overview', delay: 15000 });
return (
-
-
-
-
- Search bar
-
-
- Comparison picker
-
-
- Date picker
-
-
-
-
-
-
-
- {i18n.translate('xpack.apm.serviceOverview.latencyChartTitle', {
- defaultMessage: 'Latency',
- })}
-
-
-
-
-
-
-
-
-
-
- {i18n.translate(
- 'xpack.apm.serviceOverview.trafficChartTitle',
- {
- defaultMessage: 'Traffic',
- }
- )}
-
-
-
-
-
-
-
-
-
-
- {i18n.translate(
- 'xpack.apm.serviceOverview.transactionsTableTitle',
- {
- defaultMessage: 'Transactions',
- }
- )}
-
-
-
-
-
+
+
+
+
+
+ Search bar
+
+
+ Comparison picker
+
+
+ Date picker
+
+
+
+
+
+
+
+ {i18n.translate('xpack.apm.serviceOverview.latencyChartTitle', {
+ defaultMessage: 'Latency',
+ })}
+
+
+
+
+
+
+
+
+
+
{i18n.translate(
- 'xpack.apm.serviceOverview.transactionsTableLinkText',
+ 'xpack.apm.serviceOverview.trafficChartTitle',
{
- defaultMessage: 'View transactions',
+ defaultMessage: 'Traffic',
}
)}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {i18n.translate(
- 'xpack.apm.serviceOverview.errorRateChartTitle',
- {
- defaultMessage: 'Error rate',
- }
- )}
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.apm.serviceOverview.transactionsTableTitle',
+ {
+ defaultMessage: 'Transactions',
+ }
+ )}
+
+
+
+
+
{i18n.translate(
- 'xpack.apm.serviceOverview.errorsTableTitle',
+ 'xpack.apm.serviceOverview.transactionsTableLinkText',
{
- defaultMessage: 'Errors',
+ defaultMessage: 'View transactions',
}
)}
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
{i18n.translate(
- 'xpack.apm.serviceOverview.errorsTableLinkText',
+ 'xpack.apm.serviceOverview.errorRateChartTitle',
{
- defaultMessage: 'View errors',
+ defaultMessage: 'Error rate',
}
)}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.apm.serviceOverview.errorsTableTitle',
+ {
+ defaultMessage: 'Errors',
+ }
+ )}
+
+
+
+
+
{i18n.translate(
- 'xpack.apm.serviceOverview.averageDurationBySpanTypeChartTitle',
+ 'xpack.apm.serviceOverview.errorsTableLinkText',
{
- defaultMessage: 'Average duration by span type',
+ defaultMessage: 'View errors',
}
)}
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.apm.serviceOverview.averageDurationBySpanTypeChartTitle',
+ {
+ defaultMessage: 'Average duration by span type',
+ }
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.apm.serviceOverview.dependenciesTableTitle',
+ {
+ defaultMessage: 'Dependencies',
+ }
+ )}
+
+
+
+
+
{i18n.translate(
- 'xpack.apm.serviceOverview.dependenciesTableTitle',
+ 'xpack.apm.serviceOverview.dependenciesTableLinkText',
{
- defaultMessage: 'Dependencies',
+ defaultMessage: 'View service map',
}
)}
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.apm.serviceOverview.instancesLatencyDistributionChartTitle',
+ {
+ defaultMessage: 'Instances latency distribution',
+ }
+ )}
+
+
+
+
+
+
+
+
{i18n.translate(
- 'xpack.apm.serviceOverview.dependenciesTableLinkText',
+ 'xpack.apm.serviceOverview.instancesTableTitle',
{
- defaultMessage: 'View service map',
+ defaultMessage: 'Instances',
}
)}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {i18n.translate(
- 'xpack.apm.serviceOverview.instancesLatencyDistributionChartTitle',
- {
- defaultMessage: 'Instances latency distribution',
- }
- )}
-
-
-
-
-
-
-
-
- {i18n.translate(
- 'xpack.apm.serviceOverview.instancesTableTitle',
- {
- defaultMessage: 'Instances',
- }
- )}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
}
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx
index 4e2063930a9c9..210d353303854 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx
@@ -4,16 +4,24 @@
* 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 { CoreStart } from 'src/core/public';
+import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public';
import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext';
+import { renderWithTheme } from '../../../utils/testHelpers';
import { ServiceOverview } from './';
+const KibanaReactContext = createKibanaReactContext({
+ usageCollection: { reportUiStats: () => {} },
+} as Partial);
+
function Wrapper({ children }: { children?: ReactNode }) {
return (
- {children}
+
+ {children}
+
);
}
@@ -21,7 +29,7 @@ function Wrapper({ children }: { children?: ReactNode }) {
describe('ServiceOverview', () => {
it('renders', () => {
expect(() =>
- render(, {
+ renderWithTheme(, {
wrapper: Wrapper,
})
).not.toThrowError();
diff --git a/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx
index 270ebd1c0830d..2f63a77132be9 100644
--- a/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx
+++ b/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx
@@ -19,7 +19,7 @@ import { GenericMetricsChart } from '../../../../../server/lib/metrics/transform
import CustomPlot from '../CustomPlot';
import { Coordinate } from '../../../../../typings/timeseries';
import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue';
-import { useChartsSync } from '../../../../hooks/useChartsSync';
+import { useLegacyChartsSync as useChartsSync } from '../../../../hooks/use_charts_sync';
import { Maybe } from '../../../../../typings/common';
interface Props {
diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx
index 09e6b0e43945f..2e4b51af00d6b 100644
--- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx
+++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx
@@ -6,7 +6,7 @@
import React, { useCallback } from 'react';
import { Coordinate, TimeSeries } from '../../../../../../typings/timeseries';
-import { useChartsSync } from '../../../../../hooks/useChartsSync';
+import { useLegacyChartsSync as useChartsSync } from '../../../../../hooks/use_charts_sync';
// @ts-expect-error
import CustomPlot from '../../CustomPlot';
diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx
index 0b741447f6fec..b3c0c3b6de857 100644
--- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx
+++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx
@@ -26,7 +26,7 @@ import { IUrlParams } from '../../../../context/UrlParamsContext/types';
import { ITransactionChartData } from '../../../../selectors/chartSelectors';
import { asDecimal, tpmUnit } from '../../../../../common/utils/formatters';
import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue';
-import { ErroneousTransactionsRateChart } from '../ErroneousTransactionsRateChart';
+import { ErroneousTransactionsRateChart } from '../erroneous_transactions_rate_chart/legacy';
import { TransactionBreakdown } from '../../TransactionBreakdown';
import {
getResponseTimeTickFormatter,
diff --git a/x-pack/plugins/apm/public/components/shared/charts/annotations/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/annotations/index.tsx
new file mode 100644
index 0000000000000..683c66b2a96fe
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/shared/charts/annotations/index.tsx
@@ -0,0 +1,45 @@
+/*
+ * 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 {
+ AnnotationDomainTypes,
+ LineAnnotation,
+ Position,
+} from '@elastic/charts';
+import { EuiIcon } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React from 'react';
+import { asAbsoluteDateTime } from '../../../../../common/utils/formatters';
+import { useTheme } from '../../../../hooks/useTheme';
+import { useAnnotations } from '../../../../hooks/use_annotations';
+
+export function Annotations() {
+ const { annotations } = useAnnotations();
+ const theme = useTheme();
+
+ if (!annotations.length) {
+ return null;
+ }
+
+ const color = theme.eui.euiColorSecondary;
+
+ return (
+ ({
+ dataValue: annotation['@timestamp'],
+ header: asAbsoluteDateTime(annotation['@timestamp']),
+ details: `${i18n.translate('xpack.apm.chart.annotation.version', {
+ defaultMessage: 'Version',
+ })} ${annotation.text}`,
+ }))}
+ style={{ line: { strokeWidth: 1, stroke: color, opacity: 1 } }}
+ marker={}
+ markerPosition={Position.Top}
+ />
+ );
+}
diff --git a/x-pack/plugins/apm/public/components/shared/charts/chart_container.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/chart_container.test.tsx
new file mode 100644
index 0000000000000..409cb69575ca9
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/shared/charts/chart_container.test.tsx
@@ -0,0 +1,34 @@
+/*
+ * 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 from 'react';
+import { ChartContainer } from './chart_container';
+
+describe('ChartContainer', () => {
+ describe('when isLoading is true', () => {
+ it('shows loading the indicator', () => {
+ const component = render(
+
+ My amazing component
+
+ );
+
+ expect(component.getByTestId('loading')).toBeInTheDocument();
+ });
+ });
+
+ describe('when isLoading is false', () => {
+ it('does not show the loading indicator', () => {
+ const component = render(
+
+ My amazing component
+
+ );
+
+ expect(component.queryByTestId('loading')).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/x-pack/plugins/apm/public/components/shared/charts/chart_container.tsx b/x-pack/plugins/apm/public/components/shared/charts/chart_container.tsx
new file mode 100644
index 0000000000000..5ac5bc29ac148
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/shared/charts/chart_container.tsx
@@ -0,0 +1,32 @@
+/*
+ * 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 { EuiLoadingChart } from '@elastic/eui';
+import React from 'react';
+
+interface Props {
+ isLoading: boolean;
+ height: number;
+ children: React.ReactNode;
+}
+
+export function ChartContainer({ isLoading, children, height }: Props) {
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ return <>{children}>;
+}
diff --git a/x-pack/plugins/apm/public/components/shared/charts/erroneous_transactions_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/erroneous_transactions_rate_chart/index.tsx
new file mode 100644
index 0000000000000..dc6fecfb538b6
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/shared/charts/erroneous_transactions_rate_chart/index.tsx
@@ -0,0 +1,72 @@
+/*
+ * 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 React from 'react';
+import { useParams } from 'react-router-dom';
+import { asPercent } from '../../../../../common/utils/formatters';
+import { FETCH_STATUS, useFetcher } from '../../../../hooks/useFetcher';
+import { useTheme } from '../../../../hooks/useTheme';
+import { useUrlParams } from '../../../../hooks/useUrlParams';
+import { callApmApi } from '../../../../services/rest/createCallApmApi';
+import { LineChart } from '../line_chart';
+
+const tickFormatY = (y?: number | null) => {
+ return asPercent(y || 0, 1);
+};
+
+export function ErroneousTransactionsRateChart() {
+ const theme = useTheme();
+ const { serviceName } = useParams<{ serviceName?: string }>();
+ const { urlParams, uiFilters } = useUrlParams();
+
+ const { start, end, transactionType, transactionName } = urlParams;
+
+ const { data, status } = useFetcher(() => {
+ if (serviceName && start && end) {
+ return callApmApi({
+ pathname:
+ '/api/apm/services/{serviceName}/transaction_groups/error_rate',
+ params: {
+ path: {
+ serviceName,
+ },
+ query: {
+ start,
+ end,
+ transactionType,
+ transactionName,
+ uiFilters: JSON.stringify(uiFilters),
+ },
+ },
+ });
+ }
+ }, [serviceName, start, end, uiFilters, transactionType, transactionName]);
+
+ const errorRates = data?.transactionErrorRate || [];
+
+ return (
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/components/shared/charts/ErroneousTransactionsRateChart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/erroneous_transactions_rate_chart/legacy.tsx
similarity index 93%
rename from x-pack/plugins/apm/public/components/shared/charts/ErroneousTransactionsRateChart/index.tsx
rename to x-pack/plugins/apm/public/components/shared/charts/erroneous_transactions_rate_chart/legacy.tsx
index 8aec4184f924d..29102f606414f 100644
--- a/x-pack/plugins/apm/public/components/shared/charts/ErroneousTransactionsRateChart/index.tsx
+++ b/x-pack/plugins/apm/public/components/shared/charts/erroneous_transactions_rate_chart/legacy.tsx
@@ -10,7 +10,7 @@ import { max } from 'lodash';
import React, { useCallback } from 'react';
import { useParams } from 'react-router-dom';
import { asPercent } from '../../../../../common/utils/formatters';
-import { useChartsSync } from '../../../../hooks/useChartsSync';
+import { useLegacyChartsSync as useChartsSync } from '../../../../hooks/use_charts_sync';
import { useFetcher } from '../../../../hooks/useFetcher';
import { useUrlParams } from '../../../../hooks/useUrlParams';
import { callApmApi } from '../../../../services/rest/createCallApmApi';
@@ -21,6 +21,12 @@ const tickFormatY = (y?: number | null) => {
return asPercent(y || 0, 1);
};
+/**
+ * "Legacy" version of this chart using react-vis charts. See index.tsx for the
+ * Elastic Charts version.
+ *
+ * This will be removed with #70290.
+ */
export function ErroneousTransactionsRateChart() {
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
diff --git a/x-pack/plugins/apm/public/components/shared/charts/helper/helper.test.ts b/x-pack/plugins/apm/public/components/shared/charts/helper/helper.test.ts
new file mode 100644
index 0000000000000..585eef546e754
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/shared/charts/helper/helper.test.ts
@@ -0,0 +1,39 @@
+/*
+ * 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 { onBrushEnd } from './helper';
+import { History } from 'history';
+
+describe('Chart helper', () => {
+ describe('onBrushEnd', () => {
+ const history = ({
+ push: jest.fn(),
+ location: {
+ search: '',
+ },
+ } as unknown) as History;
+ it("doesn't push a new history when x is not defined", () => {
+ onBrushEnd({ x: undefined, history });
+ expect(history.push).not.toBeCalled();
+ });
+
+ it('pushes a new history with time range converted to ISO', () => {
+ onBrushEnd({ x: [1593409448167, 1593415727797], history });
+ expect(history.push).toBeCalledWith({
+ search:
+ 'rangeFrom=2020-06-29T05:44:08.167Z&rangeTo=2020-06-29T07:28:47.797Z',
+ });
+ });
+
+ it('pushes a new history keeping current search', () => {
+ history.location.search = '?foo=bar';
+ onBrushEnd({ x: [1593409448167, 1593415727797], history });
+ expect(history.push).toBeCalledWith({
+ search:
+ 'foo=bar&rangeFrom=2020-06-29T05:44:08.167Z&rangeTo=2020-06-29T07:28:47.797Z',
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/apm/public/components/shared/charts/helper/helper.ts b/x-pack/plugins/apm/public/components/shared/charts/helper/helper.ts
new file mode 100644
index 0000000000000..a9c1337feac99
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/shared/charts/helper/helper.ts
@@ -0,0 +1,35 @@
+/*
+ * 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 { XYBrushArea } from '@elastic/charts';
+import { History } from 'history';
+import { fromQuery, toQuery } from '../../Links/url_helpers';
+
+export const onBrushEnd = ({
+ x,
+ history,
+}: {
+ x: XYBrushArea['x'];
+ history: History;
+}) => {
+ if (x) {
+ const start = x[0];
+ const end = x[1];
+
+ const currentSearch = toQuery(history.location.search);
+ const nextSearch = {
+ rangeFrom: new Date(start).toISOString(),
+ rangeTo: new Date(end).toISOString(),
+ };
+ history.push({
+ ...history.location,
+ search: fromQuery({
+ ...currentSearch,
+ ...nextSearch,
+ }),
+ });
+ }
+};
diff --git a/x-pack/plugins/apm/public/components/shared/charts/helper/__test__/timezone.test.ts b/x-pack/plugins/apm/public/components/shared/charts/helper/timezone.test.ts
similarity index 97%
rename from x-pack/plugins/apm/public/components/shared/charts/helper/__test__/timezone.test.ts
rename to x-pack/plugins/apm/public/components/shared/charts/helper/timezone.test.ts
index 0a6daf47b3ca6..3997448d17385 100644
--- a/x-pack/plugins/apm/public/components/shared/charts/helper/__test__/timezone.test.ts
+++ b/x-pack/plugins/apm/public/components/shared/charts/helper/timezone.test.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import moment from 'moment-timezone';
-import { getDomainTZ, getTimeTicksTZ } from '../timezone';
+import { getDomainTZ, getTimeTicksTZ } from './timezone';
describe('Timezone helper', () => {
let originalTimezone: moment.MomentZone | null;
diff --git a/x-pack/plugins/apm/public/components/shared/charts/line_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/line_chart/index.tsx
new file mode 100644
index 0000000000000..659a7417e703f
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/shared/charts/line_chart/index.tsx
@@ -0,0 +1,137 @@
+/*
+ * 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 {
+ Axis,
+ Chart,
+ LegendItemListener,
+ LineSeries,
+ niceTimeFormatter,
+ Placement,
+ Position,
+ ScaleType,
+ Settings,
+ SettingsSpec,
+} from '@elastic/charts';
+import moment from 'moment';
+import React, { useEffect } from 'react';
+import { useHistory } from 'react-router-dom';
+import { TimeSeries } from '../../../../../typings/timeseries';
+import { useUrlParams } from '../../../../hooks/useUrlParams';
+import { useChartsSync } from '../../../../hooks/use_charts_sync';
+import { unit } from '../../../../style/variables';
+import { Annotations } from '../annotations';
+import { ChartContainer } from '../chart_container';
+import { onBrushEnd } from '../helper/helper';
+
+interface Props {
+ id: string;
+ isLoading: boolean;
+ onToggleLegend?: LegendItemListener;
+ tickFormatY: (y: number) => string;
+ timeseries: TimeSeries[];
+}
+
+const XY_HEIGHT = unit * 16;
+
+export function LineChart({
+ id,
+ isLoading,
+ onToggleLegend,
+ tickFormatY,
+ timeseries,
+}: Props) {
+ const history = useHistory();
+ const chartRef = React.createRef();
+ const { event, setEvent } = useChartsSync();
+ const { urlParams } = useUrlParams();
+ const { start, end } = urlParams;
+
+ useEffect(() => {
+ if (event.chartId !== id && chartRef.current) {
+ chartRef.current.dispatchExternalPointerEvent(event);
+ }
+ }, [event, chartRef, id]);
+
+ const min = moment.utc(start).valueOf();
+ const max = moment.utc(end).valueOf();
+
+ const xFormatter = niceTimeFormatter([min, max]);
+
+ const chartTheme: SettingsSpec['theme'] = {
+ lineSeriesStyle: {
+ point: { visible: false },
+ line: { strokeWidth: 2 },
+ },
+ };
+
+ const isEmpty = timeseries
+ .map((serie) => serie.data)
+ .flat()
+ .every(
+ ({ y }: { x?: number | null; y?: number | null }) =>
+ y === null || y === undefined
+ );
+
+ return (
+
+
+
+ onBrushEnd({ x, history })}
+ theme={chartTheme}
+ onPointerUpdate={(currEvent: any) => {
+ setEvent(currEvent);
+ }}
+ externalPointerEvents={{
+ tooltip: { visible: true, placement: Placement.Bottom },
+ }}
+ showLegend
+ showLegendExtra
+ legendPosition={Position.Bottom}
+ xDomain={{ min, max }}
+ onLegendItemClick={(legend) => {
+ if (onToggleLegend) {
+ onToggleLegend(legend);
+ }
+ }}
+ />
+
+
+
+
+
+ {!isEmpty &&
+ timeseries.map((serie) => {
+ return (
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/context/ChartsSyncContext.tsx b/x-pack/plugins/apm/public/context/charts_sync_context.tsx
similarity index 79%
rename from x-pack/plugins/apm/public/context/ChartsSyncContext.tsx
rename to x-pack/plugins/apm/public/context/charts_sync_context.tsx
index 7df35bc443226..6f69ae097828b 100644
--- a/x-pack/plugins/apm/public/context/ChartsSyncContext.tsx
+++ b/x-pack/plugins/apm/public/context/charts_sync_context.tsx
@@ -10,14 +10,18 @@ import { fromQuery, toQuery } from '../components/shared/Links/url_helpers';
import { useFetcher } from '../hooks/useFetcher';
import { useUrlParams } from '../hooks/useUrlParams';
-const ChartsSyncContext = React.createContext<{
+export const LegacyChartsSyncContext = React.createContext<{
hoverX: number | null;
onHover: (hoverX: number) => void;
onMouseLeave: () => void;
onSelectionEnd: (range: { start: number; end: number }) => void;
} | null>(null);
-function ChartsSyncContextProvider({ children }: { children: ReactNode }) {
+export function LegacyChartsSyncContextProvider({
+ children,
+}: {
+ children: ReactNode;
+}) {
const history = useHistory();
const [time, setTime] = useState(null);
const { serviceName } = useParams<{ serviceName?: string }>();
@@ -79,7 +83,25 @@ function ChartsSyncContextProvider({ children }: { children: ReactNode }) {
return { ...hoverXHandlers };
}, [history, time, data.annotations]);
- return ;
+ return ;
}
-export { ChartsSyncContext, ChartsSyncContextProvider };
+export const ChartsSyncContext = React.createContext<{
+ event: any;
+ setEvent: Function;
+} | null>(null);
+
+export function ChartsSyncContextProvider({
+ children,
+}: {
+ children: ReactNode;
+}) {
+ const [event, setEvent] = useState({});
+
+ return (
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/hooks/use_annotations.tsx b/x-pack/plugins/apm/public/hooks/use_annotations.tsx
new file mode 100644
index 0000000000000..2b1c2bec52b3d
--- /dev/null
+++ b/x-pack/plugins/apm/public/hooks/use_annotations.tsx
@@ -0,0 +1,38 @@
+/*
+ * 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 { useParams } from 'react-router-dom';
+import { callApmApi } from '../services/rest/createCallApmApi';
+import { useFetcher } from './useFetcher';
+import { useUrlParams } from './useUrlParams';
+
+const INITIAL_STATE = { annotations: [] };
+
+export function useAnnotations() {
+ const { serviceName } = useParams<{ serviceName?: string }>();
+ const { urlParams, uiFilters } = useUrlParams();
+ const { start, end } = urlParams;
+ const { environment } = uiFilters;
+
+ const { data = INITIAL_STATE } = useFetcher(() => {
+ if (start && end && serviceName) {
+ return callApmApi({
+ pathname: '/api/apm/services/{serviceName}/annotation/search',
+ params: {
+ path: {
+ serviceName,
+ },
+ query: {
+ start,
+ end,
+ environment,
+ },
+ },
+ });
+ }
+ }, [start, end, environment, serviceName]);
+
+ return data;
+}
diff --git a/x-pack/plugins/apm/public/hooks/useChartsSync.tsx b/x-pack/plugins/apm/public/hooks/use_charts_sync.tsx
similarity index 61%
rename from x-pack/plugins/apm/public/hooks/useChartsSync.tsx
rename to x-pack/plugins/apm/public/hooks/use_charts_sync.tsx
index 0416d2c0a7f18..52c7e4c1e3a31 100644
--- a/x-pack/plugins/apm/public/hooks/useChartsSync.tsx
+++ b/x-pack/plugins/apm/public/hooks/use_charts_sync.tsx
@@ -5,7 +5,10 @@
*/
import { useContext } from 'react';
-import { ChartsSyncContext } from '../context/ChartsSyncContext';
+import {
+ ChartsSyncContext,
+ LegacyChartsSyncContext,
+} from '../context/charts_sync_context';
export function useChartsSync() {
const context = useContext(ChartsSyncContext);
@@ -16,3 +19,13 @@ export function useChartsSync() {
return context;
}
+
+export function useLegacyChartsSync() {
+ const context = useContext(LegacyChartsSyncContext);
+
+ if (!context) {
+ throw new Error('Missing ChartsSync context provider');
+ }
+
+ return context;
+}