mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[App Search] Automated Curations UX improvements (#115773)
This commit is contained in:
parent
e3f4107d90
commit
abd5e9ffa8
12 changed files with 117 additions and 71 deletions
|
@ -49,6 +49,7 @@ describe('AutomatedCuration', () => {
|
|||
|
||||
const actions = {
|
||||
convertToManual: jest.fn(),
|
||||
onSelectPageTab: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -62,48 +63,41 @@ describe('AutomatedCuration', () => {
|
|||
const wrapper = shallow(<AutomatedCuration />);
|
||||
|
||||
expect(wrapper.is(AppSearchPageTemplate));
|
||||
expect(wrapper.find(PromotedDocuments)).toHaveLength(1);
|
||||
expect(wrapper.find(OrganicDocuments)).toHaveLength(1);
|
||||
expect(wrapper.find(History)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('includes tabs', () => {
|
||||
it('includes set of tabs in the page header', () => {
|
||||
const wrapper = shallow(<AutomatedCuration />);
|
||||
let tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
|
||||
expect(tabs).toHaveLength(3);
|
||||
const tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
|
||||
expect(tabs.at(0).prop('isSelected')).toBe(true);
|
||||
tabs.at(0).simulate('click');
|
||||
expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(1, 'promoted');
|
||||
|
||||
expect(tabs.at(1).prop('onClick')).toBeUndefined();
|
||||
expect(tabs.at(1).prop('isSelected')).toBe(false);
|
||||
expect(tabs.at(1).prop('disabled')).toBe(true);
|
||||
|
||||
expect(tabs.at(2).prop('isSelected')).toBe(false);
|
||||
|
||||
// Clicking on the History tab shows the history view
|
||||
tabs.at(2).simulate('click');
|
||||
expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(2, 'history');
|
||||
});
|
||||
|
||||
tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
it('renders promoted and organic documents when the promoted tab is selected', () => {
|
||||
setMockValues({ ...values, selectedPageTab: 'promoted' });
|
||||
const wrapper = shallow(<AutomatedCuration />);
|
||||
const tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
|
||||
expect(tabs.at(0).prop('isSelected')).toBe(false);
|
||||
expect(tabs.at(2).prop('isSelected')).toBe(true);
|
||||
|
||||
expect(wrapper.find(PromotedDocuments)).toHaveLength(0);
|
||||
expect(wrapper.find(OrganicDocuments)).toHaveLength(0);
|
||||
expect(wrapper.find(History)).toHaveLength(1);
|
||||
|
||||
// Clicking back to the Promoted tab shows promoted documents
|
||||
tabs.at(0).simulate('click');
|
||||
|
||||
tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
|
||||
expect(tabs.at(0).prop('isSelected')).toBe(true);
|
||||
expect(tabs.at(2).prop('isSelected')).toBe(false);
|
||||
expect(tabs.at(0).prop('isSelected')).toEqual(true);
|
||||
|
||||
expect(wrapper.find(PromotedDocuments)).toHaveLength(1);
|
||||
expect(wrapper.find(OrganicDocuments)).toHaveLength(1);
|
||||
expect(wrapper.find(History)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('renders curation history when the history tab is selected', () => {
|
||||
setMockValues({ ...values, selectedPageTab: 'history' });
|
||||
const wrapper = shallow(<AutomatedCuration />);
|
||||
const tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
|
||||
expect(tabs.at(2).prop('isSelected')).toEqual(true);
|
||||
|
||||
expect(wrapper.find(History)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('initializes CurationLogic with a curationId prop from URL param', () => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { useValues, useActions } from 'kea';
|
||||
|
@ -31,23 +31,19 @@ import { DeleteCurationButton } from './delete_curation_button';
|
|||
import { PromotedDocuments, OrganicDocuments } from './documents';
|
||||
import { History } from './history';
|
||||
|
||||
const PROMOTED = 'promoted';
|
||||
const HISTORY = 'history';
|
||||
|
||||
export const AutomatedCuration: React.FC = () => {
|
||||
const { curationId } = useParams<{ curationId: string }>();
|
||||
const logic = CurationLogic({ curationId });
|
||||
const { convertToManual } = useActions(logic);
|
||||
const { activeQuery, dataLoading, queries, curation } = useValues(logic);
|
||||
const { convertToManual, onSelectPageTab } = useActions(logic);
|
||||
const { activeQuery, dataLoading, queries, curation, selectedPageTab } = useValues(logic);
|
||||
const { engineName } = useValues(EngineLogic);
|
||||
const [selectedPageTab, setSelectedPageTab] = useState(PROMOTED);
|
||||
|
||||
const pageTabs = [
|
||||
{
|
||||
label: PROMOTED_DOCUMENTS_TITLE,
|
||||
append: <EuiBadge>{curation.promoted.length}</EuiBadge>,
|
||||
isSelected: selectedPageTab === PROMOTED,
|
||||
onClick: () => setSelectedPageTab(PROMOTED),
|
||||
isSelected: selectedPageTab === 'promoted',
|
||||
onClick: () => onSelectPageTab('promoted'),
|
||||
},
|
||||
{
|
||||
label: HIDDEN_DOCUMENTS_TITLE,
|
||||
|
@ -62,8 +58,8 @@ export const AutomatedCuration: React.FC = () => {
|
|||
defaultMessage: 'History',
|
||||
}
|
||||
),
|
||||
isSelected: selectedPageTab === HISTORY,
|
||||
onClick: () => setSelectedPageTab(HISTORY),
|
||||
isSelected: selectedPageTab === 'history',
|
||||
onClick: () => onSelectPageTab('history'),
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -102,9 +98,9 @@ export const AutomatedCuration: React.FC = () => {
|
|||
}}
|
||||
isLoading={dataLoading}
|
||||
>
|
||||
{selectedPageTab === PROMOTED && <PromotedDocuments />}
|
||||
{selectedPageTab === PROMOTED && <OrganicDocuments />}
|
||||
{selectedPageTab === HISTORY && (
|
||||
{selectedPageTab === 'promoted' && <PromotedDocuments />}
|
||||
{selectedPageTab === 'promoted' && <OrganicDocuments />}
|
||||
{selectedPageTab === 'history' && (
|
||||
<History query={curation.queries[0]} engineName={engineName} />
|
||||
)}
|
||||
</AppSearchPageTemplate>
|
||||
|
|
|
@ -250,7 +250,7 @@ describe('CurationLogic', () => {
|
|||
});
|
||||
|
||||
describe('onSelectPageTab', () => {
|
||||
it('should set the selected page tab', () => {
|
||||
it('should set the selected page tab and clears flash messages', () => {
|
||||
mount({
|
||||
selectedPageTab: 'promoted',
|
||||
});
|
||||
|
@ -261,6 +261,7 @@ describe('CurationLogic', () => {
|
|||
...DEFAULT_VALUES,
|
||||
selectedPageTab: 'hidden',
|
||||
});
|
||||
expect(clearFlashMessages).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@ import { DELETE_SUCCESS_MESSAGE } from '../constants';
|
|||
import { Curation } from '../types';
|
||||
import { addDocument, removeDocument } from '../utils';
|
||||
|
||||
type CurationPageTabs = 'promoted' | 'hidden';
|
||||
type CurationPageTabs = 'promoted' | 'history' | 'hidden';
|
||||
|
||||
interface CurationValues {
|
||||
dataLoading: boolean;
|
||||
|
@ -271,6 +271,9 @@ export const CurationLogic = kea<MakeLogicType<CurationValues, CurationActions,
|
|||
|
||||
actions.updateCuration();
|
||||
},
|
||||
onSelectPageTab: () => {
|
||||
clearFlashMessages();
|
||||
},
|
||||
setActiveQuery: () => actions.updateCuration(),
|
||||
setPromotedIds: () => actions.updateCuration(),
|
||||
addPromotedId: () => actions.updateCuration(),
|
||||
|
|
|
@ -22,7 +22,7 @@ jest.mock('./curation_logic', () => ({ CurationLogic: jest.fn() }));
|
|||
import { CurationLogic } from './curation_logic';
|
||||
|
||||
import { DeleteCurationButton } from './delete_curation_button';
|
||||
import { PromotedDocuments, HiddenDocuments } from './documents';
|
||||
import { PromotedDocuments, HiddenDocuments, OrganicDocuments } from './documents';
|
||||
import { ManualCuration } from './manual_curation';
|
||||
import { ActiveQuerySelect, ManageQueriesModal } from './queries';
|
||||
import { AddResultFlyout } from './results';
|
||||
|
@ -74,13 +74,13 @@ describe('ManualCuration', () => {
|
|||
expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(2, 'hidden');
|
||||
});
|
||||
|
||||
it('contains a suggested documents callout when the selectedPageTab is ', () => {
|
||||
it('contains a suggested documents callout', () => {
|
||||
const wrapper = shallow(<ManualCuration />);
|
||||
|
||||
expect(wrapper.find(SuggestedDocumentsCallout)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders promoted documents when that tab is selected', () => {
|
||||
it('renders promoted and organic documents when the promoted tab is selected', () => {
|
||||
setMockValues({ ...values, selectedPageTab: 'promoted' });
|
||||
const wrapper = shallow(<ManualCuration />);
|
||||
const tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
|
@ -88,9 +88,10 @@ describe('ManualCuration', () => {
|
|||
expect(tabs.at(0).prop('isSelected')).toEqual(true);
|
||||
|
||||
expect(wrapper.find(PromotedDocuments)).toHaveLength(1);
|
||||
expect(wrapper.find(OrganicDocuments)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders hidden documents when that tab is selected', () => {
|
||||
it('renders hidden documents when the hidden tab is selected', () => {
|
||||
setMockValues({ ...values, selectedPageTab: 'hidden' });
|
||||
const wrapper = shallow(<ManualCuration />);
|
||||
const tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
|
|
|
@ -26,10 +26,10 @@ import { SuggestedDocumentsCallout } from './suggested_documents_callout';
|
|||
|
||||
export const ManualCuration: React.FC = () => {
|
||||
const { curationId } = useParams() as { curationId: string };
|
||||
const { onSelectPageTab } = useActions(CurationLogic({ curationId }));
|
||||
const { dataLoading, queries, selectedPageTab, curation } = useValues(
|
||||
CurationLogic({ curationId })
|
||||
);
|
||||
const logic = CurationLogic({ curationId });
|
||||
const { onSelectPageTab } = useActions(logic);
|
||||
const { dataLoading, queries, selectedPageTab, curation } = useValues(logic);
|
||||
|
||||
const { isFlyoutOpen } = useValues(AddResultLogic);
|
||||
|
||||
const pageTabs = [
|
||||
|
|
|
@ -92,7 +92,7 @@ describe('CurationsLogic', () => {
|
|||
});
|
||||
|
||||
describe('onSelectPageTab', () => {
|
||||
it('should set the selected page tab', () => {
|
||||
it('should set the selected page tab and clear flash messages', () => {
|
||||
mount();
|
||||
|
||||
CurationsLogic.actions.onSelectPageTab('settings');
|
||||
|
@ -101,6 +101,7 @@ describe('CurationsLogic', () => {
|
|||
...DEFAULT_VALUES,
|
||||
selectedPageTab: 'settings',
|
||||
});
|
||||
expect(clearFlashMessages).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -126,5 +126,8 @@ export const CurationsLogic = kea<MakeLogicType<CurationsValues, CurationsAction
|
|||
flashAPIErrors(e);
|
||||
}
|
||||
},
|
||||
onSelectPageTab: () => {
|
||||
clearFlashMessages();
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -94,7 +94,7 @@ describe('CurationsRouter', () => {
|
|||
expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('skips loading curation settings when log retention is enabled', () => {
|
||||
it('skips loading curation settings when log retention is disabled', () => {
|
||||
setMockValues({
|
||||
...MOCK_VALUES,
|
||||
logRetention: {
|
||||
|
|
|
@ -137,7 +137,7 @@ export const CurationSuggestionLogic = kea<
|
|||
setQueuedSuccessMessage(
|
||||
i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyAppliedMessage',
|
||||
{ defaultMessage: 'Suggestion was succefully applied.' }
|
||||
{ defaultMessage: 'Suggestion was successfully applied.' }
|
||||
)
|
||||
);
|
||||
if (suggestion!.operation === 'delete') {
|
||||
|
@ -177,7 +177,7 @@ export const CurationSuggestionLogic = kea<
|
|||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyAutomatedMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Suggestion was succefully applied and all future suggestions for the query "{query}" will be automatically applied.',
|
||||
'Suggestion was successfully applied and all future suggestions for the query "{query}" will be automatically applied.',
|
||||
values: { query: suggestion!.query },
|
||||
}
|
||||
)
|
||||
|
@ -208,7 +208,7 @@ export const CurationSuggestionLogic = kea<
|
|||
i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyRejectedMessage',
|
||||
{
|
||||
defaultMessage: 'Suggestion was succefully rejected.',
|
||||
defaultMessage: 'Suggestion was successfully rejected.',
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -230,7 +230,7 @@ export const CurationSuggestionLogic = kea<
|
|||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyDisabledMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Suggestion was succefully rejected and you will no longer receive suggestions for the query "{query}".',
|
||||
'Suggestion was successfully rejected and you will no longer receive suggestions for the query "{query}".',
|
||||
values: { query: suggestion!.query },
|
||||
}
|
||||
)
|
||||
|
|
|
@ -47,6 +47,10 @@ describe('Curations', () => {
|
|||
},
|
||||
},
|
||||
selectedPageTab: 'overview',
|
||||
// CurationsSettingsLogic
|
||||
curationsSettings: {
|
||||
enabled: true,
|
||||
},
|
||||
// LicensingLogic
|
||||
hasPlatinumLicense: true,
|
||||
};
|
||||
|
@ -78,8 +82,6 @@ describe('Curations', () => {
|
|||
|
||||
tabs.at(2).simulate('click');
|
||||
expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(3, 'settings');
|
||||
// The settings tab should NOT have an icon next to it
|
||||
expect(tabs.at(2).prop('prepend')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('renders less tabs when less than platinum license', () => {
|
||||
|
@ -90,8 +92,47 @@ describe('Curations', () => {
|
|||
|
||||
const tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
expect(tabs.length).toBe(2);
|
||||
// The settings tab should have an icon next to it
|
||||
expect(tabs.at(1).prop('prepend')).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('renders a New! badge when less than platinum license', () => {
|
||||
setMockValues({ ...values, hasPlatinumLicense: false });
|
||||
const wrapper = shallow(<Curations />);
|
||||
|
||||
expect(getPageTitle(wrapper)).toEqual('Curated results');
|
||||
|
||||
const tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
expect(tabs.at(1).prop('append')).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('renders a New! badge when suggestions are disabled', () => {
|
||||
setMockValues({
|
||||
...values,
|
||||
curationsSettings: {
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
const wrapper = shallow(<Curations />);
|
||||
|
||||
expect(getPageTitle(wrapper)).toEqual('Curated results');
|
||||
|
||||
const tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
expect(tabs.at(2).prop('append')).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('hides the badge when suggestions are enabled and the user has a platinum license', () => {
|
||||
setMockValues({
|
||||
...values,
|
||||
hasPlatinumLicense: true,
|
||||
curationsSettings: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
const wrapper = shallow(<Curations />);
|
||||
|
||||
expect(getPageTitle(wrapper)).toEqual('Curated results');
|
||||
|
||||
const tabs = getPageHeaderTabs(wrapper).find(EuiTab);
|
||||
expect(tabs.at(2).prop('append')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('renders an overview view', () => {
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { useEffect } from 'react';
|
|||
|
||||
import { useValues, useActions } from 'kea';
|
||||
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { EuiBadge } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { LicensingLogic } from '../../../../shared/licensing';
|
||||
|
@ -31,7 +31,12 @@ export const Curations: React.FC = () => {
|
|||
const { dataLoading: curationsDataLoading, meta, selectedPageTab } = useValues(CurationsLogic);
|
||||
const { loadCurations, onSelectPageTab } = useActions(CurationsLogic);
|
||||
const { hasPlatinumLicense } = useValues(LicensingLogic);
|
||||
const { dataLoading: curationsSettingsDataLoading } = useValues(CurationsSettingsLogic);
|
||||
const {
|
||||
dataLoading: curationsSettingsDataLoading,
|
||||
curationsSettings: { enabled: curationsSettingsEnabled },
|
||||
} = useValues(CurationsSettingsLogic);
|
||||
|
||||
const suggestionsEnabled = hasPlatinumLicense && curationsSettingsEnabled;
|
||||
|
||||
const OVERVIEW_TAB = {
|
||||
label: i18n.translate(
|
||||
|
@ -61,17 +66,18 @@ export const Curations: React.FC = () => {
|
|||
),
|
||||
isSelected: selectedPageTab === 'settings',
|
||||
onClick: () => onSelectPageTab('settings'),
|
||||
append: suggestionsEnabled ? undefined : (
|
||||
<EuiBadge color="success">
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.curations.newBadgeLabel', {
|
||||
defaultMessage: 'New!',
|
||||
})}
|
||||
</EuiBadge>
|
||||
),
|
||||
};
|
||||
|
||||
const pageTabs = hasPlatinumLicense
|
||||
? [OVERVIEW_TAB, HISTORY_TAB, SETTINGS_TAB]
|
||||
: [
|
||||
OVERVIEW_TAB,
|
||||
{
|
||||
...SETTINGS_TAB,
|
||||
prepend: <EuiIcon type="cheer" />,
|
||||
},
|
||||
];
|
||||
: [OVERVIEW_TAB, SETTINGS_TAB];
|
||||
|
||||
useEffect(() => {
|
||||
loadCurations();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue