Skip to content

Commit

Permalink
App Search: Result Component Updates (#96184) (#96674)
Browse files Browse the repository at this point in the history
* Starting work on result component

* Write tests

* Fix types

* Cleanup

* Fix type errors

Co-authored-by: Jason Stoltzfus <jastoltz24@gmail.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Davey Holler <daveyholler@gmail.com>
Co-authored-by: Jason Stoltzfus <jastoltz24@gmail.com>
  • Loading branch information
3 people authored Apr 9, 2021
1 parent f73b92d commit 8c8665e
Show file tree
Hide file tree
Showing 17 changed files with 320 additions and 194 deletions.
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;
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
*/

.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, EuiButtonIconColor } 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 @@ -103,30 +83,53 @@ describe('Result', () => {
title: 'Hide',
onClick: jest.fn(),
iconType: 'eyeClosed',
iconColor: 'danger',
iconColor: 'danger' as EuiButtonIconColor,
},
{
title: 'Bookmark',
onClick: jest.fn(),
iconType: 'starFilled',
iconColor: 'primary',
iconColor: undefined,
},
];

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);

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 a link 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

0 comments on commit 8c8665e

Please sign in to comment.