[Dashboard] Makes lens default editor for creating new panels (#96181)

* Makes lens default editor in dashboard

Added all editors menu to dashboard panel toolbar

Fixed toggle on editor menu

Removed unnecessary comments

Added data test subjects to editor menu buttons

Populated editor menu with vis types

Removed unused imports

Fixed imports

Adds showCreateNewMenu prop to AddPanelFlyout

Rearranged order of editor menu options

Fixed ts errors

Added groupnig to embeddable factory

Use embeddable state transfer service to redirect to editors

Added showGroups to TypeSelectionState

Fixed add panel flyout test

Fixed data test subjects

Fixed factory groupings

Removed unused import

Fixed page object

Added telemtry to dashboard toolbar

Added telemtry to editor menu

Fix ml embeddable functional tests

Fix lens dashboard test

Fix empty dashboard test

Fixed ts errors

Fixed time to visualize security test

Fixed empty dashboard test

Fixed clickAddNewEmbeddableLink in dashboardAddPanel service

Fixed agg based vis functional tests

Revert test changes

Fixed typo

Fix tests

Fix more tests

Fix ts errors

Fixed more tests

Fixed toolbar sizes and margins to align with lens

Fix tests

Fixed callbacks

Fixed button prop type

New vis modal copy updates

Added savedObjectMetaData to log stream embeddable factory

Addressed feedback

Fixed ts error

Fix more tests

Fixed ts errors

Updated dashboard empty prompt copy

Adds tooltip to log stream embeddable factory saved object meta data

Made icons monochrome in toolbar

Fixed icon colors in dark mode

Cleaned up css

Fixed ts errors

Updated snapshot

Fixed map icon color

* Added tooltips for ML embeddables

* Restored test

* Added empty dashboard panel test

* Fixed i18n id

* Fix dashboard_embedding test

* Removed unused service

* Fixed i18n error

* Added icon and description properties to embeddable factory definition

* Fixed ts errors

* Fixed expected value

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Catherine Liu 2021-04-17 22:29:27 -07:00 committed by GitHub
parent a89b756710
commit 3b31d81196
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 686 additions and 232 deletions

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) &gt; [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) &gt; [getDescription](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getdescription.md)
## EmbeddableFactory.getDescription() method
Returns a description about the embeddable.
<b>Signature:</b>
```typescript
getDescription(): string;
```
<b>Returns:</b>
`string`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) &gt; [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) &gt; [getIconType](./kibana-plugin-plugins-embeddable-public.embeddablefactory.geticontype.md)
## EmbeddableFactory.getIconType() method
Returns an EUI Icon type to be displayed in a menu.
<b>Signature:</b>
```typescript
getIconType(): string;
```
<b>Returns:</b>
`string`

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) &gt; [EmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablefactory.md) &gt; [grouping](./kibana-plugin-plugins-embeddable-public.embeddablefactory.grouping.md)
## EmbeddableFactory.grouping property
Indicates the grouping this factory should appear in a sub-menu. Example, this is used for grouping options in the editors menu in Dashboard for creating new embeddables
<b>Signature:</b>
```typescript
readonly grouping?: UiActionsPresentableGrouping;
```

View file

@ -16,6 +16,7 @@ export interface EmbeddableFactory<TEmbeddableInput extends EmbeddableInput = Em
| Property | Type | Description |
| --- | --- | --- |
| [grouping](./kibana-plugin-plugins-embeddable-public.embeddablefactory.grouping.md) | <code>UiActionsPresentableGrouping</code> | Indicates the grouping this factory should appear in a sub-menu. Example, this is used for grouping options in the editors menu in Dashboard for creating new embeddables |
| [isContainerType](./kibana-plugin-plugins-embeddable-public.embeddablefactory.iscontainertype.md) | <code>boolean</code> | True if is this factory create embeddables that are Containers. Used in the add panel to conditionally show whether these can be added to another container. It's just not supported right now, but once nested containers are officially supported we can probably get rid of this interface. |
| [isEditable](./kibana-plugin-plugins-embeddable-public.embeddablefactory.iseditable.md) | <code>() =&gt; Promise&lt;boolean&gt;</code> | Returns whether the current user should be allowed to edit this type of embeddable. Most of the time this should be based off the capabilities service, hence it's async. |
| [savedObjectMetaData](./kibana-plugin-plugins-embeddable-public.embeddablefactory.savedobjectmetadata.md) | <code>SavedObjectMetaData&lt;TSavedObjectAttributes&gt;</code> | |
@ -29,6 +30,8 @@ export interface EmbeddableFactory<TEmbeddableInput extends EmbeddableInput = Em
| [create(initialInput, parent)](./kibana-plugin-plugins-embeddable-public.embeddablefactory.create.md) | Resolves to undefined if a new Embeddable cannot be directly created and the user will instead be redirected elsewhere.<!-- -->This will likely change in future iterations when we improve in place editing capabilities. |
| [createFromSavedObject(savedObjectId, input, parent)](./kibana-plugin-plugins-embeddable-public.embeddablefactory.createfromsavedobject.md) | Creates a new embeddable instance based off the saved object id. |
| [getDefaultInput(partial)](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getdefaultinput.md) | Can be used to get any default input, to be passed in to during the creation process. Default input will not be stored in a parent container, so any inherited input from a container will trump default input parameters. |
| [getDescription()](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getdescription.md) | Returns a description about the embeddable. |
| [getDisplayName()](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getdisplayname.md) | Returns a display name for this type of embeddable. Used in "Create new... " options in the add panel for containers. |
| [getExplicitInput()](./kibana-plugin-plugins-embeddable-public.embeddablefactory.getexplicitinput.md) | Can be used to request explicit input from the user, to be passed in to <code>EmbeddableFactory:create</code>. Explicit input is stored on the parent container for this embeddable. It overrides any inherited input passed down from the parent container. |
| [getIconType()](./kibana-plugin-plugins-embeddable-public.embeddablefactory.geticontype.md) | Returns an EUI Icon type to be displayed in a menu. |

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
export declare type EmbeddableFactoryDefinition<I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>, T extends SavedObjectAttributes = SavedObjectAttributes> = Pick<EmbeddableFactory<I, O, E, T>, 'create' | 'type' | 'isEditable' | 'getDisplayName'> & Partial<Pick<EmbeddableFactory<I, O, E, T>, 'createFromSavedObject' | 'isContainerType' | 'getExplicitInput' | 'savedObjectMetaData' | 'canCreateNew' | 'getDefaultInput' | 'telemetry' | 'extract' | 'inject' | 'migrations'>>;
export declare type EmbeddableFactoryDefinition<I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>, T extends SavedObjectAttributes = SavedObjectAttributes> = Pick<EmbeddableFactory<I, O, E, T>, 'create' | 'type' | 'isEditable' | 'getDisplayName'> & Partial<Pick<EmbeddableFactory<I, O, E, T>, 'createFromSavedObject' | 'isContainerType' | 'getExplicitInput' | 'savedObjectMetaData' | 'canCreateNew' | 'getDefaultInput' | 'telemetry' | 'extract' | 'inject' | 'migrations' | 'grouping' | 'getIconType' | 'getDescription'>>;
```

View file

@ -14,6 +14,7 @@ export declare function openAddPanelFlyout(options: {
overlays: OverlayStart;
notifications: NotificationsStart;
SavedObjectFinder: React.ComponentType<any>;
showCreateNewMenu?: boolean;
}): OverlayRef;
```
@ -21,7 +22,7 @@ export declare function openAddPanelFlyout(options: {
| Parameter | Type | Description |
| --- | --- | --- |
| options | <code>{</code><br/><code> embeddable: IContainer;</code><br/><code> getFactory: EmbeddableStart['getEmbeddableFactory'];</code><br/><code> getAllFactories: EmbeddableStart['getEmbeddableFactories'];</code><br/><code> overlays: OverlayStart;</code><br/><code> notifications: NotificationsStart;</code><br/><code> SavedObjectFinder: React.ComponentType&lt;any&gt;;</code><br/><code>}</code> | |
| options | <code>{</code><br/><code> embeddable: IContainer;</code><br/><code> getFactory: EmbeddableStart['getEmbeddableFactory'];</code><br/><code> getAllFactories: EmbeddableStart['getEmbeddableFactories'];</code><br/><code> overlays: OverlayStart;</code><br/><code> notifications: NotificationsStart;</code><br/><code> SavedObjectFinder: React.ComponentType&lt;any&gt;;</code><br/><code> showCreateNewMenu?: boolean;</code><br/><code>}</code> | |
<b>Returns:</b>

View file

@ -11,7 +11,8 @@
"share",
"uiActions",
"urlForwarding",
"presentationUtil"
"presentationUtil",
"visualizations"
],
"optionalPlugins": [
"home",

View file

@ -66,4 +66,17 @@
.dshUnsavedListingItem__actions {
flex-direction: column;
}
}
}
// Temporary fix for two tone icons to make them monochrome
.dshSolutionToolbar__editorContextMenu--dark {
.euiIcon path {
fill: $euiColorGhost;
}
}
.dshSolutionToolbar__editorContextMenu--light {
.euiIcon path {
fill: $euiColorInk;
}
}

View file

@ -80,6 +80,7 @@ export async function mountApp({
embeddable: embeddableStart,
kibanaLegacy: { dashboardConfig },
savedObjectsTaggingOss,
visualizations,
} = pluginsStart;
const spacesApi = pluginsStart.spacesOss?.isSpacesAvailable ? pluginsStart.spacesOss : undefined;
@ -123,6 +124,7 @@ export async function mountApp({
visualizeCapabilities: { save: Boolean(coreStart.application.capabilities.visualize?.save) },
storeSearchSession: Boolean(coreStart.application.capabilities.dashboard.storeSearchSession),
},
visualizations,
};
const getUrlStateStorage = (history: RouteComponentProps['history']) =>

View file

@ -49,7 +49,7 @@ export class DashboardContainerFactoryDefinition
public readonly getDisplayName = () => {
return i18n.translate('dashboard.factory.displayName', {
defaultMessage: 'dashboard',
defaultMessage: 'Dashboard',
});
};

View file

@ -287,7 +287,7 @@ exports[`DashboardEmptyScreen renders correctly with edit mode 1`] = `
<h3
className="euiTitle euiTitle--xsmall"
>
Add your first panel
Add your first visualization
</h3>
</EuiTitle>
<EuiSpacer

View file

@ -23,6 +23,7 @@ import { createKbnUrlStateStorage } from '../../services/kibana_utils';
import { savedObjectsPluginMock } from '../../../../saved_objects/public/mocks';
import { DashboardListing, DashboardListingProps } from './dashboard_listing';
import { embeddablePluginMock } from '../../../../embeddable/public/mocks';
import { visualizationsPluginMock } from '../../../../visualizations/public/mocks';
import { DashboardAppServices, DashboardCapabilities } from '../types';
import { dataPluginMock } from '../../../../data/public/mocks';
import { chromeServiceMock, coreMock } from '../../../../../core/public/mocks';
@ -76,6 +77,7 @@ function makeDefaultServices(): DashboardAppServices {
dashboardPanelStorage,
savedDashboards,
core,
visualizations: visualizationsPluginMock.createStartContract(),
};
}

View file

@ -6,26 +6,24 @@
* Side Public License, v 1.
*/
import { METRIC_TYPE } from '@kbn/analytics';
import { EuiHorizontalRule } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import angular from 'angular';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import UseUnmount from 'react-use/lib/useUnmount';
import { BaseVisType, VisTypeAlias } from '../../../../visualizations/public';
import {
AddFromLibraryButton,
PrimaryActionButton,
QuickButtonGroup,
SolutionToolbar,
QuickButtonProps,
} from '../../../../presentation_util/public';
import { useKibana } from '../../services/kibana_react';
import { IndexPattern, SavedQuery, TimefilterContract } from '../../services/data';
import {
EmbeddableFactoryNotFoundError,
EmbeddableInput,
isErrorEmbeddable,
openAddPanelFlyout,
ViewMode,
} from '../../services/embeddable';
import { isErrorEmbeddable, openAddPanelFlyout, ViewMode } from '../../services/embeddable';
import {
getSavedObjectFinder,
SavedObjectSaveOpts,
@ -55,6 +53,7 @@ import { DashboardConstants } from '../../dashboard_constants';
import { getNewDashboardTitle, unsavedChangesBadge } from '../../dashboard_strings';
import { DASHBOARD_PANELS_UNSAVED_ID } from '../lib/dashboard_panel_storage';
import { DashboardContainer } from '..';
import { EditorMenu } from './editor_menu';
export interface DashboardTopNavState {
chromeIsVisible: boolean;
@ -104,12 +103,22 @@ export function DashboardTopNav({
dashboardCapabilities,
dashboardPanelStorage,
allowByValueEmbeddables,
visualizations,
usageCollection,
} = useKibana<DashboardAppServices>().services;
const [state, setState] = useState<DashboardTopNavState>({ chromeIsVisible: false });
const [isSaveInProgress, setIsSaveInProgress] = useState(false);
const lensAlias = visualizations.getAliases().find(({ name }) => name === 'lens');
const quickButtonVisTypes = ['markdown', 'maps'];
const stateTransferService = embeddable.getStateTransfer();
const IS_DARK_THEME = uiSettings.get('theme:darkMode');
const trackUiMetric = usageCollection?.reportUiCounter.bind(
usageCollection,
DashboardConstants.DASHBOARDS_ID
);
useEffect(() => {
const visibleSubscription = chrome.getIsVisible$().subscribe((chromeIsVisible) => {
@ -152,27 +161,36 @@ export function DashboardTopNav({
uiSettings,
]);
const createNew = useCallback(async () => {
const type = 'visualization';
const factory = embeddable.getEmbeddableFactory(type);
if (!factory) {
throw new EmbeddableFactoryNotFoundError(type);
}
await factory.create({} as EmbeddableInput, dashboardContainer);
}, [dashboardContainer, embeddable]);
const createNewVisType = useCallback(
(newVisType: string) => async () => {
stateTransferService.navigateToEditor('visualize', {
path: `#/create?type=${encodeURIComponent(newVisType)}`,
(visType?: BaseVisType | VisTypeAlias) => () => {
let path = '';
let appId = '';
if (visType) {
if (trackUiMetric) {
trackUiMetric(METRIC_TYPE.CLICK, visType.name);
}
if ('aliasPath' in visType) {
appId = visType.aliasApp;
path = visType.aliasPath;
} else {
appId = 'visualize';
path = `#/create?type=${encodeURIComponent(visType.name)}`;
}
} else {
appId = 'visualize';
path = '#/create?';
}
stateTransferService.navigateToEditor(appId, {
path,
state: {
originatingApp: DashboardConstants.DASHBOARDS_ID,
},
});
},
[stateTransferService]
[trackUiMetric, stateTransferService]
);
const clearAddPanel = useCallback(() => {
@ -563,38 +581,57 @@ export function DashboardTopNav({
const { TopNavMenu } = navigation.ui;
const quickButtons = [
{
iconType: 'visText',
createType: i18n.translate('dashboard.solutionToolbar.markdownQuickButtonLabel', {
defaultMessage: 'Markdown',
}),
onClick: createNewVisType('markdown'),
'data-test-subj': 'dashboardMarkdownQuickButton',
},
{
iconType: 'controlsHorizontal',
createType: i18n.translate('dashboard.solutionToolbar.inputControlsQuickButtonLabel', {
defaultMessage: 'Input control',
}),
onClick: createNewVisType('input_control_vis'),
'data-test-subj': 'dashboardInputControlsQuickButton',
},
];
const getVisTypeQuickButton = (visTypeName: string) => {
const visType =
visualizations.get(visTypeName) ||
visualizations.getAliases().find(({ name }) => name === visTypeName);
if (visType) {
if ('aliasPath' in visType) {
const { name, icon, title } = visType as VisTypeAlias;
return {
iconType: icon,
createType: title,
onClick: createNewVisType(visType as VisTypeAlias),
'data-test-subj': `dashboardQuickButton${name}`,
isDarkModeEnabled: IS_DARK_THEME,
};
} else {
const { name, icon, title, titleInWizard } = visType as BaseVisType;
return {
iconType: icon,
createType: titleInWizard || title,
onClick: createNewVisType(visType as BaseVisType),
'data-test-subj': `dashboardQuickButton${name}`,
isDarkModeEnabled: IS_DARK_THEME,
};
}
}
return;
};
const quickButtons = quickButtonVisTypes
.map(getVisTypeQuickButton)
.filter((button) => button) as QuickButtonProps[];
return (
<>
<TopNavMenu {...getNavBarProps()} />
<EuiHorizontalRule margin="none" />
{viewMode !== ViewMode.VIEW ? (
<SolutionToolbar>
<SolutionToolbar isDarkModeEnabled={IS_DARK_THEME}>
{{
primaryActionButton: (
<PrimaryActionButton
isDarkModeEnabled={IS_DARK_THEME}
label={i18n.translate('dashboard.solutionToolbar.addPanelButtonLabel', {
defaultMessage: 'Create panel',
defaultMessage: 'Create visualization',
})}
onClick={createNew}
iconType="plusInCircleFilled"
onClick={createNewVisType(lensAlias)}
iconType="lensApp"
data-test-subj="dashboardAddNewPanelButton"
/>
),
@ -605,6 +642,12 @@ export function DashboardTopNav({
data-test-subj="dashboardAddPanelButton"
/>
),
extraButtons: [
<EditorMenu
createNewVisType={createNewVisType}
dashboardContainer={dashboardContainer}
/>,
],
}}
</SolutionToolbar>
) : null}

View file

@ -0,0 +1,255 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { useCallback } from 'react';
import {
EuiContextMenu,
EuiContextMenuPanelItemDescriptor,
EuiContextMenuItemIcon,
} from '@elastic/eui';
import { METRIC_TYPE } from '@kbn/analytics';
import { i18n } from '@kbn/i18n';
import { BaseVisType, VisGroups, VisTypeAlias } from '../../../../visualizations/public';
import { SolutionToolbarPopover } from '../../../../presentation_util/public';
import { EmbeddableFactoryDefinition, EmbeddableInput } from '../../services/embeddable';
import { useKibana } from '../../services/kibana_react';
import { DashboardAppServices } from '../types';
import { DashboardContainer } from '..';
import { DashboardConstants } from '../../dashboard_constants';
import { dashboardReplacePanelAction } from '../../dashboard_strings';
interface Props {
/** Dashboard container */
dashboardContainer: DashboardContainer;
/** Handler for creating new visualization of a specified type */
createNewVisType: (visType: BaseVisType | VisTypeAlias) => () => void;
}
interface FactoryGroup {
id: string;
appName: string;
icon: EuiContextMenuItemIcon;
panelId: number;
factories: EmbeddableFactoryDefinition[];
}
export const EditorMenu = ({ dashboardContainer, createNewVisType }: Props) => {
const {
core,
embeddable,
visualizations,
usageCollection,
uiSettings,
} = useKibana<DashboardAppServices>().services;
const IS_DARK_THEME = uiSettings.get('theme:darkMode');
const trackUiMetric = usageCollection?.reportUiCounter.bind(
usageCollection,
DashboardConstants.DASHBOARDS_ID
);
const createNewAggsBasedVis = useCallback(
(visType?: BaseVisType) => () =>
visualizations.showNewVisModal({
originatingApp: DashboardConstants.DASHBOARDS_ID,
outsideVisualizeApp: true,
showAggsSelection: true,
selectedVisType: visType,
}),
[visualizations]
);
const getVisTypesByGroup = (group: VisGroups) =>
visualizations
.getByGroup(group)
.sort(({ name: a }: BaseVisType | VisTypeAlias, { name: b }: BaseVisType | VisTypeAlias) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
})
.filter(({ hidden }: BaseVisType) => !hidden);
const promotedVisTypes = getVisTypesByGroup(VisGroups.PROMOTED);
const aggsBasedVisTypes = getVisTypesByGroup(VisGroups.AGGBASED);
const toolVisTypes = getVisTypesByGroup(VisGroups.TOOLS);
const visTypeAliases = visualizations
.getAliases()
.sort(({ promotion: a = false }: VisTypeAlias, { promotion: b = false }: VisTypeAlias) =>
a === b ? 0 : a ? -1 : 1
);
const factories = embeddable
? Array.from(embeddable.getEmbeddableFactories()).filter(
({ type, isEditable, canCreateNew, isContainerType }) =>
isEditable() && !isContainerType && canCreateNew() && type !== 'visualization'
)
: [];
const factoryGroupMap: Record<string, FactoryGroup> = {};
const ungroupedFactories: EmbeddableFactoryDefinition[] = [];
const aggBasedPanelID = 1;
let panelCount = 1 + aggBasedPanelID;
factories.forEach((factory: EmbeddableFactoryDefinition, index) => {
const { grouping } = factory;
if (grouping) {
grouping.forEach((group) => {
if (factoryGroupMap[group.id]) {
factoryGroupMap[group.id].factories.push(factory);
} else {
factoryGroupMap[group.id] = {
id: group.id,
appName: group.getDisplayName ? group.getDisplayName({ embeddable }) : group.id,
icon: (group.getIconType
? group.getIconType({ embeddable })
: 'empty') as EuiContextMenuItemIcon,
factories: [factory],
panelId: panelCount,
};
panelCount++;
}
});
} else {
ungroupedFactories.push(factory);
}
});
const getVisTypeMenuItem = (visType: BaseVisType): EuiContextMenuPanelItemDescriptor => {
const { name, title, titleInWizard, description, icon = 'empty', group } = visType;
return {
name: titleInWizard || title,
icon: icon as string,
onClick:
group === VisGroups.AGGBASED ? createNewAggsBasedVis(visType) : createNewVisType(visType),
'data-test-subj': `visType-${name}`,
toolTipContent: description,
};
};
const getVisTypeAliasMenuItem = (
visTypeAlias: VisTypeAlias
): EuiContextMenuPanelItemDescriptor => {
const { name, title, description, icon = 'empty' } = visTypeAlias;
return {
name: title,
icon,
onClick: createNewVisType(visTypeAlias),
'data-test-subj': `visType-${name}`,
toolTipContent: description,
};
};
const getEmbeddableFactoryMenuItem = (
factory: EmbeddableFactoryDefinition
): EuiContextMenuPanelItemDescriptor => {
const icon = factory?.getIconType ? factory.getIconType() : 'empty';
const toolTipContent = factory?.getDescription ? factory.getDescription() : undefined;
return {
name: factory.getDisplayName(),
icon,
toolTipContent,
onClick: async () => {
if (trackUiMetric) {
trackUiMetric(METRIC_TYPE.CLICK, factory.type);
}
let newEmbeddable;
if (factory.getExplicitInput) {
const explicitInput = await factory.getExplicitInput();
newEmbeddable = await dashboardContainer.addNewEmbeddable(factory.type, explicitInput);
} else {
newEmbeddable = await factory.create({} as EmbeddableInput, dashboardContainer);
}
if (newEmbeddable) {
core.notifications.toasts.addSuccess({
title: dashboardReplacePanelAction.getSuccessMessage(
`'${newEmbeddable.getInput().title}'` || ''
),
'data-test-subj': 'addEmbeddableToDashboardSuccess',
});
}
},
'data-test-subj': `createNew-${factory.type}`,
};
};
const aggsPanelTitle = i18n.translate('dashboard.editorMenu.aggBasedGroupTitle', {
defaultMessage: 'Aggregation based',
});
const editorMenuPanels = [
{
id: 0,
items: [
...visTypeAliases.map(getVisTypeAliasMenuItem),
...Object.values(factoryGroupMap).map(({ id, appName, icon, panelId }) => ({
name: appName,
icon,
panel: panelId,
'data-test-subj': `dashboardEditorMenu-${id}Group`,
})),
...ungroupedFactories.map(getEmbeddableFactoryMenuItem),
...promotedVisTypes.map(getVisTypeMenuItem),
{
name: aggsPanelTitle,
icon: 'visualizeApp',
panel: aggBasedPanelID,
'data-test-subj': `dashboardEditorAggBasedMenuItem`,
},
...toolVisTypes.map(getVisTypeMenuItem),
],
},
{
id: aggBasedPanelID,
title: aggsPanelTitle,
items: aggsBasedVisTypes.map(getVisTypeMenuItem),
},
...Object.values(factoryGroupMap).map(
({ appName, panelId, factories: groupFactories }: FactoryGroup) => ({
id: panelId,
title: appName,
items: groupFactories.map(getEmbeddableFactoryMenuItem),
})
),
];
return (
<SolutionToolbarPopover
ownFocus
label={i18n.translate('dashboard.solutionToolbar.editorMenuButtonLabel', {
defaultMessage: 'All types',
})}
iconType="arrowDown"
iconSide="right"
panelPaddingSize="none"
data-test-subj="dashboardEditorMenuButton"
>
<EuiContextMenu
initialPanelId={0}
panels={editorMenuPanels}
className={`dshSolutionToolbar__editorContextMenu ${
IS_DARK_THEME
? 'dshSolutionToolbar__editorContextMenu--dark'
: 'dshSolutionToolbar__editorContextMenu--light'
}`}
data-test-subj="dashboardEditorContextMenu"
/>
</SolutionToolbarPopover>
);
};

View file

@ -25,6 +25,7 @@ import { DataPublicPluginStart, IndexPatternsContract } from '../services/data';
import { SavedObjectLoader, SavedObjectsStart } from '../services/saved_objects';
import { DashboardPanelStorage } from './lib';
import { UrlForwardingStart } from '../../../url_forwarding/public';
import { VisualizationsStart } from '../../../visualizations/public';
export type DashboardRedirect = (props: RedirectToProps) => void;
export type RedirectToProps =
@ -83,4 +84,5 @@ export interface DashboardAppServices {
savedObjectsClient: SavedObjectsClientContract;
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
savedQueryService: DataPublicPluginStart['query']['savedQueries'];
visualizations: VisualizationsStart;
}

View file

@ -377,7 +377,7 @@ export const emptyScreenStrings = {
}),
getEmptyWidgetTitle: () =>
i18n.translate('dashboard.emptyWidget.addPanelTitle', {
defaultMessage: 'Add your first panel',
defaultMessage: 'Add your first visualization',
}),
getEmptyWidgetDescription: () =>
i18n.translate('dashboard.emptyWidget.addPanelDescription', {

View file

@ -24,6 +24,7 @@ import {
PluginInitializerContext,
SavedObjectsClientContract,
} from '../../../core/public';
import { VisualizationsStart } from '../../visualizations/public';
import { createKbnUrlTracker } from './services/kibana_utils';
import { UsageCollectionSetup } from './services/usage_collection';
@ -115,6 +116,7 @@ export interface DashboardStartDependencies {
presentationUtil: PresentationUtilPluginStart;
savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart;
spacesOss?: SpacesOssPluginStart;
visualizations: VisualizationsStart;
}
export type DashboardSetup = void;

View file

@ -37,11 +37,14 @@ export const defaultEmbeddableFactoryProvider = <
type: def.type,
isEditable: def.isEditable.bind(def),
getDisplayName: def.getDisplayName.bind(def),
getDescription: def.getDescription ? def.getDescription.bind(def) : () => '',
getIconType: def.getIconType ? def.getIconType.bind(def) : () => 'empty',
savedObjectMetaData: def.savedObjectMetaData,
telemetry: def.telemetry || (() => ({})),
inject: def.inject || ((state: EmbeddableStateWithType) => state),
extract: def.extract || ((state: EmbeddableStateWithType) => ({ state, references: [] })),
migrations: def.migrations || {},
grouping: def.grouping,
};
return factory;
};

View file

@ -14,6 +14,7 @@ import { IContainer } from '../containers/i_container';
import { PropertySpec } from '../types';
import { PersistableState } from '../../../../kibana_utils/common';
import { EmbeddableStateWithType } from '../../../common/types';
import { UiActionsPresentableGrouping } from '../../../../ui_actions/public';
export interface EmbeddableInstanceConfiguration {
id: string;
@ -48,6 +49,12 @@ export interface EmbeddableFactory<
readonly savedObjectMetaData?: SavedObjectMetaData<TSavedObjectAttributes>;
/**
* Indicates the grouping this factory should appear in a sub-menu. Example, this is used for grouping
* options in the editors menu in Dashboard for creating new embeddables
*/
readonly grouping?: UiActionsPresentableGrouping;
/**
* True if is this factory create embeddables that are Containers. Used in the add panel to
* conditionally show whether these can be added to another container. It's just not
@ -62,6 +69,16 @@ export interface EmbeddableFactory<
*/
getDisplayName(): string;
/**
* Returns an EUI Icon type to be displayed in a menu.
*/
getIconType(): string;
/**
* Returns a description about the embeddable.
*/
getDescription(): string;
/**
* If false, this type of embeddable can't be created with the "createNew" functionality. Instead,
* use createFromSavedObject, where an existing saved object must first exist.

View file

@ -33,5 +33,8 @@ export type EmbeddableFactoryDefinition<
| 'extract'
| 'inject'
| 'migrations'
| 'grouping'
| 'getIconType'
| 'getDescription'
>
>;

View file

@ -61,6 +61,7 @@ test('createNewEmbeddable() add embeddable to container', async () => {
getAllFactories={start.getEmbeddableFactories}
notifications={core.notifications}
SavedObjectFinder={() => null}
showCreateNewMenu
/>
) as ReactWrapper<unknown, unknown, AddPanelFlyout>;
@ -112,6 +113,7 @@ test('selecting embeddable in "Create new ..." list calls createNewEmbeddable()'
getAllFactories={start.getEmbeddableFactories}
notifications={core.notifications}
SavedObjectFinder={(props) => <DummySavedObjectFinder {...props} />}
showCreateNewMenu
/>
) as ReactWrapper<any, {}, AddPanelFlyout>;

View file

@ -26,6 +26,7 @@ interface Props {
getAllFactories: EmbeddableStart['getEmbeddableFactories'];
notifications: CoreSetup['notifications'];
SavedObjectFinder: React.ComponentType<any>;
showCreateNewMenu?: boolean;
}
interface State {
@ -134,7 +135,9 @@ export class AddPanelFlyout extends React.Component<Props, State> {
defaultMessage: 'No matching objects found.',
})}
>
<SavedObjectFinderCreateNew menuItems={this.getCreateMenuItems()} />
{this.props.showCreateNewMenu ? (
<SavedObjectFinderCreateNew menuItems={this.getCreateMenuItems()} />
) : null}
</SavedObjectFinder>
);

View file

@ -20,6 +20,7 @@ export function openAddPanelFlyout(options: {
overlays: OverlayStart;
notifications: NotificationsStart;
SavedObjectFinder: React.ComponentType<any>;
showCreateNewMenu?: boolean;
}): OverlayRef {
const {
embeddable,
@ -28,6 +29,7 @@ export function openAddPanelFlyout(options: {
overlays,
notifications,
SavedObjectFinder,
showCreateNewMenu,
} = options;
const flyoutSession = overlays.openFlyout(
toMountPoint(
@ -42,6 +44,7 @@ export function openAddPanelFlyout(options: {
getAllFactories={getAllFactories}
notifications={notifications}
SavedObjectFinder={SavedObjectFinder}
showCreateNewMenu={showCreateNewMenu}
/>
),
{

View file

@ -378,8 +378,12 @@ export interface EmbeddableFactory<TEmbeddableInput extends EmbeddableInput = Em
create(initialInput: TEmbeddableInput, parent?: IContainer): Promise<TEmbeddable | ErrorEmbeddable | undefined>;
createFromSavedObject(savedObjectId: string, input: Partial<TEmbeddableInput>, parent?: IContainer): Promise<TEmbeddable | ErrorEmbeddable>;
getDefaultInput(partial: Partial<TEmbeddableInput>): Partial<TEmbeddableInput>;
getDescription(): string;
getDisplayName(): string;
getExplicitInput(): Promise<Partial<TEmbeddableInput>>;
getIconType(): string;
// Warning: (ae-forgotten-export) The symbol "PresentableGrouping" needs to be exported by the entry point index.d.ts
readonly grouping?: PresentableGrouping;
readonly isContainerType: boolean;
readonly isEditable: () => Promise<boolean>;
// Warning: (ae-forgotten-export) The symbol "SavedObjectMetaData" needs to be exported by the entry point index.d.ts
@ -393,7 +397,7 @@ export interface EmbeddableFactory<TEmbeddableInput extends EmbeddableInput = Em
// Warning: (ae-missing-release-tag) "EmbeddableFactoryDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type EmbeddableFactoryDefinition<I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>, T extends SavedObjectAttributes = SavedObjectAttributes> = Pick<EmbeddableFactory<I, O, E, T>, 'create' | 'type' | 'isEditable' | 'getDisplayName'> & Partial<Pick<EmbeddableFactory<I, O, E, T>, 'createFromSavedObject' | 'isContainerType' | 'getExplicitInput' | 'savedObjectMetaData' | 'canCreateNew' | 'getDefaultInput' | 'telemetry' | 'extract' | 'inject' | 'migrations'>>;
export type EmbeddableFactoryDefinition<I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>, T extends SavedObjectAttributes = SavedObjectAttributes> = Pick<EmbeddableFactory<I, O, E, T>, 'create' | 'type' | 'isEditable' | 'getDisplayName'> & Partial<Pick<EmbeddableFactory<I, O, E, T>, 'createFromSavedObject' | 'isContainerType' | 'getExplicitInput' | 'savedObjectMetaData' | 'canCreateNew' | 'getDefaultInput' | 'telemetry' | 'extract' | 'inject' | 'migrations' | 'grouping' | 'getIconType' | 'getDescription'>>;
// Warning: (ae-missing-release-tag) "EmbeddableFactoryNotFoundError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@ -724,6 +728,7 @@ export function openAddPanelFlyout(options: {
overlays: OverlayStart_2;
notifications: NotificationsStart_2;
SavedObjectFinder: React.ComponentType<any>;
showCreateNewMenu?: boolean;
}): OverlayRef_2;
// Warning: (ae-missing-release-tag) "OutputSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)

View file

@ -1,4 +1,3 @@
.solutionToolbarButton {
line-height: $euiButtonHeight; // Keeps alignment of text and chart icon
background-color: $euiColorEmptyShade;

View file

@ -12,17 +12,19 @@ import { EuiButtonPropsForButton } from '@elastic/eui/src/components/button/butt
import './button.scss';
export interface Props extends Pick<EuiButtonPropsForButton, 'onClick' | 'iconType'> {
export interface Props
extends Pick<EuiButtonPropsForButton, 'onClick' | 'iconType' | 'iconSide' | 'className'> {
label: string;
primary?: boolean;
isDarkModeEnabled?: boolean;
}
export const SolutionToolbarButton = ({ label, primary, ...rest }: Props) => (
export const SolutionToolbarButton = ({ label, primary, className, ...rest }: Props) => (
<EuiButton
{...rest}
size="s"
size="m"
color={primary ? 'primary' : 'text'}
className="solutionToolbarButton"
className={`solutionToolbarButton ${className}`}
fill={primary}
>
{label}

View file

@ -20,14 +20,20 @@ type AllowedPopoverProps = Omit<
export type Props = AllowedButtonProps & AllowedPopoverProps;
export const SolutionToolbarPopover = ({ label, iconType, primary, ...popover }: Props) => {
export const SolutionToolbarPopover = ({
label,
iconType,
primary,
iconSide,
...popover
}: Props) => {
const [isOpen, setIsOpen] = useState(false);
const onButtonClick = () => setIsOpen((status) => !status);
const closePopover = () => setIsOpen(false);
const button = (
<SolutionToolbarButton {...{ label, iconType, primary }} onClick={onButtonClick} />
<SolutionToolbarButton {...{ label, iconType, primary, iconSide }} onClick={onButtonClick} />
);
return (

View file

@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
// Temporary fix for lensApp icon not support ghost color
.solutionToolbar__primaryButton--dark {
.euiIcon path {
fill: $euiColorInk;
}
}
.solutionToolbar__primaryButton--light {
.euiIcon path {
fill: $euiColorGhost;
}
}

View file

@ -10,6 +10,20 @@ import React from 'react';
import { SolutionToolbarButton, Props as SolutionToolbarButtonProps } from './button';
export const PrimaryActionButton = (props: Omit<SolutionToolbarButtonProps, 'primary'>) => (
<SolutionToolbarButton primary={true} {...props} />
import './primary_button.scss';
export interface Props extends Omit<SolutionToolbarButtonProps, 'primary'> {
isDarkModeEnabled?: boolean;
}
export const PrimaryActionButton = ({ isDarkModeEnabled, ...props }: Props) => (
<SolutionToolbarButton
primary={true}
className={`solutionToolbar__primaryButton ${
isDarkModeEnabled
? 'solutionToolbar__primaryButton--dark'
: 'solutionToolbar__primaryButton--light'
}`}
{...props}
/>
);

View file

@ -2,4 +2,17 @@
.quickButtonGroup__button {
background-color: $euiColorEmptyShade;
}
// Temporary fix for two tone icons to make them monochrome
.quickButtonGroup__button--dark {
.euiIcon path {
fill: $euiColorGhost;
}
}
// Temporary fix for two tone icons to make them monochrome
.quickButtonGroup__button--light {
.euiIcon path {
fill: $euiColorInk;
}
}
}

View file

@ -17,23 +17,27 @@ import './quick_group.scss';
export interface QuickButtonProps extends Pick<EuiButtonGroupOptionProps, 'iconType'> {
createType: string;
onClick: () => void;
isDarkModeEnabled?: boolean;
}
export interface Props {
buttons: QuickButtonProps[];
}
type Option = EuiButtonGroupOptionProps & Omit<QuickButtonProps, 'createType'>;
type Option = EuiButtonGroupOptionProps &
Omit<QuickButtonProps, 'createType' | 'isDarkModeEnabled'>;
export const QuickButtonGroup = ({ buttons }: Props) => {
const buttonGroupOptions: Option[] = buttons.map((button: QuickButtonProps, index) => {
const { createType: label, ...rest } = button;
const { createType: label, isDarkModeEnabled, ...rest } = button;
const title = strings.getAriaButtonLabel(label);
return {
...rest,
'aria-label': title,
className: 'quickButtonGroup__button',
className: `quickButtonGroup__button ${
isDarkModeEnabled ? 'quickButtonGroup__button--dark' : 'quickButtonGroup__button--light'
}`,
id: `${htmlIdGenerator()()}${index}`,
label,
title,
@ -46,7 +50,7 @@ export const QuickButtonGroup = ({ buttons }: Props) => {
return (
<EuiButtonGroup
buttonSize="s"
buttonSize="m"
className="quickButtonGroup"
legend={strings.getLegend()}
options={buttonGroupOptions}

View file

@ -1,4 +1,17 @@
.solutionToolbar {
padding: 0 $euiSizeS $euiSizeS;
padding: $euiSize $euiSizeS;
flex-grow: 0;
// Temporary fix for two tone icons to make them monochrome
.solutionToolbar--dark {
.euiIcon path {
fill: $euiColorInk;
}
}
.solutionToolbar--light {
.euiIcon path {
fill: $euiColorGhost;
}
}
}

View file

@ -28,10 +28,11 @@ interface NamedSlots {
}
export interface Props {
isDarkModeEnabled?: boolean;
children: NamedSlots;
}
export const SolutionToolbar = ({ children }: Props) => {
export const SolutionToolbar = ({ isDarkModeEnabled, children }: Props) => {
const {
primaryActionButton,
quickButtonGroup,
@ -49,8 +50,10 @@ export const SolutionToolbar = ({ children }: Props) => {
return (
<EuiFlexGroup
className="solutionToolbar"
id="kbnPresentationToolbar__solutionToolbar"
className={`solutionToolbar ${
isDarkModeEnabled ? 'solutionToolbar--dark' : 'solutionToolbar--light'
}`}
id={`kbnPresentationToolbar__solutionToolbar`}
gutterSize="s"
>
<EuiFlexItem grow={false}>{primaryActionButton}</EuiFlexItem>

View file

@ -19,6 +19,7 @@ export {
LazySavedObjectSaveModalDashboard,
withSuspense,
} from './components';
export {
AddFromLibraryButton,
PrimaryActionButton,

View file

@ -113,7 +113,7 @@ export class VisualizeEmbeddableFactory
public getDisplayName() {
return i18n.translate('visualizations.displayName', {
defaultMessage: 'visualization',
defaultMessage: 'Visualization',
});
}

View file

@ -25,7 +25,7 @@ export { getVisSchemas } from './vis_schemas';
/** @public types */
export { VisualizationsSetup, VisualizationsStart };
export { VisGroups } from './vis_types';
export type { VisTypeAlias, VisTypeDefinition, Schema, ISchemas } from './vis_types';
export type { BaseVisType, VisTypeAlias, VisTypeDefinition, Schema, ISchemas } from './vis_types';
export { SerializedVis, SerializedVisData, VisData } from './vis';
export type VisualizeEmbeddableFactoryContract = PublicContract<VisualizeEmbeddableFactory>;
export type VisualizeEmbeddableContract = PublicContract<VisualizeEmbeddable>;

View file

@ -24,7 +24,7 @@ function DialogNavigation(props: DialogNavigationProps) {
</EuiFlexItem>
<EuiFlexItem>
{i18n.translate('visualizations.newVisWizard.goBackLink', {
defaultMessage: 'Go back',
defaultMessage: 'Select a different visualization',
})}
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -41,6 +41,8 @@ interface TypeSelectionProps {
outsideVisualizeApp?: boolean;
stateTransfer?: EmbeddableStateTransfer;
originatingApp?: string;
showAggsSelection?: boolean;
selectedVisType?: BaseVisType;
}
interface TypeSelectionState {
@ -69,8 +71,9 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
this.isLabsEnabled = props.uiSettings.get(VISUALIZE_ENABLE_LABS_SETTING);
this.state = {
showSearchVisModal: false,
showGroups: true,
showSearchVisModal: Boolean(this.props.selectedVisType),
showGroups: !this.props.showAggsSelection,
visType: this.props.selectedVisType,
};
this.trackUiMetric = this.props.usageCollection?.reportUiCounter.bind(

View file

@ -20,6 +20,7 @@ import {
getEmbeddable,
getDocLinks,
} from '../services';
import type { BaseVisType } from '../vis_types';
const NewVisModal = lazy(() => import('./new_vis_modal'));
@ -29,6 +30,8 @@ export interface ShowNewVisModalParams {
originatingApp?: string;
outsideVisualizeApp?: boolean;
createByValue?: boolean;
showAggsSelection?: boolean;
selectedVisType?: BaseVisType;
}
/**
@ -41,6 +44,8 @@ export function showNewVisModal({
onClose,
originatingApp,
outsideVisualizeApp,
showAggsSelection,
selectedVisType,
}: ShowNewVisModalParams = {}) {
const container = document.createElement('div');
let isClosed = false;
@ -78,6 +83,8 @@ export function showNewVisModal({
usageCollection={getUsageCollector()}
application={getApplication()}
docLinks={getDocLinks()}
showAggsSelection={showAggsSelection}
selectedVisType={selectedVisType}
/>
</Suspense>
</I18nProvider>

View file

@ -13,31 +13,12 @@ import { PluginFunctionalProviderContext } from 'test/plugin_functional/services
export default function ({ getService }: PluginFunctionalProviderContext) {
const testSubjects = getService('testSubjects');
const flyout = getService('flyout');
const retry = getService('retry');
describe('creating and adding children', () => {
describe('adding children', () => {
before(async () => {
await testSubjects.click('embeddablePanelExample');
});
it('Can create a new child', async () => {
await testSubjects.click('embeddablePanelToggleMenuIcon');
await testSubjects.click('embeddablePanelAction-ACTION_ADD_PANEL');
// this seem like an overkill, but clicking this button which opens context menu was flaky
await testSubjects.waitForEnabled('createNew');
await retry.waitFor('createNew popover opened', async () => {
await testSubjects.click('createNew');
return await testSubjects.exists('createNew-TODO_EMBEDDABLE');
});
await testSubjects.click('createNew-TODO_EMBEDDABLE');
await testSubjects.setValue('taskInputField', 'new task');
await testSubjects.click('createTodoEmbeddable');
const tasks = await testSubjects.getVisibleTextAll('todoEmbeddableTask');
expect(tasks).to.eql(['Goes out on Wednesdays!', 'new task']);
});
it('Can add a child backed off a saved object', async () => {
await testSubjects.click('embeddablePanelToggleMenuIcon');
await testSubjects.click('embeddablePanelAction-ACTION_ADD_PANEL');
@ -46,7 +27,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
await testSubjects.moveMouseTo('euiFlyoutCloseButton');
await flyout.ensureClosed('dashboardAddPanel');
const tasks = await testSubjects.getVisibleTextAll('todoEmbeddableTask');
expect(tasks).to.eql(['Goes out on Wednesdays!', 'new task', 'Take the garbage out']);
expect(tasks).to.eql(['Goes out on Wednesdays!', 'Take the garbage out']);
});
});
}

View file

@ -35,8 +35,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('adds new visualization via the top nav link', async () => {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
await PageObjects.dashboard.switchToEditMode();
await dashboardAddPanel.clickCreateNewLink();
await PageObjects.visualize.clickAggBasedVisualizations();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationExpectSuccess(
@ -52,9 +52,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('adds a new visualization', async () => {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickAddNewEmbeddableLink('visualization');
await PageObjects.visualize.clickAggBasedVisualizations();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationExpectSuccess(
@ -71,7 +70,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('adds a markdown visualization via the quick button', async () => {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
await PageObjects.dashboard.clickMarkdownQuickButton();
await dashboardAddPanel.clickMarkdownQuickButton();
await PageObjects.visualize.saveVisualizationExpectSuccess(
'visualization from markdown quick button',
{ redirectToOrigin: true }
@ -84,21 +83,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.waitForRenderComplete();
});
it('adds an input control visualization via the quick button', async () => {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
await PageObjects.dashboard.clickInputControlsQuickButton();
await PageObjects.visualize.saveVisualizationExpectSuccess(
'visualization from input control 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();

View file

@ -25,8 +25,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('dashboard unsaved listing', () => {
const addSomePanels = async () => {
// add an area chart by value
await dashboardAddPanel.clickCreateNewLink();
await PageObjects.visualize.clickAggBasedVisualizations();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationAndReturn();
@ -132,8 +132,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.switchToEditMode();
// add another panel so we can delete it later
await dashboardAddPanel.clickCreateNewLink();
await PageObjects.visualize.clickAggBasedVisualizations();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationExpectSuccess('Wildvis', {

View file

@ -41,8 +41,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('shows the unsaved changes badge after adding panels', async () => {
await PageObjects.dashboard.switchToEditMode();
// add an area chart by value
await dashboardAddPanel.clickCreateNewLink();
await PageObjects.visualize.clickAggBasedVisualizations();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationAndReturn();

View file

@ -13,10 +13,9 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']);
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
const kibanaServer = getService('kibanaServer');
const dashboardPanelActions = getService('dashboardPanelActions');
const dashboardVisualizations = getService('dashboardVisualizations');
const dashboardAddPanel = getService('dashboardAddPanel');
describe('edit embeddable redirects', () => {
before(async () => {
@ -88,10 +87,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const newTitle = 'test create panel originatingApp';
await PageObjects.dashboard.loadSavedDashboard('few panels');
await PageObjects.dashboard.switchToEditMode();
await testSubjects.exists('dashboardAddNewPanelButton');
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await PageObjects.visualize.clickMarkdownWidget();
await dashboardAddPanel.clickMarkdownQuickButton();
await PageObjects.visualize.saveVisualizationExpectSuccess(newTitle, {
saveAsNew: true,
redirectToOrigin: false,

View file

@ -14,13 +14,14 @@ export default function ({ getService, getPageObjects }) {
const testSubjects = getService('testSubjects');
const appsMenu = getService('appsMenu');
const kibanaServer = getService('kibanaServer');
const dashboardAddPanel = getService('dashboardAddPanel');
const dashboardPanelActions = getService('dashboardPanelActions');
const originalMarkdownText = 'Original markdown text';
const modifiedMarkdownText = 'Modified markdown text';
const createMarkdownVis = async (title) => {
await PageObjects.dashboard.clickMarkdownQuickButton();
await dashboardAddPanel.clickMarkdownQuickButton();
await PageObjects.visEditor.setMarkdownTxt(originalMarkdownText);
await PageObjects.visEditor.clickGo();
if (title) {

View file

@ -41,15 +41,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should open add panel when add button is clicked', async () => {
await testSubjects.click('dashboardAddPanelButton');
await dashboardAddPanel.clickOpenAddPanel();
const isAddPanelOpen = await dashboardAddPanel.isAddPanelOpen();
expect(isAddPanelOpen).to.be(true);
await testSubjects.click('euiFlyoutCloseButton');
});
it('should add new visualization from dashboard', async () => {
await testSubjects.exists('dashboardAddNewPanelButton');
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.createAndAddMarkdown({
name: 'Dashboard Test Markdown',
markdown: 'Markdown text',
@ -57,5 +55,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.markdownWithValuesExists(['Markdown text']);
});
it('should open editor menu when editor button is clicked', async () => {
await dashboardAddPanel.clickEditorMenuButton();
await testSubjects.existOrFail('dashboardEditorContextMenu');
});
});
}

View file

@ -113,10 +113,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('when a new vis is added', async function () {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickAddNewEmbeddableLink('visualization');
await PageObjects.visualize.clickAggBasedVisualizations();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationExpectSuccess('new viz panel', {

View file

@ -413,16 +413,6 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide
await testSubjects.click('confirmSaveSavedObjectButton');
}
public async clickMarkdownQuickButton() {
log.debug('Click markdown quick button');
await testSubjects.click('dashboardMarkdownQuickButton');
}
public async clickInputControlsQuickButton() {
log.debug('Click input controls quick button');
await testSubjects.click('dashboardInputControlsQuickButton');
}
/**
*
* @param dashboardTitle {String}

View file

@ -30,15 +30,41 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }: FtrPro
await PageObjects.common.sleep(500);
}
async clickQuickButton(visType: string) {
log.debug(`DashboardAddPanel.clickQuickButton${visType}`);
await testSubjects.click(`dashboardQuickButton${visType}`);
}
async clickMarkdownQuickButton() {
await this.clickQuickButton('markdown');
}
async clickMapQuickButton() {
await this.clickQuickButton('map');
}
async clickEditorMenuButton() {
log.debug('DashboardAddPanel.clickEditorMenuButton');
await testSubjects.click('dashboardEditorMenuButton');
}
async clickAggBasedVisualizations() {
log.debug('DashboardAddPanel.clickEditorMenuAggBasedMenuItem');
await testSubjects.click('dashboardEditorAggBasedMenuItem');
}
async clickVisType(visType: string) {
log.debug('DashboardAddPanel.clickVisType');
await testSubjects.click(`visType-${visType}`);
}
async clickEmbeddableFactoryGroupButton(groupId: string) {
log.debug('DashboardAddPanel.clickEmbeddableFactoryGroupButton');
await testSubjects.click(`dashboardEditorMenu-${groupId}Group`);
}
async clickAddNewEmbeddableLink(type: string) {
await testSubjects.click('createNew');
await testSubjects.click(`createNew-${type}`);
await testSubjects.missingOrFail(`createNew-${type}`);
}
async toggleFilterPopover() {

View file

@ -10,8 +10,6 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export function DashboardVisualizationProvider({ getService, getPageObjects }: FtrProviderContext) {
const log = getService('log');
const find = getService('find');
const retry = getService('retry');
const queryBar = getService('queryBar');
const testSubjects = getService('testSubjects');
const dashboardAddPanel = getService('dashboardAddPanel');
@ -31,8 +29,8 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F
if (inViewMode) {
await PageObjects.dashboard.switchToEditMode();
}
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickAddNewEmbeddableLink('visualization');
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAddNewEmbeddableLink('metrics');
await PageObjects.visualize.clickVisualBuilder();
await PageObjects.visualize.saveVisualizationExpectSuccess(name);
}
@ -87,39 +85,13 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F
await dashboardAddPanel.addSavedSearch(name);
}
async clickAddVisualizationButton() {
log.debug('DashboardVisualizations.clickAddVisualizationButton');
await testSubjects.click('dashboardAddNewPanelButton');
}
async isNewVisDialogShowing() {
log.debug('DashboardVisualizations.isNewVisDialogShowing');
return await find.existsByCssSelector('.visNewVisDialog');
}
async ensureNewVisualizationDialogIsShowing() {
let isShowing = await this.isNewVisDialogShowing();
log.debug(`DashboardVisualizations.ensureNewVisualizationDialogIsShowing:${isShowing}`);
if (!isShowing) {
await retry.try(async () => {
await this.clickAddVisualizationButton();
isShowing = await this.isNewVisDialogShowing();
log.debug(`DashboardVisualizations.ensureNewVisualizationDialogIsShowing:${isShowing}`);
if (!isShowing) {
throw new Error('New Vis Dialog still not open, trying again.');
}
});
}
}
async createAndAddMarkdown({ name, markdown }: { name: string; markdown: string }) {
log.debug(`createAndAddMarkdown(${markdown})`);
const inViewMode = await PageObjects.dashboard.getIsInViewMode();
if (inViewMode) {
await PageObjects.dashboard.switchToEditMode();
}
await this.ensureNewVisualizationDialogIsShowing();
await PageObjects.visualize.clickMarkdownWidget();
await dashboardAddPanel.clickMarkdownQuickButton();
await PageObjects.visEditor.setMarkdownTxt(markdown);
await PageObjects.visEditor.clickGo();
await PageObjects.visualize.saveVisualizationExpectSuccess(name, {
@ -134,10 +106,10 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F
if (inViewMode) {
await PageObjects.dashboard.switchToEditMode();
}
await this.ensureNewVisualizationDialogIsShowing();
await PageObjects.visualize.clickAggBasedVisualizations();
await PageObjects.visualize.clickMetric();
await find.clickByCssSelector('li.euiListGroupItem:nth-of-type(2)');
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAggBasedVisualizations();
await dashboardAddPanel.clickVisType('metric');
await testSubjects.click('savedObjectTitlelogstash-*');
await testSubjects.exists('visualizesaveAndReturnButton');
await testSubjects.click('visualizesaveAndReturnButton');
}
@ -148,8 +120,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F
if (inViewMode) {
await PageObjects.dashboard.switchToEditMode();
}
await this.ensureNewVisualizationDialogIsShowing();
await PageObjects.visualize.clickMarkdownWidget();
await dashboardAddPanel.clickMarkdownQuickButton();
await PageObjects.visEditor.setMarkdownTxt(markdown);
await PageObjects.visEditor.clickGo();
await testSubjects.click('visualizesaveAndReturnButton');

View file

@ -22,7 +22,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const dashboardExpect = getService('dashboardExpect');
const testSubjects = getService('testSubjects');
const dashboardVisualizations = getService('dashboardVisualizations');
const PageObjects = getPageObjects([
'common',
@ -47,8 +46,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('adding a metric visualization', async function () {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
expect(originalPanelCount).to.eql(0);
await testSubjects.exists('dashboardAddNewPanelButton');
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.createAndEmbedMetric('Embedding Vis Test');
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.metricValuesExist(['0']);
@ -59,8 +56,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('adding a markdown', async function () {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
expect(originalPanelCount).to.eql(1);
await testSubjects.exists('dashboardAddNewPanelButton');
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.createAndEmbedMarkdown({
name: 'Embedding Markdown Test',
markdown: 'Nice to meet you, markdown is my name',

View file

@ -40,6 +40,16 @@ export class LogStreamEmbeddableFactoryDefinition
});
}
public getDescription() {
return i18n.translate('xpack.infra.logStreamEmbeddable.description', {
defaultMessage: 'Add a table of live streaming logs.',
});
}
public getIconType() {
return 'logsApp';
}
public async getExplicitInput() {
return {
title: i18n.translate('xpack.infra.logStreamEmbeddable.title', {

View file

@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n';
import type { StartServicesAccessor } from 'kibana/public';
import { PLUGIN_ICON, PLUGIN_ID, ML_APP_NAME } from '../../../common/constants/app';
import type {
EmbeddableFactoryDefinition,
IContainer,
@ -27,6 +28,14 @@ export class AnomalyChartsEmbeddableFactory
implements EmbeddableFactoryDefinition<AnomalyChartsEmbeddableInput> {
public readonly type = ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE;
public readonly grouping = [
{
id: PLUGIN_ID,
getDisplayName: () => ML_APP_NAME,
getIconType: () => PLUGIN_ICON,
},
];
constructor(
private getStartServices: StartServicesAccessor<MlStartDependencies, MlPluginStart>
) {}
@ -37,7 +46,13 @@ export class AnomalyChartsEmbeddableFactory
public getDisplayName() {
return i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.displayName', {
defaultMessage: 'ML anomaly chart',
defaultMessage: 'Anomaly chart',
});
}
public getDescription() {
return i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.description', {
defaultMessage: 'View anomaly detection results in a chart.',
});
}

View file

@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n';
import type { StartServicesAccessor } from 'kibana/public';
import { PLUGIN_ID, PLUGIN_ICON, ML_APP_NAME } from '../../../common/constants/app';
import type {
EmbeddableFactoryDefinition,
IContainer,
@ -26,6 +27,14 @@ export class AnomalySwimlaneEmbeddableFactory
implements EmbeddableFactoryDefinition<AnomalySwimlaneEmbeddableInput> {
public readonly type = ANOMALY_SWIMLANE_EMBEDDABLE_TYPE;
public readonly grouping = [
{
id: PLUGIN_ID,
getDisplayName: () => ML_APP_NAME,
getIconType: () => PLUGIN_ICON,
},
];
constructor(
private getStartServices: StartServicesAccessor<MlStartDependencies, MlPluginStart>
) {}
@ -36,7 +45,13 @@ export class AnomalySwimlaneEmbeddableFactory
public getDisplayName() {
return i18n.translate('xpack.ml.components.jobAnomalyScoreEmbeddable.displayName', {
defaultMessage: 'ML anomaly swim lane',
defaultMessage: 'Anomaly swim lane',
});
}
public getDescription() {
return i18n.translate('xpack.ml.components.jobAnomalyScoreEmbeddable.description', {
defaultMessage: 'View anomaly detection results in a timeline.',
});
}

View file

@ -96,8 +96,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('can open job selection flyout', async () => {
await PageObjects.dashboard.clickCreateDashboardPrompt();
await ml.dashboardEmbeddables.assertDashboardIsEmpty();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickEmbeddableFactoryGroupButton('ml');
await dashboardAddPanel.clickAddNewEmbeddableLink('ml_anomaly_charts');
await ml.dashboardJobSelectionTable.assertJobSelectionTableExists();
await a11y.testAppSnapshot();

View file

@ -15,7 +15,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
const dashboardPanelActions = getService('dashboardPanelActions');
const dashboardVisualizations = getService('dashboardVisualizations');
describe('dashboard lens by value', function () {
before(async () => {
@ -27,7 +26,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('can add a lens panel by value', async () => {
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await PageObjects.lens.createAndAddLensFromDashboard({});
const newPanelCount = await PageObjects.dashboard.getPanelCount();
expect(newPanelCount).to.eql(1);

View file

@ -19,10 +19,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const log = getService('log');
const esArchiver = getService('esArchiver');
const dashboardVisualizations = getService('dashboardVisualizations');
const dashboardPanelActions = getService('dashboardPanelActions');
const testSubjects = getService('testSubjects');
const appsMenu = getService('appsMenu');
const dashboardAddPanel = getService('dashboardAddPanel');
const LAYER_NAME = 'World Countries';
let mapCounter = 0;
@ -33,7 +33,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
if (inViewMode) {
await PageObjects.dashboard.switchToEditMode();
}
await PageObjects.visualize.clickMapsApp();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickVisType('maps');
await PageObjects.maps.clickSaveAndReturnButton();
}
@ -82,8 +83,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
describe('adding a map by value', () => {
it('can add a map by value', async () => {
await createNewDashboard();
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await createAndAddMapByValue();
const newPanelCount = await PageObjects.dashboard.getPanelCount();
expect(newPanelCount).to.eql(1);
@ -93,7 +92,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
describe('editing a map by value', () => {
before(async () => {
await createNewDashboard();
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await createAndAddMapByValue();
await editByValueMap();
});
@ -112,7 +110,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
describe('editing a map and adding to map library', () => {
beforeEach(async () => {
await createNewDashboard();
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await createAndAddMapByValue();
});

View file

@ -21,7 +21,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
'lens',
]);
const dashboardVisualizations = getService('dashboardVisualizations');
const dashboardAddPanel = getService('dashboardAddPanel');
const dashboardPanelActions = getService('dashboardPanelActions');
const dashboardExpect = getService('dashboardExpect');
const testSubjects = getService('testSubjects');
@ -85,7 +85,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('can add a lens panel by value', async () => {
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await PageObjects.lens.createAndAddLensFromDashboard({});
const newPanelCount = await PageObjects.dashboard.getPanelCount();
expect(newPanelCount).to.eql(1);
@ -171,9 +170,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.waitForRenderComplete();
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await PageObjects.visualize.clickMarkdownWidget();
await dashboardAddPanel.clickMarkdownQuickButton();
await PageObjects.visEditor.setMarkdownTxt(originalMarkdownText);
await PageObjects.visEditor.clickGo();

View file

@ -49,7 +49,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await elasticChart.setNewChartUiDebugFlag(true);
await PageObjects.dashboard.clickCreateDashboardPrompt();
await dashboardAddPanel.clickCreateNewLink();
await dashboardAddPanel.clickVisType('lens');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.lens.goToTimeRange();
@ -68,7 +67,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.lens.save('vis1', false, true);
await PageObjects.header.waitUntilLoadingHasFinished();
await dashboardAddPanel.clickCreateNewLink();
await dashboardAddPanel.clickVisType('lens');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.lens.configureDimension({

View file

@ -10,7 +10,6 @@ import expect from '@kbn/expect';
export default function ({ getPageObjects, getService }) {
const testSubjects = getService('testSubjects');
const esArchiver = getService('esArchiver');
const dashboardVisualizations = getService('dashboardVisualizations');
const dashboardPanelActions = getService('dashboardPanelActions');
const PageObjects = getPageObjects(['common', 'dashboard', 'visualize', 'lens']);
@ -29,9 +28,6 @@ export default function ({ getPageObjects, getService }) {
it('adds Lens visualization to empty dashboard', async () => {
const title = 'Dashboard Test Lens';
await testSubjects.exists('dashboardAddNewPanelButton');
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await PageObjects.lens.createAndAddLensFromDashboard({ title, redirectToOrigin: true });
await PageObjects.dashboard.waitForRenderComplete();
await testSubjects.exists(`embeddablePanelHeading-${title}`);
@ -87,9 +83,6 @@ export default function ({ getPageObjects, getService }) {
const title = 'non-dashboard Test Lens';
await PageObjects.dashboard.loadSavedDashboard('empty dashboard test');
await PageObjects.dashboard.switchToEditMode();
await testSubjects.exists('dashboardAddNewPanelButton');
await testSubjects.click('dashboardAddNewPanelButton');
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await PageObjects.lens.createAndAddLensFromDashboard({ title });
await PageObjects.lens.notLinkedToOriginatingApp();
await PageObjects.common.navigateToApp('dashboard');

View file

@ -134,7 +134,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await filterBar.addFilter('geo.dest', 'is', 'LS');
await dashboardAddPanel.clickCreateNewLink();
await dashboardAddPanel.clickVisType('lens');
await PageObjects.header.waitUntilLoadingHasFinished();
const hasGeoDestFilter = await filterBar.hasFilter('geo.dest', 'LS');
expect(hasGeoDestFilter).to.be(false);
@ -200,7 +199,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickCreateNewLink();
await dashboardAddPanel.clickVisType('lens');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.lens.goToTimeRange();

View file

@ -14,7 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const retry = getService('retry');
const find = getService('find');
const dashboardVisualizations = getService('dashboardVisualizations');
const dashboardAddPanel = getService('dashboardAddPanel');
const dashboardPanelActions = getService('dashboardPanelActions');
const PageObjects = getPageObjects([
'common',
@ -39,8 +39,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('adds a new tag to a Lens visualization', async () => {
// create lens
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await PageObjects.visualize.clickLensWidget();
await dashboardAddPanel.clickCreateNewLink();
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',

View file

@ -15,7 +15,6 @@ export default function ({ getPageObjects, getService }) {
const security = getService('security');
const dashboardAddPanel = getService('dashboardAddPanel');
const dashboardPanelActions = getService('dashboardPanelActions');
const dashboardVisualizations = getService('dashboardVisualizations');
describe('maps in embeddable library', () => {
before(async () => {
@ -34,8 +33,7 @@ export default function ({ getPageObjects, getService }) {
});
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickCreateNewLink();
await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await dashboardAddPanel.clickEditorMenuButton();
await PageObjects.visualize.clickMapsApp();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.maps.waitForLayersToLoad();

View file

@ -11,7 +11,6 @@ export default function ({ getPageObjects, getService }) {
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'maps', 'visualize']);
const dashboardAddPanel = getService('dashboardAddPanel');
const dashboardPanelActions = getService('dashboardPanelActions');
const dashboardVisualizations = getService('dashboardVisualizations');
const testSubjects = getService('testSubjects');
const security = getService('security');
@ -37,9 +36,8 @@ export default function ({ getPageObjects, getService }) {
beforeEach(async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickCreateNewLink();
await await dashboardVisualizations.ensureNewVisualizationDialogIsShowing();
await PageObjects.visualize.clickMapsApp();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickVisType('maps');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.maps.waitForLayersToLoad();
});

View file

@ -87,8 +87,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('can open job selection flyout', async () => {
await PageObjects.dashboard.clickCreateDashboardPrompt();
await ml.dashboardEmbeddables.assertDashboardIsEmpty();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickEmbeddableFactoryGroupButton('ml');
await dashboardAddPanel.clickAddNewEmbeddableLink('ml_anomaly_charts');
await ml.dashboardJobSelectionTable.assertJobSelectionTableExists();
});

View file

@ -18,6 +18,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
const find = getService('find');
const comboBox = getService('comboBox');
const browser = getService('browser');
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects([
'common',
@ -753,7 +754,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
if (inViewMode) {
await PageObjects.dashboard.switchToEditMode();
}
await PageObjects.visualize.clickLensWidget();
await dashboardAddPanel.clickCreateNewLink();
await this.goToTimeRange();
await this.configureDimension({
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',