diff --git a/x-pack/plugins/enterprise_search/kibana.json b/x-pack/plugins/enterprise_search/kibana.json
index 36a3895c6161..daae8cf57f63 100644
--- a/x-pack/plugins/enterprise_search/kibana.json
+++ b/x-pack/plugins/enterprise_search/kibana.json
@@ -2,7 +2,7 @@
"id": "enterpriseSearch",
"version": "kibana",
"kibanaVersion": "kibana",
- "requiredPlugins": ["features", "licensing"],
+ "requiredPlugins": ["features", "licensing", "charts"],
"configPath": ["enterpriseSearch"],
"optionalPlugins": ["usageCollection", "security", "home", "spaces", "cloud"],
"server": true,
diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts
index 95843a243a3c..e1e20adbe575 100644
--- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts
@@ -4,15 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { chartPluginMock } from '../../../../../../src/plugins/charts/public/mocks';
import { mockHistory } from './';
export const mockKibanaValues = {
config: { host: 'http://localhost:3002' },
- history: mockHistory,
+ charts: chartPluginMock.createStartContract(),
cloud: {
isCloudEnabled: false,
cloudDeploymentUrl: 'https://cloud.elastic.co/deployments/some-id',
},
+ history: mockHistory,
navigateToUrl: jest.fn(),
setBreadcrumbs: jest.fn(),
setDocTitle: jest.fn(),
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx
new file mode 100644
index 000000000000..4e071ac7982b
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx
@@ -0,0 +1,70 @@
+/*
+ * 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 { mockKibanaValues } from '../../../../__mocks__';
+
+import React from 'react';
+import { shallow } from 'enzyme';
+import { Chart, Settings, LineSeries, Axis } from '@elastic/charts';
+
+import { AnalyticsChart } from './';
+
+describe('AnalyticsChart', () => {
+ const MOCK_DATA = [
+ { x: '1970-01-01', y: 0 },
+ { x: '1970-01-02', y: 1 },
+ { x: '1970-01-03', y: 5 },
+ { x: '1970-01-04', y: 50 },
+ { x: '1970-01-05', y: 25 },
+ ];
+
+ beforeAll(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders an Elastic line chart', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find(Chart).prop('size')).toEqual({ height: 300 });
+ expect(wrapper.find(Axis)).toHaveLength(2);
+ expect(mockKibanaValues.charts.theme.useChartsTheme).toHaveBeenCalled();
+ expect(mockKibanaValues.charts.theme.useChartsBaseTheme).toHaveBeenCalled();
+
+ expect(wrapper.find(LineSeries)).toHaveLength(1);
+ expect(wrapper.find(LineSeries).prop('id')).toEqual('test');
+ expect(wrapper.find(LineSeries).prop('data')).toEqual(MOCK_DATA);
+ });
+
+ it('renders multiple lines', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find(LineSeries)).toHaveLength(3);
+ });
+
+ it('formats x-axis dates correctly', () => {
+ const wrapper = shallow();
+ const dateFormatter: Function = wrapper.find('#bottom-axis').prop('tickFormat');
+
+ expect(dateFormatter('1970-02-28')).toEqual('2/28');
+ });
+
+ it('formats tooltip dates correctly', () => {
+ const wrapper = shallow();
+ const dateFormatter: Function = (wrapper.find(Settings).prop('tooltip') as any).headerFormatter;
+
+ expect(dateFormatter({ value: '1970-12-03' })).toEqual('December 3, 1970');
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx
new file mode 100644
index 000000000000..02ad2dff9282
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx
@@ -0,0 +1,61 @@
+/*
+ * 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 { useValues } from 'kea';
+
+import moment from 'moment';
+import { Chart, Settings, LineSeries, CurveType, Axis } from '@elastic/charts';
+
+import { KibanaLogic } from '../../../../shared/kibana';
+
+import { X_AXIS_DATE_FORMAT, TOOLTIP_DATE_FORMAT } from '../constants';
+
+interface ChartPoint {
+ x: string; // Date string
+ y: number; // # of clicks, queries, etc.
+}
+export type ChartData = ChartPoint[];
+
+interface Props {
+ height?: number;
+ lines: Array<{
+ id: string;
+ data: ChartData;
+ }>;
+}
+export const AnalyticsChart: React.FC = ({ height = 300, lines }) => {
+ const { charts } = useValues(KibanaLogic);
+
+ return (
+
+ moment(tooltip.value).format(TOOLTIP_DATE_FORMAT),
+ }}
+ />
+ {lines.map(({ id, data }) => (
+
+ ))}
+ moment(d).format(X_AXIS_DATE_FORMAT)}
+ showGridLines
+ />
+
+
+ );
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/index.ts
new file mode 100644
index 000000000000..31aa998d86d9
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/index.ts
@@ -0,0 +1,7 @@
+/*
+ * 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.
+ */
+
+export { AnalyticsChart } from './analytics_chart';
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts
index 1e25582e3d56..9985753d0970 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts
@@ -30,3 +30,8 @@ export const TOTAL_CLICKS = i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.analytics.totalClicks',
{ defaultMessage: 'Total clicks' }
);
+
+// Moment date format conversions
+export const SERVER_DATE_FORMAT = 'YYYY-MM-DD';
+export const TOOLTIP_DATE_FORMAT = 'MMMM D, YYYY';
+export const X_AXIS_DATE_FORMAT = 'M/D';
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts
index 7cddef645e83..3b201b38703d 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts
@@ -5,3 +5,5 @@
*/
export { ANALYTICS_TITLE } from './constants';
+export { AnalyticsChart } from './components';
+export { convertToChartData } from './utils';
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/utils.test.ts
new file mode 100644
index 000000000000..2f769dc44fda
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/utils.test.ts
@@ -0,0 +1,24 @@
+/*
+ * 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 { convertToChartData } from './utils';
+
+describe('convertToChartData', () => {
+ it('converts server-side analytics data into an array of objects that Elastic Charts can consume', () => {
+ expect(
+ convertToChartData({
+ startDate: '1970-01-01',
+ data: [0, 1, 5, 50, 25],
+ })
+ ).toEqual([
+ { x: '1970-01-01', y: 0 },
+ { x: '1970-01-02', y: 1 },
+ { x: '1970-01-03', y: 5 },
+ { x: '1970-01-04', y: 50 },
+ { x: '1970-01-05', y: 25 },
+ ]);
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/utils.ts
new file mode 100644
index 000000000000..a3aebc043e25
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/utils.ts
@@ -0,0 +1,22 @@
+/*
+ * 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 moment from 'moment';
+
+import { SERVER_DATE_FORMAT } from './constants';
+import { ChartData } from './components/analytics_chart';
+
+interface ConvertToChartData {
+ data: number[];
+ startDate: string;
+}
+export const convertToChartData = ({ data, startDate }: ConvertToChartData): ChartData => {
+ const date = moment(startDate, SERVER_DATE_FORMAT);
+ return data.map((y, index) => ({
+ x: moment(date).add(index, 'days').format(SERVER_DATE_FORMAT),
+ y,
+ }));
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx
index 775a74921d0d..b1350b7e102e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx
@@ -10,6 +10,7 @@ import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { EuiButtonTo } from '../../../../shared/react_router_helpers';
+import { AnalyticsChart } from '../../analytics';
import { TotalCharts } from './total_charts';
@@ -21,7 +22,6 @@ describe('TotalCharts', () => {
setMockValues({
engineName: 'some-engine',
startDate: '1970-01-01',
- endDate: '1970-01-08',
queriesPerDay: [0, 1, 2, 3, 5, 10, 50],
operationsPerDay: [0, 0, 0, 0, 0, 0, 0],
});
@@ -33,7 +33,7 @@ describe('TotalCharts', () => {
expect(chart.find('h2').text()).toEqual('Total queries');
expect(chart.find(EuiButtonTo).prop('to')).toEqual('/engines/some-engine/analytics');
- // TODO: find chart component
+ expect(chart.find(AnalyticsChart)).toHaveLength(1);
});
it('renders the total API operations chart', () => {
@@ -41,6 +41,6 @@ describe('TotalCharts', () => {
expect(chart.find('h2').text()).toEqual('Total API operations');
expect(chart.find(EuiButtonTo).prop('to')).toEqual('/engines/some-engine/api-logs');
- // TODO: find chart component
+ expect(chart.find(AnalyticsChart)).toHaveLength(1);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx
index 214a6bd74aab..4ef4e08dee76 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx
@@ -24,6 +24,8 @@ import { ENGINE_ANALYTICS_PATH, ENGINE_API_LOGS_PATH, getEngineRoute } from '../
import { TOTAL_QUERIES, TOTAL_API_OPERATIONS } from '../../analytics/constants';
import { VIEW_ANALYTICS, VIEW_API_LOGS, LAST_7_DAYS } from '../constants';
+import { AnalyticsChart, convertToChartData } from '../../analytics';
+
import { EngineLogic } from '../../engine';
import { EngineOverviewLogic } from '../';
@@ -31,12 +33,7 @@ export const TotalCharts: React.FC = () => {
const { engineName } = useValues(EngineLogic);
const engineRoute = getEngineRoute(engineName);
- const {
- // startDate,
- // endDate,
- // queriesPerDay,
- // operationsPerDay,
- } = useValues(EngineOverviewLogic);
+ const { startDate, queriesPerDay, operationsPerDay } = useValues(EngineOverviewLogic);
return (
@@ -58,12 +55,14 @@ export const TotalCharts: React.FC = () => {
- TODO: Analytics chart
- {/* */}
+
@@ -85,12 +84,14 @@ export const TotalCharts: React.FC = () => {
- TODO: API Logs chart
- {/* */}
+
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts
index 2063f706a474..61319245bba0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.test.ts
@@ -28,7 +28,6 @@ describe('EngineOverviewLogic', () => {
apiLogsUnavailable: true,
documentCount: 10,
startDate: '1970-01-30',
- endDate: '1970-01-31',
operationsPerDay: [0, 0, 0, 0, 0, 0, 0],
queriesPerDay: [0, 0, 0, 0, 0, 25, 50],
totalClicks: 50,
@@ -40,7 +39,6 @@ describe('EngineOverviewLogic', () => {
apiLogsUnavailable: false,
documentCount: 0,
startDate: '',
- endDate: '',
operationsPerDay: [],
queriesPerDay: [],
totalClicks: 0,
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.ts
index 3fc7ce8083e0..414159fff065 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_logic.ts
@@ -16,7 +16,6 @@ interface EngineOverviewApiData {
apiLogsUnavailable: boolean;
documentCount: number;
startDate: string;
- endDate: string;
operationsPerDay: number[];
queriesPerDay: number[];
totalClicks: number;
@@ -61,12 +60,6 @@ export const EngineOverviewLogic = kea startDate,
},
],
- endDate: [
- '',
- {
- setPolledData: (_, { endDate }) => endDate,
- },
- ],
queriesPerDay: [
[],
{
diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx
index d5b9513d0dbb..bbf42eae3708 100644
--- a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx
@@ -9,6 +9,7 @@ import { getContext } from 'kea';
import { coreMock } from 'src/core/public/mocks';
import { licensingMock } from '../../../licensing/public/mocks';
+import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks';
import { renderApp, renderHeaderActions } from './';
import { EnterpriseSearch } from './enterprise_search';
@@ -20,7 +21,10 @@ describe('renderApp', () => {
const kibanaDeps = {
params: coreMock.createAppMountParamters(),
core: coreMock.createStart(),
- plugins: { licensing: licensingMock.createStart() },
+ plugins: {
+ licensing: licensingMock.createStart(),
+ charts: chartPluginMock.createStartContract(),
+ },
} as any;
const pluginData = {
config: {},
diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx
index 1271015e40e5..6ebeb82ee326 100644
--- a/x-pack/plugins/enterprise_search/public/applications/index.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx
@@ -41,6 +41,7 @@ export const renderApp = (
const unmountKibanaLogic = mountKibanaLogic({
config,
+ charts: plugins.charts,
cloud: plugins.cloud || {},
history: params.history,
navigateToUrl: core.application.navigateToUrl,
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts
index 7d3db4d36692..282b951e1514 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts
@@ -9,6 +9,7 @@ import { kea, MakeLogicType } from 'kea';
import { FC } from 'react';
import { History } from 'history';
import { ApplicationStart, ChromeBreadcrumb } from 'src/core/public';
+import { ChartsPluginStart } from 'src/plugins/charts/public';
import { CloudSetup } from '../../../../../cloud/public';
import { HttpLogic } from '../http';
@@ -18,6 +19,7 @@ interface KibanaLogicProps {
config: { host?: string };
history: History;
cloud: Partial;
+ charts: ChartsPluginStart;
navigateToUrl: ApplicationStart['navigateToUrl'];
setBreadcrumbs(crumbs: ChromeBreadcrumb[]): void;
setDocTitle(title: string): void;
@@ -31,8 +33,9 @@ export const KibanaLogic = kea>({
path: ['enterprise_search', 'kibana_logic'],
reducers: ({ props }) => ({
config: [props.config || {}, {}],
- history: [props.history, {}],
+ charts: [props.charts, {}],
cloud: [props.cloud || {}, {}],
+ history: [props.history, {}],
navigateToUrl: [
(url: string, options?: CreateHrefOptions) => {
const deps = { history: props.history, http: HttpLogic.values.http };
diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts
index 94e9ea88ea75..632bb425f203 100644
--- a/x-pack/plugins/enterprise_search/public/plugin.ts
+++ b/x-pack/plugins/enterprise_search/public/plugin.ts
@@ -18,6 +18,7 @@ import {
} from '../../../../src/plugins/home/public';
import { CloudSetup } from '../../cloud/public';
import { LicensingPluginStart } from '../../licensing/public';
+import { ChartsPluginStart } from '../../../../src/plugins/charts/public';
import {
APP_SEARCH_PLUGIN,
@@ -41,6 +42,7 @@ interface PluginsSetup {
export interface PluginsStart {
cloud?: CloudSetup;
licensing: LicensingPluginStart;
+ charts: ChartsPluginStart;
}
export class EnterpriseSearchPlugin implements Plugin {