Skip to content

Commit

Permalink
[Visualizations] Show TSVB and Agg based as "legacy" when users creat…
Browse files Browse the repository at this point in the history
…e visualizations from the library (#195966)

## Summary

Fixes #191489

Redesign of "Create Visualization" Modal

To encourage users to adopt Lens and reduce reliance on Agg-based and
TSVB visualizations, we've redesigned the "Create Visualization" modal.
The new layout now features two distinct tabs: one highlighting
recommended visualizations, and another clearly marking legacy options.
This separation ensures users are well-informed about which
visualization types are preferred moving forward.

<img width="757" alt="Screenshot 2024-10-15 at 13 15 49"
src="https://github.com/user-attachments/assets/fe353504-5511-46ab-aef4-5a340650027b">


![image](https://github.com/user-attachments/assets/86c399fc-e737-453f-84e3-938d85aa22d3)
  • Loading branch information
mbondyra authored Oct 21, 2024
1 parent ff44bf2 commit 46eda4c
Show file tree
Hide file tree
Showing 21 changed files with 346 additions and 556 deletions.
4 changes: 2 additions & 2 deletions src/plugins/vis_type_markdown/public/markdown_vis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export const markdownVisDefinition: VisTypeDefinition<MarkdownVisParams> = {
icon: 'visText',
group: VisGroups.TOOLS,
titleInWizard: i18n.translate('visTypeMarkdown.markdownTitleInWizard', {
defaultMessage: 'Text',
defaultMessage: 'Markdown text',
}),
description: i18n.translate('visTypeMarkdown.markdownDescription', {
defaultMessage: 'Add text and images to your dashboard.',
defaultMessage: 'Add custom text or images to dashboards.',
}),
order: 30,
toExpressionAst,
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/vis_types/timeseries/public/metrics_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export const metricsVisDefinition: VisTypeDefinition<
name: VIS_TYPE,
title: i18n.translate('visTypeTimeseries.kbnVisTypes.metricsTitle', { defaultMessage: 'TSVB' }),
description: i18n.translate('visTypeTimeseries.kbnVisTypes.metricsDescription', {
defaultMessage: 'Perform advanced analysis of your time series data.',
defaultMessage: 'Create visualizations using time series data.',
}),
icon: 'visVisualBuilder',
group: VisGroups.LEGACY,
Expand Down
5 changes: 1 addition & 4 deletions src/plugins/vis_types/vega/public/vega_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,9 @@ export const createVegaTypeDefinition = (): VisTypeDefinition<VisParams> => {
title: 'Vega',
getInfoMessage,
description: i18n.translate('visTypeVega.type.vegaDescription', {
defaultMessage: 'Use Vega to create new types of visualizations.',
defaultMessage: 'Use the Vega syntax to create new types of visualizations.',
description: 'Vega and Vega-Lite are product names and should not be translated',
}),
note: i18n.translate('visTypeVega.type.vegaNote', {
defaultMessage: 'Requires knowledge of Vega syntax.',
}),
icon: 'visVega',
group: VisGroups.PROMOTED,
titleInWizard: i18n.translate('visTypeVega.type.vegaTitleInWizard', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,26 @@ describe('AggBasedSelection', () => {
jest.clearAllMocks();
});

it('should call the toggleGroups if the user clicks the goBack link', () => {
const toggleGroups = jest.fn();
it('should call the showMainDialog if the user clicks the goBack link', () => {
const showMainDialog = jest.fn();
const wrapper = mountWithIntl(
<AggBasedSelection
visTypesRegistry={visTypes}
toggleGroups={toggleGroups}
showMainDialog={showMainDialog}
onVisTypeSelected={jest.fn()}
/>
);
const aggBasedGroupCard = wrapper.find('[data-test-subj="goBackLink"]').last();
aggBasedGroupCard.simulate('click');
expect(toggleGroups).toHaveBeenCalled();
expect(showMainDialog).toHaveBeenCalled();
});

describe('filter for visualization types', () => {
it('should render as expected', () => {
const wrapper = mountWithIntl(
<AggBasedSelection
visTypesRegistry={visTypes}
toggleGroups={jest.fn()}
showMainDialog={jest.fn()}
onVisTypeSelected={jest.fn()}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ interface AggBasedSelectionProps {
openedAsRoot?: boolean;
onVisTypeSelected: (visType: BaseVisType) => void;
visTypesRegistry: TypesStart;
toggleGroups: (flag: boolean) => void;
showMainDialog: (flag: boolean) => void;
}
interface AggBasedSelectionState {
query: string;
Expand Down Expand Up @@ -67,7 +67,7 @@ class AggBasedSelection extends React.Component<AggBasedSelectionProps, AggBased
</EuiModalHeader>
<EuiModalBody>
{this.props.openedAsRoot ? null : (
<DialogNavigation goBack={() => this.props.toggleGroups(true)} />
<DialogNavigation goBack={() => this.props.showMainDialog(true)} />
)}
<EuiFieldSearch
placeholder="Filter"
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/visualizations/public/wizard/dialog.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$modalWidth: $euiSizeL * 34;
$modalHeight: $euiSizeL * 30;
$modalWidth: $euiSizeS * 100;
$modalHeight: $euiSizeS * 90;

.visNewVisDialog {
max-width: $modalWidth;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
*/

import React from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import userEvent from '@testing-library/user-event';
import { TypesStart, BaseVisType, VisGroups } from '../../vis_types';
import { GroupSelection } from './group_selection';
import { GroupSelection, GroupSelectionProps } from './group_selection';
import { DocLinksStart } from '@kbn/core/public';
import { VisParams } from '../../../common';
import { render, screen } from '@testing-library/react';
import { I18nProvider } from '@kbn/i18n-react';

describe('GroupSelection', () => {
const defaultVisTypeParams = {
Expand Down Expand Up @@ -99,172 +101,80 @@ describe('GroupSelection', () => {
jest.clearAllMocks();
});

it('should render the header title', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes)}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="groupModalHeader"]').at(0).text()).toBe(
'New visualization'
const renderGroupSelectionComponent = (overrideProps?: Partial<GroupSelectionProps>) => {
return render(
<I18nProvider>
<GroupSelection
tab="recommended"
setTab={jest.fn()}
visTypesRegistry={visTypesRegistry(_visTypes)}
docLinks={docLinks as DocLinksStart}
showMainDialog={jest.fn()}
onVisTypeSelected={jest.fn()}
{...overrideProps}
/>
</I18nProvider>
);
};

it('should render the header title', () => {
renderGroupSelectionComponent();
expect(screen.getByTestId('groupModalHeader')).toHaveTextContent('Create visualization');
});

it('should not render the aggBased group card if no aggBased visualization is registered', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes)}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(false);
it('should not render tabs if no legacy, tools or tsvb visualizations are registered', async () => {
renderGroupSelectionComponent();
expect(screen.queryByRole('tab', { name: /legacy/i })).toBeNull();
expect(screen.queryByRole('tab', { name: /recommended/i })).toBeNull();
});

it('should render the aggBased group card if an aggBased group vis is registered', () => {
it('should render tabs and the aggBased group card if an aggBased group vis is registered', async () => {
const aggBasedVisType = {
name: 'visWithSearch',
title: 'Vis with search',
group: VisGroups.AGGBASED,
stage: 'production',
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, aggBasedVisType] as BaseVisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(true);
});

it('should not render the tools group card if no tools visualization is registered', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes)}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(false);
});
renderGroupSelectionComponent({
visTypesRegistry: visTypesRegistry([..._visTypes, aggBasedVisType] as BaseVisType[]),
tab: 'legacy',
});

it('should render the tools group card if a tools group vis is registered', () => {
const toolsVisType = {
name: 'vis3',
title: 'Vis3',
stage: 'production',
group: VisGroups.TOOLS,
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, toolsVisType] as BaseVisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(true);
expect(screen.queryByRole('tab', { name: /legacy/i })).toBeInTheDocument();
expect(screen.queryByRole('tab', { name: /recommended/i })).toBeInTheDocument();
expect(screen.getByTestId('visType-aggbased')).toHaveTextContent('Aggregation-based');
});

it('should call the toggleGroups if the aggBased group card is clicked', () => {
const toggleGroups = jest.fn();
it('should call the showMainDialog if the aggBased group card is clicked', async () => {
const showMainDialog = jest.fn();
const aggBasedVisType = {
name: 'visWithSearch',
title: 'Vis with search',
group: VisGroups.AGGBASED,
stage: 'production',
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, aggBasedVisType] as BaseVisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={toggleGroups}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
const aggBasedGroupCard = wrapper.find('[data-test-subj="visGroupAggBasedExploreLink"]').last();
aggBasedGroupCard.simulate('click');
expect(toggleGroups).toHaveBeenCalled();
renderGroupSelectionComponent({
showMainDialog,
visTypesRegistry: visTypesRegistry([..._visTypes, aggBasedVisType] as BaseVisType[]),
tab: 'legacy',
});

await userEvent.click(screen.getByRole('button', { name: /Aggregation-based/i }));
expect(showMainDialog).toHaveBeenCalledWith(false);
});

it('should sort promoted visualizations first', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes as BaseVisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
it('should only show promoted visualizations in recommended tab', () => {
renderGroupSelectionComponent();

const cards = [
...new Set(
wrapper.find('[data-test-subj^="visType-"]').map((card) => card.prop('data-test-subj'))
),
];
const cards = screen.getAllByRole('button').map((el) => el.textContent);

expect(cards).toEqual([
'visType-visAliasWithPromotion',
'visType-vis1',
'visType-vis2',
'visType-visWithAliasUrl',
'Vis alias with promotion',
'Vis Type 1',
'Vis Type 2',
'Vis with alias Url',
]);
});

it('should not show tools experimental visualizations if showExperimentalis false', () => {
const expVis = {
name: 'visExp',
title: 'Experimental Vis',
group: VisGroups.TOOLS,
stage: 'experimental',
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, expVis] as BaseVisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={false}
/>
);
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(false);
});

it('should show tools experimental visualizations if showExperimental is true', () => {
const expVis = {
name: 'visExp',
title: 'Experimental Vis',
group: VisGroups.TOOLS,
stage: 'experimental',
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, expVis] as BaseVisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(true);
});
});
Loading

0 comments on commit 46eda4c

Please sign in to comment.