Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

App Search: Result Component Updates #96184

Merged
merged 8 commits into from
Apr 8, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { EuiButtonIconColor } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

export const CURATIONS_TITLE = i18n.translate(
Expand Down Expand Up @@ -49,26 +50,26 @@ export const PROMOTE_DOCUMENT_ACTION = {
defaultMessage: 'Promote this result',
}),
iconType: 'starPlusEmpty',
iconColor: 'primary',
iconColor: 'primary' as EuiButtonIconColor,
};
export const DEMOTE_DOCUMENT_ACTION = {
title: i18n.translate('xpack.enterpriseSearch.appSearch.engine.curations.demoteButtonLabel', {
defaultMessage: 'Demote this result',
}),
iconType: 'starMinusFilled',
iconColor: 'primary',
iconColor: 'primary' as EuiButtonIconColor,
};
export const HIDE_DOCUMENT_ACTION = {
title: i18n.translate('xpack.enterpriseSearch.appSearch.engine.curations.hideButtonLabel', {
defaultMessage: 'Hide this result',
}),
iconType: 'eyeClosed',
iconColor: 'danger',
iconColor: 'danger' as EuiButtonIconColor,
};
export const SHOW_DOCUMENT_ACTION = {
title: i18n.translate('xpack.enterpriseSearch.appSearch.engine.curations.showButtonLabel', {
defaultMessage: 'Show this result',
}),
iconType: 'eye',
iconColor: 'primary',
iconColor: 'primary' as EuiButtonIconColor,
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
EuiDragDropContext,
EuiDroppable,
EuiDraggable,
EuiButtonIconColor,
} from '@elastic/eui';

import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
Expand Down Expand Up @@ -78,7 +79,7 @@ export const Library: React.FC = () => {
title: 'Fill this action button',
onClick: () => setIsActionButtonFilled(!isActionButtonFilled),
iconType: isActionButtonFilled ? 'starFilled' : 'starEmpty',
iconColor: 'primary',
iconColor: 'primary' as EuiButtonIconColor,
},
];

Expand Down Expand Up @@ -221,7 +222,7 @@ export const Library: React.FC = () => {
<h3>With custom actions and a link</h3>
</EuiTitle>
<EuiSpacer />
<Result {...props} actions={actions} shouldLinkToDetailPage />
<Result {...props} actions={actions} shouldLinkToDetailPage showScore isMetaEngine />
<EuiSpacer />

<EuiSpacer />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
*/

export { ResultFieldValue } from './result_field_value';
export { ResultToken } from './result_token';
export { Result } from './result';
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
'drag content actions'
'drag toggle actions';
overflow: hidden; // Prevents child background-colors from clipping outside of panel border-radius
border: $euiBorderThin; // TODO: Remove after EUI version is bumped beyond 31.8.0

&__content {
grid-area: content;
Expand Down Expand Up @@ -44,9 +45,13 @@
display: flex;
justify-content: center;
align-items: center;
width: $euiSizeL * 2;
width: $euiSize * 2;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
width: $euiSize * 2;
width: $euiSizeXL;

border-left: $euiBorderThin;

&:first-child {
border-left: none;
}

&:hover,
&:focus {
background-color: $euiPageBackgroundColor;
Expand All @@ -62,22 +67,3 @@
border-right: $euiBorderThin;
}
}

/**
* CSS for hover specific logic
* It's mildly horrific, so I pulled it out to its own section here
Copy link
Member

Choose a reason for hiding this comment

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

💯 I'm super glad I pulled this out into its own easily delete-able section. H E L L Y E A

*/

.appSearchResult--link {
&:hover,
&:focus {
@include euiSlightShadowHover;
}
}
.appSearchResult__content--link:hover {
cursor: pointer;

& ~ .appSearchResult__actionButtons .appSearchResult__actionButton--link {
background-color: $euiPageBackgroundColor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import { DraggableProvidedDragHandleProps } from 'react-beautiful-dnd';

import { shallow, ShallowWrapper } from 'enzyme';

import { EuiPanel } from '@elastic/eui';
import { EuiButtonIcon, EuiPanel } from '@elastic/eui';

import { ReactRouterHelper } from '../../../shared/react_router_helpers/eui_components';
import { SchemaTypes } from '../../../shared/types';

import { Result } from './result';
Expand Down Expand Up @@ -64,37 +63,18 @@ describe('Result', () => {
]);
});

it('passes showScore, resultMeta, and isMetaEngine to ResultHeader', () => {
it('renders a header', () => {
const wrapper = shallow(<Result {...props} showScore isMetaEngine />);
expect(wrapper.find(ResultHeader).props()).toEqual({
isMetaEngine: true,
showScore: true,
resultMeta: {
id: '1',
score: 100,
engine: 'my-engine',
},
});
});

describe('document detail link', () => {
it('will render a link if shouldLinkToDetailPage is true', () => {
const wrapper = shallow(<Result {...props} shouldLinkToDetailPage />);
wrapper.find(ReactRouterHelper).forEach((link) => {
expect(link.prop('to')).toEqual('/engines/my-engine/documents/1');
});
expect(wrapper.hasClass('appSearchResult--link')).toBe(true);
expect(wrapper.find('.appSearchResult__content--link').exists()).toBe(true);
expect(wrapper.find('.appSearchResult__actionButton--link').exists()).toBe(true);
});

it('will not render a link if shouldLinkToDetailPage is not set', () => {
const wrapper = shallow(<Result {...props} />);
expect(wrapper.find(ReactRouterHelper).exists()).toBe(false);
expect(wrapper.hasClass('appSearchResult--link')).toBe(false);
expect(wrapper.find('.appSearchResult__content--link').exists()).toBe(false);
expect(wrapper.find('.appSearchResult__actionButton--link').exists()).toBe(false);
});
const header = wrapper.find(ResultHeader);
expect(header.exists()).toBe(true);
expect(header.prop('isMetaEngine')).toBe(true); // passed through from props
expect(header.prop('showScore')).toBe(true); // passed through from props
expect(header.prop('shouldLinkToDetailPage')).toBe(false); // passed through from props
expect(header.prop('resultMeta')).toEqual({
id: '1',
score: 100,
engine: 'my-engine',
}); // passed through from meta in result prop
});

describe('actions', () => {
Expand All @@ -109,24 +89,48 @@ describe('Result', () => {
title: 'Bookmark',
onClick: jest.fn(),
iconType: 'starFilled',
iconColor: 'primary',
iconColor: '',
},
];

it('will render an action button for each action passed', () => {
it('will render an action button in the header for each action passed', () => {
const wrapper = shallow(<Result {...props} actions={actions} />);
expect(wrapper.find('.appSearchResult__actionButton')).toHaveLength(2);

wrapper.find('.appSearchResult__actionButton').first().simulate('click');
const header = wrapper.find(ResultHeader);
const renderedActions = shallow(header.prop('actions') as any);
const buttons = renderedActions.find(EuiButtonIcon);
expect(buttons).toHaveLength(2);

// console.log(buttons.first().props());
expect(buttons.first().prop('iconType')).toEqual('eyeClosed');
expect(buttons.first().prop('color')).toEqual('danger');
buttons.first().simulate('click');
expect(actions[0].onClick).toHaveBeenCalled();

wrapper.find('.appSearchResult__actionButton').last().simulate('click');
expect(buttons.last().prop('iconType')).toEqual('starFilled');
// Note that no iconColor was passed so it was defaulted to primary
expect(buttons.last().prop('color')).toEqual('primary');
buttons.last().simulate('click');
expect(actions[1].onClick).toHaveBeenCalled();
});

it('will render custom actions seamlessly next to the document detail link', () => {
it('will render a document detail link as the first action if shouldLinkToDetailPage is passed', () => {
const wrapper = shallow(<Result {...props} actions={actions} shouldLinkToDetailPage />);
expect(wrapper.find('.appSearchResult__actionButton')).toHaveLength(3);
const header = wrapper.find(ResultHeader);
const renderedActions = shallow(header.prop('actions') as any);
const buttons = renderedActions.find(EuiButtonIcon);

// In addition to the 2 actions passed, we also have an action
expect(buttons).toHaveLength(3);

expect(buttons.first().prop('data-test-subj')).toEqual('DocumentDetailLink');
});

it('will not render anything if no actions are passed and shouldLinkToDetailPage is false', () => {
const wrapper = shallow(<Result {...props} actions={undefined} />);
const header = wrapper.find(ResultHeader);
const renderedActions = shallow(header.prop('actions') as any);
const buttons = renderedActions.find(EuiButtonIcon);
expect(buttons).toHaveLength(0);
});
});

Expand Down
Loading