mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Port PR 3746 from ent-search (#103765)
* Port the changes as is with no modifications * Fix accessibility errors * Rename variable * Fix Stylelint issues and remove unused CSS * Extract getAsLocalDatetimeString as a util function and use it everywhere * Update backend schema Also replace schema.maybe with schema.nullable. Previously assigning "Leave unassigned" value to subtitle and description caused a server error, because we were receiving null for these values that server did not expect. * Update exampleResult mock * Add tests for DisplaySettingsLogic * Add tests for ExampleSearchResultGroup * Add tests for ExampleStandoutResult * Add tests for SearchResults * Add missed null fallback type
This commit is contained in:
parent
71a57454c7
commit
712ab004c5
16 changed files with 561 additions and 12 deletions
|
@ -305,6 +305,10 @@ export const exampleResult = {
|
|||
urlField: 'myLink',
|
||||
color: '#e3e3e3',
|
||||
descriptionField: 'about',
|
||||
typeField: 'otherType',
|
||||
mediaTypeField: 'otherMediaType',
|
||||
createdByField: 'otherCreatedBy',
|
||||
updatedByField: 'otherUpdatedBy',
|
||||
detailFields: [
|
||||
{ fieldName: 'cats', label: 'Felines' },
|
||||
{ fieldName: 'dogs', label: 'Canines' },
|
||||
|
|
|
@ -204,6 +204,10 @@ export interface SearchResultConfig {
|
|||
titleField: string | null;
|
||||
subtitleField: string | null;
|
||||
descriptionField: string | null;
|
||||
typeField: string | null;
|
||||
mediaTypeField: string | null;
|
||||
createdByField: string | null;
|
||||
updatedByField: string | null;
|
||||
urlField: string | null;
|
||||
color: string;
|
||||
detailFields: DetailField[];
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 { getAsLocalDateTimeString } from './';
|
||||
|
||||
describe('getAsLocalDateTimeString', () => {
|
||||
it('returns localized date if string can be parsed as date', () => {
|
||||
const date = '2021-06-28';
|
||||
|
||||
expect(getAsLocalDateTimeString(date)).toEqual(new Date(Date.parse(date)).toLocaleString());
|
||||
});
|
||||
|
||||
it('returns null if string cannot be parsed as date', () => {
|
||||
const date = 'foo';
|
||||
|
||||
expect(getAsLocalDateTimeString(date)).toEqual(null);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const getAsLocalDateTimeString = (str: string) => {
|
||||
const dateValue = Date.parse(str);
|
||||
return dateValue ? new Date(dateValue).toLocaleString() : null;
|
||||
};
|
|
@ -6,3 +6,4 @@
|
|||
*/
|
||||
|
||||
export { toSentenceSerial } from './to_sentence_serial';
|
||||
export { getAsLocalDateTimeString } from './get_as_local_datetime_string';
|
||||
|
|
|
@ -22,6 +22,23 @@
|
|||
0 0 20px $euiColorLightestShade;
|
||||
}
|
||||
|
||||
@mixin searchResultTag {
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 .25rem;
|
||||
background: #E9EDF2;
|
||||
color: #647487;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .075em;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
// Wrapper
|
||||
.custom-source-display-settings {
|
||||
font-size: 16px;
|
||||
|
@ -73,6 +90,28 @@
|
|||
color: $euiColorDarkShade;
|
||||
}
|
||||
}
|
||||
|
||||
&__tag {
|
||||
@include searchResultTag;
|
||||
}
|
||||
|
||||
&__tag-content {
|
||||
display: inline-flex;
|
||||
height: 20px;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__meta {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
margin-top: .5rem;
|
||||
font-size: .8em;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.example-result-content-placeholder {
|
||||
|
|
|
@ -52,6 +52,10 @@ describe('DisplaySettingsLogic', () => {
|
|||
urlFieldHover: false,
|
||||
subtitleFieldHover: false,
|
||||
descriptionFieldHover: false,
|
||||
typeFieldHover: false,
|
||||
mediaTypeFieldHover: false,
|
||||
createdByFieldHover: false,
|
||||
updatedByFieldHover: false,
|
||||
fieldOptions: [],
|
||||
optionalFieldOptions: [
|
||||
{
|
||||
|
@ -182,6 +186,50 @@ describe('DisplaySettingsLogic', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('setTypeField', () => {
|
||||
const TYPE = 'new type';
|
||||
DisplaySettingsLogic.actions.setServerResponseData(serverProps);
|
||||
DisplaySettingsLogic.actions.setTypeField(TYPE);
|
||||
|
||||
expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({
|
||||
...searchResultConfig,
|
||||
typeField: TYPE,
|
||||
});
|
||||
});
|
||||
|
||||
it('setMediaTypeField', () => {
|
||||
const MEDIA_TYPE = 'new media type';
|
||||
DisplaySettingsLogic.actions.setServerResponseData(serverProps);
|
||||
DisplaySettingsLogic.actions.setMediaTypeField(MEDIA_TYPE);
|
||||
|
||||
expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({
|
||||
...searchResultConfig,
|
||||
mediaTypeField: MEDIA_TYPE,
|
||||
});
|
||||
});
|
||||
|
||||
it('setCreatedByField', () => {
|
||||
const CREATED_BY = 'new created by';
|
||||
DisplaySettingsLogic.actions.setServerResponseData(serverProps);
|
||||
DisplaySettingsLogic.actions.setCreatedByField(CREATED_BY);
|
||||
|
||||
expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({
|
||||
...searchResultConfig,
|
||||
createdByField: CREATED_BY,
|
||||
});
|
||||
});
|
||||
|
||||
it('setUpdatedByField', () => {
|
||||
const UPDATED_BY = 'new updated by';
|
||||
DisplaySettingsLogic.actions.setServerResponseData(serverProps);
|
||||
DisplaySettingsLogic.actions.setUpdatedByField(UPDATED_BY);
|
||||
|
||||
expect(DisplaySettingsLogic.values.searchResultConfig).toEqual({
|
||||
...searchResultConfig,
|
||||
updatedByField: UPDATED_BY,
|
||||
});
|
||||
});
|
||||
|
||||
it('setDetailFields', () => {
|
||||
const result = {
|
||||
destination: {
|
||||
|
@ -286,6 +334,36 @@ describe('DisplaySettingsLogic', () => {
|
|||
|
||||
expect(DisplaySettingsLogic.values.urlFieldHover).toEqual(!defaultValues.urlFieldHover);
|
||||
});
|
||||
|
||||
it('toggleTypeFieldHover', () => {
|
||||
DisplaySettingsLogic.actions.toggleTypeFieldHover();
|
||||
|
||||
expect(DisplaySettingsLogic.values.typeFieldHover).toEqual(!defaultValues.typeFieldHover);
|
||||
});
|
||||
|
||||
it('toggleMediaTypeFieldHover', () => {
|
||||
DisplaySettingsLogic.actions.toggleMediaTypeFieldHover();
|
||||
|
||||
expect(DisplaySettingsLogic.values.mediaTypeFieldHover).toEqual(
|
||||
!defaultValues.mediaTypeFieldHover
|
||||
);
|
||||
});
|
||||
|
||||
it('toggleCreatedByFieldHover', () => {
|
||||
DisplaySettingsLogic.actions.toggleCreatedByFieldHover();
|
||||
|
||||
expect(DisplaySettingsLogic.values.createdByFieldHover).toEqual(
|
||||
!defaultValues.createdByFieldHover
|
||||
);
|
||||
});
|
||||
|
||||
it('toggleUpdatedByFieldHover', () => {
|
||||
DisplaySettingsLogic.actions.toggleUpdatedByFieldHover();
|
||||
|
||||
expect(DisplaySettingsLogic.values.updatedByFieldHover).toEqual(
|
||||
!defaultValues.updatedByFieldHover
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('listeners', () => {
|
||||
|
|
|
@ -55,6 +55,10 @@ interface DisplaySettingsActions {
|
|||
setUrlField(urlField: string): string;
|
||||
setSubtitleField(subtitleField: string | null): string | null;
|
||||
setDescriptionField(descriptionField: string | null): string | null;
|
||||
setTypeField(typeField: string | null): string | null;
|
||||
setMediaTypeField(mediaTypeField: string | null): string | null;
|
||||
setCreatedByField(createdByField: string | null): string | null;
|
||||
setUpdatedByField(updatedByField: string | null): string | null;
|
||||
setColorField(hex: string): string;
|
||||
setDetailFields(result: DropResult): { result: DropResult };
|
||||
openEditDetailField(editFieldIndex: number | null): number | null;
|
||||
|
@ -70,6 +74,10 @@ interface DisplaySettingsActions {
|
|||
toggleTitleFieldHover(): void;
|
||||
toggleSubtitleFieldHover(): void;
|
||||
toggleDescriptionFieldHover(): void;
|
||||
toggleTypeFieldHover(): void;
|
||||
toggleMediaTypeFieldHover(): void;
|
||||
toggleCreatedByFieldHover(): void;
|
||||
toggleUpdatedByFieldHover(): void;
|
||||
toggleUrlFieldHover(): void;
|
||||
}
|
||||
|
||||
|
@ -89,6 +97,10 @@ interface DisplaySettingsValues {
|
|||
urlFieldHover: boolean;
|
||||
subtitleFieldHover: boolean;
|
||||
descriptionFieldHover: boolean;
|
||||
typeFieldHover: boolean;
|
||||
mediaTypeFieldHover: boolean;
|
||||
createdByFieldHover: boolean;
|
||||
updatedByFieldHover: boolean;
|
||||
fieldOptions: OptionValue[];
|
||||
optionalFieldOptions: OptionValue[];
|
||||
availableFieldOptions: OptionValue[];
|
||||
|
@ -100,6 +112,10 @@ export const defaultSearchResultConfig = {
|
|||
subtitleField: '',
|
||||
descriptionField: '',
|
||||
urlField: '',
|
||||
typeField: '',
|
||||
mediaTypeField: '',
|
||||
createdByField: '',
|
||||
updatedByField: '',
|
||||
color: '#000000',
|
||||
detailFields: [],
|
||||
};
|
||||
|
@ -115,7 +131,11 @@ export const DisplaySettingsLogic = kea<
|
|||
setTitleField: (titleField: string) => titleField,
|
||||
setUrlField: (urlField: string) => urlField,
|
||||
setSubtitleField: (subtitleField: string | null) => subtitleField,
|
||||
setDescriptionField: (descriptionField: string) => descriptionField,
|
||||
setDescriptionField: (descriptionField: string | null) => descriptionField,
|
||||
setTypeField: (typeField: string | null) => typeField,
|
||||
setMediaTypeField: (mediaTypeField: string | null) => mediaTypeField,
|
||||
setCreatedByField: (createdByField: string | null) => createdByField,
|
||||
setUpdatedByField: (updatedByField: string | null) => updatedByField,
|
||||
setColorField: (hex: string) => hex,
|
||||
setDetailFields: (result: DropResult) => ({ result }),
|
||||
openEditDetailField: (editFieldIndex: number | null) => editFieldIndex,
|
||||
|
@ -128,6 +148,10 @@ export const DisplaySettingsLogic = kea<
|
|||
toggleTitleFieldHover: () => true,
|
||||
toggleSubtitleFieldHover: () => true,
|
||||
toggleDescriptionFieldHover: () => true,
|
||||
toggleTypeFieldHover: () => true,
|
||||
toggleMediaTypeFieldHover: () => true,
|
||||
toggleCreatedByFieldHover: () => true,
|
||||
toggleUpdatedByFieldHover: () => true,
|
||||
toggleUrlFieldHover: () => true,
|
||||
initializeDisplaySettings: () => true,
|
||||
setServerData: () => true,
|
||||
|
@ -181,6 +205,19 @@ export const DisplaySettingsLogic = kea<
|
|||
...searchResultConfig,
|
||||
descriptionField,
|
||||
}),
|
||||
setTypeField: (searchResultConfig, typeField) => ({ ...searchResultConfig, typeField }),
|
||||
setMediaTypeField: (searchResultConfig, mediaTypeField) => ({
|
||||
...searchResultConfig,
|
||||
mediaTypeField,
|
||||
}),
|
||||
setCreatedByField: (searchResultConfig, createdByField) => ({
|
||||
...searchResultConfig,
|
||||
createdByField,
|
||||
}),
|
||||
setUpdatedByField: (searchResultConfig, updatedByField) => ({
|
||||
...searchResultConfig,
|
||||
updatedByField,
|
||||
}),
|
||||
setColorField: (searchResultConfig, color) => ({ ...searchResultConfig, color }),
|
||||
setDetailFields: (searchResultConfig, { result: { destination, source } }) => {
|
||||
const detailFields = cloneDeep(searchResultConfig.detailFields);
|
||||
|
@ -273,7 +310,31 @@ export const DisplaySettingsLogic = kea<
|
|||
descriptionFieldHover: [
|
||||
false,
|
||||
{
|
||||
toggleDescriptionFieldHover: (addFieldModalVisible) => !addFieldModalVisible,
|
||||
toggleDescriptionFieldHover: (descriptionFieldHover) => !descriptionFieldHover,
|
||||
},
|
||||
],
|
||||
typeFieldHover: [
|
||||
false,
|
||||
{
|
||||
toggleTypeFieldHover: (typeFieldHover) => !typeFieldHover,
|
||||
},
|
||||
],
|
||||
mediaTypeFieldHover: [
|
||||
false,
|
||||
{
|
||||
toggleMediaTypeFieldHover: (mediaTypeFieldHover) => !mediaTypeFieldHover,
|
||||
},
|
||||
],
|
||||
createdByFieldHover: [
|
||||
false,
|
||||
{
|
||||
toggleCreatedByFieldHover: (createdByFieldHover) => !createdByFieldHover,
|
||||
},
|
||||
],
|
||||
updatedByFieldHover: [
|
||||
false,
|
||||
{
|
||||
toggleUpdatedByFieldHover: (updatedByFieldHover) => !updatedByFieldHover,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -13,16 +13,12 @@ import { useValues } from 'kea';
|
|||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
|
||||
import { URL_LABEL } from '../../../../constants';
|
||||
import { getAsLocalDateTimeString } from '../../../../utils';
|
||||
|
||||
import { CustomSourceIcon } from './custom_source_icon';
|
||||
import { DisplaySettingsLogic } from './display_settings_logic';
|
||||
import { TitleField } from './title_field';
|
||||
|
||||
const getAsLocalDateTimeString = (str: string) => {
|
||||
const dateValue = Date.parse(str);
|
||||
return dateValue ? new Date(dateValue).toLocaleString() : null;
|
||||
};
|
||||
|
||||
export const ExampleResultDetailCard: React.FC = () => {
|
||||
const {
|
||||
sourceName,
|
||||
|
|
|
@ -41,4 +41,26 @@ describe('ExampleSearchResultGroup', () => {
|
|||
|
||||
expect(wrapper.find('[data-test-subj="DefaultDescriptionLabel"]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders optional fields if they exist in result', () => {
|
||||
setMockValues({
|
||||
...exampleResult,
|
||||
exampleDocuments: [
|
||||
{
|
||||
myLink: 'http://foo',
|
||||
otherTitle: 'foo',
|
||||
otherType: 'File',
|
||||
otherMediaType: 'PDF',
|
||||
otherCreatedBy: 'bar',
|
||||
otherUpdatedBy: 'baz',
|
||||
},
|
||||
],
|
||||
});
|
||||
const wrapper = shallow(<ExampleSearchResultGroup />);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="CreatedByField"]')).toHaveLength(1);
|
||||
expect(wrapper.find('[data-test-subj="UpdatedByField"]')).toHaveLength(1);
|
||||
expect(wrapper.find('[data-test-subj="TypeField"]')).toHaveLength(1);
|
||||
expect(wrapper.find('[data-test-subj="MediaTypeField"]')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,6 +13,7 @@ import { useValues } from 'kea';
|
|||
import { isColorDark, hexToRgb } from '@elastic/eui';
|
||||
|
||||
import { DESCRIPTION_LABEL } from '../../../../constants';
|
||||
import { getAsLocalDateTimeString } from '../../../../utils';
|
||||
|
||||
import { CustomSourceIcon } from './custom_source_icon';
|
||||
import { DisplaySettingsLogic } from './display_settings_logic';
|
||||
|
@ -22,10 +23,23 @@ import { TitleField } from './title_field';
|
|||
export const ExampleSearchResultGroup: React.FC = () => {
|
||||
const {
|
||||
sourceName,
|
||||
searchResultConfig: { titleField, subtitleField, descriptionField, color },
|
||||
searchResultConfig: {
|
||||
titleField,
|
||||
subtitleField,
|
||||
descriptionField,
|
||||
typeField,
|
||||
mediaTypeField,
|
||||
createdByField,
|
||||
updatedByField,
|
||||
color,
|
||||
},
|
||||
titleFieldHover,
|
||||
subtitleFieldHover,
|
||||
descriptionFieldHover,
|
||||
typeFieldHover,
|
||||
mediaTypeFieldHover,
|
||||
createdByFieldHover,
|
||||
updatedByFieldHover,
|
||||
exampleDocuments,
|
||||
} = useValues(DisplaySettingsLogic);
|
||||
|
||||
|
@ -72,6 +86,60 @@ export const ExampleSearchResultGroup: React.FC = () => {
|
|||
</span>
|
||||
)}
|
||||
</div>
|
||||
{createdByField && result[createdByField] && (
|
||||
<div
|
||||
className={classNames('example-result-content__subtitle', {
|
||||
'example-result-field-hover': createdByFieldHover,
|
||||
})}
|
||||
data-test-subj="CreatedByField"
|
||||
>
|
||||
Created by {result[createdByField]}
|
||||
</div>
|
||||
)}
|
||||
<div className="example-result-content__meta">
|
||||
{typeField && result[typeField] && (
|
||||
<div
|
||||
className={classNames('example-result-content__tag', {
|
||||
'example-result-field-hover': typeFieldHover,
|
||||
})}
|
||||
data-test-subj="TypeField"
|
||||
>
|
||||
<span className="example-search-result__tag-content">
|
||||
{result[typeField]}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{mediaTypeField && result[mediaTypeField] && (
|
||||
<div
|
||||
className={classNames('example-result-content__tag', {
|
||||
'example-result-field-hover': mediaTypeFieldHover,
|
||||
})}
|
||||
data-test-subj="MediaTypeField"
|
||||
>
|
||||
<span className="example-search-result__tag-content">
|
||||
{result[mediaTypeField]}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="example-result-content__tag-content">
|
||||
<span>
|
||||
Last updated
|
||||
{updatedByField && result[updatedByField] && (
|
||||
<span
|
||||
className={classNames('example-result-content__tag-content', {
|
||||
'example-result-field-hover': updatedByFieldHover,
|
||||
})}
|
||||
data-test-subj="UpdatedByField"
|
||||
>
|
||||
{' '}
|
||||
by {result[updatedByField]}
|
||||
</span>
|
||||
)}
|
||||
{getAsLocalDateTimeString(result.last_updated as string) ||
|
||||
result.last_updated}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
@ -41,4 +41,26 @@ describe('ExampleStandoutResult', () => {
|
|||
|
||||
expect(wrapper.find('[data-test-subj="DefaultDescriptionLabel"]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders optional fields if they exist in result', () => {
|
||||
setMockValues({
|
||||
...exampleResult,
|
||||
exampleDocuments: [
|
||||
{
|
||||
myLink: 'http://foo',
|
||||
otherTitle: 'foo',
|
||||
otherType: 'File',
|
||||
otherMediaType: 'PDF',
|
||||
otherCreatedBy: 'bar',
|
||||
otherUpdatedBy: 'baz',
|
||||
},
|
||||
],
|
||||
});
|
||||
const wrapper = shallow(<ExampleStandoutResult />);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="CreatedByField"]')).toHaveLength(1);
|
||||
expect(wrapper.find('[data-test-subj="UpdatedByField"]')).toHaveLength(1);
|
||||
expect(wrapper.find('[data-test-subj="TypeField"]')).toHaveLength(1);
|
||||
expect(wrapper.find('[data-test-subj="MediaTypeField"]')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,6 +13,7 @@ import { useValues } from 'kea';
|
|||
import { isColorDark, hexToRgb } from '@elastic/eui';
|
||||
|
||||
import { DESCRIPTION_LABEL } from '../../../../constants';
|
||||
import { getAsLocalDateTimeString } from '../../../../utils';
|
||||
|
||||
import { CustomSourceIcon } from './custom_source_icon';
|
||||
import { DisplaySettingsLogic } from './display_settings_logic';
|
||||
|
@ -22,10 +23,23 @@ import { TitleField } from './title_field';
|
|||
export const ExampleStandoutResult: React.FC = () => {
|
||||
const {
|
||||
sourceName,
|
||||
searchResultConfig: { titleField, subtitleField, descriptionField, color },
|
||||
searchResultConfig: {
|
||||
titleField,
|
||||
subtitleField,
|
||||
descriptionField,
|
||||
typeField,
|
||||
mediaTypeField,
|
||||
createdByField,
|
||||
updatedByField,
|
||||
color,
|
||||
},
|
||||
titleFieldHover,
|
||||
subtitleFieldHover,
|
||||
descriptionFieldHover,
|
||||
typeFieldHover,
|
||||
mediaTypeFieldHover,
|
||||
createdByFieldHover,
|
||||
updatedByFieldHover,
|
||||
exampleDocuments,
|
||||
} = useValues(DisplaySettingsLogic);
|
||||
|
||||
|
@ -66,6 +80,55 @@ export const ExampleStandoutResult: React.FC = () => {
|
|||
</span>
|
||||
)}
|
||||
</div>
|
||||
{createdByField && result[createdByField] && (
|
||||
<div
|
||||
className={classNames('example-result-content__subtitle', {
|
||||
'example-result-field-hover': createdByFieldHover,
|
||||
})}
|
||||
data-test-subj="CreatedByField"
|
||||
>
|
||||
Created by {result[createdByField]}
|
||||
</div>
|
||||
)}
|
||||
<div className="example-result-content__meta">
|
||||
{typeField && result[typeField] && (
|
||||
<div
|
||||
className={classNames('example-result-content__tag', {
|
||||
'example-result-field-hover': typeFieldHover,
|
||||
})}
|
||||
data-test-subj="TypeField"
|
||||
>
|
||||
<span className="example-search-result__tag-content">{result[typeField]}</span>
|
||||
</div>
|
||||
)}
|
||||
{mediaTypeField && result[mediaTypeField] && (
|
||||
<div
|
||||
className={classNames('example-result-content__tag', {
|
||||
'example-result-field-hover': mediaTypeFieldHover,
|
||||
})}
|
||||
data-test-subj="MediaTypeField"
|
||||
>
|
||||
<span className="example-search-result__tag-content">{result[mediaTypeField]}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="example-result-content__tag-content">
|
||||
<span>
|
||||
Last updated
|
||||
{updatedByField && result[updatedByField] && (
|
||||
<span
|
||||
className={classNames('example-result-content__tag-content', {
|
||||
'example-result-field-hover': updatedByFieldHover,
|
||||
})}
|
||||
data-test-subj="UpdatedByField"
|
||||
>
|
||||
{' '}
|
||||
by {result[updatedByField]}
|
||||
</span>
|
||||
)}
|
||||
{getAsLocalDateTimeString(result.last_updated as string) || result.last_updated}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -41,6 +41,10 @@ describe('SearchResults', () => {
|
|||
const setDescriptionField = jest.fn();
|
||||
const setUrlField = jest.fn();
|
||||
const setColorField = jest.fn();
|
||||
const setTypeField = jest.fn();
|
||||
const setMediaTypeField = jest.fn();
|
||||
const setCreatedByField = jest.fn();
|
||||
const setUpdatedByField = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
setMockActions({
|
||||
|
@ -52,6 +56,10 @@ describe('SearchResults', () => {
|
|||
setDescriptionField,
|
||||
setUrlField,
|
||||
setColorField,
|
||||
setTypeField,
|
||||
setMediaTypeField,
|
||||
setCreatedByField,
|
||||
setUpdatedByField,
|
||||
});
|
||||
setMockValues({
|
||||
searchResultConfig,
|
||||
|
@ -103,6 +111,42 @@ describe('SearchResults', () => {
|
|||
expect(setDescriptionField).toHaveBeenCalledWith(searchResultConfig.descriptionField);
|
||||
});
|
||||
|
||||
it('calls setTypeField on change', () => {
|
||||
const wrapper = shallow(<SearchResults />);
|
||||
wrapper
|
||||
.find('[data-test-subj="TypeFieldSelect"]')
|
||||
.simulate('change', { target: { value: searchResultConfig.typeField } });
|
||||
|
||||
expect(setTypeField).toHaveBeenCalledWith(searchResultConfig.typeField);
|
||||
});
|
||||
|
||||
it('calls setMediaTypeField on change', () => {
|
||||
const wrapper = shallow(<SearchResults />);
|
||||
wrapper
|
||||
.find('[data-test-subj="MediaTypeFieldSelect"]')
|
||||
.simulate('change', { target: { value: searchResultConfig.mediaTypeField } });
|
||||
|
||||
expect(setMediaTypeField).toHaveBeenCalledWith(searchResultConfig.mediaTypeField);
|
||||
});
|
||||
|
||||
it('calls setCreatedByField on change', () => {
|
||||
const wrapper = shallow(<SearchResults />);
|
||||
wrapper
|
||||
.find('[data-test-subj="CreatedByFieldSelect"]')
|
||||
.simulate('change', { target: { value: searchResultConfig.createdByField } });
|
||||
|
||||
expect(setCreatedByField).toHaveBeenCalledWith(searchResultConfig.createdByField);
|
||||
});
|
||||
|
||||
it('calls setUpdatedByField on change', () => {
|
||||
const wrapper = shallow(<SearchResults />);
|
||||
wrapper
|
||||
.find('[data-test-subj="UpdatedByFieldSelect"]')
|
||||
.simulate('change', { target: { value: searchResultConfig.updatedByField } });
|
||||
|
||||
expect(setUpdatedByField).toHaveBeenCalledWith(searchResultConfig.updatedByField);
|
||||
});
|
||||
|
||||
it('handles blank fallbacks', () => {
|
||||
setMockValues({
|
||||
searchResultConfig: { detailFields: [] },
|
||||
|
@ -116,9 +160,25 @@ describe('SearchResults', () => {
|
|||
wrapper
|
||||
.find('[data-test-subj="DescriptionFieldSelect"]')
|
||||
.simulate('change', { target: { value: LEAVE_UNASSIGNED_FIELD } });
|
||||
wrapper
|
||||
.find('[data-test-subj="TypeFieldSelect"]')
|
||||
.simulate('change', { target: { value: LEAVE_UNASSIGNED_FIELD } });
|
||||
wrapper
|
||||
.find('[data-test-subj="MediaTypeFieldSelect"]')
|
||||
.simulate('change', { target: { value: LEAVE_UNASSIGNED_FIELD } });
|
||||
wrapper
|
||||
.find('[data-test-subj="CreatedByFieldSelect"]')
|
||||
.simulate('change', { target: { value: LEAVE_UNASSIGNED_FIELD } });
|
||||
wrapper
|
||||
.find('[data-test-subj="UpdatedByFieldSelect"]')
|
||||
.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);
|
||||
expect(setTypeField).toHaveBeenCalledWith(null);
|
||||
expect(setMediaTypeField).toHaveBeenCalledWith(null);
|
||||
expect(setCreatedByField).toHaveBeenCalledWith(null);
|
||||
expect(setUpdatedByField).toHaveBeenCalledWith(null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -42,15 +42,33 @@ export const SearchResults: React.FC = () => {
|
|||
toggleTitleFieldHover,
|
||||
toggleSubtitleFieldHover,
|
||||
toggleDescriptionFieldHover,
|
||||
toggleTypeFieldHover,
|
||||
toggleMediaTypeFieldHover,
|
||||
toggleCreatedByFieldHover,
|
||||
toggleUpdatedByFieldHover,
|
||||
setTitleField,
|
||||
setSubtitleField,
|
||||
setDescriptionField,
|
||||
setTypeField,
|
||||
setMediaTypeField,
|
||||
setCreatedByField,
|
||||
setUpdatedByField,
|
||||
setUrlField,
|
||||
setColorField,
|
||||
} = useActions(DisplaySettingsLogic);
|
||||
|
||||
const {
|
||||
searchResultConfig: { titleField, descriptionField, subtitleField, urlField, color },
|
||||
searchResultConfig: {
|
||||
titleField,
|
||||
descriptionField,
|
||||
subtitleField,
|
||||
typeField,
|
||||
mediaTypeField,
|
||||
createdByField,
|
||||
updatedByField,
|
||||
urlField,
|
||||
color,
|
||||
},
|
||||
fieldOptions,
|
||||
optionalFieldOptions,
|
||||
} = useValues(DisplaySettingsLogic);
|
||||
|
@ -136,6 +154,82 @@ export const SearchResults: React.FC = () => {
|
|||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label="Type"
|
||||
helpText="This area is optional"
|
||||
onMouseOver={toggleTypeFieldHover}
|
||||
onFocus={toggleTypeFieldHover}
|
||||
onMouseOut={toggleTypeFieldHover}
|
||||
onBlur={toggleTypeFieldHover}
|
||||
>
|
||||
<EuiSelect
|
||||
options={optionalFieldOptions}
|
||||
className="field-selector"
|
||||
hasNoInitialSelection
|
||||
data-test-subj="TypeFieldSelect"
|
||||
value={typeField || LEAVE_UNASSIGNED_FIELD}
|
||||
onChange={({ target: { value } }) =>
|
||||
setTypeField(value === LEAVE_UNASSIGNED_FIELD ? null : value)
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label="Media Type"
|
||||
helpText="This area is optional"
|
||||
onMouseOver={toggleMediaTypeFieldHover}
|
||||
onFocus={toggleMediaTypeFieldHover}
|
||||
onMouseOut={toggleMediaTypeFieldHover}
|
||||
onBlur={toggleMediaTypeFieldHover}
|
||||
>
|
||||
<EuiSelect
|
||||
options={optionalFieldOptions}
|
||||
className="field-selector"
|
||||
hasNoInitialSelection
|
||||
data-test-subj="MediaTypeFieldSelect"
|
||||
value={mediaTypeField || LEAVE_UNASSIGNED_FIELD}
|
||||
onChange={({ target: { value } }) =>
|
||||
setMediaTypeField(value === LEAVE_UNASSIGNED_FIELD ? null : value)
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label="Created By"
|
||||
helpText="This area is optional"
|
||||
onMouseOver={toggleCreatedByFieldHover}
|
||||
onFocus={toggleCreatedByFieldHover}
|
||||
onMouseOut={toggleCreatedByFieldHover}
|
||||
onBlur={toggleCreatedByFieldHover}
|
||||
>
|
||||
<EuiSelect
|
||||
options={optionalFieldOptions}
|
||||
className="field-selector"
|
||||
hasNoInitialSelection
|
||||
data-test-subj="CreatedByFieldSelect"
|
||||
value={createdByField || LEAVE_UNASSIGNED_FIELD}
|
||||
onChange={({ target: { value } }) =>
|
||||
setCreatedByField(value === LEAVE_UNASSIGNED_FIELD ? null : value)
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label="Updated By"
|
||||
helpText="This area is optional"
|
||||
onMouseOver={toggleUpdatedByFieldHover}
|
||||
onFocus={toggleUpdatedByFieldHover}
|
||||
onMouseOut={toggleUpdatedByFieldHover}
|
||||
onBlur={toggleUpdatedByFieldHover}
|
||||
>
|
||||
<EuiSelect
|
||||
options={optionalFieldOptions}
|
||||
className="field-selector"
|
||||
hasNoInitialSelection
|
||||
data-test-subj="UpdatedByFieldSelect"
|
||||
value={updatedByField || LEAVE_UNASSIGNED_FIELD}
|
||||
onChange={({ target: { value } }) =>
|
||||
setUpdatedByField(value === LEAVE_UNASSIGNED_FIELD ? null : value)
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiForm>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
|
|
|
@ -45,9 +45,13 @@ const displayFieldSchema = schema.object({
|
|||
|
||||
const displaySettingsSchema = schema.object({
|
||||
titleField: schema.maybe(schema.string()),
|
||||
subtitleField: schema.maybe(schema.string()),
|
||||
descriptionField: schema.maybe(schema.string()),
|
||||
subtitleField: schema.nullable(schema.string()),
|
||||
descriptionField: schema.nullable(schema.string()),
|
||||
urlField: schema.maybe(schema.string()),
|
||||
typeField: schema.nullable(schema.string()),
|
||||
mediaTypeField: schema.nullable(schema.string()),
|
||||
createdByField: schema.nullable(schema.string()),
|
||||
updatedByField: schema.nullable(schema.string()),
|
||||
color: schema.string(),
|
||||
urlFieldIsLinkable: schema.boolean(),
|
||||
detailFields: schema.oneOf([schema.arrayOf(displayFieldSchema), displayFieldSchema]),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue