diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts index a3042f2df7ac7..edd69fa626b1d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts @@ -226,3 +226,26 @@ export const sourceConfigData = { consumerKey: 'elastic_enterprise_search_123', }, }; + +export const exampleResult = { + sourceName: 'source', + searchResultConfig: { + titleField: 'otherTitle', + subtitleField: 'otherSubtitle', + urlField: 'myLink', + color: '#e3e3e3', + descriptionField: 'about', + detailFields: [ + { fieldName: 'cats', label: 'Felines' }, + { fieldName: 'dogs', label: 'Canines' }, + ], + }, + titleFieldHover: false, + urlFieldHover: false, + exampleDocuments: [ + { + myLink: 'http://foo', + otherTitle: 'foo', + }, + ], +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts index ed4946a019bb0..16ca141d91f47 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts @@ -181,7 +181,7 @@ export interface CustomSource { } export interface Result { - [key: string]: string; + [key: string]: string | string[]; } export interface OptionValue { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/custom_source_icon.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/custom_source_icon.test.tsx new file mode 100644 index 0000000000000..9d82ca9c1df19 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/custom_source_icon.test.tsx @@ -0,0 +1,18 @@ +/* + * 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 { CustomSourceIcon } from './custom_source_icon'; + +describe('CustomSourceIcon', () => { + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find('svg')).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.test.tsx new file mode 100644 index 0000000000000..73e40eec7774d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.test.tsx @@ -0,0 +1,157 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { mockKibanaValues } from '../../../../../__mocks__'; + +import { setMockValues, setMockActions } from '../../../../../__mocks__'; +import { unmountHandler } from '../../../../../__mocks__/shallow_useeffect.mock'; + +import { shallow } from 'enzyme'; + +import React from 'react'; + +import { EuiButton, EuiTabbedContent } from '@elastic/eui'; + +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; + +import { Loading } from '../../../../../shared/loading'; +import { ViewContentHeader } from '../../../../components/shared/view_content_header'; + +import { FieldEditorModal } from './field_editor_modal'; + +import { DisplaySettings } from './display_settings'; + +describe('DisplaySettings', () => { + const { navigateToUrl } = mockKibanaValues; + const { exampleDocuments, searchResultConfig } = exampleResult; + const initializeDisplaySettings = jest.fn(); + const setServerData = jest.fn(); + const setColorField = jest.fn(); + + const values = { + isOrganization: true, + dataLoading: false, + sourceId: '123', + addFieldModalVisible: false, + unsavedChanges: false, + exampleDocuments, + searchResultConfig, + }; + + beforeEach(() => { + setMockActions({ + initializeDisplaySettings, + setServerData, + setColorField, + }); + setMockValues({ ...values }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find('form')).toHaveLength(1); + }); + + it('returns loading when loading', () => { + setMockValues({ ...values, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('handles window.onbeforeunload change', () => { + setMockValues({ ...values, unsavedChanges: true }); + shallow(); + + unmountHandler(); + + expect(window.onbeforeunload).toEqual(null); + }); + + it('handles window.onbeforeunload unmount', () => { + setMockValues({ ...values, unsavedChanges: true }); + shallow(); + + expect(window.onbeforeunload!({} as any)).toEqual( + 'Your display settings have not been saved. Are you sure you want to leave?' + ); + }); + + describe('tabbed content', () => { + const tabs = [ + { + id: 'search_results', + name: 'Search Results', + content: <>, + }, + { + id: 'result_detail', + name: 'Result Detail', + content: <>, + }, + ]; + + it('handles first tab click', () => { + const wrapper = shallow(); + const tabsEl = wrapper.find(EuiTabbedContent); + tabsEl.prop('onTabClick')!(tabs[0]); + + expect(navigateToUrl).toHaveBeenCalledWith('/sources/123/display_settings/'); + }); + + it('handles second tab click', () => { + const wrapper = shallow(); + const tabsEl = wrapper.find(EuiTabbedContent); + tabsEl.prop('onTabClick')!(tabs[1]); + + expect(navigateToUrl).toHaveBeenCalledWith('/sources/123/display_settings/result_detail'); + }); + }); + + describe('header action', () => { + it('renders button when hasDocuments', () => { + const wrapper = shallow(); + const button = ( + + Save + + ); + + expect(wrapper.find(ViewContentHeader).prop('action')).toStrictEqual(button); + }); + + it('renders null when no documents', () => { + setMockValues({ ...values, exampleDocuments: [] }); + const wrapper = shallow(); + + expect(wrapper.find(ViewContentHeader).prop('action')).toBeNull(); + }); + }); + + it('submits the form', () => { + const wrapper = shallow(); + const simulatedEvent = { + form: 0, + target: { getAttribute: () => '_self' }, + preventDefault: jest.fn(), + }; + + const form = wrapper.find('form'); + form.simulate('submit', simulatedEvent); + expect(simulatedEvent.preventDefault).toHaveBeenCalled(); + expect(setServerData).toHaveBeenCalled(); + }); + + it('renders FieldEditorModal', () => { + setMockValues({ ...values, addFieldModalVisible: true }); + const wrapper = shallow(); + + expect(wrapper.find(FieldEditorModal)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.tsx index e34728beef5e5..cf066f3157e39 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings.tsx @@ -6,9 +6,7 @@ import React, { FormEvent, useEffect } from 'react'; -import { History } from 'history'; import { useActions, useValues } from 'kea'; -import { useHistory } from 'react-router-dom'; import './display_settings.scss'; @@ -26,6 +24,9 @@ import { getContentSourcePath, } from '../../../../routes'; +import { clearFlashMessages } from '../../../../../shared/flash_messages'; + +import { KibanaLogic } from '../../../../../shared/kibana'; import { AppLogic } from '../../../../app_logic'; import { Loading } from '../../../../../shared/loading'; @@ -45,10 +46,7 @@ interface DisplaySettingsProps { } export const DisplaySettings: React.FC = ({ tabId }) => { - const history = useHistory() as History; - const { initializeDisplaySettings, setServerData, resetDisplaySettingsState } = useActions( - DisplaySettingsLogic - ); + const { initializeDisplaySettings, setServerData } = useActions(DisplaySettingsLogic); const { dataLoading, @@ -64,7 +62,7 @@ export const DisplaySettings: React.FC = ({ tabId }) => { useEffect(() => { initializeDisplaySettings(); - return resetDisplaySettingsState; + return clearFlashMessages; }, []); useEffect(() => { @@ -95,7 +93,7 @@ export const DisplaySettings: React.FC = ({ tabId }) => { ? getContentSourcePath(DISPLAY_SETTINGS_RESULT_DETAIL_PATH, sourceId, isOrganization) : getContentSourcePath(DISPLAY_SETTINGS_SEARCH_RESULT_PATH, sourceId, isOrganization); - history.push(path); + KibanaLogic.values.navigateToUrl(path); }; const handleFormSubmit = (e: FormEvent) => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.test.ts new file mode 100644 index 0000000000000..aed99bdd950c5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.test.ts @@ -0,0 +1,389 @@ +/* + * 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 { LogicMounter } from '../../../../../__mocks__/kea.mock'; + +import { + mockFlashMessageHelpers, + mockHttpValues, + expectedAsyncError, +} from '../../../../../__mocks__'; + +const contentSource = { id: 'source123' }; +jest.mock('../../source_logic', () => ({ + SourceLogic: { values: { contentSource } }, +})); + +import { AppLogic } from '../../../../app_logic'; +jest.mock('../../../../app_logic', () => ({ + AppLogic: { values: { isOrganization: true } }, +})); + +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; +import { LEAVE_UNASSIGNED_FIELD } from './constants'; + +import { DisplaySettingsLogic, defaultSearchResultConfig } from './display_settings_logic'; + +describe('DisplaySettingsLogic', () => { + const { http } = mockHttpValues; + const { clearFlashMessages, flashAPIErrors, setSuccessMessage } = mockFlashMessageHelpers; + const { mount } = new LogicMounter(DisplaySettingsLogic); + + const { searchResultConfig, exampleDocuments } = exampleResult; + + const defaultValues = { + sourceName: '', + sourceId: '', + schemaFields: {}, + exampleDocuments: [], + serverSearchResultConfig: defaultSearchResultConfig, + searchResultConfig: defaultSearchResultConfig, + serverRoute: '', + editFieldIndex: null, + dataLoading: true, + addFieldModalVisible: false, + titleFieldHover: false, + urlFieldHover: false, + subtitleFieldHover: false, + descriptionFieldHover: false, + fieldOptions: [], + optionalFieldOptions: [ + { + value: LEAVE_UNASSIGNED_FIELD, + text: LEAVE_UNASSIGNED_FIELD, + }, + ], + availableFieldOptions: [], + unsavedChanges: false, + }; + + const serverProps = { + sourceName: 'foo', + sourceId: '123', + serverRoute: '/foo', + searchResultConfig, + exampleDocuments, + schemaFields: {}, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mount(); + }); + + it('has expected default values', () => { + expect(DisplaySettingsLogic.values).toEqual(defaultValues); + }); + + describe('actions', () => { + it('onInitializeDisplaySettings', () => { + DisplaySettingsLogic.actions.onInitializeDisplaySettings(serverProps); + + expect(DisplaySettingsLogic.values.sourceName).toEqual(serverProps.sourceName); + expect(DisplaySettingsLogic.values.sourceId).toEqual(serverProps.sourceId); + expect(DisplaySettingsLogic.values.schemaFields).toEqual(serverProps.schemaFields); + expect(DisplaySettingsLogic.values.exampleDocuments).toEqual(serverProps.exampleDocuments); + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual( + serverProps.searchResultConfig + ); + expect(DisplaySettingsLogic.values.serverRoute).toEqual(serverProps.serverRoute); + expect(DisplaySettingsLogic.values.dataLoading).toEqual(false); + }); + + it('setServerResponseData', () => { + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + + expect(DisplaySettingsLogic.values.serverSearchResultConfig).toEqual( + serverProps.searchResultConfig + ); + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual( + serverProps.searchResultConfig + ); + + expect(setSuccessMessage).toHaveBeenCalled(); + }); + + it('handles empty color', () => { + const propsWithoutColor = { + ...serverProps, + searchResultConfig: { + ...serverProps.searchResultConfig, + color: '', + }, + }; + const configWithDefaultColor = { + ...serverProps.searchResultConfig, + color: '#000000', + }; + DisplaySettingsLogic.actions.onInitializeDisplaySettings(propsWithoutColor); + + expect(DisplaySettingsLogic.values.serverSearchResultConfig).toEqual(configWithDefaultColor); + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual(configWithDefaultColor); + }); + + it('setTitleField', () => { + const TITLE = 'newTitle'; + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + DisplaySettingsLogic.actions.setTitleField(TITLE); + + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({ + ...searchResultConfig, + titleField: TITLE, + }); + }); + + it('setUrlField', () => { + const URL = 'http://new.url'; + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + DisplaySettingsLogic.actions.setUrlField(URL); + + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({ + ...searchResultConfig, + urlField: URL, + }); + }); + + it('setSubtitleField', () => { + const SUBTITLE = 'new sub title'; + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + DisplaySettingsLogic.actions.setSubtitleField(SUBTITLE); + + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({ + ...searchResultConfig, + subtitleField: SUBTITLE, + }); + }); + + it('setDescriptionField', () => { + const DESCRIPTION = 'new description'; + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + DisplaySettingsLogic.actions.setDescriptionField(DESCRIPTION); + + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({ + ...searchResultConfig, + descriptionField: DESCRIPTION, + }); + }); + + it('setColorField', () => { + const HEX = '#e3e3e3'; + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + DisplaySettingsLogic.actions.setColorField(HEX); + + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({ + ...searchResultConfig, + color: HEX, + }); + }); + + it('setDetailFields', () => { + const result = { + destination: { + index: 0, + }, + source: { + index: 1, + }, + }; + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + DisplaySettingsLogic.actions.setDetailFields(result as any); + + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({ + ...searchResultConfig, + detailFields: [searchResultConfig.detailFields[1], searchResultConfig.detailFields[0]], + }); + }); + + it('removeDetailField', () => { + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + DisplaySettingsLogic.actions.removeDetailField(0); + + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({ + ...searchResultConfig, + detailFields: [searchResultConfig.detailFields[1]], + }); + }); + + it('addDetailField', () => { + const newField = { label: 'Monkey', fieldName: 'primate' }; + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + DisplaySettingsLogic.actions.addDetailField(newField); + + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({ + ...searchResultConfig, + detailFields: [ + searchResultConfig.detailFields[0], + searchResultConfig.detailFields[1], + newField, + ], + }); + }); + + it('updateDetailField', () => { + const updatedField = { label: 'Monkey', fieldName: 'primate' }; + DisplaySettingsLogic.actions.setServerResponseData(serverProps); + DisplaySettingsLogic.actions.updateDetailField(updatedField, 0); + + expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({ + ...searchResultConfig, + detailFields: [updatedField, searchResultConfig.detailFields[1]], + }); + }); + + it('openEditDetailField', () => { + const INDEX = 2; + DisplaySettingsLogic.actions.openEditDetailField(INDEX); + + expect(DisplaySettingsLogic.values.editFieldIndex).toEqual(INDEX); + }); + + it('toggleFieldEditorModal', () => { + DisplaySettingsLogic.actions.toggleFieldEditorModal(); + + expect(DisplaySettingsLogic.values.editFieldIndex).toEqual(null); + expect(DisplaySettingsLogic.values.addFieldModalVisible).toEqual( + !defaultValues.addFieldModalVisible + ); + expect(clearFlashMessages).toHaveBeenCalled(); + }); + + it('toggleTitleFieldHover', () => { + DisplaySettingsLogic.actions.toggleTitleFieldHover(); + + expect(DisplaySettingsLogic.values.titleFieldHover).toEqual(!defaultValues.titleFieldHover); + }); + + it('toggleSubtitleFieldHover', () => { + DisplaySettingsLogic.actions.toggleSubtitleFieldHover(); + + expect(DisplaySettingsLogic.values.subtitleFieldHover).toEqual( + !defaultValues.subtitleFieldHover + ); + }); + + it('toggleDescriptionFieldHover', () => { + DisplaySettingsLogic.actions.toggleDescriptionFieldHover(); + + expect(DisplaySettingsLogic.values.descriptionFieldHover).toEqual( + !defaultValues.descriptionFieldHover + ); + }); + + it('toggleUrlFieldHover', () => { + DisplaySettingsLogic.actions.toggleUrlFieldHover(); + + expect(DisplaySettingsLogic.values.urlFieldHover).toEqual(!defaultValues.urlFieldHover); + }); + }); + + describe('listeners', () => { + describe('initializeDisplaySettings', () => { + it('calls API and sets values (org)', async () => { + const onInitializeDisplaySettingsSpy = jest.spyOn( + DisplaySettingsLogic.actions, + 'onInitializeDisplaySettings' + ); + const promise = Promise.resolve(serverProps); + http.get.mockReturnValue(promise); + DisplaySettingsLogic.actions.initializeDisplaySettings(); + + expect(http.get).toHaveBeenCalledWith( + '/api/workplace_search/org/sources/source123/display_settings/config' + ); + await promise; + expect(onInitializeDisplaySettingsSpy).toHaveBeenCalledWith({ + ...serverProps, + isOrganization: true, + }); + }); + + it('calls API and sets values (account)', async () => { + AppLogic.values.isOrganization = false; + + const onInitializeDisplaySettingsSpy = jest.spyOn( + DisplaySettingsLogic.actions, + 'onInitializeDisplaySettings' + ); + const promise = Promise.resolve(serverProps); + http.get.mockReturnValue(promise); + DisplaySettingsLogic.actions.initializeDisplaySettings(); + + expect(http.get).toHaveBeenCalledWith( + '/api/workplace_search/account/sources/source123/display_settings/config' + ); + await promise; + expect(onInitializeDisplaySettingsSpy).toHaveBeenCalledWith({ + ...serverProps, + isOrganization: false, + }); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + http.get.mockReturnValue(promise); + DisplaySettingsLogic.actions.initializeDisplaySettings(); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + }); + }); + + describe('setServerData', () => { + it('calls API and sets values', async () => { + const setServerResponseDataSpy = jest.spyOn( + DisplaySettingsLogic.actions, + 'setServerResponseData' + ); + const promise = Promise.resolve(serverProps); + http.post.mockReturnValue(promise); + DisplaySettingsLogic.actions.onInitializeDisplaySettings(serverProps); + DisplaySettingsLogic.actions.setServerData(); + + expect(http.post).toHaveBeenCalledWith(serverProps.serverRoute, { + body: JSON.stringify({ ...searchResultConfig }), + }); + await promise; + expect(setServerResponseDataSpy).toHaveBeenCalledWith({ + ...serverProps, + }); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + http.post.mockReturnValue(promise); + DisplaySettingsLogic.actions.setServerData(); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + }); + }); + }); + + describe('selectors', () => { + describe('availableFieldOptions', () => { + it('should handle duplicate', () => { + const propsWithDuplicates = { + ...serverProps, + schemaFields: { + cats: 'string', + dogs: 'string', + monkeys: 'string', + }, + searchResultConfig: { + ...searchResultConfig, + detailsFields: [searchResultConfig.detailFields[0], searchResultConfig.detailFields[1]], + }, + }; + + DisplaySettingsLogic.actions.onInitializeDisplaySettings(propsWithDuplicates); + + expect(DisplaySettingsLogic.values.availableFieldOptions).toEqual([ + { text: 'monkeys', value: 'monkeys' }, + ]); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts index a8636b4a34da1..0e85e2ec57937 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_logic.ts @@ -13,7 +13,7 @@ import { HttpLogic } from '../../../../../shared/http'; import { setSuccessMessage, - FlashMessagesLogic, + clearFlashMessages, flashAPIErrors, } from '../../../../../shared/flash_messages'; @@ -61,7 +61,6 @@ interface DisplaySettingsActions { toggleSubtitleFieldHover(): void; toggleDescriptionFieldHover(): void; toggleUrlFieldHover(): void; - resetDisplaySettingsState(): void; } interface DisplaySettingsValues { @@ -85,7 +84,7 @@ interface DisplaySettingsValues { unsavedChanges: boolean; } -const defaultSearchResultConfig = { +export const defaultSearchResultConfig = { titleField: '', subtitleField: '', descriptionField: '', @@ -117,7 +116,6 @@ export const DisplaySettingsLogic = kea< toggleSubtitleFieldHover: () => true, toggleDescriptionFieldHover: () => true, toggleUrlFieldHover: () => true, - resetDisplaySettingsState: () => true, initializeDisplaySettings: () => true, setServerData: () => true, }, @@ -330,10 +328,7 @@ export const DisplaySettingsLogic = kea< setSuccessMessage(SUCCESS_MESSAGE); }, toggleFieldEditorModal: () => { - FlashMessagesLogic.actions.clearFlashMessages(); - }, - resetDisplaySettingsState: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_router.test.tsx new file mode 100644 index 0000000000000..726bccb201c68 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_router.test.tsx @@ -0,0 +1,29 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues } from '../../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { Route, Switch } from 'react-router-dom'; + +import { DisplaySettings } from './display_settings'; + +import { DisplaySettingsRouter } from './display_settings_router'; + +describe('DisplaySettingsRouter', () => { + it('renders', () => { + setMockValues({ isOrganization: true }); + const wrapper = shallow(); + + expect(wrapper.find(DisplaySettings)).toHaveLength(2); + expect(wrapper.find(Switch)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(2); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_router.tsx index 01ac93735b8a8..2155d8358daed 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/display_settings_router.tsx @@ -23,16 +23,12 @@ export const DisplaySettingsRouter: React.FC = () => { const { isOrganization } = useValues(AppLogic); return ( - } - /> - } - /> + + + + + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.test.tsx new file mode 100644 index 0000000000000..30fcb0f6b1ac8 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues } from '../../../../../__mocks__'; +import { shallow } from 'enzyme'; + +import React from 'react'; + +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; + +import { ExampleResultDetailCard } from './example_result_detail_card'; + +describe('ExampleResultDetailCard', () => { + beforeEach(() => { + setMockValues({ ...exampleResult }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="ExampleResultDetailCard"]')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="DetailField"]')).toHaveLength( + exampleResult.searchResultConfig.detailFields.length + ); + }); + + it('shows fallback URL label when no override set', () => { + setMockValues({ ...exampleResult, searchResultConfig: { detailFields: [] } }); + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="DefaultUrlLabel"]')).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.tsx index 468f7d2f7ad05..3278140a2dfe6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_result_detail_card.tsx @@ -28,7 +28,7 @@ export const ExampleResultDetailCard: React.FC = () => { const result = exampleDocuments[0]; return ( -
+
@@ -49,7 +49,9 @@ export const ExampleResultDetailCard: React.FC = () => { {urlField ? (
{result[urlField]}
) : ( - URL + + URL + )}
@@ -57,7 +59,11 @@ export const ExampleResultDetailCard: React.FC = () => {
{detailFields.length > 0 ? ( detailFields.map(({ fieldName, label }, index) => ( -
+

{label}

diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.test.tsx new file mode 100644 index 0000000000000..375e436b6a8a2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.test.tsx @@ -0,0 +1,44 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues } from '../../../../../__mocks__'; +import { shallow } from 'enzyme'; + +import React from 'react'; + +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; + +import { CustomSourceIcon } from './custom_source_icon'; + +import { ExampleSearchResultGroup } from './example_search_result_group'; + +describe('ExampleSearchResultGroup', () => { + beforeEach(() => { + setMockValues({ ...exampleResult }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="ExampleSearchResultGroup"]')).toHaveLength(1); + }); + + it('sets correct color prop when dark', () => { + setMockValues({ ...exampleResult, searchResultConfig: { color: '#000', detailFields: [] } }); + const wrapper = shallow(); + + expect(wrapper.find(CustomSourceIcon).prop('color')).toEqual('white'); + }); + + it('shows fallback URL label when no override set', () => { + setMockValues({ ...exampleResult, searchResultConfig: { detailFields: [], color: '#111' } }); + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="DefaultDescriptionLabel"]')).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.tsx index 14239b1654308..aa7bc4d917886 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_search_result_group.tsx @@ -27,7 +27,7 @@ export const ExampleSearchResultGroup: React.FC = () => { } = useValues(DisplaySettingsLogic); return ( -
+
{ {descriptionField ? (
{result[descriptionField]}
) : ( - Description + + Description + )}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.test.tsx new file mode 100644 index 0000000000000..4e56753bfa7e2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.test.tsx @@ -0,0 +1,44 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues } from '../../../../../__mocks__'; +import { shallow } from 'enzyme'; + +import React from 'react'; + +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; + +import { CustomSourceIcon } from './custom_source_icon'; + +import { ExampleStandoutResult } from './example_standout_result'; + +describe('ExampleStandoutResult', () => { + beforeEach(() => { + setMockValues({ ...exampleResult }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="ExampleStandoutResult"]')).toHaveLength(1); + }); + + it('sets correct color prop when dark', () => { + setMockValues({ ...exampleResult, searchResultConfig: { color: '#000', detailFields: [] } }); + const wrapper = shallow(); + + expect(wrapper.find(CustomSourceIcon).prop('color')).toEqual('white'); + }); + + it('shows fallback URL label when no override set', () => { + setMockValues({ ...exampleResult, searchResultConfig: { detailFields: [], color: '#111' } }); + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="DefaultDescriptionLabel"]')).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.tsx index 4ef3b1fe14b93..a80680d219aef 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/example_standout_result.tsx @@ -30,7 +30,7 @@ export const ExampleStandoutResult: React.FC = () => { const result = exampleDocuments[0]; return ( -
+
{ {descriptionField ? ( {result[descriptionField]} ) : ( - Description + + Description + )}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/field_editor_modal.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/field_editor_modal.test.tsx new file mode 100644 index 0000000000000..5471df9a6f0be --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/field_editor_modal.test.tsx @@ -0,0 +1,102 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../../../__mocks__'; +import { shallow } from 'enzyme'; + +import React from 'react'; + +import { EuiModal, EuiSelect, EuiFieldText } from '@elastic/eui'; + +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; + +import { FieldEditorModal } from './field_editor_modal'; + +describe('FieldEditorModal', () => { + const { searchResultConfig } = exampleResult; + const fieldOptions = [ + { + value: 'foo', + text: 'Foo', + }, + ]; + const availableFieldOptions = [ + { + value: 'bar', + text: 'Bar', + }, + ]; + const toggleFieldEditorModal = jest.fn(); + const addDetailField = jest.fn(); + const updateDetailField = jest.fn(); + + beforeEach(() => { + setMockActions({ + toggleFieldEditorModal, + addDetailField, + updateDetailField, + }); + setMockValues({ + searchResultConfig, + fieldOptions, + availableFieldOptions, + editFieldIndex: 0, + }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiModal)).toHaveLength(1); + }); + + it('sets value on select change', () => { + const wrapper = shallow(); + const select = wrapper.find(EuiSelect); + + select.simulate('change', { target: { value: 'cats' } }); + + expect(select.prop('value')).toEqual('cats'); + }); + + it('sets value on input change', () => { + const wrapper = shallow(); + const input = wrapper.find(EuiFieldText); + + input.simulate('change', { target: { value: 'Felines' } }); + + expect(input.prop('value')).toEqual('Felines'); + }); + + it('handles form submission when creating', () => { + setMockValues({ + searchResultConfig, + fieldOptions, + availableFieldOptions, + editFieldIndex: null, + }); + + const wrapper = shallow(); + + const preventDefault = jest.fn(); + wrapper.find('form').simulate('submit', { preventDefault }); + + expect(preventDefault).toHaveBeenCalled(); + expect(addDetailField).toHaveBeenCalled(); + }); + + it('handles form submission when editing', () => { + const wrapper = shallow(); + + const preventDefault = jest.fn(); + wrapper.find('form').simulate('submit', { preventDefault }); + + expect(preventDefault).toHaveBeenCalled(); + expect(updateDetailField).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/result_detail.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/result_detail.test.tsx new file mode 100644 index 0000000000000..6d9d60351f3db --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/result_detail.test.tsx @@ -0,0 +1,131 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../../../__mocks__'; +import { shallow, mount } from 'enzyme'; + +/** + * Mocking necessary due to console warnings from react d-n-d, which EUI uses + * https://stackoverflow.com/a/56674119/1949235 + */ +jest.mock('react-beautiful-dnd', () => ({ + Droppable: ({ children }: { children: any }) => + children( + { + draggableProps: { + style: {}, + }, + innerRef: jest.fn(), + }, + {} + ), + Draggable: ({ children }: { children: any }) => + children( + { + draggableProps: { + style: {}, + }, + innerRef: jest.fn(), + }, + {} + ), + DragDropContext: ({ children }: { children: any }) => children, +})); + +import React from 'react'; + +import { EuiTextColor } from '@elastic/eui'; + +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; + +import { ExampleResultDetailCard } from './example_result_detail_card'; + +import { ResultDetail } from './result_detail'; + +describe('ResultDetail', () => { + const { searchResultConfig, exampleDocuments } = exampleResult; + const availableFieldOptions = [ + { + value: 'foo', + text: 'Foo', + }, + ]; + const toggleFieldEditorModal = jest.fn(); + const setDetailFields = jest.fn(); + const openEditDetailField = jest.fn(); + const removeDetailField = jest.fn(); + + beforeEach(() => { + setMockActions({ + toggleFieldEditorModal, + setDetailFields, + openEditDetailField, + removeDetailField, + }); + setMockValues({ + searchResultConfig, + availableFieldOptions, + exampleDocuments, + }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(ExampleResultDetailCard)).toHaveLength(1); + }); + + it('calls setTitleField on change', () => { + const wrapper = shallow(); + wrapper.find('[data-test-subj="AddFieldButton"]').simulate('click'); + + expect(toggleFieldEditorModal).toHaveBeenCalled(); + }); + + it('handles empty detailFields', () => { + setMockValues({ + searchResultConfig: { + ...searchResultConfig, + detailFields: [], + }, + availableFieldOptions, + exampleDocuments, + }); + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="EmptyFieldsDescription"]')).toHaveLength(1); + }); + + it('handles drag and drop', () => { + const wrapper = mount(); + wrapper.find('[data-test-subj="EditFieldButton"]').first().simulate('click'); + wrapper.find('[data-test-subj="RemoveFieldButton"]').first().simulate('click'); + + expect(openEditDetailField).toHaveBeenCalled(); + expect(removeDetailField).toHaveBeenCalled(); + expect(wrapper.find(EuiTextColor).first().text()).toEqual('“Felines”'); + }); + + it('handles empty label fallback', () => { + setMockValues({ + searchResultConfig: { + ...searchResultConfig, + detailFields: [ + { + fieldName: 'foo', + }, + ], + }, + availableFieldOptions, + exampleDocuments, + }); + const wrapper = mount(); + + expect(wrapper.find(EuiTextColor).first().text()).toEqual('“”'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/result_detail.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/result_detail.tsx index cb65d8ef671e6..5ee484250ca62 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/result_detail.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/result_detail.tsx @@ -80,7 +80,7 @@ export const ResultDetail: React.FC = () => { <> {detailFields.map(({ fieldName, label }, index) => ( {
openEditDetailField(index)} /> removeDetailField(index)} @@ -125,7 +127,9 @@ export const ResultDetail: React.FC = () => { ) : ( -

Add fields and move them into the order you want them to appear.

+

+ Add fields and move them into the order you want them to appear. +

)} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.test.tsx new file mode 100644 index 0000000000000..776bf012aa895 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.test.tsx @@ -0,0 +1,124 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../../../__mocks__'; +import { shallow } from 'enzyme'; + +import React from 'react'; + +import { exampleResult } from '../../../../__mocks__/content_sources.mock'; + +import { ExampleSearchResultGroup } from './example_search_result_group'; +import { ExampleStandoutResult } from './example_standout_result'; + +import { LEAVE_UNASSIGNED_FIELD } from './constants'; +import { SearchResults } from './search_results'; + +describe('SearchResults', () => { + const { searchResultConfig } = exampleResult; + const fieldOptions = [ + { + value: 'foo', + text: 'Foo', + }, + ]; + const optionalFieldOptions = [ + { + value: 'bar', + text: 'Bar', + }, + ]; + const toggleTitleFieldHover = jest.fn(); + const toggleSubtitleFieldHover = jest.fn(); + const toggleDescriptionFieldHover = jest.fn(); + const setTitleField = jest.fn(); + const setSubtitleField = jest.fn(); + const setDescriptionField = jest.fn(); + const setUrlField = jest.fn(); + const setColorField = jest.fn(); + + beforeEach(() => { + setMockActions({ + toggleTitleFieldHover, + toggleSubtitleFieldHover, + toggleDescriptionFieldHover, + setTitleField, + setSubtitleField, + setDescriptionField, + setUrlField, + setColorField, + }); + setMockValues({ + searchResultConfig, + fieldOptions, + optionalFieldOptions, + }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(ExampleSearchResultGroup)).toHaveLength(1); + expect(wrapper.find(ExampleStandoutResult)).toHaveLength(1); + }); + + it('calls setTitleField on change', () => { + const wrapper = shallow(); + wrapper + .find('[data-test-subj="TitleFieldSelect"]') + .simulate('change', { target: { value: searchResultConfig.titleField } }); + + expect(setTitleField).toHaveBeenCalled(); + }); + + it('calls setUrlField on change', () => { + const wrapper = shallow(); + wrapper + .find('[data-test-subj="UrlFieldSelect"]') + .simulate('change', { target: { value: searchResultConfig.urlField } }); + + expect(setUrlField).toHaveBeenCalled(); + }); + + it('calls setSubtitleField on change', () => { + const wrapper = shallow(); + wrapper + .find('[data-test-subj="SubtitleFieldSelect"]') + .simulate('change', { target: { value: searchResultConfig.titleField } }); + + expect(setSubtitleField).toHaveBeenCalledWith(searchResultConfig.titleField); + }); + + it('calls setDescriptionField on change', () => { + const wrapper = shallow(); + wrapper + .find('[data-test-subj="DescriptionFieldSelect"]') + .simulate('change', { target: { value: searchResultConfig.descriptionField } }); + + expect(setDescriptionField).toHaveBeenCalledWith(searchResultConfig.descriptionField); + }); + + it('handles blank fallbacks', () => { + setMockValues({ + searchResultConfig: { detailFields: [] }, + fieldOptions, + optionalFieldOptions, + }); + const wrapper = shallow(); + wrapper + .find('[data-test-subj="SubtitleFieldSelect"]') + .simulate('change', { target: { value: LEAVE_UNASSIGNED_FIELD } }); + wrapper + .find('[data-test-subj="DescriptionFieldSelect"]') + .simulate('change', { target: { value: LEAVE_UNASSIGNED_FIELD } }); + + expect(wrapper.find('[data-test-subj="UrlFieldSelect"]').prop('value')).toEqual(''); + expect(setSubtitleField).toHaveBeenCalledWith(null); + expect(setDescriptionField).toHaveBeenCalledWith(null); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.tsx index 96b7a6fbe14b5..c1a65d1c52b65 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/search_results.tsx @@ -59,8 +59,8 @@ export const SearchResults: React.FC = () => { label="Title" onMouseOver={toggleTitleFieldHover} onMouseOut={toggleTitleFieldHover} - onFocus={() => null} // FIXME - onBlur={() => null} // FIXME + onFocus={toggleTitleFieldHover} + onBlur={toggleTitleFieldHover} > { /> - null} // FIXME - onBlur={() => null} // FIXME - /> + null} // FIXME - onBlur={() => null} // FIXME + onFocus={toggleSubtitleFieldHover} + onBlur={toggleSubtitleFieldHover} > { helpText="This area is optional" onMouseOver={toggleDescriptionFieldHover} onMouseOut={toggleDescriptionFieldHover} - onFocus={() => null} // FIXME - onBlur={() => null} // FIXME + onFocus={toggleDescriptionFieldHover} + onBlur={toggleDescriptionFieldHover} > { + const result = { foo: 'bar' }; + it('renders', () => { + const props = { + result, + subtitleField: 'foo', + subtitleFieldHover: false, + }; + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="SubtitleField"]')).toHaveLength(1); + }); + + it('shows fallback URL label when no override set', () => { + const props = { + result, + subtitleField: null, + subtitleFieldHover: false, + }; + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="DefaultSubtitleLabel"]')).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/subtitle_field.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/subtitle_field.tsx index e27052ddffc04..d2f26cd6726df 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/subtitle_field.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/subtitle_field.tsx @@ -22,6 +22,7 @@ export const SubtitleField: React.FC = ({ subtitleFieldHover, }) => (
= ({ {subtitleField ? (
{result[subtitleField]}
) : ( - Subtitle + + Subtitle + )}
); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.test.tsx new file mode 100644 index 0000000000000..558fd7aa8c86a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.test.tsx @@ -0,0 +1,46 @@ +/* + * 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 { TitleField } from './title_field'; + +describe('TitleField', () => { + const result = { foo: 'bar' }; + it('renders', () => { + const props = { + result, + titleField: 'foo', + titleFieldHover: false, + }; + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="TitleField"]')).toHaveLength(1); + }); + + it('handles title when array', () => { + const props = { + result: { foo: ['baz', 'bar'] }, + titleField: 'foo', + titleFieldHover: false, + }; + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="CustomTitleLabel"]').text()).toEqual('baz, bar'); + }); + + it('shows fallback URL label when no override set', () => { + const props = { + result, + titleField: null, + titleFieldHover: false, + }; + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="DefaultTitleLabel"]')).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.tsx index a54c0977b464f..fa975c8b11ce0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/display_settings/title_field.tsx @@ -21,14 +21,19 @@ export const TitleField: React.FC = ({ result, titleField, titl const titleDisplay = Array.isArray(title) ? title.join(', ') : title; return (
{titleField ? ( -
{titleDisplay}
+
+ {titleDisplay} +
) : ( - Title + + Title + )}
);