[App Search] Convert Result Settings & Relevance Tuning pages to new page template (#102845)

* Convert Result Settings page to new page template

+ remove wrapper around empty state (auto handled by new page template)
+ update tests w/ new test helpers

* Convert Relevance Tuning page to new page template

- Remove old relevance_tuning_layout (which handled breadcrumbs, page header, flash messages, and callouts) in favor of simply using the new templtate + callouts (yay DRYing)

- Remove panel wrapper around empty state (handled by new page template)

* Update router

* [Polish] Spacing & icon polish from Davey

see https://github.com/elastic/kibana/pull/101958/files
This commit is contained in:
Constance 2021-06-22 14:43:54 -07:00 committed by GitHub
parent b161bf03be
commit e580d5a1e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 226 additions and 350 deletions

View file

@ -114,6 +114,16 @@ export const EngineRouter: React.FC = () => {
<SchemaRouter />
</Route>
)}
{canManageEngineRelevanceTuning && (
<Route path={ENGINE_RELEVANCE_TUNING_PATH}>
<RelevanceTuning />
</Route>
)}
{canManageEngineResultSettings && (
<Route path={ENGINE_RESULT_SETTINGS_PATH}>
<ResultSettings />
</Route>
)}
{canManageEngineSearchUi && (
<Route path={ENGINE_SEARCH_UI_PATH}>
<SearchUI />
@ -131,21 +141,11 @@ export const EngineRouter: React.FC = () => {
<CurationsRouter />
</Route>
)}
{canManageEngineRelevanceTuning && (
<Route path={ENGINE_RELEVANCE_TUNING_PATH}>
<RelevanceTuning />
</Route>
)}
{canManageEngineSynonyms && (
<Route path={ENGINE_SYNONYMS_PATH}>
<Synonyms />
</Route>
)}
{canManageEngineResultSettings && (
<Route path={ENGINE_RESULT_SETTINGS_PATH}>
<ResultSettings />
</Route>
)}
{canViewMetaEngineSourceEngines && (
<Route path={META_ENGINE_SOURCE_ENGINES_PATH}>
<SourceEngines />

View file

@ -7,42 +7,40 @@
import React from 'react';
import { EuiButton, EuiEmptyPrompt, EuiPanel } from '@elastic/eui';
import { EuiButton, EuiEmptyPrompt } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DOCS_PREFIX } from '../../../routes';
export const EmptyState: React.FC = () => (
<EuiPanel color="subdued">
<EuiEmptyPrompt
iconType="wrench"
title={
<h2>
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.relevanceTuning.empty.title', {
defaultMessage: 'Add documents to tune relevance',
})}
</h2>
<EuiEmptyPrompt
iconType="wrench"
title={
<h2>
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.relevanceTuning.empty.title', {
defaultMessage: 'Add documents to tune relevance',
})}
</h2>
}
body={i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.empty.description',
{
defaultMessage:
'A schema will be automatically created for you after you index some documents.',
}
body={i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.empty.description',
{
defaultMessage:
'A schema will be automatically created for you after you index some documents.',
}
)}
actions={
<EuiButton
size="s"
target="_blank"
iconType="popout"
href={`${DOCS_PREFIX}/relevance-tuning-guide.html`}
>
{i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.empty.buttonLabel',
{ defaultMessage: 'Read the relevance tuning guide' }
)}
</EuiButton>
}
/>
</EuiPanel>
)}
actions={
<EuiButton
size="s"
target="_blank"
iconType="popout"
href={`${DOCS_PREFIX}/relevance-tuning-guide.html`}
>
{i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.empty.buttonLabel',
{ defaultMessage: 'Read the relevance tuning guide' }
)}
</EuiButton>
}
/>
);

View file

@ -13,14 +13,14 @@ import React from 'react';
import { shallow } from 'enzyme';
import { EuiEmptyPrompt } from '@elastic/eui';
import { Loading } from '../../../shared/loading';
import { UnsavedChangesPrompt } from '../../../shared/unsaved_changes_prompt';
import { getPageHeaderActions } from '../../../test_helpers';
import { EmptyState } from './components';
import { RelevanceTuning } from './relevance_tuning';
import { RelevanceTuningCallouts } from './relevance_tuning_callouts';
import { RelevanceTuningForm } from './relevance_tuning_form';
import { RelevanceTuningPreview } from './relevance_tuning_preview';
describe('RelevanceTuning', () => {
const values = {
@ -50,9 +50,9 @@ describe('RelevanceTuning', () => {
it('renders', () => {
const wrapper = subject();
expect(wrapper.find(RelevanceTuningCallouts).exists()).toBe(true);
expect(wrapper.find(RelevanceTuningForm).exists()).toBe(true);
expect(wrapper.find(Loading).exists()).toBe(false);
expect(wrapper.find(EmptyState).exists()).toBe(false);
expect(wrapper.find(RelevanceTuningPreview).exists()).toBe(true);
});
it('initializes relevance tuning data', () => {
@ -60,28 +60,6 @@ describe('RelevanceTuning', () => {
expect(actions.initializeRelevanceTuning).toHaveBeenCalled();
});
it('will render an empty message when the engine has no schema', () => {
setMockValues({
...values,
engineHasSchemaFields: false,
});
const wrapper = subject();
expect(wrapper.find(EmptyState).dive().find(EuiEmptyPrompt).exists()).toBe(true);
expect(wrapper.find(Loading).exists()).toBe(false);
expect(wrapper.find(RelevanceTuningForm).exists()).toBe(false);
});
it('will show a loading message if data is loading', () => {
setMockValues({
...values,
dataLoading: true,
});
const wrapper = subject();
expect(wrapper.find(Loading).exists()).toBe(true);
expect(wrapper.find(EmptyState).exists()).toBe(false);
expect(wrapper.find(RelevanceTuningForm).exists()).toBe(false);
});
it('will prevent user from leaving the page if there are unsaved changes', () => {
setMockValues({
...values,
@ -89,4 +67,31 @@ describe('RelevanceTuning', () => {
});
expect(subject().find(UnsavedChangesPrompt).prop('hasUnsavedChanges')).toBe(true);
});
describe('header actions', () => {
it('renders a Save button that will save the current changes', () => {
const buttons = getPageHeaderActions(subject());
expect(buttons.children().length).toBe(2);
const saveButton = buttons.find('[data-test-subj="SaveRelevanceTuning"]');
saveButton.simulate('click');
expect(actions.updateSearchSettings).toHaveBeenCalled();
});
it('renders a Reset button that will remove all weights and boosts', () => {
const buttons = getPageHeaderActions(subject());
expect(buttons.children().length).toBe(2);
const resetButton = buttons.find('[data-test-subj="ResetRelevanceTuning"]');
resetButton.simulate('click');
expect(actions.resetSearchSettings).toHaveBeenCalled();
});
it('will not render buttons if the engine has no schema', () => {
setMockValues({
...values,
engineHasSchemaFields: false,
});
const buttons = getPageHeaderActions(subject());
expect(buttons.children().length).toBe(0);
});
});
});

View file

@ -9,43 +9,77 @@ import React, { useEffect } from 'react';
import { useActions, useValues } from 'kea';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Loading } from '../../../shared/loading';
import { SAVE_BUTTON_LABEL } from '../../../shared/constants';
import { UnsavedChangesPrompt } from '../../../shared/unsaved_changes_prompt';
import { RESTORE_DEFAULTS_BUTTON_LABEL } from '../../constants';
import { getEngineBreadcrumbs } from '../engine';
import { AppSearchPageTemplate } from '../layout';
import { EmptyState } from './components';
import { RELEVANCE_TUNING_TITLE } from './constants';
import { RelevanceTuningCallouts } from './relevance_tuning_callouts';
import { RelevanceTuningForm } from './relevance_tuning_form';
import { RelevanceTuningLayout } from './relevance_tuning_layout';
import { RelevanceTuningPreview } from './relevance_tuning_preview';
import { RelevanceTuningLogic } from '.';
export const RelevanceTuning: React.FC = () => {
const { dataLoading, engineHasSchemaFields, unsavedChanges } = useValues(RelevanceTuningLogic);
const { initializeRelevanceTuning } = useActions(RelevanceTuningLogic);
const { initializeRelevanceTuning, resetSearchSettings, updateSearchSettings } = useActions(
RelevanceTuningLogic
);
useEffect(() => {
initializeRelevanceTuning();
}, []);
if (dataLoading) return <Loading />;
return (
<RelevanceTuningLayout>
<AppSearchPageTemplate
pageChrome={getEngineBreadcrumbs([RELEVANCE_TUNING_TITLE])}
pageHeader={{
pageTitle: RELEVANCE_TUNING_TITLE,
description: i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.description',
{ defaultMessage: 'Set field weights and boosts.' }
),
rightSideItems: engineHasSchemaFields
? [
<EuiButton
data-test-subj="SaveRelevanceTuning"
color="primary"
fill
onClick={updateSearchSettings}
>
{SAVE_BUTTON_LABEL}
</EuiButton>,
<EuiButton
data-test-subj="ResetRelevanceTuning"
color="danger"
onClick={resetSearchSettings}
>
{RESTORE_DEFAULTS_BUTTON_LABEL}
</EuiButton>,
]
: [],
}}
isLoading={dataLoading}
isEmptyState={!engineHasSchemaFields}
emptyState={<EmptyState />}
>
<UnsavedChangesPrompt hasUnsavedChanges={unsavedChanges} />
{engineHasSchemaFields ? (
<EuiFlexGroup alignItems="flexStart">
<EuiFlexItem grow={3}>
<RelevanceTuningForm />
</EuiFlexItem>
<EuiFlexItem grow={4}>
<RelevanceTuningPreview />
</EuiFlexItem>
</EuiFlexGroup>
) : (
<EmptyState />
)}
</RelevanceTuningLayout>
<RelevanceTuningCallouts />
<EuiFlexGroup alignItems="flexStart">
<EuiFlexItem grow={3}>
<RelevanceTuningForm />
</EuiFlexItem>
<EuiFlexItem grow={4}>
<RelevanceTuningPreview />
</EuiFlexItem>
</EuiFlexGroup>
</AppSearchPageTemplate>
);
};

View file

@ -42,7 +42,7 @@ export const RelevanceTuningForm: React.FC = () => {
return (
<section className="relevanceTuningForm">
<form>
<EuiSpacer size="s" />
<EuiSpacer size="m" />
<EuiTitle size="m">
<h2>
{i18n.translate(

View file

@ -1,64 +0,0 @@
/*
* 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 { setMockActions, setMockValues } from '../../../__mocks__/kea_logic';
import '../../__mocks__/engine_logic.mock';
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { EuiPageHeader } from '@elastic/eui';
import { RelevanceTuningLayout } from './relevance_tuning_layout';
describe('RelevanceTuningLayout', () => {
const values = {
engineHasSchemaFields: true,
schemaFieldsWithConflicts: [],
};
const actions = {
updateSearchSettings: jest.fn(),
resetSearchSettings: jest.fn(),
};
beforeEach(() => {
jest.clearAllMocks();
setMockValues(values);
setMockActions(actions);
});
const subject = () => shallow(<RelevanceTuningLayout />);
const findButtons = (wrapper: ShallowWrapper) =>
wrapper.find(EuiPageHeader).prop('rightSideItems') as React.ReactElement[];
it('renders a Save button that will save the current changes', () => {
const buttons = findButtons(subject());
expect(buttons.length).toBe(2);
const saveButton = shallow(buttons[0]);
saveButton.simulate('click');
expect(actions.updateSearchSettings).toHaveBeenCalled();
});
it('renders a Reset button that will remove all weights and boosts', () => {
const buttons = findButtons(subject());
expect(buttons.length).toBe(2);
const resetButton = shallow(buttons[1]);
resetButton.simulate('click');
expect(actions.resetSearchSettings).toHaveBeenCalled();
});
it('will not render buttons if the engine has no schema', () => {
setMockValues({
...values,
engineHasSchemaFields: false,
});
const buttons = findButtons(subject());
expect(buttons.length).toBe(0);
});
});

View file

@ -1,73 +0,0 @@
/*
* 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 { useActions, useValues } from 'kea';
import { EuiPageHeader, EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { SAVE_BUTTON_LABEL } from '../../../shared/constants';
import { FlashMessages } from '../../../shared/flash_messages';
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { RESTORE_DEFAULTS_BUTTON_LABEL } from '../../constants';
import { getEngineBreadcrumbs } from '../engine';
import { RELEVANCE_TUNING_TITLE } from './constants';
import { RelevanceTuningCallouts } from './relevance_tuning_callouts';
import { RelevanceTuningLogic } from './relevance_tuning_logic';
export const RelevanceTuningLayout: React.FC = ({ children }) => {
const { resetSearchSettings, updateSearchSettings } = useActions(RelevanceTuningLogic);
const { engineHasSchemaFields } = useValues(RelevanceTuningLogic);
const pageHeader = () => (
<EuiPageHeader
className="relevanceTuningHeader"
pageTitle={RELEVANCE_TUNING_TITLE}
description={i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.description',
{
defaultMessage: 'Set field weights and boosts.',
}
)}
rightSideItems={
engineHasSchemaFields
? [
<EuiButton
data-test-subj="SaveRelevanceTuning"
color="primary"
fill
onClick={updateSearchSettings}
>
{SAVE_BUTTON_LABEL}
</EuiButton>,
<EuiButton
data-test-subj="ResetRelevanceTuning"
color="danger"
onClick={resetSearchSettings}
>
{RESTORE_DEFAULTS_BUTTON_LABEL}
</EuiButton>,
]
: []
}
/>
);
return (
<>
<SetPageChrome trail={getEngineBreadcrumbs([RELEVANCE_TUNING_TITLE])} />
{pageHeader()}
<FlashMessages />
<RelevanceTuningCallouts />
{children}
</>
);
};

View file

@ -21,6 +21,7 @@ import { RelevanceTuningLogic } from '.';
const emptyCallout = (
<EuiEmptyPrompt
data-test-subj="EmptyQueryPrompt"
iconType="glasses"
body={i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.preview.enterQueryMessage',
{

View file

@ -7,42 +7,40 @@
import React from 'react';
import { EuiButton, EuiEmptyPrompt, EuiPanel } from '@elastic/eui';
import { EuiButton, EuiEmptyPrompt } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DOCS_PREFIX } from '../../../routes';
export const EmptyState: React.FC = () => (
<EuiPanel color="subdued">
<EuiEmptyPrompt
iconType="gear"
title={
<h2>
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.resultSettings.empty.title', {
defaultMessage: 'Add documents to adjust settings',
})}
</h2>
<EuiEmptyPrompt
iconType="gear"
title={
<h2>
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.resultSettings.empty.title', {
defaultMessage: 'Add documents to adjust settings',
})}
</h2>
}
body={i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.empty.description',
{
defaultMessage:
'A schema will be automatically created for you after you index some documents.',
}
body={i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.empty.description',
{
defaultMessage:
'A schema will be automatically created for you after you index some documents.',
}
)}
actions={
<EuiButton
size="s"
target="_blank"
iconType="popout"
href={`${DOCS_PREFIX}/result-settings-guide.html`}
>
{i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.empty.buttonLabel',
{ defaultMessage: 'Read the result settings guide' }
)}
</EuiButton>
}
/>
</EuiPanel>
)}
actions={
<EuiButton
size="s"
target="_blank"
iconType="popout"
href={`${DOCS_PREFIX}/result-settings-guide.html`}
>
{i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.empty.buttonLabel',
{ defaultMessage: 'Read the result settings guide' }
)}
</EuiButton>
}
/>
);

View file

@ -13,11 +13,9 @@ import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { EuiPageHeader } from '@elastic/eui';
import { UnsavedChangesPrompt } from '../../../shared/unsaved_changes_prompt';
import { getPageHeaderActions } from '../../../test_helpers';
import { EmptyState } from './components';
import { ResultSettings } from './result_settings';
import { ResultSettingsTable } from './result_settings_table';
import { SampleResponse } from './sample_response';
@ -46,8 +44,6 @@ describe('ResultSettings', () => {
});
const subject = () => shallow(<ResultSettings />);
const findButtons = (wrapper: ShallowWrapper) =>
wrapper.find(EuiPageHeader).prop('rightSideItems') as React.ReactElement[];
it('renders', () => {
const wrapper = subject();
@ -60,19 +56,10 @@ describe('ResultSettings', () => {
expect(actions.initializeResultSettingsData).toHaveBeenCalled();
});
it('renders a loading screen if data has not loaded yet', () => {
setMockValues({
dataLoading: true,
});
const wrapper = subject();
expect(wrapper.find(ResultSettingsTable).exists()).toBe(false);
expect(wrapper.find(SampleResponse).exists()).toBe(false);
});
it('renders a "save" button that will save the current changes', () => {
const buttons = findButtons(subject());
expect(buttons.length).toBe(3);
const saveButton = shallow(buttons[0]);
const buttons = getPageHeaderActions(subject());
expect(buttons.children().length).toBe(3);
const saveButton = buttons.find('[data-test-subj="SaveResultSettings"]');
saveButton.simulate('click');
expect(actions.saveResultSettings).toHaveBeenCalled();
});
@ -82,8 +69,8 @@ describe('ResultSettings', () => {
...values,
stagedUpdates: false,
});
const buttons = findButtons(subject());
const saveButton = shallow(buttons[0]);
const buttons = getPageHeaderActions(subject());
const saveButton = buttons.find('[data-test-subj="SaveResultSettings"]');
expect(saveButton.prop('disabled')).toBe(true);
});
@ -93,15 +80,15 @@ describe('ResultSettings', () => {
stagedUpdates: true,
resultFieldsEmpty: true,
});
const buttons = findButtons(subject());
const saveButton = shallow(buttons[0]);
const buttons = getPageHeaderActions(subject());
const saveButton = buttons.find('[data-test-subj="SaveResultSettings"]');
expect(saveButton.prop('disabled')).toBe(true);
});
it('renders a "restore defaults" button that will reset all values to their defaults', () => {
const buttons = findButtons(subject());
expect(buttons.length).toBe(3);
const resetButton = shallow(buttons[1]);
const buttons = getPageHeaderActions(subject());
expect(buttons.children().length).toBe(3);
const resetButton = buttons.find('[data-test-subj="ResetResultSettings"]');
resetButton.simulate('click');
expect(actions.confirmResetAllFields).toHaveBeenCalled();
});
@ -111,15 +98,15 @@ describe('ResultSettings', () => {
...values,
resultFieldsAtDefaultSettings: true,
});
const buttons = findButtons(subject());
const resetButton = shallow(buttons[1]);
const buttons = getPageHeaderActions(subject());
const resetButton = buttons.find('[data-test-subj="ResetResultSettings"]');
expect(resetButton.prop('disabled')).toBe(true);
});
it('renders a "clear" button that will remove all selected options', () => {
const buttons = findButtons(subject());
expect(buttons.length).toBe(3);
const clearButton = shallow(buttons[2]);
const buttons = getPageHeaderActions(subject());
expect(buttons.children().length).toBe(3);
const clearButton = buttons.find('[data-test-subj="ClearResultSettings"]');
clearButton.simulate('click');
expect(actions.clearAllFields).toHaveBeenCalled();
});
@ -143,17 +130,12 @@ describe('ResultSettings', () => {
});
it('will not render action buttons', () => {
const buttons = findButtons(wrapper);
expect(buttons.length).toBe(0);
});
it('will not render the main page content', () => {
expect(wrapper.find(ResultSettingsTable).exists()).toBe(false);
expect(wrapper.find(SampleResponse).exists()).toBe(false);
const buttons = getPageHeaderActions(wrapper);
expect(buttons.children().length).toBe(0);
});
it('will render an empty state', () => {
expect(wrapper.find(EmptyState).exists()).toBe(true);
expect(wrapper.prop('isEmptyState')).toBe(true);
});
});
});

View file

@ -9,17 +9,15 @@ import React, { useEffect } from 'react';
import { useActions, useValues } from 'kea';
import { EuiPageHeader, EuiFlexGroup, EuiFlexItem, EuiButton, EuiButtonEmpty } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiButtonEmpty } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { SAVE_BUTTON_LABEL } from '../../../shared/constants';
import { FlashMessages } from '../../../shared/flash_messages';
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { Loading } from '../../../shared/loading';
import { UnsavedChangesPrompt } from '../../../shared/unsaved_changes_prompt';
import { RESTORE_DEFAULTS_BUTTON_LABEL } from '../../constants';
import { getEngineBreadcrumbs } from '../engine';
import { AppSearchPageTemplate } from '../layout';
import { EmptyState } from './components';
import { RESULT_SETTINGS_TITLE } from './constants';
@ -57,59 +55,56 @@ export const ResultSettings: React.FC = () => {
initializeResultSettingsData();
}, []);
if (dataLoading) return <Loading />;
const hasSchema = Object.keys(schema).length > 0;
return (
<>
<SetPageChrome trail={getEngineBreadcrumbs([RESULT_SETTINGS_TITLE])} />
<UnsavedChangesPrompt hasUnsavedChanges={stagedUpdates} messageText={UNSAVED_MESSAGE} />
<EuiPageHeader
pageTitle={RESULT_SETTINGS_TITLE}
description={i18n.translate(
<AppSearchPageTemplate
pageChrome={getEngineBreadcrumbs([RESULT_SETTINGS_TITLE])}
pageHeader={{
pageTitle: RESULT_SETTINGS_TITLE,
description: i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.pageDescription',
{ defaultMessage: 'Enrich search results and select which fields will appear.' }
)}
rightSideItems={
hasSchema
? [
<EuiButton
data-test-subj="SaveResultSettings"
color="primary"
fill
onClick={saveResultSettings}
disabled={resultFieldsEmpty || !stagedUpdates}
>
{SAVE_BUTTON_LABEL}
</EuiButton>,
<EuiButton
data-test-subj="ResetResultSettings"
color="danger"
onClick={confirmResetAllFields}
disabled={resultFieldsAtDefaultSettings}
>
{RESTORE_DEFAULTS_BUTTON_LABEL}
</EuiButton>,
<EuiButtonEmpty data-test-subj="ClearResultSettings" onClick={clearAllFields}>
{CLEAR_BUTTON_LABEL}
</EuiButtonEmpty>,
]
: []
}
/>
<FlashMessages />
{hasSchema ? (
<EuiFlexGroup alignItems="flexStart">
<EuiFlexItem grow={5}>
<ResultSettingsTable />
</EuiFlexItem>
<EuiFlexItem grow={3}>
<SampleResponse />
</EuiFlexItem>
</EuiFlexGroup>
) : (
<EmptyState />
)}
</>
),
rightSideItems: hasSchema
? [
<EuiButton
data-test-subj="SaveResultSettings"
color="primary"
fill
onClick={saveResultSettings}
disabled={resultFieldsEmpty || !stagedUpdates}
>
{SAVE_BUTTON_LABEL}
</EuiButton>,
<EuiButton
data-test-subj="ResetResultSettings"
color="danger"
onClick={confirmResetAllFields}
disabled={resultFieldsAtDefaultSettings}
>
{RESTORE_DEFAULTS_BUTTON_LABEL}
</EuiButton>,
<EuiButtonEmpty data-test-subj="ClearResultSettings" onClick={clearAllFields}>
{CLEAR_BUTTON_LABEL}
</EuiButtonEmpty>,
]
: [],
}}
isLoading={dataLoading}
isEmptyState={!hasSchema}
emptyState={<EmptyState />}
>
<UnsavedChangesPrompt hasUnsavedChanges={stagedUpdates} messageText={UNSAVED_MESSAGE} />
<EuiFlexGroup alignItems="flexStart">
<EuiFlexItem grow={5}>
<ResultSettingsTable />
</EuiFlexItem>
<EuiFlexItem grow={3}>
<SampleResponse />
</EuiFlexItem>
</EuiFlexGroup>
</AppSearchPageTemplate>
);
};