diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts index 30a64219403b..0901ff273780 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts @@ -29,6 +29,15 @@ describe('AnalyticsLogic', () => { dataLoading: true, analyticsUnavailable: false, allTags: [], + totalQueries: 0, + totalQueriesNoResults: 0, + totalClicks: 0, + totalQueriesForQuery: 0, + queriesPerDay: [], + queriesNoResultsPerDay: [], + clicksPerDay: [], + queriesPerDayForQuery: [], + startDate: '', }; const MOCK_TOP_QUERIES = [ @@ -66,6 +75,7 @@ describe('AnalyticsLogic', () => { const MOCK_ANALYTICS_RESPONSE = { analyticsUnavailable: false, allTags: ['some-tag'], + startDate: '1970-01-01', recentQueries: MOCK_RECENT_QUERIES, topQueries: MOCK_TOP_QUERIES, topQueriesNoResults: MOCK_TOP_QUERIES, @@ -81,6 +91,7 @@ describe('AnalyticsLogic', () => { const MOCK_QUERY_RESPONSE = { analyticsUnavailable: false, allTags: ['some-tag'], + startDate: '1970-01-01', totalQueriesForQuery: 50, queriesPerDayForQuery: [25, 0, 25], topClicksForQuery: MOCK_TOP_CLICKS, @@ -120,7 +131,14 @@ describe('AnalyticsLogic', () => { dataLoading: false, analyticsUnavailable: false, allTags: ['some-tag'], - // TODO: more state will get set here in future PRs + startDate: '1970-01-01', + totalClicks: 1000, + totalQueries: 5000, + totalQueriesNoResults: 500, + queriesPerDay: [10, 50, 100], + queriesNoResultsPerDay: [1, 2, 3], + clicksPerDay: [0, 10, 50], + // TODO: Replace this with ...MOCK_ANALYTICS_RESPONSE once all data is set }); }); }); @@ -135,7 +153,10 @@ describe('AnalyticsLogic', () => { dataLoading: false, analyticsUnavailable: false, allTags: ['some-tag'], - // TODO: more state will get set here in future PRs + startDate: '1970-01-01', + totalQueriesForQuery: 50, + queriesPerDayForQuery: [25, 0, 25], + // TODO: Replace this with ...MOCK_QUERY_RESPONSE once all data is set }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts index 4d8603f4bb6d..537de02a0fee 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts @@ -62,6 +62,61 @@ export const AnalyticsLogic = kea allTags, }, ], + totalQueries: [ + 0, + { + onAnalyticsDataLoad: (_, { totalQueries }) => totalQueries, + }, + ], + totalQueriesNoResults: [ + 0, + { + onAnalyticsDataLoad: (_, { totalQueriesNoResults }) => totalQueriesNoResults, + }, + ], + totalClicks: [ + 0, + { + onAnalyticsDataLoad: (_, { totalClicks }) => totalClicks, + }, + ], + queriesPerDay: [ + [], + { + onAnalyticsDataLoad: (_, { queriesPerDay }) => queriesPerDay, + }, + ], + queriesNoResultsPerDay: [ + [], + { + onAnalyticsDataLoad: (_, { queriesNoResultsPerDay }) => queriesNoResultsPerDay, + }, + ], + clicksPerDay: [ + [], + { + onAnalyticsDataLoad: (_, { clicksPerDay }) => clicksPerDay, + }, + ], + totalQueriesForQuery: [ + 0, + { + onQueryDataLoad: (_, { totalQueriesForQuery }) => totalQueriesForQuery, + }, + ], + queriesPerDayForQuery: [ + [], + { + onQueryDataLoad: (_, { queriesPerDayForQuery }) => queriesPerDayForQuery, + }, + ], + startDate: [ + '', + { + onAnalyticsDataLoad: (_, { startDate }) => startDate, + onQueryDataLoad: (_, { startDate }) => startDate, + }, + ], }), listeners: ({ actions }) => ({ loadAnalyticsData: async () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx new file mode 100644 index 000000000000..eee6517d5b48 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.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 React from 'react'; +import { shallow } from 'enzyme'; +import { EuiStat } from '@elastic/eui'; + +import { AnalyticsCards } from './'; + +describe('AnalyticsCards', () => { + it('renders', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(EuiStat)).toHaveLength(2); + expect(wrapper.find('[data-test-subj="RedFish"]').prop('title')).toEqual(100); + expect(wrapper.find('[data-test-subj="RedFish"]').prop('description')).toEqual('Red fish'); + expect(wrapper.find('[data-test-subj="BlueFish"]').prop('title')).toEqual(2000); + expect(wrapper.find('[data-test-subj="BlueFish"]').prop('description')).toEqual('Blue fish'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx new file mode 100644 index 000000000000..035928305917 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.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 React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui'; + +interface Props { + stats: Array<{ + text: string; + stat: number; + dataTestSubj?: string; + }>; +} +export const AnalyticsCards: React.FC = ({ stats }) => ( + + {stats.map(({ text, stat, dataTestSubj }) => ( + + + + + + ))} + +); 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 index 4e071ac7982b..b2e3e615e481 100644 --- 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 @@ -54,6 +54,14 @@ describe('AnalyticsChart', () => { expect(wrapper.find(LineSeries)).toHaveLength(3); }); + it('renders dashed lines', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(LineSeries).prop('lineSeriesStyle')?.line?.dash).toBeTruthy(); + }); + it('formats x-axis dates correctly', () => { const wrapper = shallow(); const dateFormatter: Function = wrapper.find('#bottom-axis').prop('tickFormat'); 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 index 02ad2dff9282..a73d3c9054ed 100644 --- 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 @@ -25,6 +25,7 @@ interface Props { lines: Array<{ id: string; data: ChartData; + isDashed?: boolean; }>; } export const AnalyticsChart: React.FC = ({ height = 300, lines }) => { @@ -39,7 +40,7 @@ export const AnalyticsChart: React.FC = ({ height = 300, lines }) => { headerFormatter: (tooltip) => moment(tooltip.value).format(TOOLTIP_DATE_FORMAT), }} /> - {lines.map(({ id, data }) => ( + {lines.map(({ id, data, isDashed }) => ( = ({ height = 300, lines }) => { xAccessor={'x'} yAccessors={['y']} curve={CurveType.CURVE_MONOTONE_X} + lineSeriesStyle={isDashed ? { line: { dash: [5, 5] } } : undefined} /> ))} { it('renders', () => { + setMockValues({ + totalQueries: 3, + totalQueriesNoResults: 2, + totalClicks: 1, + queriesPerDay: [10, 20, 30], + queriesNoResultsPerDay: [1, 2, 3], + clicksPerDay: [0, 1, 5], + startDate: '1970-01-01', + }); const wrapper = shallow(); - expect(wrapper.isEmptyRender()).toBe(false); // TODO + expect(wrapper.find(AnalyticsCards)).toHaveLength(1); + expect(wrapper.find(AnalyticsChart)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx index 5febeae203ab..d3c3bff5a294 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx @@ -5,13 +5,73 @@ */ import React from 'react'; +import { useValues } from 'kea'; -import { ANALYTICS_TITLE } from '../constants'; +import { EuiSpacer } from '@elastic/eui'; + +import { + ANALYTICS_TITLE, + TOTAL_QUERIES, + TOTAL_QUERIES_NO_RESULTS, + TOTAL_CLICKS, +} from '../constants'; import { AnalyticsLayout } from '../analytics_layout'; +import { AnalyticsLogic, AnalyticsCards, AnalyticsChart, convertToChartData } from '../'; export const Analytics: React.FC = () => { + const { + totalQueries, + totalQueriesNoResults, + totalClicks, + queriesPerDay, + queriesNoResultsPerDay, + clicksPerDay, + startDate, + } = useValues(AnalyticsLogic); + return ( + + + + + +

TODO: Analytics overview

); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx index 2e4bd36d7938..99485340f6b8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx @@ -5,6 +5,7 @@ */ import '../../../../__mocks__/react_router_history.mock'; +import { setMockValues } from '../../../../__mocks__'; import React from 'react'; import { useParams } from 'react-router-dom'; @@ -12,6 +13,7 @@ import { shallow } from 'enzyme'; import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; +import { AnalyticsCards, AnalyticsChart } from '../components'; import { QueryDetail } from './'; describe('QueryDetail', () => { @@ -19,6 +21,11 @@ describe('QueryDetail', () => { beforeEach(() => { (useParams as jest.Mock).mockReturnValueOnce({ query: 'some-query' }); + + setMockValues({ + totalQueriesForQuery: 100, + queriesPerDayForQuery: [0, 5, 10], + }); }); it('renders', () => { @@ -31,5 +38,8 @@ describe('QueryDetail', () => { 'Query', 'some-query', ]); + + expect(wrapper.find(AnalyticsCards)).toHaveLength(1); + expect(wrapper.find(AnalyticsChart)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx index b3b7e5258c53..53c1dc8b845b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx @@ -6,12 +6,16 @@ import React from 'react'; import { useParams } from 'react-router-dom'; +import { useValues } from 'kea'; + import { i18n } from '@kbn/i18n'; +import { EuiSpacer } from '@elastic/eui'; import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; import { BreadcrumbTrail } from '../../../../shared/kibana_chrome/generate_breadcrumbs'; import { AnalyticsLayout } from '../analytics_layout'; +import { AnalyticsLogic, AnalyticsCards, AnalyticsChart, convertToChartData } from '../'; const QUERY_DETAIL_TITLE = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.analytics.queryDetail.title', @@ -24,10 +28,41 @@ interface Props { export const QueryDetail: React.FC = ({ breadcrumbs }) => { const { query } = useParams() as { query: string }; + const { totalQueriesForQuery, queriesPerDayForQuery, startDate } = useValues(AnalyticsLogic); + return ( + + + + + +

TODO: Query detail page

); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx index 6cb47e8b419f..2464eb258452 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx @@ -7,45 +7,19 @@ import { setMockValues } from '../../../../__mocks__/kea.mock'; import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import { EuiStat } from '@elastic/eui'; +import { shallow } from 'enzyme'; +import { AnalyticsCards } from '../../analytics'; import { TotalStats } from './total_stats'; describe('TotalStats', () => { - let wrapper: ShallowWrapper; - - beforeAll(() => { - jest.clearAllMocks(); + it('renders', () => { setMockValues({ totalQueries: 11, documentCount: 22, totalClicks: 33, }); - wrapper = shallow(); - }); - - it('renders the total queries stat', () => { - expect(wrapper.find('[data-test-subj="TotalQueriesCard"]')).toHaveLength(1); - - const card = wrapper.find(EuiStat).at(0); - expect(card.prop('title')).toEqual(11); - expect(card.prop('description')).toEqual('Total queries'); - }); - - it('renders the total documents stat', () => { - expect(wrapper.find('[data-test-subj="TotalDocumentsCard"]')).toHaveLength(1); - - const card = wrapper.find(EuiStat).at(1); - expect(card.prop('title')).toEqual(22); - expect(card.prop('description')).toEqual('Total documents'); - }); - - it('renders the total clicks stat', () => { - expect(wrapper.find('[data-test-subj="TotalClicksCard"]')).toHaveLength(1); - - const card = wrapper.find(EuiStat).at(2); - expect(card.prop('title')).toEqual(33); - expect(card.prop('description')).toEqual('Total clicks'); + const wrapper = shallow(); + expect(wrapper.find(AnalyticsCards)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx index a27142938f55..a7f592349845 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx @@ -6,9 +6,9 @@ import React from 'react'; import { useValues } from 'kea'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui'; import { TOTAL_QUERIES, TOTAL_DOCUMENTS, TOTAL_CLICKS } from '../../analytics/constants'; +import { AnalyticsCards } from '../../analytics'; import { EngineOverviewLogic } from '../'; @@ -16,22 +16,24 @@ export const TotalStats: React.FC = () => { const { totalQueries, documentCount, totalClicks } = useValues(EngineOverviewLogic); return ( - - - - - - - - - - - - - - - - - + ); };