Skip to content

Commit

Permalink
[Dashboard] Editing toolbar update (#154966)
Browse files Browse the repository at this point in the history
  • Loading branch information
cqliu1 committed Jul 5, 2023
1 parent 2049683 commit 112a0f9
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 115 deletions.
Expand Up @@ -22,12 +22,13 @@ const label = {
/**
* A button that acts to add an item from the library to a solution, typically through a modal.
*/
export const AddFromLibraryButton = ({ onClick, ...rest }: Props) => (
export const AddFromLibraryButton = ({ onClick, size = 'm', ...rest }: Props) => (
<ToolbarButton
{...rest}
type="empty"
onClick={onClick}
iconType="folderOpen"
size={size}
label={label.getLibraryButtonLabel()}
/>
);
Expand Up @@ -42,14 +42,16 @@ export interface Props {
legend: EuiButtonGroupProps['legend'];
/** Array of `IconButton` */
buttons: IconButton[];
/** Button size */
buttonSize?: EuiButtonGroupProps['buttonSize'];
}

type Option = EuiButtonGroupOptionProps & Omit<IconButton, 'label'>;

/**
* A group of buttons each performing an action, represented by an icon.
*/
export const IconButtonGroup = ({ buttons, legend }: Props) => {
export const IconButtonGroup = ({ buttons, legend, buttonSize = 'm' }: Props) => {
const euiTheme = useEuiTheme();
const iconButtonGroupStyles = IconButtonGroupStyles(euiTheme);

Expand All @@ -71,7 +73,7 @@ export const IconButtonGroup = ({ buttons, legend }: Props) => {

return (
<EuiButtonGroup
buttonSize="m"
buttonSize={buttonSize}
legend={legend}
options={buttonGroupOptions}
onChange={onChangeIconsMulti}
Expand Down
Expand Up @@ -18,7 +18,10 @@ type ToolbarButtonTypes = 'primary' | 'empty';
* Props for `PrimaryButton`.
*/
export interface Props
extends Pick<EuiButtonPropsForButton, 'onClick' | 'iconType' | 'iconSide' | 'data-test-subj'> {
extends Pick<
EuiButtonPropsForButton,
'onClick' | 'iconType' | 'iconSide' | 'size' | 'data-test-subj'
> {
label: string;
type?: ToolbarButtonTypes;
}
Expand All @@ -27,6 +30,7 @@ export const ToolbarButton: React.FunctionComponent<Props> = ({
label,
type = 'empty',
iconSide = 'left',
size = 'm',
...rest
}) => {
const euiTheme = useEuiTheme();
Expand All @@ -36,7 +40,7 @@ export const ToolbarButton: React.FunctionComponent<Props> = ({
: { color: 'text', css: ToolbarButtonStyles(euiTheme).emptyButton };

return (
<EuiButton size="m" {...toolbarButtonStyleProps} {...{ iconSide, ...rest }}>
<EuiButton size={size} {...toolbarButtonStyleProps} {...{ iconSide, ...rest }}>
{label}
</EuiButton>
);
Expand Down
10 changes: 9 additions & 1 deletion packages/shared-ux/button_toolbar/src/popover/popover.tsx
Expand Up @@ -29,7 +29,14 @@ export type Props = AllowedButtonProps &
/**
* A button which opens a popover of additional actions within the toolbar.
*/
export const ToolbarPopover = ({ type, label, iconType, children, ...popover }: Props) => {
export const ToolbarPopover = ({
type,
label,
iconType,
size = 'm',
children,
...popover
}: Props) => {
const [isOpen, setIsOpen] = useState(false);

const onButtonClick = () => setIsOpen((status) => !status);
Expand All @@ -38,6 +45,7 @@ export const ToolbarPopover = ({ type, label, iconType, children, ...popover }:
const button = (
<ToolbarButton
onClick={onButtonClick}
size={size}
{...{ type, label, iconType: iconType || 'arrowDown', iconSide: iconType ? 'left' : 'right' }}
/>
);
Expand Down
Expand Up @@ -43,7 +43,7 @@ export const AddTimeSliderControlButton = ({ closePopover, controlGroup, ...rest
return (
<EuiContextMenuItem
{...rest}
icon="plusInCircle"
icon="timeslider"
onClick={async () => {
await controlGroup.addTimeSliderControl();
dashboard.scrollToTop();
Expand Down
Expand Up @@ -27,6 +27,8 @@ export function ControlsToolbarButton({ controlGroup }: { controlGroup: ControlG
panelPaddingSize="none"
label={getControlButtonTitle()}
zIndex={Number(euiTheme.levels.header) - 1}
size="s"
iconType="controlsHorizontal"
data-test-subj="dashboard-controls-menu-button"
>
{({ closePopover }: { closePopover: () => void }) => (
Expand Down
Expand Up @@ -9,22 +9,13 @@
import { css } from '@emotion/react';
import React, { useCallback } from 'react';
import { METRIC_TYPE } from '@kbn/analytics';
import { IconType, useEuiTheme } from '@elastic/eui';

import {
AddFromLibraryButton,
IconButton,
IconButtonGroup,
Toolbar,
ToolbarButton,
} from '@kbn/shared-ux-button-toolbar';
import { useEuiTheme } from '@elastic/eui';

import { AddFromLibraryButton, Toolbar, ToolbarButton } from '@kbn/shared-ux-button-toolbar';
import { EmbeddableFactory } from '@kbn/embeddable-plugin/public';
import { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public';

import {
getCreateVisualizationButtonTitle,
getQuickCreateButtonGroupLegend,
} from '../_dashboard_app_strings';
import { getCreateVisualizationButtonTitle } from '../_dashboard_app_strings';
import { EditorMenu } from './editor_menu';
import { useDashboardAPI } from '../dashboard_app';
import { pluginServices } from '../../services/plugin_services';
Expand All @@ -37,8 +28,8 @@ export function DashboardEditingToolbar() {
usageCollection,
data: { search },
notifications: { toasts },
embeddable: { getStateTransfer, getEmbeddableFactory },
visualizations: { get: getVisualization, getAliases: getVisTypeAliases },
embeddable: { getStateTransfer },
visualizations: { getAliases: getVisTypeAliases },
} = pluginServices.getServices();
const { euiTheme } = useEuiTheme();

Expand All @@ -47,13 +38,6 @@ export function DashboardEditingToolbar() {
const stateTransferService = getStateTransfer();

const lensAlias = getVisTypeAliases().find(({ name }) => name === 'lens');
const quickButtonVisTypes: Array<
{ type: 'vis'; visType: string } | { type: 'embeddable'; embeddableType: string }
> = [
{ type: 'vis', visType: 'markdown' },
{ type: 'embeddable', embeddableType: 'image' },
{ type: 'vis', visType: 'maps' },
];

const trackUiMetric = usageCollection.reportUiCounter?.bind(
usageCollection,
Expand Down Expand Up @@ -121,61 +105,11 @@ export function DashboardEditingToolbar() {
[trackUiMetric, dashboard, toasts]
);

const getVisTypeQuickButton = (
quickButtonForType: typeof quickButtonVisTypes[0]
): IconButton | undefined => {
if (quickButtonForType.type === 'vis') {
const visTypeName = quickButtonForType.visType;
const visType =
getVisualization(visTypeName) ||
getVisTypeAliases().find(({ name }) => name === visTypeName);

if (visType) {
if ('aliasPath' in visType) {
const { name, icon, title } = visType as VisTypeAlias;
return {
label: title,
iconType: icon,
onClick: createNewVisType(visType as VisTypeAlias),
'data-test-subj': `dashboardQuickButton${name}`,
};
} else {
const { name, icon, title, titleInWizard } = visType as BaseVisType & { icon: IconType };
return {
label: titleInWizard || title,
iconType: icon,
onClick: createNewVisType(visType as BaseVisType),
'data-test-subj': `dashboardQuickButton${name}`,
};
}
}
} else {
const embeddableType = quickButtonForType.embeddableType;
const embeddableFactory = getEmbeddableFactory(embeddableType);
if (embeddableFactory) {
return {
label: embeddableFactory.getDisplayName(),
iconType: embeddableFactory.getIconType(),
onClick: () => {
if (embeddableFactory) {
createNewEmbeddable(embeddableFactory);
}
},
'data-test-subj': `dashboardQuickButton${embeddableType}`,
};
}
}
};

const quickButtons: IconButton[] = quickButtonVisTypes.reduce((accumulator, type) => {
const button = getVisTypeQuickButton(type);
return button ? [...accumulator, button] : accumulator;
}, [] as IconButton[]);

const extraButtons = [
<EditorMenu createNewVisType={createNewVisType} createNewEmbeddable={createNewEmbeddable} />,
<AddFromLibraryButton
onClick={() => dashboard.addFromLibrary()}
size="s"
data-test-subj="dashboardAddPanelButton"
/>,
];
Expand All @@ -195,14 +129,12 @@ export function DashboardEditingToolbar() {
<ToolbarButton
type="primary"
iconType="lensApp"
size="s"
onClick={createNewVisType(lensAlias)}
label={getCreateVisualizationButtonTitle()}
data-test-subj="dashboardAddNewPanelButton"
/>
),
iconButtonGroup: (
<IconButtonGroup buttons={quickButtons} legend={getQuickCreateButtonGroupLegend()} />
),
extraButtons,
}}
</Toolbar>
Expand Down
23 changes: 14 additions & 9 deletions src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx
Expand Up @@ -87,16 +87,18 @@ export const EditorMenu = ({ createNewVisType, createNewEmbeddable }: Props) =>

const getSortedVisTypesByGroup = (group: VisGroups) =>
getVisTypesByGroup(group)
.sort(({ name: a }: BaseVisType | VisTypeAlias, { name: b }: BaseVisType | VisTypeAlias) => {
if (a < b) {
.sort((a: BaseVisType | VisTypeAlias, b: BaseVisType | VisTypeAlias) => {
const labelA = 'titleInWizard' in a ? a.titleInWizard || a.title : a.title;
const labelB = 'titleInWizard' in b ? b.titleInWizard || a.title : a.title;
if (labelA < labelB) {
return -1;
}
if (a > b) {
if (labelA > labelB) {
return 1;
}
return 0;
})
.filter(({ disableCreate, stage }: BaseVisType) => !disableCreate);
.filter(({ disableCreate }: BaseVisType) => !disableCreate);

const promotedVisTypes = getSortedVisTypesByGroup(VisGroups.PROMOTED);
const aggsBasedVisTypes = getSortedVisTypesByGroup(VisGroups.AGGBASED);
Expand Down Expand Up @@ -220,15 +222,17 @@ export const EditorMenu = ({ createNewVisType, createNewEmbeddable }: Props) =>
const getEditorMenuPanels = (closePopover: () => void) => {
const initialPanelItems = [
...visTypeAliases.map(getVisTypeAliasMenuItem),
...toolVisTypes.map(getVisTypeMenuItem),
...ungroupedFactories.map((factory) => {
return getEmbeddableFactoryMenuItem(factory, closePopover);
}),
...Object.values(factoryGroupMap).map(({ id, appName, icon, panelId }) => ({
name: appName,
icon,
panel: panelId,
'data-test-subj': `dashboardEditorMenu-${id}Group`,
})),
...ungroupedFactories.map((factory) => {
return getEmbeddableFactoryMenuItem(factory, closePopover);
}),

...promotedVisTypes.map(getVisTypeMenuItem),
];
if (aggsBasedVisTypes.length > 0) {
Expand All @@ -239,7 +243,6 @@ export const EditorMenu = ({ createNewVisType, createNewEmbeddable }: Props) =>
'data-test-subj': `dashboardEditorAggBasedMenuItem`,
});
}
initialPanelItems.push(...toolVisTypes.map(getVisTypeMenuItem));

return [
{
Expand Down Expand Up @@ -268,8 +271,10 @@ export const EditorMenu = ({ createNewVisType, createNewEmbeddable }: Props) =>
repositionOnScroll
ownFocus
label={i18n.translate('dashboard.solutionToolbar.editorMenuButtonLabel', {
defaultMessage: 'Select type',
defaultMessage: 'Add panel',
})}
size="s"
iconType="plusInCircle"
panelPaddingSize="none"
data-test-subj="dashboardEditorMenuButton"
>
Expand Down
Expand Up @@ -103,21 +103,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.waitForRenderComplete();
});

it('adds a markdown visualization via the quick button', async () => {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
await dashboardAddPanel.clickMarkdownQuickButton();
await PageObjects.visualize.saveVisualizationExpectSuccess(
'visualization from markdown quick button',
{ redirectToOrigin: true }
);

await retry.try(async () => {
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(originalPanelCount + 1);
});
await PageObjects.dashboard.waitForRenderComplete();
});

it('saves the listing page instead of the visualization to the app link', async () => {
await PageObjects.header.clickVisualize(true);
const currentUrl = await browser.getCurrentUrl();
Expand Down
Expand Up @@ -12,6 +12,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'dashboard', 'discover', 'header']);
const testSubjects = getService('testSubjects');
const dashboardAddPanel = getService('dashboardAddPanel');
const kibanaServer = getService('kibanaServer');
const retry = getService('retry');

Expand All @@ -36,7 +37,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {

it('should create an image embeddable', async () => {
// create an image embeddable
await testSubjects.click(`dashboardQuickButtonimage`);
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAddNewEmbeddableLink('image');
await testSubjects.exists(`createImageEmbeddableFlyout`);
await PageObjects.common.setFileInputPath(require.resolve('./elastic_logo.png'));
await testSubjects.clickWhenNotDisabled(`imageEmbeddableEditorSave`);
Expand Down
11 changes: 4 additions & 7 deletions test/functional/services/dashboard/add_panel.ts
Expand Up @@ -38,17 +38,14 @@ export class DashboardAddPanelService extends FtrService {
});
}

async clickQuickButton(visType: string) {
this.log.debug(`DashboardAddPanel.clickQuickButton${visType}`);
await this.testSubjects.click(`dashboardQuickButton${visType}`);
}

async clickMarkdownQuickButton() {
await this.clickQuickButton('markdown');
await this.clickEditorMenuButton();
await this.clickVisType('markdown');
}

async clickMapQuickButton() {
await this.clickQuickButton('map');
await this.clickEditorMenuButton();
await this.clickVisType('map');
}

async clickEditorMenuButton() {
Expand Down

0 comments on commit 112a0f9

Please sign in to comment.