Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[App Search] Migrate Create Meta Engine View #92127

Merged
merged 32 commits into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d436c4b
New empty MetaEngineCreation component
byronhulcher Feb 11, 2021
cd05bc7
Added MetaEngineCreation to AppSearchConfigured router
byronhulcher Feb 11, 2021
eba36a2
Empty MetaEngineCreationLogic
byronhulcher Feb 11, 2021
1dc3e90
Add rawName value and setRawName action to MetaEngineCreationLogic
byronhulcher Feb 11, 2021
6627d24
Add indexedEngineNames value and setIndexedEngineNames action to MEta…
byronhulcher Feb 11, 2021
ebaff11
Add selectedIndexedEngineNames value and setIndexedEngineNames action…
byronhulcher Feb 11, 2021
a780e1b
Add description to MetaEngineCreation
byronhulcher Feb 17, 2021
369d82a
Add name selector to MetaEngineCreationLogic
byronhulcher Feb 17, 2021
76f88c0
Added MetaEngineCreationNameInput to MetaEngineCreation
byronhulcher Feb 17, 2021
6dfc1bd
Add fetchIndexedEngineNames listener to MetaEngineCreationLogic
byronhulcher Feb 17, 2021
6c42c62
Call fetchIndexedEngineNames when MetaEngineCreation first renders
byronhulcher Feb 17, 2021
200802e
Add EuiComboBox for selectedEngineNames to MetaEngineCreation
byronhulcher Feb 18, 2021
753bad1
WIP Add meta engine source engine limit warning to MetaEngineCreation
byronhulcher Feb 18, 2021
c999341
Add submitEngine listener to MetaEngineCreationLogic
byronhulcher Feb 19, 2021
23805ed
Add onEngineCreationSuccess to MetaEngineCreationLogic
byronhulcher Feb 19, 2021
41da254
Fixing tests for MetaEngineCreationLogic
byronhulcher Feb 21, 2021
3047390
Fix tests for MetaEngineCreation
byronhulcher Feb 21, 2021
6bae359
Add Create a meta engine button to EnginesOverview
byronhulcher Feb 23, 2021
04b371f
Use DEFAULT_META for fetching indexed engine names in MetaEngineCreat…
byronhulcher Feb 24, 2021
1c1cc8e
Copy fixes
byronhulcher Feb 24, 2021
0423d9e
Updating POST /api/engines tests
byronhulcher Feb 24, 2021
f7741eb
Add noItemsMessage prop to EnginesTable
byronhulcher Feb 24, 2021
2cd7cd1
Add empty prompt to Meta Engines table in EnginesOverview
byronhulcher Feb 24, 2021
31070ee
Apply suggestions from code review
byronhulcher Feb 25, 2021
61b83f8
Better form functionality in MetaEngineCreation
byronhulcher Mar 1, 2021
4a012ce
Fix errors from github
byronhulcher Mar 1, 2021
4736433
More MetaEngineCreation coverage
byronhulcher Mar 1, 2021
65e931c
Meta MetaEngineCreationLogic coverage
byronhulcher Mar 1, 2021
e9adff2
Merge remote-tracking branch 'origin/master' into meta-engine-creation
byronhulcher Mar 1, 2021
16a07f3
Update x-pack/plugins/enterprise_search/public/applications/app_searc…
byronhulcher Mar 1, 2021
bd7a05f
Update x-pack/plugins/enterprise_search/public/applications/app_searc…
byronhulcher Mar 1, 2021
6ca7a26
Update x-pack/plugins/enterprise_search/public/applications/app_searc…
byronhulcher Mar 1, 2021
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 @@ -22,3 +22,25 @@ export const CREATE_AN_ENGINE_BUTTON_LABEL = i18n.translate(
defaultMessage: 'Create an engine',
}
);

export const CREATE_A_META_ENGINE_BUTTON_LABEL = i18n.translate(
'xpack.enterpriseSearch.appSearch.engines.createAMetaEngineButton.ButtonLabel',
{
defaultMessage: 'Create a meta engine',
}
);

export const META_ENGINE_EMPTY_PROMPT_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.engines.metaEngines.emptyPrompTitle',
{
defaultMessage: 'No meta engines yet',
}
);

export const META_ENGINE_EMPTY_PROMPT_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.appSearch.engines.metaEngines.emptyPromptDescription',
{
defaultMessage:
'Meta engines allow you to combine multiple engines into one searchable engine.',
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import React from 'react';

import { shallow, ShallowWrapper } from 'enzyme';

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

import { LoadingState, EmptyState } from './components';
import { EnginesTable } from './engines_table';

import { EnginesOverview } from './';

describe('EnginesOverview', () => {
const values = {
hasPlatinumLicense: false,
dataLoading: false,
engines: [],
enginesMeta: {
Expand All @@ -39,6 +40,7 @@ describe('EnginesOverview', () => {
},
},
metaEnginesLoading: false,
hasPlatinumLicense: false,
};
const actions = {
loadEngines: jest.fn(),
Expand Down Expand Up @@ -73,7 +75,7 @@ describe('EnginesOverview', () => {
const valuesWithEngines = {
...values,
dataLoading: false,
engines: ['dummy-engine'],
engines: ['test-engine'],
enginesMeta: {
page: {
current: 1,
Expand All @@ -84,6 +86,7 @@ describe('EnginesOverview', () => {
};

beforeEach(() => {
jest.clearAllMocks();
setMockValues(valuesWithEngines);
});

Expand All @@ -102,18 +105,47 @@ describe('EnginesOverview', () => {
).toEqual('/engine_creation');
});

describe('when on a platinum license', () => {
it('renders a 2nd meta engines table & makes a 2nd meta engines API call', async () => {
describe('when user has a platinum license', () => {
let wrapper: ShallowWrapper;

beforeEach(() => {
setMockValues({
...valuesWithEngines,
hasPlatinumLicense: true,
metaEngines: ['dummy-meta-engine'],
});
const wrapper = shallow(<EnginesOverview />);
wrapper = shallow(<EnginesOverview />);
});

it('renders a 2nd meta engines table ', async () => {
expect(wrapper.find(EnginesTable)).toHaveLength(2);
});

it('makes a 2nd meta engines call', () => {
expect(actions.loadMetaEngines).toHaveBeenCalled();
});

it('renders a create engine button which takes users to the create meta engine page', () => {
expect(
wrapper.find('[data-test-subj="appSearchEnginesMetaEngineCreationButton"]').prop('to')
).toEqual('/meta_engine_creation');
});

it('contains an EuiEmptyPrompt that takes users to the create meta when metaEngines is empty', () => {
setMockValues({
...valuesWithEngines,
hasPlatinumLicense: true,
metaEngines: [],
});
wrapper = shallow(<EnginesOverview />);
const metaEnginesTable = wrapper.find(EnginesTable).last().dive();
const emptyPrompt = metaEnginesTable.dive().find(EuiEmptyPrompt).dive();

expect(
emptyPrompt
.find('[data-test-subj="appSearchMetaEnginesEmptyStateCreationButton"]')
.prop('to')
).toEqual('/meta_engine_creation');
});
});

describe('pagination', () => {
Expand Down Expand Up @@ -150,7 +182,7 @@ describe('EnginesOverview', () => {
setMockValues({
...valuesWithEngines,
hasPlatinumLicense: true,
metaEngines: ['dummy-meta-engine'],
metaEngines: ['test-meta-engine'],
});
const wrapper = shallow(<EnginesOverview />);
const pageEvent = { page: { index: 0 } };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
EuiPageContentBody,
EuiTitle,
EuiSpacer,
EuiEmptyPrompt,
} from '@elastic/eui';

import { FlashMessages } from '../../../shared/flash_messages';
Expand All @@ -24,19 +25,27 @@ import { LicensingLogic } from '../../../shared/licensing';
import { EuiButtonTo } from '../../../shared/react_router_helpers';
import { convertMetaToPagination, handlePageChange } from '../../../shared/table_pagination';
import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
import { ENGINE_CREATION_PATH } from '../../routes';
import { ENGINE_CREATION_PATH, META_ENGINE_CREATION_PATH } from '../../routes';

import { EngineIcon } from './assets/engine_icon';
import { MetaEngineIcon } from './assets/meta_engine_icon';
import { EnginesOverviewHeader, LoadingState, EmptyState } from './components';
import { CREATE_AN_ENGINE_BUTTON_LABEL, ENGINES_TITLE, META_ENGINES_TITLE } from './constants';
import {
CREATE_AN_ENGINE_BUTTON_LABEL,
CREATE_A_META_ENGINE_BUTTON_LABEL,
ENGINES_TITLE,
META_ENGINE_EMPTY_PROMPT_DESCRIPTION,
META_ENGINE_EMPTY_PROMPT_TITLE,
META_ENGINES_TITLE,
} from './constants';
import { EnginesLogic } from './engines_logic';
import { EnginesTable } from './engines_table';

import './engines_overview.scss';

export const EnginesOverview: React.FC = () => {
const { hasPlatinumLicense } = useValues(LicensingLogic);

const {
dataLoading,
engines,
Expand All @@ -46,6 +55,7 @@ export const EnginesOverview: React.FC = () => {
metaEnginesMeta,
metaEnginesLoading,
} = useValues(EnginesLogic);

const { loadEngines, loadMetaEngines, onEnginesPagination, onMetaEnginesPagination } = useActions(
EnginesLogic
);
Expand Down Expand Up @@ -100,15 +110,27 @@ export const EnginesOverview: React.FC = () => {
/>
</EuiPageContentBody>

{metaEngines.length > 0 && (
{hasPlatinumLicense && (
<>
<EuiSpacer size="xl" />
<EuiPageContentHeader>
<EuiTitle size="s">
<h2>
<MetaEngineIcon /> {META_ENGINES_TITLE}
</h2>
</EuiTitle>
<EuiPageContentHeaderSection>
<EuiTitle size="s">
<h2>
<MetaEngineIcon /> {META_ENGINES_TITLE}
</h2>
</EuiTitle>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>
<EuiButtonTo
color="primary"
fill
data-test-subj="appSearchEnginesMetaEngineCreationButton"
to={META_ENGINE_CREATION_PATH}
>
{CREATE_A_META_ENGINE_BUTTON_LABEL}
cee-chen marked this conversation as resolved.
Show resolved Hide resolved
</EuiButtonTo>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody data-test-subj="appSearchMetaEngines">
<EnginesTable
Expand All @@ -118,6 +140,21 @@ export const EnginesOverview: React.FC = () => {
...convertMetaToPagination(metaEnginesMeta),
hidePerPageOptions: true,
}}
noItemsMessage={
<EuiEmptyPrompt
byronhulcher marked this conversation as resolved.
Show resolved Hide resolved
title={<h2>{META_ENGINE_EMPTY_PROMPT_TITLE}</h2>}
body={<p>{META_ENGINE_EMPTY_PROMPT_DESCRIPTION}</p>}
actions={
<EuiButtonTo
data-test-subj="appSearchMetaEnginesEmptyStateCreationButton"
fill
to={META_ENGINE_CREATION_PATH}
>
{CREATE_A_META_ENGINE_BUTTON_LABEL}
</EuiButtonTo>
}
/>
}
onChange={handlePageChange(onMetaEnginesPagination)}
/>
</EuiPageContentBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ describe('EnginesTable', () => {
});
});

describe('noItemsMessage', () => {
it('passes the noItemsMessage prop', () => {
const wrapper = mountWithIntl(<EnginesTable {...props} noItemsMessage={'No items.'} />);
expect(wrapper.find(EuiBasicTable).prop('noItemsMessage')).toEqual('No items.');
});
});

describe('language field', () => {
it('renders language when available', () => {
const wrapper = mountWithIntl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React from 'react';
import React, { ReactNode } from 'react';

import { useActions } from 'kea';

Expand All @@ -24,6 +24,7 @@ import { EngineDetails } from '../engine/types';
interface EnginesTableProps {
items: EngineDetails[];
loading: boolean;
noItemsMessage?: ReactNode;
pagination: {
pageIndex: number;
pageSize: number;
Expand All @@ -36,6 +37,7 @@ interface EnginesTableProps {
export const EnginesTable: React.FC<EnginesTableProps> = ({
items,
loading,
noItemsMessage,
pagination,
onChange,
}) => {
Expand Down Expand Up @@ -148,6 +150,7 @@ export const EnginesTable: React.FC<EnginesTableProps> = ({
loading={loading}
pagination={pagination}
onChange={onChange}
noItemsMessage={noItemsMessage}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* 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 { EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';

import { DOCS_PREFIX } from '../../routes';

export const DEFAULT_LANGUAGE = 'Universal';

export const META_ENGINE_CREATION_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.title',
{
defaultMessage: 'Create a meta engine',
}
);

export const META_ENGINE_CREATION_FORM_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.title',
{
defaultMessage: 'Name your meta engine',
}
);

export const META_ENGINE_CREATION_FORM_SUBMIT_BUTTON_LABEL = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.submitButton.buttonLabel',
{
defaultMessage: 'Create meta engine',
}
);

export const META_ENGINE_CREATION_FORM_META_ENGINE_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.metaEngineDescription',
{
defaultMessage:
'Meta engines allow you to combine multiple engines into one searchable engine.',
}
);

export const META_ENGINE_CREATION_FORM_DOCUMENTATION_LINK = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.documentationLink',
{
defaultMessage: 'Read the documentation',
}
);

export const META_ENGINE_CREATION_FORM_DOCUMENTATION_DESCRIPTION = (
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.metaEngineCreation.form.documentationDescription"
defaultMessage="{documentationLink} for information about how to get started."
values={{
documentationLink: (
<EuiLink href={`${DOCS_PREFIX}/meta-engines-guide.html`} target="_blank">
{META_ENGINE_CREATION_FORM_DOCUMENTATION_LINK}
</EuiLink>
),
}}
/>
);

export const META_ENGINE_CREATION_FORM_ENGINE_NAME_LABEL = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.engineName.label',
{
defaultMessage: 'Meta engine name',
}
);

export const ALLOWED_CHARS_NOTE = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.engineName.allowedCharactersHelpText',
{
defaultMessage: 'Meta engine names can only contain lowercase letters, numbers, and hyphens',
}
);

export const SANITIZED_NAME_NOTE = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.engineName.sanitizedNameHelpText',
{
defaultMessage: 'Your meta engine will be named',
}
);

export const META_ENGINE_CREATION_FORM_ENGINE_NAME_PLACEHOLDER = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.engineName.placeholder',
{
defaultMessage: 'i.e., my-meta-engine',
}
);

export const META_ENGINE_CREATION_FORM_ENGINE_SOURCE_ENGINES_LABEL = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.sourceEngines.label',
{
defaultMessage: 'Add source engines to this meta engine',
}
);

export const META_ENGINE_CREATION_FORM_MAX_SOURCE_ENGINES_WARNING_TITLE = (
maxEnginesPerMetaEngine: number
) =>
Comment on lines +103 to +105
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[follow up from above comment] ah, I guess the main downside is slightly extra cruft for values/variables compared to inline copy - hmmmm, I'll percolate on this more

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like these functions in a separate file more than inline fwiw

i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.form.sourceEngines.maxSourceEnginesWarningTitle',
{
defaultMessage: 'Meta engines have a limit of {maxEnginesPerMetaEngine} source engines',
values: { maxEnginesPerMetaEngine },
}
);

export const META_ENGINE_CREATION_SUCCESS_MESSAGE = i18n.translate(
'xpack.enterpriseSearch.appSearch.metaEngineCreation.successMessage',
{
defaultMessage: 'Successfully created meta engine.',
}
);
Copy link
Contributor

@cee-chen cee-chen Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading this, I'm starting to become more of a fan of putting all our i18n copy into constants.tsx files rather than inline - it's nice in that it focuses i18n IDs as well as makes it easier to skim a single file for grammar/sentence casing it. @JasonStoltz any thoughts here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a i18n.ts/i18n.tsx file? They're still "constants" but I think if we intend to keep all of these out of the component .tsx files I'd rather give them a unique file (and use constants for stuff like DEFAULT_META)

Loading