Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ interface Props {
}>;
}
export const AnalyticsCards: React.FC<Props> = ({ stats }) => (
<EuiFlexGroup>
<EuiFlexGroup direction="column">
{stats.map(({ text, stat, dataTestSubj }) => (
<EuiFlexItem key={text}>
<EuiPanel>
<EuiPanel color="subdued" hasShadow={false}>
<EuiStat
title={stat}
description={text}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import React from 'react';

import { shallow } from 'enzyme';

import { EuiIcon } from '@elastic/eui';

import { AnalyticsSection } from './';

describe('AnalyticsSection', () => {
Expand All @@ -23,4 +25,12 @@ describe('AnalyticsSection', () => {
expect(wrapper.find('p').text()).toEqual('Dolor sit amet.');
expect(wrapper.find('[data-test-subj="HelloWorld"]')).toHaveLength(1);
});

it('renders an optional icon', () => {
const wrapper = shallow(
<AnalyticsSection title="Lorem ipsum" subtitle="Dolor sit amet." iconType="eye" />
);

expect(wrapper.find(EuiIcon).prop('type')).toEqual('eye');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,43 @@

import React from 'react';

import { EuiPageContentBody, EuiTitle, EuiText, EuiSpacer } from '@elastic/eui';
import {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiPageContentBody,
EuiSpacer,
EuiText,
EuiTitle,
IconType,
} from '@elastic/eui';

interface Props {
title: string;
subtitle: string;
iconType?: IconType;
}
export const AnalyticsSection: React.FC<Props> = ({ title, subtitle, children }) => (
export const AnalyticsSection: React.FC<Props> = ({ title, subtitle, iconType, children }) => (
<section>
<header>
<EuiTitle size="m">
<h2>{title}</h2>
</EuiTitle>
<EuiFlexGroup
gutterSize="xs"
alignItems="center"
justifyContent="flexStart"
responsive={false}
>
{iconType && (
<EuiFlexItem grow={false}>
<EuiIcon type={iconType} size="l" />
</EuiFlexItem>
)}
<EuiFlexItem>
<EuiTitle size="s">
<h2>{title}</h2>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xs" />
<EuiText size="s" color="subdued">
<p>{subtitle}</p>
</EuiText>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ describe('AnalyticsTable', () => {
expect(tableContent).toContain('0');
});

it('renders tag counts instead of tag names if isSmall is passed', () => {
const wrapper = mountWithIntl(<AnalyticsTable items={items} isSmall />);
const tableContent = wrapper.find(EuiBasicTable).text();

expect(tableContent).toContain('Analytics tags');
expect(tableContent).toContain('1 tag');
expect(tableContent).toContain('2 tags');
expect(wrapper.find(EuiBadge)).toHaveLength(3);
});

describe('renders an action column', () => {
const wrapper = mountWithIntl(<AnalyticsTable items={items} />);
runActionColumnTests(wrapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,27 @@ import { Query } from '../../types';

import {
TERM_COLUMN_PROPS,
TAGS_COLUMN,
TAGS_LIST_COLUMN,
TAGS_COUNT_COLUMN,
COUNT_COLUMN_PROPS,
ACTIONS_COLUMN,
} from './shared_columns';

interface Props {
items: Query[];
hasClicks?: boolean;
isSmall?: boolean;
}
type Columns = Array<EuiBasicTableColumn<Query>>;

export const AnalyticsTable: React.FC<Props> = ({ items, hasClicks }) => {
export const AnalyticsTable: React.FC<Props> = ({ items, hasClicks, isSmall }) => {
const TERM_COLUMN = {
field: 'key',
...TERM_COLUMN_PROPS,
};

const TAGS_COLUMN = isSmall ? TAGS_COUNT_COLUMN : TAGS_LIST_COLUMN;

const COUNT_COLUMNS = [
{
field: 'searches.doc_count',
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { generateEnginePath } from '../../../engine';

import { QueryClick } from '../../types';

import { FIRST_COLUMN_PROPS, TAGS_COLUMN, COUNT_COLUMN_PROPS } from './shared_columns';
import { FIRST_COLUMN_PROPS, TAGS_LIST_COLUMN, COUNT_COLUMN_PROPS } from './shared_columns';

interface Props {
items: QueryClick[];
Expand Down Expand Up @@ -55,7 +55,7 @@ export const QueryClicksTable: React.FC<Props> = ({ items }) => {

return (
<EuiBasicTable
columns={[DOCUMENT_COLUMN, TAGS_COLUMN, CLICKS_COLUMN] as Columns}
columns={[DOCUMENT_COLUMN, TAGS_LIST_COLUMN, CLICKS_COLUMN] as Columns}
items={items}
responsive
noItemsMessage={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { RecentQuery } from '../../types';

import {
TERM_COLUMN_PROPS,
TAGS_COLUMN,
TAGS_LIST_COLUMN,
COUNT_COLUMN_PROPS,
ACTIONS_COLUMN,
} from './shared_columns';
Expand Down Expand Up @@ -53,7 +53,9 @@ export const RecentQueriesTable: React.FC<Props> = ({ items }) => {

return (
<EuiBasicTable
columns={[TERM_COLUMN, TIME_COLUMN, TAGS_COLUMN, RESULTS_COLUMN, ACTIONS_COLUMN] as Columns}
columns={
[TERM_COLUMN, TIME_COLUMN, TAGS_LIST_COLUMN, RESULTS_COLUMN, ACTIONS_COLUMN] as Columns
}
items={items}
responsive
hasActions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { ENGINE_ANALYTICS_QUERY_DETAIL_PATH, ENGINE_CURATION_PATH } from '../../
import { generateEnginePath, EngineLogic } from '../../../engine';
import { Query, RecentQuery } from '../../types';

import { InlineTagsList } from './inline_tags_list';
import { TagsList, TagsCount } from './tags';

/**
* Shared columns / column properties between separate analytics tables
Expand Down Expand Up @@ -49,7 +49,7 @@ export const TERM_COLUMN_PROPS = {
};

export const ACTIONS_COLUMN = {
width: '120px',
width: '90px',
actions: [
{
name: i18n.translate('xpack.enterpriseSearch.appSearch.engine.analytics.table.viewAction', {
Expand All @@ -76,10 +76,10 @@ export const ACTIONS_COLUMN = {
}),
description: i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.analytics.table.editTooltip',
{ defaultMessage: 'Edit query' }
{ defaultMessage: 'Manage curation' }
),
type: 'icon',
icon: 'pencil',
icon: 'package',
onClick: async (item: Query | RecentQuery) => {
const { http } = HttpLogic.values;
const { navigateToUrl } = KibanaLogic.values;
Expand All @@ -101,13 +101,20 @@ export const ACTIONS_COLUMN = {
],
};

export const TAGS_COLUMN = {
export const TAGS_COLUMN_PROPS = {
field: 'tags',
name: i18n.translate('xpack.enterpriseSearch.appSearch.engine.analytics.table.tagsColumn', {
defaultMessage: 'Analytics tags',
}),
truncateText: true,
render: (tags: Query['tags']) => <InlineTagsList tags={tags} />,
};
export const TAGS_LIST_COLUMN = {
...TAGS_COLUMN_PROPS,
render: (tags: Query['tags']) => <TagsList tags={tags} />,
};
export const TAGS_COUNT_COLUMN = {
...TAGS_COLUMN_PROPS,
render: (tags: Query['tags']) => <TagsCount tags={tags} />,
};

export const COUNT_COLUMN_PROPS = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.tagsList .euiBadge {
max-width: $euiSize * 9;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';

import { shallow } from 'enzyme';

import { EuiBadge, EuiToolTip } from '@elastic/eui';

import { TagsList, TagsCount } from './tags';

describe('TagsList', () => {
it('renders', () => {
const wrapper = shallow(<TagsList tags={['test']} />);

expect(wrapper.find(EuiBadge)).toHaveLength(1);
expect(wrapper.find(EuiBadge).prop('children')).toEqual('test');
});

it('renders >2 badges in a tooltip list', () => {
const wrapper = shallow(<TagsList tags={['1', '2', '3', '4', '5']} />);

expect(wrapper.find(EuiBadge)).toHaveLength(3);
expect(wrapper.find(EuiToolTip)).toHaveLength(1);

expect(wrapper.find(EuiBadge).at(0).prop('children')).toEqual('1');
expect(wrapper.find(EuiBadge).at(1).prop('children')).toEqual('2');
expect(wrapper.find(EuiBadge).at(2).prop('children')).toEqual('and 3 more');
expect(wrapper.find(EuiToolTip).prop('content')).toEqual('3, 4, 5');
});

it('does not render if missing tags', () => {
const wrapper = shallow(<TagsList tags={[]} />);

expect(wrapper.isEmptyRender()).toBe(true);
});
});

describe('TagsCount', () => {
it('renders a count and all tags in a tooltip', () => {
const wrapper = shallow(<TagsCount tags={['1', '2', '3']} />);

expect(wrapper.find(EuiToolTip)).toHaveLength(1);
expect(wrapper.find(EuiBadge)).toHaveLength(1);
expect(wrapper.find(EuiBadge).prop('children')).toEqual('3 tags');
expect(wrapper.find(EuiToolTip).prop('content')).toEqual('1, 2, 3');
});

it('handles pluralization correctly', () => {
const wrapper = shallow(<TagsCount tags={['1']} />);

expect(wrapper.find(EuiToolTip)).toHaveLength(1);
expect(wrapper.find(EuiBadge)).toHaveLength(1);
expect(wrapper.find(EuiBadge).prop('children')).toEqual('1 tag');
expect(wrapper.find(EuiToolTip).prop('content')).toEqual('1');
});

it('does not render if missing tags', () => {
const wrapper = shallow(<TagsCount tags={[]} />);

expect(wrapper.isEmptyRender()).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,35 @@ import { i18n } from '@kbn/i18n';

import { Query } from '../../types';

import './tags.scss';

interface Props {
tags?: Query['tags'];
}
export const InlineTagsList: React.FC<Props> = ({ tags }) => {

export const TagsCount: React.FC<Props> = ({ tags }) => {
if (!tags?.length) return null;

return (
<EuiToolTip position="bottom" content={tags.join(', ')}>
<EuiBadge color={'hollow'}>
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.analytics.table.tagsCountBadge', {
defaultMessage: '{tagsCount, plural, one {# tag} other {# tags}}',
values: { tagsCount: tags.length },
})}
</EuiBadge>
</EuiToolTip>
);
};

export const TagsList: React.FC<Props> = ({ tags }) => {
if (!tags?.length) return null;

const displayedTags = tags.slice(0, 2);
const tooltipTags = tags.slice(2);

return (
<EuiBadgeGroup>
<EuiBadgeGroup className="tagsList">
{displayedTags.map((tag: string) => (
<EuiBadge color="hollow" key={tag}>
{tag}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.analyticsOverviewTables {
@include euiBreakpoint('xs', 's', 'm', 'l') {
flex-direction: column; // Force full width on table panels earlier to ensure content stays legible
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('Analytics overview', () => {

expect(wrapper.find(AnalyticsCards)).toHaveLength(1);
expect(wrapper.find(AnalyticsChart)).toHaveLength(1);
expect(wrapper.find(AnalyticsSection)).toHaveLength(3);
expect(wrapper.find(AnalyticsSection)).toHaveLength(2);
expect(wrapper.find(AnalyticsTable)).toHaveLength(4);
expect(wrapper.find(RecentQueriesTable)).toHaveLength(1);
});
Expand Down
Loading