kibana/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx
Marta Bondyra 2cf4e72394
[Lens] (Accessibility) Added focus state and accessible name to suggestions (#84653)
* [Lens] (Accessibility) Added focus state and accessible name to suggestions

* Apply suggestions from code review

* Update x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx

padding oops

* cr
2020-12-04 19:28:33 +01:00

294 lines
9 KiB
TypeScript

/*
* 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 { mountWithIntl as mount } from '@kbn/test/jest';
import { Visualization } from '../../types';
import {
createMockVisualization,
createMockDatasource,
createExpressionRendererMock,
DatasourceMock,
createMockFramePublicAPI,
} from '../mocks';
import { act } from 'react-dom/test-utils';
import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public';
import { esFilters, IFieldType, IIndexPattern } from '../../../../../../src/plugins/data/public';
import { SuggestionPanel, SuggestionPanelProps } from './suggestion_panel';
import { getSuggestions, Suggestion } from './suggestion_helpers';
import { EuiIcon, EuiPanel, EuiToolTip } from '@elastic/eui';
import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks';
import { LensIconChartDatatable } from '../../assets/chart_datatable';
jest.mock('./suggestion_helpers');
const getSuggestionsMock = getSuggestions as jest.Mock;
describe('suggestion_panel', () => {
let mockVisualization: Visualization;
let mockDatasource: DatasourceMock;
let expressionRendererMock: ReactExpressionRendererType;
let dispatchMock: jest.Mock;
const suggestion1State = { suggestion1: true };
const suggestion2State = { suggestion2: true };
let defaultProps: SuggestionPanelProps;
beforeEach(() => {
mockVisualization = createMockVisualization();
mockDatasource = createMockDatasource('a');
expressionRendererMock = createExpressionRendererMock();
dispatchMock = jest.fn();
getSuggestionsMock.mockReturnValue([
{
datasourceState: {},
previewIcon: 'empty',
score: 0.5,
visualizationState: suggestion1State,
visualizationId: 'vis',
title: 'Suggestion1',
keptLayerIds: ['a'],
},
{
datasourceState: {},
previewIcon: 'empty',
score: 0.5,
visualizationState: suggestion2State,
visualizationId: 'vis',
title: 'Suggestion2',
keptLayerIds: ['a'],
},
] as Suggestion[]);
defaultProps = {
activeDatasourceId: 'mock',
datasourceMap: {
mock: mockDatasource,
},
datasourceStates: {
mock: {
isLoading: false,
state: {},
},
},
activeVisualizationId: 'vis',
visualizationMap: {
vis: mockVisualization,
vis2: createMockVisualization(),
},
visualizationState: {},
dispatch: dispatchMock,
ExpressionRenderer: expressionRendererMock,
frame: createMockFramePublicAPI(),
plugins: { data: dataPluginMock.createStartContract() },
};
});
it('should list passed in suggestions', () => {
const wrapper = mount(<SuggestionPanel {...defaultProps} />);
expect(
wrapper
.find('[data-test-subj="lnsSuggestion"]')
.find(EuiPanel)
.map((el) => el.parents(EuiToolTip).prop('content'))
).toEqual(['Current visualization', 'Suggestion1', 'Suggestion2']);
});
describe('uncommitted suggestions', () => {
let suggestionState: Pick<
SuggestionPanelProps,
'datasourceStates' | 'activeVisualizationId' | 'visualizationState'
>;
let stagedPreview: SuggestionPanelProps['stagedPreview'];
beforeEach(() => {
suggestionState = {
datasourceStates: {
mock: {
isLoading: false,
state: {},
},
},
activeVisualizationId: 'vis2',
visualizationState: {},
};
stagedPreview = {
datasourceStates: defaultProps.datasourceStates,
visualization: {
state: defaultProps.visualizationState,
activeId: defaultProps.activeVisualizationId,
},
};
});
it('should not update suggestions if current state is moved to staged preview', () => {
const wrapper = mount(<SuggestionPanel {...defaultProps} />);
getSuggestionsMock.mockClear();
wrapper.setProps({
stagedPreview,
...suggestionState,
});
wrapper.update();
expect(getSuggestionsMock).not.toHaveBeenCalled();
});
it('should update suggestions if staged preview is removed', () => {
const wrapper = mount(<SuggestionPanel {...defaultProps} />);
getSuggestionsMock.mockClear();
wrapper.setProps({
stagedPreview,
...suggestionState,
});
wrapper.update();
wrapper.setProps({
stagedPreview: undefined,
...suggestionState,
});
wrapper.update();
expect(getSuggestionsMock).toHaveBeenCalledTimes(1);
});
it('should highlight currently active suggestion', () => {
const wrapper = mount(<SuggestionPanel {...defaultProps} />);
act(() => {
wrapper.find('[data-test-subj="lnsSuggestion"]').at(2).simulate('click');
});
wrapper.update();
expect(wrapper.find('[data-test-subj="lnsSuggestion"]').at(2).prop('className')).toContain(
'lnsSuggestionPanel__button-isSelected'
);
});
it('should rollback suggestion if current panel is clicked', () => {
const wrapper = mount(<SuggestionPanel {...defaultProps} />);
act(() => {
wrapper.find('[data-test-subj="lnsSuggestion"]').at(2).simulate('click');
});
wrapper.update();
act(() => {
wrapper.find('[data-test-subj="lnsSuggestion"]').at(0).simulate('click');
});
wrapper.update();
expect(dispatchMock).toHaveBeenCalledWith({
type: 'ROLLBACK_SUGGESTION',
});
});
});
it('should dispatch visualization switch action if suggestion is clicked', () => {
const wrapper = mount(<SuggestionPanel {...defaultProps} />);
act(() => {
wrapper.find('button[data-test-subj="lnsSuggestion"]').at(1).simulate('click');
});
wrapper.update();
expect(dispatchMock).toHaveBeenCalledWith(
expect.objectContaining({
type: 'SELECT_SUGGESTION',
initialState: suggestion1State,
})
);
});
it('should render preview expression if there is one', () => {
mockDatasource.getLayers.mockReturnValue(['first']);
(getSuggestions as jest.Mock).mockReturnValue([
{
datasourceState: {},
previewIcon: 'empty',
score: 0.5,
visualizationState: suggestion1State,
visualizationId: 'vis',
title: 'Suggestion1',
},
{
datasourceState: {},
previewIcon: 'empty',
score: 0.5,
visualizationState: suggestion2State,
visualizationId: 'vis',
title: 'Suggestion2',
},
] as Suggestion[]);
(mockVisualization.toPreviewExpression as jest.Mock).mockReturnValueOnce(undefined);
(mockVisualization.toPreviewExpression as jest.Mock).mockReturnValueOnce('test | expression');
mockDatasource.toExpression.mockReturnValue('datasource_expression');
const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern;
const field = ({ name: 'myfield' } as unknown) as IFieldType;
mount(
<SuggestionPanel
{...defaultProps}
frame={{
...createMockFramePublicAPI(),
filters: [esFilters.buildExistsFilter(field, indexPattern)],
}}
/>
);
expect(expressionRendererMock).toHaveBeenCalledTimes(1);
const passedExpression = (expressionRendererMock as jest.Mock).mock.calls[0][0].expression;
expect(passedExpression).toMatchInlineSnapshot(`
"kibana
| lens_merge_tables layerIds=\\"first\\" tables={datasource_expression}
| test
| expression"
`);
});
it('should render render icon if there is no preview expression', () => {
mockDatasource.getLayers.mockReturnValue(['first']);
getSuggestionsMock.mockReturnValue([
{
datasourceState: {},
previewIcon: LensIconChartDatatable,
score: 0.5,
visualizationState: suggestion1State,
visualizationId: 'vis',
title: 'Suggestion1',
},
{
datasourceState: {},
previewIcon: 'empty',
score: 0.5,
visualizationState: suggestion2State,
visualizationId: 'vis',
title: 'Suggestion2',
previewExpression: 'test | expression',
},
] as Suggestion[]);
(mockVisualization.toPreviewExpression as jest.Mock).mockReturnValueOnce(undefined);
(mockVisualization.toPreviewExpression as jest.Mock).mockReturnValueOnce('test | expression');
// this call will go to the currently active visualization
(mockVisualization.toPreviewExpression as jest.Mock).mockReturnValueOnce('current | preview');
mockDatasource.toExpression.mockReturnValue('datasource_expression');
const wrapper = mount(<SuggestionPanel {...defaultProps} />);
expect(wrapper.find(EuiIcon)).toHaveLength(1);
expect(wrapper.find(EuiIcon).prop('type')).toEqual(LensIconChartDatatable);
});
});