From 067158a8db90a6c929b493222af4f920da8a0e74 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 26 Aug 2019 17:56:11 +0200 Subject: [PATCH 01/37] create graph listing page --- x-pack/legacy/plugins/graph/public/app.js | 183 +++++++----------- .../plugins/graph/public/breadcrumbs.js | 29 --- .../graph/public/components/graph_listing.tsx | 164 ++++++++++++++++ .../public/templates/listing_ng_wrapper.html | 10 + .../plugins/graph/public/types/workspace.ts | 14 ++ 5 files changed, 253 insertions(+), 147 deletions(-) delete mode 100644 x-pack/legacy/plugins/graph/public/breadcrumbs.js create mode 100644 x-pack/legacy/plugins/graph/public/components/graph_listing.tsx create mode 100644 x-pack/legacy/plugins/graph/public/templates/listing_ng_wrapper.html create mode 100644 x-pack/legacy/plugins/graph/public/types/workspace.ts diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 2f65077226a10..ae0c000e8f955 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -30,14 +30,17 @@ import { IndexPatternsProvider } from 'ui/index_patterns'; import { SavedObjectsClientProvider } from 'ui/saved_objects'; import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; import { npStart } from 'ui/new_platform'; +import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import appTemplate from './templates/index.html'; -import { getHomeBreadcrumbs, getWorkspaceBreadcrumbs } from './breadcrumbs'; +import listingTemplate from './templates/listing_ng_wrapper.html'; import { getReadonlyBadge } from './badge'; import { FormattedMessage } from '@kbn/i18n/react'; +import { GraphListing } from './components/graph_listing'; + import './angular-venn-simple.js'; import gws from './graphClientWorkspace.js'; import utils from './utils.js'; @@ -85,42 +88,53 @@ app.directive('focusOn', function () { }; }); +app.directive('graphListing', function (reactDirective) { + return reactDirective(GraphListing); +}); + if (uiRoutes.enable) { uiRoutes.enable(); } uiRoutes .when('/home', { - template: appTemplate, - k7Breadcrumbs: getHomeBreadcrumbs, + template: listingTemplate, badge: getReadonlyBadge, - resolve: { - //Copied from example found in wizard.js ( Kibana TODO - can't - // IndexPatternsProvider abstract these implementation details better?) - indexPatterns: function (Private) { - const savedObjectsClient = Private(SavedObjectsClientProvider); - - return savedObjectsClient.find({ - type: 'index-pattern', - fields: ['title', 'type'], - perPage: 10000 - }).then(response => response.savedObjects); - }, - GetIndexPatternProvider: function (Private) { - return Private(IndexPatternsProvider); - }, - SavedWorkspacesProvider: function (Private) { - return Private(SavedWorkspacesProvider); - }, - CheckLicense: checkLicense + controller($injector, $location, $scope, $route, Private, config, Promise, kbnBaseUrl) { + checkLicense(Promise, kbnBaseUrl); + const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName; + const kbnUrl = $injector.get('kbnUrl'); + + $scope.listingLimit = config.get('savedObjects:listingLimit'); + $scope.create = () => { + kbnUrl.redirect('/workspace/'); + }; + $scope.find = (search) => { + return services['Graph workspace'].find(search, $scope.listingLimit); + }; + $scope.editItem = ({ id }) => { + kbnUrl.redirect(`/workspace/${id}`); + }; + $scope.getViewUrl = ({ id }) => { + return chrome.addBasePath(`#/workspace/${id}`); + }; + $scope.delete = (ids) => { + return services.dashboards.delete(ids); + }; + $scope.capabilities = capabilities.get().graph; + $scope.initialFilter = ($location.search()).filter || ''; + chrome.breadcrumbs.set([{ + text: i18n.translate('xpack.graph.graphBreadcrumbsTitle', { + defaultMessage: 'Graph', + }), + }]); } }) - .when('/workspace/:id', { + .when('/workspace/:id?', { template: appTemplate, - k7Breadcrumbs: getWorkspaceBreadcrumbs, resolve: { savedWorkspace: function (savedGraphWorkspaces, courier, $route) { - return savedGraphWorkspaces.get($route.current.params.id) + return $route.current.params.id && savedGraphWorkspaces.get($route.current.params.id) .catch( function () { toastNotifications.addDanger( @@ -148,8 +162,7 @@ uiRoutes }, SavedWorkspacesProvider: function (Private) { return Private(SavedWorkspacesProvider); - }, - CheckLicense: checkLicense + } } }) .otherwise({ @@ -344,13 +357,16 @@ app.controller('graphuiPlugin', function ( } const confirmModalOptions = { onConfirm: yesFn, - onCancel: noFn, + onCancel: noFn || (() => {}), confirmButtonText: i18n.translate('xpack.graph.clearWorkspace.confirmButtonLabel', { - defaultMessage: 'Clear workspace', - }) + defaultMessage: 'Continue', + }), + title: i18n.translate('xpack.graph.clearWorkspace.modalTitle', { + defaultMessage: 'Discard changes to workspace?', + }), }; confirmModal(i18n.translate('xpack.graph.clearWorkspace.confirmText', { - defaultMessage: 'This will clear the workspace - are you sure?', + defaultMessage: 'Once you discard changes made to a workspace, there is no getting them back.', }), confirmModalOptions); } @@ -841,7 +857,7 @@ app.controller('graphuiPlugin', function ( }), run: function () { canWipeWorkspace(function () { - kbnUrl.change('/home', {}); + kbnUrl.change('/workspace/', {}); }); }, }); @@ -882,92 +898,6 @@ app.controller('graphuiPlugin', function ( testId: 'graphSaveButton', }); } - $scope.topNavMenu.push({ - key: 'open', - label: i18n.translate('xpack.graph.topNavMenu.loadWorkspaceLabel', { - defaultMessage: 'Open', - }), - description: i18n.translate('xpack.graph.topNavMenu.loadWorkspaceAriaLabel', { - defaultMessage: 'Load Saved Workspace', - }), - tooltip: i18n.translate('xpack.graph.topNavMenu.loadWorkspaceTooltip', { - defaultMessage: 'Load a saved workspace', - }), - run: () => { - $scope.$evalAsync(() => { - const curState = $scope.menus.showLoad; - $scope.closeMenus(); - $scope.menus.showLoad = !curState; - }); - }, - testId: 'graphOpenButton', - }); - // if deleting is disabled using uiCapabilities, we don't want to render the delete - // button so it's consistent with all of the other applications - if (capabilities.get().graph.delete) { - - // allSavingDisabled is based on the xpack.graph.savePolicy, we'll maintain this functionality - if (!$scope.allSavingDisabled) { - $scope.topNavMenu.push({ - key: 'delete', - disableButton: function () { - return $route.current.locals === undefined || $route.current.locals.savedWorkspace === undefined; - }, - label: i18n.translate('xpack.graph.topNavMenu.deleteWorkspace.enabledLabel', { - defaultMessage: 'Delete', - }), - description: i18n.translate('xpack.graph.topNavMenu.deleteWorkspace.enabledAriaLabel', { - defaultMessage: 'Delete Saved Workspace', - }), - tooltip: i18n.translate('xpack.graph.topNavMenu.deleteWorkspace.enabledAriaTooltip', { - defaultMessage: 'Delete this workspace', - }), - testId: 'graphDeleteButton', - run: function () { - const title = $route.current.locals.savedWorkspace.title; - function doDelete() { - $route.current.locals.SavedWorkspacesProvider.delete($route.current.locals.savedWorkspace.id); - kbnUrl.change('/home', {}); - - toastNotifications.addSuccess( - i18n.translate('xpack.graph.topNavMenu.deleteWorkspaceNotification', { - defaultMessage: `Deleted '{workspaceTitle}'`, - values: { workspaceTitle: title }, - }) - ); - } - const confirmModalOptions = { - onConfirm: doDelete, - confirmButtonText: i18n.translate('xpack.graph.topNavMenu.deleteWorkspace.confirmButtonLabel', { - defaultMessage: 'Delete workspace', - }), - }; - confirmModal( - i18n.translate('xpack.graph.topNavMenu.deleteWorkspace.confirmText', { - defaultMessage: 'Are you sure you want to delete the workspace {title} ?', - values: { title }, - }), - confirmModalOptions - ); - } - }); - } else { - $scope.topNavMenu.push({ - key: 'delete', - disableButton: true, - label: i18n.translate('xpack.graph.topNavMenu.deleteWorkspace.disabledLabel', { - defaultMessage: 'Delete', - }), - description: i18n.translate('xpack.graph.topNavMenu.deleteWorkspace.disabledAriaLabel', { - defaultMessage: 'Delete Saved Workspace', - }), - tooltip: i18n.translate('xpack.graph.topNavMenu.deleteWorkspace.disabledTooltip', { - defaultMessage: 'No changes to saved workspaces are permitted by the current save policy', - }), - testId: 'graphDeleteButton', - }); - } - } $scope.topNavMenu.push({ key: 'settings', disableButton: function () { return $scope.selectedIndex === null; }, @@ -986,6 +916,23 @@ app.controller('graphuiPlugin', function ( }, }); + chrome.breadcrumbs.set([{ + text: i18n.translate('xpack.graph.graphBreadcrumbsTitle', { + defaultMessage: 'Graph', + }), + onClick: () => { + canWipeWorkspace(function () { + $scope.$evalAsync(() => { + kbnUrl.changePath('/home/'); + }); + }); + } + }, { + text: $route.current.locals.savedWorkspace + ? $route.current.locals.savedWorkspace.title + : i18n.translate('xpack.graph.newWorkspaceTitle', { defaultMessage: 'Unsaved workspace' }), + }]); + $scope.menus = { showSave: false, showLoad: false, diff --git a/x-pack/legacy/plugins/graph/public/breadcrumbs.js b/x-pack/legacy/plugins/graph/public/breadcrumbs.js deleted file mode 100644 index 63887e23288f2..0000000000000 --- a/x-pack/legacy/plugins/graph/public/breadcrumbs.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export function getHomeBreadcrumbs() { - return [ - { - text: i18n.translate('xpack.graph.home.breadcrumb', { - defaultMessage: 'Graph' - }), - href: '#/home' - } - ]; -} - -export function getWorkspaceBreadcrumbs($route) { - const { savedWorkspace } = $route.current.locals; - - return [ - ...getHomeBreadcrumbs(), - { - text: savedWorkspace.title - } - ]; -} diff --git a/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx b/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx new file mode 100644 index 0000000000000..77e6649d06fee --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; +import React, { Fragment } from 'react'; +import { EuiEmptyPrompt, EuiLink, EuiButton } from '@elastic/eui'; + +// @ts-ignore +import { TableListView } from '../../../../../../src/legacy/core_plugins/kibana/public/table_list_view/table_list_view'; +import { SavedGraphWorkspace } from '../types/workspace'; + +export interface GraphListingProps { + createItem: () => void; + findItems: (query: string, limit: number) => Promise; + deleteItems: (ids: string[]) => Promise; + editItem: (record: SavedGraphWorkspace) => void; + getViewUrl: (record: SavedGraphWorkspace) => string; + listingLimit: number; + hideWriteControls: boolean; + capabilities: { save: boolean; delete: boolean }; + initialFilter: string; +} + +export function GraphListing(props: GraphListingProps) { + return ( + + + + ); +} + +function getNoItemsMessage(hideWriteControls: boolean, createItem: () => void) { + if (hideWriteControls) { + return ( +
+ + + + } + /> +
+ ); + } + + return ( +
+ + + + } + body={ + +

+ +

+

+ + + + ), + }} + /> +

+
+ } + actions={ + + + + } + /> +
+ ); +} + +// TODO this is an EUI type but EUI doesn't provide this typing yet +interface DataColumn { + field: string; + name: string; + sortable?: boolean; + render?: (value: string, item: SavedGraphWorkspace) => React.ReactNode; + dataType?: 'auto' | 'string' | 'number' | 'date' | 'boolean'; +} + +function getTableColumns(getViewUrl: (record: SavedGraphWorkspace) => string): DataColumn[] { + return [ + { + field: 'title', + name: i18n.translate('xpack.graph.listing.table.titleColumnName', { + defaultMessage: 'Title', + }), + sortable: true, + render: (field, record) => ( + + {field} + + ), + }, + { + field: 'description', + name: i18n.translate('xpack.graph.listing.table.descriptionColumnName', { + defaultMessage: 'Description', + }), + dataType: 'string', + sortable: true, + }, + ]; +} diff --git a/x-pack/legacy/plugins/graph/public/templates/listing_ng_wrapper.html b/x-pack/legacy/plugins/graph/public/templates/listing_ng_wrapper.html new file mode 100644 index 0000000000000..09899453dd351 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/templates/listing_ng_wrapper.html @@ -0,0 +1,10 @@ + diff --git a/x-pack/legacy/plugins/graph/public/types/workspace.ts b/x-pack/legacy/plugins/graph/public/types/workspace.ts new file mode 100644 index 0000000000000..646163664ddf9 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/types/workspace.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface SavedGraphWorkspace { + title: string; + description: string; + numLinks: number; + numVertices: number; + version: number; + wsState: string; +} From 5fddc108c599126e14f7576b7f5196f712a26f7e Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 26 Aug 2019 19:34:37 +0200 Subject: [PATCH 02/37] clean up app folder --- src/core/public/chrome/chrome_service.tsx | 1 + .../{ => angular}/__tests__/workspace.js | 0 .../angular_venn_simple.js} | 0 .../{ => angular}/directives/graph_load.js | 0 .../{ => angular}/directives/graph_save.js | 0 .../directives/graph_settings.js | 0 .../graph_client_workspace.js} | 0 .../services/outlink_encoders.js | 0 .../{ => angular}/services/saved_workspace.js | 0 .../services/saved_workspace_references.js | 0 .../saved_workspace_references.test.js | 0 .../services/saved_workspaces.js | 0 .../{ => angular}/templates/_graph.scss | 0 .../{ => angular}/templates/_index.scss | 0 .../{ => angular}/templates/_settings.scss | 0 .../{ => angular}/templates/_sidebar.scss | 0 .../public/{ => angular}/templates/index.html | 0 .../templates/listing_ng_wrapper.html | 0 .../templates/load_workspace.html | 0 .../templates/save_workspace.html | 0 .../{ => angular}/templates/settings.html | 0 x-pack/legacy/plugins/graph/public/app.js | 63 ++++++++---------- .../graph/public/components/graph_listing.tsx | 4 +- x-pack/legacy/plugins/graph/public/index.scss | 2 +- .../plugins/graph/public/services/url.ts | 66 +++++++++++++++++++ .../types/{workspace.ts => persistence.ts} | 8 ++- 26 files changed, 104 insertions(+), 40 deletions(-) rename x-pack/legacy/plugins/graph/public/{ => angular}/__tests__/workspace.js (100%) rename x-pack/legacy/plugins/graph/public/{angular-venn-simple.js => angular/angular_venn_simple.js} (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/directives/graph_load.js (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/directives/graph_save.js (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/directives/graph_settings.js (100%) rename x-pack/legacy/plugins/graph/public/{graphClientWorkspace.js => angular/graph_client_workspace.js} (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/services/outlink_encoders.js (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/services/saved_workspace.js (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/services/saved_workspace_references.js (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/services/saved_workspace_references.test.js (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/services/saved_workspaces.js (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/templates/_graph.scss (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/templates/_index.scss (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/templates/_settings.scss (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/templates/_sidebar.scss (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/templates/index.html (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/templates/listing_ng_wrapper.html (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/templates/load_workspace.html (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/templates/save_workspace.html (100%) rename x-pack/legacy/plugins/graph/public/{ => angular}/templates/settings.html (100%) create mode 100644 x-pack/legacy/plugins/graph/public/services/url.ts rename x-pack/legacy/plugins/graph/public/types/{workspace.ts => persistence.ts} (62%) diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index 668bce522bf4e..8798c1a88bff7 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -63,6 +63,7 @@ export interface ChromeBreadcrumb { text: string; href?: string; 'data-test-subj'?: string; + onClick?: () => void; } /** @public */ diff --git a/x-pack/legacy/plugins/graph/public/__tests__/workspace.js b/x-pack/legacy/plugins/graph/public/angular/__tests__/workspace.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/__tests__/workspace.js rename to x-pack/legacy/plugins/graph/public/angular/__tests__/workspace.js diff --git a/x-pack/legacy/plugins/graph/public/angular-venn-simple.js b/x-pack/legacy/plugins/graph/public/angular/angular_venn_simple.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular-venn-simple.js rename to x-pack/legacy/plugins/graph/public/angular/angular_venn_simple.js diff --git a/x-pack/legacy/plugins/graph/public/directives/graph_load.js b/x-pack/legacy/plugins/graph/public/angular/directives/graph_load.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/directives/graph_load.js rename to x-pack/legacy/plugins/graph/public/angular/directives/graph_load.js diff --git a/x-pack/legacy/plugins/graph/public/directives/graph_save.js b/x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/directives/graph_save.js rename to x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js diff --git a/x-pack/legacy/plugins/graph/public/directives/graph_settings.js b/x-pack/legacy/plugins/graph/public/angular/directives/graph_settings.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/directives/graph_settings.js rename to x-pack/legacy/plugins/graph/public/angular/directives/graph_settings.js diff --git a/x-pack/legacy/plugins/graph/public/graphClientWorkspace.js b/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/graphClientWorkspace.js rename to x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js diff --git a/x-pack/legacy/plugins/graph/public/services/outlink_encoders.js b/x-pack/legacy/plugins/graph/public/angular/services/outlink_encoders.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/outlink_encoders.js rename to x-pack/legacy/plugins/graph/public/angular/services/outlink_encoders.js diff --git a/x-pack/legacy/plugins/graph/public/services/saved_workspace.js b/x-pack/legacy/plugins/graph/public/angular/services/saved_workspace.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/saved_workspace.js rename to x-pack/legacy/plugins/graph/public/angular/services/saved_workspace.js diff --git a/x-pack/legacy/plugins/graph/public/services/saved_workspace_references.js b/x-pack/legacy/plugins/graph/public/angular/services/saved_workspace_references.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/saved_workspace_references.js rename to x-pack/legacy/plugins/graph/public/angular/services/saved_workspace_references.js diff --git a/x-pack/legacy/plugins/graph/public/services/saved_workspace_references.test.js b/x-pack/legacy/plugins/graph/public/angular/services/saved_workspace_references.test.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/saved_workspace_references.test.js rename to x-pack/legacy/plugins/graph/public/angular/services/saved_workspace_references.test.js diff --git a/x-pack/legacy/plugins/graph/public/services/saved_workspaces.js b/x-pack/legacy/plugins/graph/public/angular/services/saved_workspaces.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/saved_workspaces.js rename to x-pack/legacy/plugins/graph/public/angular/services/saved_workspaces.js diff --git a/x-pack/legacy/plugins/graph/public/templates/_graph.scss b/x-pack/legacy/plugins/graph/public/angular/templates/_graph.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/templates/_graph.scss rename to x-pack/legacy/plugins/graph/public/angular/templates/_graph.scss diff --git a/x-pack/legacy/plugins/graph/public/templates/_index.scss b/x-pack/legacy/plugins/graph/public/angular/templates/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/templates/_index.scss rename to x-pack/legacy/plugins/graph/public/angular/templates/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/templates/_settings.scss b/x-pack/legacy/plugins/graph/public/angular/templates/_settings.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/templates/_settings.scss rename to x-pack/legacy/plugins/graph/public/angular/templates/_settings.scss diff --git a/x-pack/legacy/plugins/graph/public/templates/_sidebar.scss b/x-pack/legacy/plugins/graph/public/angular/templates/_sidebar.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/templates/_sidebar.scss rename to x-pack/legacy/plugins/graph/public/angular/templates/_sidebar.scss diff --git a/x-pack/legacy/plugins/graph/public/templates/index.html b/x-pack/legacy/plugins/graph/public/angular/templates/index.html similarity index 100% rename from x-pack/legacy/plugins/graph/public/templates/index.html rename to x-pack/legacy/plugins/graph/public/angular/templates/index.html diff --git a/x-pack/legacy/plugins/graph/public/templates/listing_ng_wrapper.html b/x-pack/legacy/plugins/graph/public/angular/templates/listing_ng_wrapper.html similarity index 100% rename from x-pack/legacy/plugins/graph/public/templates/listing_ng_wrapper.html rename to x-pack/legacy/plugins/graph/public/angular/templates/listing_ng_wrapper.html diff --git a/x-pack/legacy/plugins/graph/public/templates/load_workspace.html b/x-pack/legacy/plugins/graph/public/angular/templates/load_workspace.html similarity index 100% rename from x-pack/legacy/plugins/graph/public/templates/load_workspace.html rename to x-pack/legacy/plugins/graph/public/angular/templates/load_workspace.html diff --git a/x-pack/legacy/plugins/graph/public/templates/save_workspace.html b/x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html similarity index 100% rename from x-pack/legacy/plugins/graph/public/templates/save_workspace.html rename to x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html diff --git a/x-pack/legacy/plugins/graph/public/templates/settings.html b/x-pack/legacy/plugins/graph/public/angular/templates/settings.html similarity index 100% rename from x-pack/legacy/plugins/graph/public/templates/settings.html rename to x-pack/legacy/plugins/graph/public/angular/templates/settings.html diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index ae0c000e8f955..79c3a387ca5f4 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -31,20 +31,21 @@ import { SavedObjectsClientProvider } from 'ui/saved_objects'; import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; import { npStart } from 'ui/new_platform'; import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; +import { capabilities } from 'ui/capabilities'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import appTemplate from './templates/index.html'; -import listingTemplate from './templates/listing_ng_wrapper.html'; +import appTemplate from './angular/templates/index.html'; +import listingTemplate from './angular/templates/listing_ng_wrapper.html'; import { getReadonlyBadge } from './badge'; import { FormattedMessage } from '@kbn/i18n/react'; import { GraphListing } from './components/graph_listing'; -import './angular-venn-simple.js'; -import gws from './graphClientWorkspace.js'; +import './angular/angular_venn_simple.js'; +import gws from './angular/graph_client_workspace.js'; import utils from './utils.js'; -import { SavedWorkspacesProvider } from './services/saved_workspaces'; +import { SavedWorkspacesProvider } from './angular/services/saved_workspaces'; import { iconChoices, colorChoices, @@ -54,16 +55,16 @@ import { } from './style_choices'; import { getOutlinkEncoders, -} from './services/outlink_encoders'; -import { capabilities } from 'ui/capabilities'; +} from './angular/services/outlink_encoders'; +import { getEditUrl, getNewPath, getEditPath, setBreadcrumbs } from './services/url'; -import saveTemplate from './templates/save_workspace.html'; -import loadTemplate from './templates/load_workspace.html'; -import settingsTemplate from './templates/settings.html'; +import saveTemplate from './angular/templates/save_workspace.html'; +import loadTemplate from './angular/templates/load_workspace.html'; +import settingsTemplate from './angular/templates/settings.html'; -import './directives/graph_load'; -import './directives/graph_save'; -import './directives/graph_settings'; +import './angular/directives/graph_load'; +import './angular/directives/graph_save'; +import './angular/directives/graph_settings'; const app = uiModules.get('app/graph'); @@ -103,31 +104,26 @@ uiRoutes controller($injector, $location, $scope, $route, Private, config, Promise, kbnBaseUrl) { checkLicense(Promise, kbnBaseUrl); const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName; + const graphService = services['Graph workspace']; const kbnUrl = $injector.get('kbnUrl'); $scope.listingLimit = config.get('savedObjects:listingLimit'); $scope.create = () => { - kbnUrl.redirect('/workspace/'); + kbnUrl.redirect(getNewPath()); }; $scope.find = (search) => { - return services['Graph workspace'].find(search, $scope.listingLimit); - }; - $scope.editItem = ({ id }) => { - kbnUrl.redirect(`/workspace/${id}`); + return graphService.find(search, $scope.listingLimit); }; - $scope.getViewUrl = ({ id }) => { - return chrome.addBasePath(`#/workspace/${id}`); + $scope.editItem = (workspace) => { + kbnUrl.redirect(getEditPath(workspace)); }; + $scope.getViewUrl = (workspace) => getEditUrl(chrome, workspace); $scope.delete = (ids) => { - return services.dashboards.delete(ids); + return graphService.delete(ids); }; $scope.capabilities = capabilities.get().graph; $scope.initialFilter = ($location.search()).filter || ''; - chrome.breadcrumbs.set([{ - text: i18n.translate('xpack.graph.graphBreadcrumbsTitle', { - defaultMessage: 'Graph', - }), - }]); + setBreadcrumbs({ chrome }); } }) .when('/workspace/:id?', { @@ -916,22 +912,17 @@ app.controller('graphuiPlugin', function ( }, }); - chrome.breadcrumbs.set([{ - text: i18n.translate('xpack.graph.graphBreadcrumbsTitle', { - defaultMessage: 'Graph', - }), - onClick: () => { + setBreadcrumbs({ + chrome, + savedWorkspace: $route.current.locals.savedWorkspace, + navigateTo: () => { canWipeWorkspace(function () { $scope.$evalAsync(() => { kbnUrl.changePath('/home/'); }); }); } - }, { - text: $route.current.locals.savedWorkspace - ? $route.current.locals.savedWorkspace.title - : i18n.translate('xpack.graph.newWorkspaceTitle', { defaultMessage: 'Unsaved workspace' }), - }]); + }); $scope.menus = { showSave: false, diff --git a/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx b/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx index 77e6649d06fee..f271fcc27d42a 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx +++ b/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx @@ -11,7 +11,7 @@ import { EuiEmptyPrompt, EuiLink, EuiButton } from '@elastic/eui'; // @ts-ignore import { TableListView } from '../../../../../../src/legacy/core_plugins/kibana/public/table_list_view/table_list_view'; -import { SavedGraphWorkspace } from '../types/workspace'; +import { SavedGraphWorkspace } from '../types/persistence'; export interface GraphListingProps { createItem: () => void; @@ -146,7 +146,7 @@ function getTableColumns(getViewUrl: (record: SavedGraphWorkspace) => string): D render: (field, record) => ( {field} diff --git a/x-pack/legacy/plugins/graph/public/index.scss b/x-pack/legacy/plugins/graph/public/index.scss index eb166638ca248..3986f64c6f4c9 100644 --- a/x-pack/legacy/plugins/graph/public/index.scss +++ b/x-pack/legacy/plugins/graph/public/index.scss @@ -11,4 +11,4 @@ // gphChart__legend-isLoading @import './main'; -@import './templates/index'; +@import './angular/templates/index'; diff --git a/x-pack/legacy/plugins/graph/public/services/url.ts b/x-pack/legacy/plugins/graph/public/services/url.ts new file mode 100644 index 0000000000000..d4c79b0bcc273 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/services/url.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { Chrome } from 'ui/chrome'; +import { SavedGraphWorkspace } from '../types/persistence'; + +export function getHomePath() { + return '/home'; +} + +export function getNewPath() { + return '/workspace'; +} + +export function getEditPath({ id }: SavedGraphWorkspace) { + return `/workspace/${id}`; +} + +export function getEditUrl(chrome: Chrome, workspace: SavedGraphWorkspace) { + return chrome.addBasePath(`#${getEditPath(workspace)}`); +} + +export type SetBreadcrumbOptions = + | { + chrome: Chrome; + } + | { + chrome: Chrome; + savedWorkspace?: SavedGraphWorkspace; + navigateTo: (path: string) => void; + }; + +export function setBreadcrumbs(options: SetBreadcrumbOptions) { + if ('savedWorkspace' in options) { + options.chrome.breadcrumbs.set([ + { + text: i18n.translate('xpack.graph.graphBreadcrumbsTitle', { + defaultMessage: 'Graph', + }), + onClick: () => { + options.navigateTo(getHomePath()); + }, + }, + { + text: options.savedWorkspace + ? options.savedWorkspace.title + : i18n.translate('xpack.graph.newWorkspaceTitle', { + defaultMessage: 'Unsaved workspace', + }), + }, + ]); + } else { + options.chrome.breadcrumbs.set([ + { + text: i18n.translate('xpack.graph.graphBreadcrumbsTitle', { + defaultMessage: 'Graph', + }), + href: `#${getHomePath()}`, + }, + ]); + } +} diff --git a/x-pack/legacy/plugins/graph/public/types/workspace.ts b/x-pack/legacy/plugins/graph/public/types/persistence.ts similarity index 62% rename from x-pack/legacy/plugins/graph/public/types/workspace.ts rename to x-pack/legacy/plugins/graph/public/types/persistence.ts index 646163664ddf9..56040aa30db4c 100644 --- a/x-pack/legacy/plugins/graph/public/types/workspace.ts +++ b/x-pack/legacy/plugins/graph/public/types/persistence.ts @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface SavedGraphWorkspace { +import { SavedObject } from 'ui/saved_objects/saved_object'; + +/** + * Workspace fetched from server. + * This type is returned by `SavedWorkspacesProvider#get`. + */ +export interface SavedGraphWorkspace extends SavedObject { title: string; description: string; numLinks: number; From 95f003b4120d92ef569f65a2c7bb79867781a3d4 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 11:02:30 +0200 Subject: [PATCH 03/37] remove inline loading menu --- .../public/angular/directives/graph_load.js | 17 ----------------- .../graph/public/angular/templates/index.html | 3 +-- .../angular/templates/load_workspace.html | 15 --------------- x-pack/legacy/plugins/graph/public/app.js | 10 +--------- .../plugins/graph/public/app_with_autoload.js | 8 -------- 5 files changed, 2 insertions(+), 51 deletions(-) delete mode 100644 x-pack/legacy/plugins/graph/public/angular/directives/graph_load.js delete mode 100644 x-pack/legacy/plugins/graph/public/angular/templates/load_workspace.html delete mode 100644 x-pack/legacy/plugins/graph/public/app_with_autoload.js diff --git a/x-pack/legacy/plugins/graph/public/angular/directives/graph_load.js b/x-pack/legacy/plugins/graph/public/angular/directives/graph_load.js deleted file mode 100644 index 369243fff7487..0000000000000 --- a/x-pack/legacy/plugins/graph/public/angular/directives/graph_load.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from '../templates/load_workspace.html'; -const app = uiModules.get('app/graph'); - -app.directive('graphLoad', function () { - return { - replace: true, - restrict: 'E', - template, - }; -}); diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/index.html b/x-pack/legacy/plugins/graph/public/angular/templates/index.html index fc536d54547e1..4dfc1846bb259 100644 --- a/x-pack/legacy/plugins/graph/public/angular/templates/index.html +++ b/x-pack/legacy/plugins/graph/public/angular/templates/index.html @@ -3,8 +3,7 @@ -
- +
diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/load_workspace.html b/x-pack/legacy/plugins/graph/public/angular/templates/load_workspace.html deleted file mode 100644 index 5f8948caf1e97..0000000000000 --- a/x-pack/legacy/plugins/graph/public/angular/templates/load_workspace.html +++ /dev/null @@ -1,15 +0,0 @@ -
-
- - - -
\ No newline at end of file diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 79c3a387ca5f4..8189b355e35e7 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -59,10 +59,8 @@ import { import { getEditUrl, getNewPath, getEditPath, setBreadcrumbs } from './services/url'; import saveTemplate from './angular/templates/save_workspace.html'; -import loadTemplate from './angular/templates/load_workspace.html'; import settingsTemplate from './angular/templates/settings.html'; -import './angular/directives/graph_load'; import './angular/directives/graph_save'; import './angular/directives/graph_settings'; @@ -257,11 +255,6 @@ app.controller('graphuiPlugin', function ( urlTemplate.icon === icon ? urlTemplate.icon = null : urlTemplate.icon = icon; }; - $scope.openSavedWorkspace = function (savedWorkspace) { - kbnUrl.change('/workspace/{{id}}', { id: savedWorkspace.id }); - }; - - $scope.nodeClick = function (n, $event) { //Selection logic - shift key+click helps selects multiple nodes @@ -926,7 +919,6 @@ app.controller('graphuiPlugin', function ( $scope.menus = { showSave: false, - showLoad: false, showSettings: false, }; @@ -1199,7 +1191,7 @@ app.controller('graphuiPlugin', function ( 'data-test-subj': 'saveGraphSuccess', }); if ($scope.savedWorkspace.id === $route.current.params.id) return; - $scope.openSavedWorkspace($scope.savedWorkspace); + kbnUrl.change(getEditPath($scope.savedWorkspace)); } }, fatalError); diff --git a/x-pack/legacy/plugins/graph/public/app_with_autoload.js b/x-pack/legacy/plugins/graph/public/app_with_autoload.js deleted file mode 100644 index ca01a93fefc09..0000000000000 --- a/x-pack/legacy/plugins/graph/public/app_with_autoload.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -require('ui/autoload/all'); -require('./app'); From ae44388e127cc5206acca6af9c7dbbaa1037afdc Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 11:08:13 +0200 Subject: [PATCH 04/37] also add badge to workspace route --- x-pack/legacy/plugins/graph/public/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 8189b355e35e7..4b221e7d9ef2e 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -126,6 +126,7 @@ uiRoutes }) .when('/workspace/:id?', { template: appTemplate, + badge: getReadonlyBadge, resolve: { savedWorkspace: function (savedGraphWorkspaces, courier, $route) { return $route.current.params.id && savedGraphWorkspaces.get($route.current.params.id) From 2f1056f194f3b44934919d5f9bd6e760f484a1ba Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 12:07:26 +0200 Subject: [PATCH 05/37] fix tests --- .../public/angular/__tests__/workspace.js | 2 +- .../plugins/graph/public/services/url.ts | 3 ++ .../graph/feature_controls/graph_security.ts | 31 ++++++++++--------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/x-pack/legacy/plugins/graph/public/angular/__tests__/workspace.js b/x-pack/legacy/plugins/graph/public/angular/__tests__/workspace.js index 592872c96ee05..2575edb333af1 100644 --- a/x-pack/legacy/plugins/graph/public/angular/__tests__/workspace.js +++ b/x-pack/legacy/plugins/graph/public/angular/__tests__/workspace.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -const gws = require('../graphClientWorkspace.js'); +const gws = require('../graph_client_workspace.js'); const expect = require('@kbn/expect'); describe('graphui-workspace', function () { diff --git a/x-pack/legacy/plugins/graph/public/services/url.ts b/x-pack/legacy/plugins/graph/public/services/url.ts index d4c79b0bcc273..acdb701557ba8 100644 --- a/x-pack/legacy/plugins/graph/public/services/url.ts +++ b/x-pack/legacy/plugins/graph/public/services/url.ts @@ -44,6 +44,7 @@ export function setBreadcrumbs(options: SetBreadcrumbOptions) { onClick: () => { options.navigateTo(getHomePath()); }, + 'data-test-subj': 'graphHomeBreadcrumb', }, { text: options.savedWorkspace @@ -51,6 +52,7 @@ export function setBreadcrumbs(options: SetBreadcrumbOptions) { : i18n.translate('xpack.graph.newWorkspaceTitle', { defaultMessage: 'Unsaved workspace', }), + 'data-test-subj': 'graphCurrentWorkspaceBreadcrumb', }, ]); } else { @@ -60,6 +62,7 @@ export function setBreadcrumbs(options: SetBreadcrumbOptions) { defaultMessage: 'Graph', }), href: `#${getHomePath()}`, + 'data-test-subj': 'graphHomeBreadcrumb', }, ]); } diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts index 05f6717106bad..2dbf6427c347e 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts @@ -71,18 +71,25 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { ]); }); - it('shows save button', async () => { + it('landing page shows "Create new Workspace" button', async () => { await PageObjects.common.navigateToApp('graph'); - await testSubjects.existOrFail('graphSaveButton'); + await testSubjects.existOrFail('workspaceLandingPage', { timeout: 10000 }); + await testSubjects.existOrFail('newItemButton'); }); - it('shows delete button', async () => { + it(`doesn't show read-only badge`, async () => { + await globalNav.badgeMissingOrFail(); + }); + + it('allows creating a new workspace', async () => { await PageObjects.common.navigateToApp('graph'); - await testSubjects.existOrFail('graphDeleteButton'); + await testSubjects.click('newItemButton'); + const breadcrumb = await testSubjects.find('graphCurrentWorkspaceBreadcrumb'); + expect(await breadcrumb.getVisibleText()).to.equal('Unsaved workspace'); }); - it(`doesn't show read-only badge`, async () => { - await globalNav.badgeMissingOrFail(); + it('shows save button', async () => { + await testSubjects.existOrFail('graphSaveButton'); }); }); @@ -129,16 +136,10 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { expect(navLinks).to.eql(['Graph', 'Management']); }); - it(`doesn't show save button`, async () => { - await PageObjects.common.navigateToApp('graph'); - await testSubjects.existOrFail('graphOpenButton'); - await testSubjects.missingOrFail('graphSaveButton'); - }); - - it(`doesn't show delete button`, async () => { + it('does not show a "Create new Workspace" button', async () => { await PageObjects.common.navigateToApp('graph'); - await testSubjects.existOrFail('graphOpenButton'); - await testSubjects.missingOrFail('graphDeleteButton'); + await testSubjects.existOrFail('workspaceLandingPage', { timeout: 10000 }); + await testSubjects.missingOrFail('newItemButton'); }); it(`shows read-only badge`, async () => { From 5d637f525f6fc4eea859aa20cc935737d9f196d9 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 13:00:51 +0200 Subject: [PATCH 06/37] fix graph spaces functional test --- .../apps/graph/feature_controls/graph_spaces.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts index a0b81a8df702a..4e7c2233286a2 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts @@ -41,18 +41,21 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { expect(navLinks).to.contain('Graph'); }); - it('shows save button', async () => { - await PageObjects.common.navigateToApp('graph', { + it('landing page shows "Create new Workspace" button', async () => { + await PageObjects.common.navigateToApp('home', { basePath: '/s/custom_space', }); - await testSubjects.existOrFail('graphSaveButton'); + await testSubjects.existOrFail('workspaceLandingPage', { timeout: 10000 }); + await testSubjects.existOrFail('newItemButton'); }); - it('shows delete button', async () => { - await PageObjects.common.navigateToApp('graph', { + it('allows creating a new workspace', async () => { + await PageObjects.common.navigateToApp('home', { basePath: '/s/custom_space', }); - await testSubjects.existOrFail('graphDeleteButton'); + await testSubjects.click('newItemButton'); + const breadcrumb = await testSubjects.find('graphCurrentWorkspaceBreadcrumb'); + expect(await breadcrumb.getVisibleText()).to.equal('Unsaved workspace'); }); }); From 9d17862f25e6268a9071dac6fd7d568a1529416f Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 13:06:37 +0200 Subject: [PATCH 07/37] generate documentation for new breadcrumb property --- .../public/kibana-plugin-public.chromebreadcrumb.md | 1 + .../kibana-plugin-public.chromebreadcrumb.onclick.md | 11 +++++++++++ src/core/public/public.api.md | 2 ++ 3 files changed, 14 insertions(+) create mode 100644 docs/development/core/public/kibana-plugin-public.chromebreadcrumb.onclick.md diff --git a/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md b/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md index 9a6e0a7cc8715..f395defd5eecd 100644 --- a/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md +++ b/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md @@ -17,5 +17,6 @@ export interface ChromeBreadcrumb | --- | --- | --- | | [data-test-subj](./kibana-plugin-public.chromebreadcrumb.data-test-subj.md) | string | | | [href](./kibana-plugin-public.chromebreadcrumb.href.md) | string | | +| [onClick](./kibana-plugin-public.chromebreadcrumb.onclick.md) | () => void | | | [text](./kibana-plugin-public.chromebreadcrumb.text.md) | string | | diff --git a/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.onclick.md b/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.onclick.md new file mode 100644 index 0000000000000..806b209b5cb35 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.onclick.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeBreadcrumb](./kibana-plugin-public.chromebreadcrumb.md) > [onClick](./kibana-plugin-public.chromebreadcrumb.onclick.md) + +## ChromeBreadcrumb.onClick property + +Signature: + +```typescript +onClick?: () => void; +``` diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index a5c31e41e0267..71260a856ad07 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -63,6 +63,8 @@ export interface ChromeBreadcrumb { // (undocumented) href?: string; // (undocumented) + onClick?: () => void; + // (undocumented) text: string; } From db1d74065bb0715cac304f148ac14704149488bb Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 13:11:18 +0200 Subject: [PATCH 08/37] fix test subject names --- .../legacy/plugins/graph/public/components/graph_listing.tsx | 4 ++-- .../functional/apps/graph/feature_controls/graph_security.ts | 4 ++-- .../functional/apps/graph/feature_controls/graph_spaces.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx b/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx index f271fcc27d42a..75dcc61017c7a 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx +++ b/x-pack/legacy/plugins/graph/public/components/graph_listing.tsx @@ -113,10 +113,10 @@ function getNoItemsMessage(hideWriteControls: boolean, createItem: () => void) { onClick={createItem} fill iconType="plusInCircle" - data-test-subj="createDashboardPromptButton" + data-test-subj="graphCreateWorkspacePromptButton" > diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts index 2dbf6427c347e..808a21388bcda 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts @@ -74,7 +74,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('landing page shows "Create new Workspace" button', async () => { await PageObjects.common.navigateToApp('graph'); await testSubjects.existOrFail('workspaceLandingPage', { timeout: 10000 }); - await testSubjects.existOrFail('newItemButton'); + await testSubjects.existOrFail('graphCreateWorkspacePromptButton'); }); it(`doesn't show read-only badge`, async () => { @@ -83,7 +83,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('allows creating a new workspace', async () => { await PageObjects.common.navigateToApp('graph'); - await testSubjects.click('newItemButton'); + await testSubjects.click('graphCreateWorkspacePromptButton'); const breadcrumb = await testSubjects.find('graphCurrentWorkspaceBreadcrumb'); expect(await breadcrumb.getVisibleText()).to.equal('Unsaved workspace'); }); diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts index 4e7c2233286a2..9afbc0a8f2ea2 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts @@ -46,14 +46,14 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { basePath: '/s/custom_space', }); await testSubjects.existOrFail('workspaceLandingPage', { timeout: 10000 }); - await testSubjects.existOrFail('newItemButton'); + await testSubjects.existOrFail('graphCreateWorkspacePromptButton'); }); it('allows creating a new workspace', async () => { await PageObjects.common.navigateToApp('home', { basePath: '/s/custom_space', }); - await testSubjects.click('newItemButton'); + await testSubjects.click('graphCreateWorkspacePromptButton'); const breadcrumb = await testSubjects.find('graphCurrentWorkspaceBreadcrumb'); expect(await breadcrumb.getVisibleText()).to.equal('Unsaved workspace'); }); From e09b70d7ed7bdca8439632084523f954775ffbba Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 14:31:24 +0200 Subject: [PATCH 09/37] remove unused translations --- x-pack/legacy/plugins/graph/public/services/url.ts | 4 ++-- .../plugins/translations/translations/ja-JP.json | 14 -------------- .../plugins/translations/translations/zh-CN.json | 14 -------------- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/x-pack/legacy/plugins/graph/public/services/url.ts b/x-pack/legacy/plugins/graph/public/services/url.ts index acdb701557ba8..dd9db8fa1f07a 100644 --- a/x-pack/legacy/plugins/graph/public/services/url.ts +++ b/x-pack/legacy/plugins/graph/public/services/url.ts @@ -38,7 +38,7 @@ export function setBreadcrumbs(options: SetBreadcrumbOptions) { if ('savedWorkspace' in options) { options.chrome.breadcrumbs.set([ { - text: i18n.translate('xpack.graph.graphBreadcrumbsTitle', { + text: i18n.translate('xpack.graph.home.breadcrumb', { defaultMessage: 'Graph', }), onClick: () => { @@ -58,7 +58,7 @@ export function setBreadcrumbs(options: SetBreadcrumbOptions) { } else { options.chrome.breadcrumbs.set([ { - text: i18n.translate('xpack.graph.graphBreadcrumbsTitle', { + text: i18n.translate('xpack.graph.home.breadcrumb', { defaultMessage: 'Graph', }), href: `#${getHomePath()}`, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ddc9037785dc6..7e5ab25b38786 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4638,23 +4638,9 @@ "xpack.graph.topNavMenu.addedFieldTooltip": "検索が {fName} を返した場合、Shift を押しながらクリックすると切り替わります", "xpack.graph.topNavMenu.addFieldButtonAriaLabel": "フィールドを追加", "xpack.graph.topNavMenu.addFieldButtonTooltip": "頂点のフィールドソースを追加", - "xpack.graph.topNavMenu.deleteWorkspace.confirmButtonLabel": "ワークスペースを削除", - "xpack.graph.topNavMenu.deleteWorkspace.confirmText": "ワークスペース {title} を削除してよろしいですか?", - "xpack.graph.topNavMenu.deleteWorkspace.disabledAriaLabel": "保存されたワークスペースを削除", - "xpack.graph.topNavMenu.deleteWorkspace.disabledLabel": "削除", - "xpack.graph.topNavMenu.deleteWorkspace.disabledTooltip": "現在の保存ポリシーでは、保存されたワークスペースへの変更が許可されていません", - "xpack.graph.topNavMenu.deleteWorkspace.enabledAriaLabel": "保存されたワークスペースを削除", - "xpack.graph.topNavMenu.deleteWorkspace.enabledAriaTooltip": "このワークスペースを削除します", - "xpack.graph.topNavMenu.deleteWorkspace.enabledLabel": "削除", - "xpack.graph.topNavMenu.deleteWorkspaceNotification": "「{workspaceTitle}」が削除されました", - "xpack.graph.topNavMenu.loadWorkspaceAriaLabel": "保存されたワークスペースを読み込む", - "xpack.graph.topNavMenu.loadWorkspaceLabel": "開く", - "xpack.graph.topNavMenu.loadWorkspaceTooltip": "保存されたワークスペースを読み込みます", "xpack.graph.topNavMenu.newWorkspaceAriaLabel": "新規ワークスペース", "xpack.graph.topNavMenu.newWorkspaceLabel": "新規", "xpack.graph.topNavMenu.newWorkspaceTooltip": "新規ワークスペースを作成します", - "xpack.graph.topNavMenu.open.openWorkspaceTitle": "ワークスペースを開く", - "xpack.graph.topNavMenu.open.workspacesListTooltip": "保存されたワークスペース", "xpack.graph.topNavMenu.save.confirmButtonAriaLabel": "ワークスペースを保存", "xpack.graph.topNavMenu.save.confirmButtonLabel": "保存", "xpack.graph.topNavMenu.save.descriptionInputLabel": "説明", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a44195ba28a43..3438002e5cb06 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4781,23 +4781,9 @@ "xpack.graph.topNavMenu.addedFieldTooltip": "如果搜索返回 {fName},则按住 Shift 并点击来进行切换", "xpack.graph.topNavMenu.addFieldButtonAriaLabel": "添加字段", "xpack.graph.topNavMenu.addFieldButtonTooltip": "为顶点添加字段源", - "xpack.graph.topNavMenu.deleteWorkspace.confirmButtonLabel": "删除工作空间", - "xpack.graph.topNavMenu.deleteWorkspace.confirmText": "是否确定要删除工作空间 {title}?", - "xpack.graph.topNavMenu.deleteWorkspace.disabledAriaLabel": "删除已保存的工作空间", - "xpack.graph.topNavMenu.deleteWorkspace.disabledLabel": "删除", - "xpack.graph.topNavMenu.deleteWorkspace.disabledTooltip": "当前保存策略不允许对已保存的工作空间做任何更改", - "xpack.graph.topNavMenu.deleteWorkspace.enabledAriaLabel": "删除已保存的工作空间", - "xpack.graph.topNavMenu.deleteWorkspace.enabledAriaTooltip": "删除此工作空间", - "xpack.graph.topNavMenu.deleteWorkspace.enabledLabel": "删除", - "xpack.graph.topNavMenu.deleteWorkspaceNotification": "已删除“{workspaceTitle}”", - "xpack.graph.topNavMenu.loadWorkspaceAriaLabel": "加载已保存的工作空间", - "xpack.graph.topNavMenu.loadWorkspaceLabel": "打开", - "xpack.graph.topNavMenu.loadWorkspaceTooltip": "加载已保存的工作空间", "xpack.graph.topNavMenu.newWorkspaceAriaLabel": "新建工作空间", "xpack.graph.topNavMenu.newWorkspaceLabel": "新建", "xpack.graph.topNavMenu.newWorkspaceTooltip": "新建工作空间", - "xpack.graph.topNavMenu.open.openWorkspaceTitle": "打开工作空间", - "xpack.graph.topNavMenu.open.workspacesListTooltip": "已保存的工作空间", "xpack.graph.topNavMenu.save.confirmButtonAriaLabel": "保存工作空间", "xpack.graph.topNavMenu.save.confirmButtonLabel": "保存", "xpack.graph.topNavMenu.save.descriptionInputLabel": "描述", From 2dab9948855075844974967f006257d37a169ff2 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 15:38:47 +0200 Subject: [PATCH 10/37] start implementing save modal flow for Graph --- x-pack/legacy/plugins/graph/public/app.js | 53 ++++++++++++++++--- .../public/components/graph_save_modal.tsx | 31 +++++++++++ 2 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 4b221e7d9ef2e..4ede1e68e7268 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -32,6 +32,7 @@ import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; import { npStart } from 'ui/new_platform'; import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; import { capabilities } from 'ui/capabilities'; +import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; @@ -41,6 +42,7 @@ import { getReadonlyBadge } from './badge'; import { FormattedMessage } from '@kbn/i18n/react'; import { GraphListing } from './components/graph_listing'; +import { GraphSaveModal } from './components/graph_save_modal'; import './angular/angular_venn_simple.js'; import gws from './angular/graph_client_workspace.js'; @@ -879,11 +881,42 @@ app.controller('graphuiPlugin', function ( return $scope.allSavingDisabled || $scope.selectedFields.length === 0; }, run: () => { - $scope.$evalAsync(() => { - const curState = $scope.menus.showSave; - $scope.closeMenus(); - $scope.menus.showSave = !curState; - }); + const currentTitle = $scope.savedWorkspace.title; + const currentDescription = $scope.savedWorkspace.title; + const onSave = ({ + newTitle, + newDescription, + newCopyOnSave, + newTimeRestore, + isTitleDuplicateConfirmed, + onTitleDuplicate, + }) => { + $scope.savedWorkspace.title = newTitle; + $scope.savedWorkspace.description = newDescription; + const saveOptions = { + confirmOverwrite: false, + isTitleDuplicateConfirmed, + onTitleDuplicate, + }; + return $scope.saveWorkspace(saveOptions).then((response) => { + // If the save wasn't successful, put the original values back. + if (!(response.id)) { + $scope.savedWorkspace.title = currentTitle; + $scope.savedWorkspace.description = currentDescription; + } + return response; + }); + }; + const graphSaveModal = ( + {}} + title={$scope.savedWorkspace.title} + description={$scope.savedWorkspace.description} + showCopyOnSave={$scope.savedWorkspace.id} + /> + ); + showSaveModal(graphSaveModal); }, testId: 'graphSaveButton', }); @@ -1171,7 +1204,7 @@ app.controller('graphuiPlugin', function ( $scope.savedWorkspace.description = $scope.description; - $scope.savedWorkspace.save().then(function (id) { + return $scope.savedWorkspace.save().then(function (id) { $scope.closeMenus(); $scope.userHasConfirmedSaveWorkspaceData = false; //reset flag if (id) { @@ -1191,8 +1224,12 @@ app.controller('graphuiPlugin', function ( text, 'data-test-subj': 'saveGraphSuccess', }); - if ($scope.savedWorkspace.id === $route.current.params.id) return; - kbnUrl.change(getEditPath($scope.savedWorkspace)); + if ($scope.savedWorkspace.id !== $route.current.params.id) { + kbnUrl.change(getEditPath($scope.savedWorkspace)); + } + return { id }; + } else { + return { error: 'Save was not sucessfull' }; } }, fatalError); diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx new file mode 100644 index 0000000000000..a5c80f29c06c2 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; + +export function GraphSaveModal({ + onSave, + onClose, + title, + showCopyOnSave, +}: { + onSave: () => any; + onClose: () => void; + title: string; + showCopyOnSave: boolean; +}) { + return ( + + ); +} From c34d84555ee79a08a149a4e9483a001e2dfb073e Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 17:27:57 +0200 Subject: [PATCH 11/37] fix spaces functional test --- .../functional/apps/graph/feature_controls/graph_spaces.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts index 9afbc0a8f2ea2..25c094dc5ec99 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts @@ -42,7 +42,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it('landing page shows "Create new Workspace" button', async () => { - await PageObjects.common.navigateToApp('home', { + await PageObjects.common.navigateToApp('graph', { basePath: '/s/custom_space', }); await testSubjects.existOrFail('workspaceLandingPage', { timeout: 10000 }); @@ -50,7 +50,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it('allows creating a new workspace', async () => { - await PageObjects.common.navigateToApp('home', { + await PageObjects.common.navigateToApp('graph', { basePath: '/s/custom_space', }); await testSubjects.click('graphCreateWorkspacePromptButton'); From 2d775e6724df47471af1e77fd11a5fc802d7b1a4 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 17:52:53 +0200 Subject: [PATCH 12/37] wip save modal --- .../components/saved_object_save_modal.tsx | 2 +- ...al.js => show_saved_object_save_modal.tsx} | 0 x-pack/legacy/plugins/graph/public/app.js | 37 ++++++++++--------- .../public/components/graph_save_modal.tsx | 4 +- .../plugins/graph/public/services/save.tsx | 0 5 files changed, 23 insertions(+), 20 deletions(-) rename src/legacy/ui/public/saved_objects/{show_saved_object_save_modal.js => show_saved_object_save_modal.tsx} (100%) create mode 100644 x-pack/legacy/plugins/graph/public/services/save.tsx diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx index b6802758b958a..05685817bb0da 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx @@ -36,7 +36,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { Fragment } from 'react'; import { EuiText } from '@elastic/eui'; -interface OnSaveProps { +export interface OnSaveProps { newTitle: string; newCopyOnSave: boolean; isTitleDuplicateConfirmed: boolean; diff --git a/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.js b/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx similarity index 100% rename from src/legacy/ui/public/saved_objects/show_saved_object_save_modal.js rename to src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 4ede1e68e7268..5906f469f631c 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -209,6 +209,20 @@ app.controller('graphuiPlugin', function ( }); } + function updateBreadcrumbs() { + setBreadcrumbs({ + chrome, + savedWorkspace: $route.current.locals.savedWorkspace, + navigateTo: () => { + canWipeWorkspace(function () { + $scope.$evalAsync(() => { + kbnUrl.changePath('/home/'); + }); + }); + } + }); + } + $scope.title = 'Graph'; $scope.spymode = 'request'; @@ -887,12 +901,12 @@ app.controller('graphuiPlugin', function ( newTitle, newDescription, newCopyOnSave, - newTimeRestore, isTitleDuplicateConfirmed, onTitleDuplicate, }) => { $scope.savedWorkspace.title = newTitle; $scope.savedWorkspace.description = newDescription; + $scope.savedWorkspace.copyOnSave = newCopyOnSave; const saveOptions = { confirmOverwrite: false, isTitleDuplicateConfirmed, @@ -939,17 +953,7 @@ app.controller('graphuiPlugin', function ( }, }); - setBreadcrumbs({ - chrome, - savedWorkspace: $route.current.locals.savedWorkspace, - navigateTo: () => { - canWipeWorkspace(function () { - $scope.$evalAsync(() => { - kbnUrl.changePath('/home/'); - }); - }); - } - }); + updateBreadcrumbs(); $scope.menus = { showSave: false, @@ -1114,7 +1118,7 @@ app.controller('graphuiPlugin', function ( }); } - $scope.saveWorkspace = function () { + $scope.saveWorkspace = function (saveOptions) { if ($scope.allSavingDisabled) { // It should not be possible to navigate to this function if allSavingDisabled is set // but adding check here as a safeguard. @@ -1204,7 +1208,7 @@ app.controller('graphuiPlugin', function ( $scope.savedWorkspace.description = $scope.description; - return $scope.savedWorkspace.save().then(function (id) { + return $scope.savedWorkspace.save(saveOptions).then(function (id) { $scope.closeMenus(); $scope.userHasConfirmedSaveWorkspaceData = false; //reset flag if (id) { @@ -1227,10 +1231,9 @@ app.controller('graphuiPlugin', function ( if ($scope.savedWorkspace.id !== $route.current.params.id) { kbnUrl.change(getEditPath($scope.savedWorkspace)); } - return { id }; - } else { - return { error: 'Save was not sucessfull' }; + updateBreadcrumbs(); } + return { id }; }, fatalError); }; diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx index a5c80f29c06c2..834251ddba2f5 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; +import { SavedObjectSaveModal, OnSaveProps } from 'ui/saved_objects/components/saved_object_save_modal'; export function GraphSaveModal({ onSave, @@ -13,7 +13,7 @@ export function GraphSaveModal({ title, showCopyOnSave, }: { - onSave: () => any; + onSave: (props: OnSaveProps) => void; onClose: () => void; title: string; showCopyOnSave: boolean; diff --git a/x-pack/legacy/plugins/graph/public/services/save.tsx b/x-pack/legacy/plugins/graph/public/services/save.tsx new file mode 100644 index 0000000000000..e69de29bb2d1d From ebe80d151bf5ccae426d6aefb2839ba3c4d56c1a Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 27 Aug 2019 18:27:04 +0200 Subject: [PATCH 13/37] wip save modal --- .../show_saved_object_save_modal.tsx | 27 +++++---- x-pack/legacy/plugins/graph/public/app.js | 45 ++------------- .../public/components/graph_save_modal.tsx | 38 +++++++++++-- .../plugins/graph/public/services/save.tsx | 55 +++++++++++++++++++ 4 files changed, 107 insertions(+), 58 deletions(-) diff --git a/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx b/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx index bd2fbba3aa145..9c03a3de34d75 100644 --- a/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx +++ b/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx @@ -21,7 +21,12 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { I18nContext } from 'ui/i18n'; -export function showSaveModal(saveModal) { +interface MinimalSaveModalProps { + onSave: (...args: any[]) => Promise<{ id?: string } | { error: Error }>; + onClose: () => void; +} + +export function showSaveModal(saveModal: React.ReactElement) { const container = document.createElement('div'); const closeModal = () => { ReactDOM.unmountComponentAtNode(container); @@ -30,21 +35,19 @@ export function showSaveModal(saveModal) { const onSave = saveModal.props.onSave; - const onSaveConfirmed = (...args) => { - onSave(...args).then(({ id, error }) => { - if (id || error) { + const onSaveConfirmed: MinimalSaveModalProps['onSave'] = (...args) => { + return onSave(...args).then(response => { + if (('id' in response && response.id) || 'error' in response) { closeModal(); } + return response; }); }; document.body.appendChild(container); - const element = React.cloneElement( - saveModal, - { - onSave: onSaveConfirmed, - onClose: closeModal - } - ); + const element = React.cloneElement(saveModal, { + onSave: onSaveConfirmed, + onClose: closeModal, + }); ReactDOM.render({element}, container); -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 5906f469f631c..2b688e711fd41 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -32,7 +32,6 @@ import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; import { npStart } from 'ui/new_platform'; import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; import { capabilities } from 'ui/capabilities'; -import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; @@ -42,7 +41,9 @@ import { getReadonlyBadge } from './badge'; import { FormattedMessage } from '@kbn/i18n/react'; import { GraphListing } from './components/graph_listing'; -import { GraphSaveModal } from './components/graph_save_modal'; + +import { getEditUrl, getNewPath, getEditPath, setBreadcrumbs } from './services/url'; +import { save } from './services/save'; import './angular/angular_venn_simple.js'; import gws from './angular/graph_client_workspace.js'; @@ -58,7 +59,6 @@ import { import { getOutlinkEncoders, } from './angular/services/outlink_encoders'; -import { getEditUrl, getNewPath, getEditPath, setBreadcrumbs } from './services/url'; import saveTemplate from './angular/templates/save_workspace.html'; import settingsTemplate from './angular/templates/settings.html'; @@ -895,42 +895,7 @@ app.controller('graphuiPlugin', function ( return $scope.allSavingDisabled || $scope.selectedFields.length === 0; }, run: () => { - const currentTitle = $scope.savedWorkspace.title; - const currentDescription = $scope.savedWorkspace.title; - const onSave = ({ - newTitle, - newDescription, - newCopyOnSave, - isTitleDuplicateConfirmed, - onTitleDuplicate, - }) => { - $scope.savedWorkspace.title = newTitle; - $scope.savedWorkspace.description = newDescription; - $scope.savedWorkspace.copyOnSave = newCopyOnSave; - const saveOptions = { - confirmOverwrite: false, - isTitleDuplicateConfirmed, - onTitleDuplicate, - }; - return $scope.saveWorkspace(saveOptions).then((response) => { - // If the save wasn't successful, put the original values back. - if (!(response.id)) { - $scope.savedWorkspace.title = currentTitle; - $scope.savedWorkspace.description = currentDescription; - } - return response; - }); - }; - const graphSaveModal = ( - {}} - title={$scope.savedWorkspace.title} - description={$scope.savedWorkspace.description} - showCopyOnSave={$scope.savedWorkspace.id} - /> - ); - showSaveModal(graphSaveModal); + save($scope.savedWorkspace, $scope.saveWorkspace); }, testId: 'graphSaveButton', }); @@ -1205,8 +1170,6 @@ app.controller('graphuiPlugin', function ( }); $scope.savedWorkspace.numVertices = vertices.length; $scope.savedWorkspace.numLinks = links.length; - $scope.savedWorkspace.description = $scope.description; - return $scope.savedWorkspace.save(saveOptions).then(function (id) { $scope.closeMenus(); diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx index 834251ddba2f5..29a606d49ccd4 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx @@ -4,28 +4,56 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { SavedObjectSaveModal, OnSaveProps } from 'ui/saved_objects/components/saved_object_save_modal'; +import React, { useState } from 'react'; +import { + SavedObjectSaveModal, + OnSaveProps, +} from 'ui/saved_objects/components/saved_object_save_modal'; +import { EuiFormRow, EuiTextArea } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export interface OnSaveGraphProps extends OnSaveProps { + newDescription: string; +} export function GraphSaveModal({ onSave, onClose, title, + description, showCopyOnSave, }: { - onSave: (props: OnSaveProps) => void; + onSave: (props: OnSaveGraphProps) => void; onClose: () => void; title: string; + description: string; showCopyOnSave: boolean; }) { + const [newDescription, setDescription] = useState(description); return ( { + onSave({ ...props, newDescription }); + }} onClose={onClose} title={title} showCopyOnSave={showCopyOnSave} objectType="graph-workspace" - options={null} + options={ + + { + setDescription(e.target.value); + }} + /> + + } /> ); } diff --git a/x-pack/legacy/plugins/graph/public/services/save.tsx b/x-pack/legacy/plugins/graph/public/services/save.tsx index e69de29bb2d1d..9991aa2b9eada 100644 --- a/x-pack/legacy/plugins/graph/public/services/save.tsx +++ b/x-pack/legacy/plugins/graph/public/services/save.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; +import { SavedGraphWorkspace } from '../types/persistence'; +import { GraphSaveModal, OnSaveGraphProps } from '../components/graph_save_modal'; + +export function save( + workspace: SavedGraphWorkspace, + saveWorkspace: (saveOptions: { + confirmOverwrite: boolean; + isTitleDuplicateConfirmed: boolean; + onTitleDuplicate: () => void; + }) => Promise<{ id?: string } | { error: string }> +) { + const currentTitle = workspace.title; + const currentDescription = workspace.title; + const onSave = ({ + newTitle, + newDescription, + newCopyOnSave, + isTitleDuplicateConfirmed, + onTitleDuplicate, + }: OnSaveGraphProps) => { + workspace.title = newTitle; + workspace.description = newDescription; + workspace.copyOnSave = newCopyOnSave; + const saveOptions = { + confirmOverwrite: false, + isTitleDuplicateConfirmed, + onTitleDuplicate, + }; + return saveWorkspace(saveOptions).then(response => { + // If the save wasn't successful, put the original values back. + if (!('id' in response) || !Boolean(response.id)) { + workspace.title = currentTitle; + workspace.description = currentDescription; + } + return response; + }); + }; + showSaveModal( + {}} + title={workspace.title} + description={workspace.description} + showCopyOnSave={Boolean(workspace.id)} + /> + ); +} From fab1577c75026bc707ec7a8276c2e40fcc5dfa30 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 28 Aug 2019 16:27:01 +0200 Subject: [PATCH 14/37] add and style save modal --- .../public/angular/directives/graph_save.js | 17 ----- .../graph/public/angular/templates/index.html | 3 +- .../angular/templates/save_workspace.html | 62 ---------------- x-pack/legacy/plugins/graph/public/app.js | 16 ++--- .../public/components/graph_save_modal.scss | 6 ++ .../public/components/graph_save_modal.tsx | 72 +++++++++++++++---- x-pack/legacy/plugins/graph/public/index.scss | 2 + .../plugins/graph/public/services/save.tsx | 32 ++++++--- .../plugins/graph/public/types/config.ts | 7 ++ 9 files changed, 104 insertions(+), 113 deletions(-) delete mode 100644 x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js delete mode 100644 x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html create mode 100644 x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss create mode 100644 x-pack/legacy/plugins/graph/public/types/config.ts diff --git a/x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js b/x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js deleted file mode 100644 index e8a2889feda24..0000000000000 --- a/x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from '../templates/save_workspace.html'; -const app = uiModules.get('app/graph'); - -app.directive('graphSave', function () { - return { - replace: true, - restrict: 'E', - template, - }; -}); diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/index.html b/x-pack/legacy/plugins/graph/public/angular/templates/index.html index 4dfc1846bb259..5aaa944d45b27 100644 --- a/x-pack/legacy/plugins/graph/public/angular/templates/index.html +++ b/x-pack/legacy/plugins/graph/public/angular/templates/index.html @@ -3,8 +3,7 @@ -
- +
diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html b/x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html deleted file mode 100644 index 76c492397b42c..0000000000000 --- a/x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html +++ /dev/null @@ -1,62 +0,0 @@ -
-
-
- - -
- -
- - -
-
- - {{ ::'xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning' | i18n: { - defaultMessage: 'The data in this workspace will be cleared and only the configuration will be saved', - } }} -
-
- -
- - - -
- - - {{ ::'xpack.graph.topNavMenu.save.saveConfigurationOnlyText' | i18n: { - defaultMessage: 'The data in this workspace will be cleared and only the configuration will be saved', - } }} - -
- -
diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 2b688e711fd41..d996d69b7f33a 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -60,10 +60,8 @@ import { getOutlinkEncoders, } from './angular/services/outlink_encoders'; -import saveTemplate from './angular/templates/save_workspace.html'; import settingsTemplate from './angular/templates/settings.html'; -import './angular/directives/graph_save'; import './angular/directives/graph_settings'; const app = uiModules.get('app/graph'); @@ -671,7 +669,6 @@ app.controller('graphuiPlugin', function ( $scope.resetWorkspace = function () { $scope.clearWorkspace(); - $scope.userHasConfirmedSaveWorkspaceData = false; $scope.selectedIndex = null; $scope.proposedIndex = null; $scope.detail = null; @@ -895,7 +892,12 @@ app.controller('graphuiPlugin', function ( return $scope.allSavingDisabled || $scope.selectedFields.length === 0; }, run: () => { - save($scope.savedWorkspace, $scope.saveWorkspace); + save({ + savePolicy: $scope.graphSavePolicy, + hasData: $scope.workspace && ($scope.workspace.nodes.length > 0 || $scope.workspace.blacklistedNodes.length > 0), + workspace: $scope.savedWorkspace, + saveWorkspace: $scope.saveWorkspace + }); }, testId: 'graphSaveButton', }); @@ -921,7 +923,6 @@ app.controller('graphuiPlugin', function ( updateBreadcrumbs(); $scope.menus = { - showSave: false, showSettings: false, }; @@ -1083,7 +1084,7 @@ app.controller('graphuiPlugin', function ( }); } - $scope.saveWorkspace = function (saveOptions) { + $scope.saveWorkspace = function (saveOptions, userHasConfirmedSaveWorkspaceData) { if ($scope.allSavingDisabled) { // It should not be possible to navigate to this function if allSavingDisabled is set // but adding check here as a safeguard. @@ -1094,7 +1095,7 @@ app.controller('graphuiPlugin', function ( } initWorkspaceIfRequired(); const canSaveData = $scope.graphSavePolicy === 'configAndData' || - ($scope.graphSavePolicy === 'configAndDataWithConsent' && $scope.userHasConfirmedSaveWorkspaceData); + ($scope.graphSavePolicy === 'configAndDataWithConsent' && userHasConfirmedSaveWorkspaceData); let blacklist = []; @@ -1173,7 +1174,6 @@ app.controller('graphuiPlugin', function ( return $scope.savedWorkspace.save(saveOptions).then(function (id) { $scope.closeMenus(); - $scope.userHasConfirmedSaveWorkspaceData = false; //reset flag if (id) { const title = i18n.translate('xpack.graph.saveWorkspace.successNotificationTitle', { defaultMessage: 'Saved "{workspaceTitle}"', diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss new file mode 100644 index 0000000000000..def32603643a2 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss @@ -0,0 +1,6 @@ +// Import the EUI global scope so we can use EUI constants +@import 'src/legacy/ui/public/styles/_styling_constants'; + +.gphSaveModal__warning { + width: $euiSizeXXL * 10; +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx index 29a606d49ccd4..a6215f7215944 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx @@ -9,11 +9,14 @@ import { SavedObjectSaveModal, OnSaveProps, } from 'ui/saved_objects/components/saved_object_save_modal'; -import { EuiFormRow, EuiTextArea } from '@elastic/eui'; +import { EuiFormRow, EuiTextArea, EuiCallOut, EuiCheckbox, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { GraphSavePolicy } from '../types/config'; + export interface OnSaveGraphProps extends OnSaveProps { newDescription: string; + dataConsent: boolean; } export function GraphSaveModal({ @@ -22,37 +25,76 @@ export function GraphSaveModal({ title, description, showCopyOnSave, + savePolicy, + hasData, }: { onSave: (props: OnSaveGraphProps) => void; onClose: () => void; title: string; description: string; showCopyOnSave: boolean; + savePolicy: GraphSavePolicy; + hasData: boolean; }) { const [newDescription, setDescription] = useState(description); + const [dataConsent, setDataConsent] = useState(false); return ( { - onSave({ ...props, newDescription }); + onSave({ ...props, newDescription, dataConsent }); }} onClose={onClose} title={title} showCopyOnSave={showCopyOnSave} objectType="graph-workspace" options={ - - { - setDescription(e.target.value); - }} - /> - + <> + + { + setDescription(e.target.value); + }} + fullWidth + rows={5} + /> + + {savePolicy === 'configAndDataWithConsent' && hasData && !dataConsent && ( + <> + +

+ {i18n.translate('xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning', { + defaultMessage: + 'The data in this workspace will be cleared and only the configuration will be saved.', + })} +

+
+ + + )} + {savePolicy === 'configAndDataWithConsent' && hasData && ( + { + setDataConsent(e.target.checked); + }} + /> + )} + } /> ); diff --git a/x-pack/legacy/plugins/graph/public/index.scss b/x-pack/legacy/plugins/graph/public/index.scss index 3986f64c6f4c9..236551973ee7a 100644 --- a/x-pack/legacy/plugins/graph/public/index.scss +++ b/x-pack/legacy/plugins/graph/public/index.scss @@ -12,3 +12,5 @@ @import './main'; @import './angular/templates/index'; + +@import './components/graph_save_modal'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/graph/public/services/save.tsx b/x-pack/legacy/plugins/graph/public/services/save.tsx index 9991aa2b9eada..f942a4ba1f746 100644 --- a/x-pack/legacy/plugins/graph/public/services/save.tsx +++ b/x-pack/legacy/plugins/graph/public/services/save.tsx @@ -8,15 +8,26 @@ import React from 'react'; import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; import { SavedGraphWorkspace } from '../types/persistence'; import { GraphSaveModal, OnSaveGraphProps } from '../components/graph_save_modal'; +import { GraphSavePolicy } from '../types/config'; -export function save( - workspace: SavedGraphWorkspace, - saveWorkspace: (saveOptions: { - confirmOverwrite: boolean; - isTitleDuplicateConfirmed: boolean; - onTitleDuplicate: () => void; - }) => Promise<{ id?: string } | { error: string }> -) { +export function save({ + savePolicy, + hasData, + workspace, + saveWorkspace, +}: { + savePolicy: GraphSavePolicy; + hasData: boolean; + workspace: SavedGraphWorkspace; + saveWorkspace: ( + saveOptions: { + confirmOverwrite: boolean; + isTitleDuplicateConfirmed: boolean; + onTitleDuplicate: () => void; + }, + dataConsent: boolean + ) => Promise<{ id?: string } | { error: string }>; +}) { const currentTitle = workspace.title; const currentDescription = workspace.title; const onSave = ({ @@ -25,6 +36,7 @@ export function save( newCopyOnSave, isTitleDuplicateConfirmed, onTitleDuplicate, + dataConsent, }: OnSaveGraphProps) => { workspace.title = newTitle; workspace.description = newDescription; @@ -34,7 +46,7 @@ export function save( isTitleDuplicateConfirmed, onTitleDuplicate, }; - return saveWorkspace(saveOptions).then(response => { + return saveWorkspace(saveOptions, dataConsent).then(response => { // If the save wasn't successful, put the original values back. if (!('id' in response) || !Boolean(response.id)) { workspace.title = currentTitle; @@ -45,6 +57,8 @@ export function save( }; showSaveModal( {}} title={workspace.title} diff --git a/x-pack/legacy/plugins/graph/public/types/config.ts b/x-pack/legacy/plugins/graph/public/types/config.ts new file mode 100644 index 0000000000000..eaff11d9d4548 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/types/config.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export type GraphSavePolicy = 'configAndDataWithConsent' | 'configAndData' | 'config' | 'none'; From a9d31c8cc42c5b426941f85b0d58f0b3ac7a7820 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 28 Aug 2019 16:44:30 +0200 Subject: [PATCH 15/37] add placeholder to description field --- .../plugins/graph/public/components/graph_save_modal.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx index a6215f7215944..d53f5a64cd73c 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx @@ -58,6 +58,10 @@ export function GraphSaveModal({ { setDescription(e.target.value); }} From d187eae6b22d78d85f4d995f3079af19a322886e Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 29 Aug 2019 10:17:17 +0200 Subject: [PATCH 16/37] disable dirty check on breadcrumb navigation and fix delete function --- x-pack/legacy/plugins/graph/public/app.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 4b221e7d9ef2e..910bdd13c03c4 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -99,7 +99,7 @@ uiRoutes .when('/home', { template: listingTemplate, badge: getReadonlyBadge, - controller($injector, $location, $scope, $route, Private, config, Promise, kbnBaseUrl) { + controller($injector, $location, $scope, Private, config, Promise, kbnBaseUrl) { checkLicense(Promise, kbnBaseUrl); const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName; const graphService = services['Graph workspace']; @@ -116,8 +116,8 @@ uiRoutes kbnUrl.redirect(getEditPath(workspace)); }; $scope.getViewUrl = (workspace) => getEditUrl(chrome, workspace); - $scope.delete = (ids) => { - return graphService.delete(ids); + $scope.delete = (workspaces) => { + return graphService.delete(workspaces.map(({ id }) => id)); }; $scope.capabilities = capabilities.get().graph; $scope.initialFilter = ($location.search()).filter || ''; @@ -910,10 +910,11 @@ app.controller('graphuiPlugin', function ( chrome, savedWorkspace: $route.current.locals.savedWorkspace, navigateTo: () => { - canWipeWorkspace(function () { - $scope.$evalAsync(() => { - kbnUrl.changePath('/home/'); - }); + // TODO this should be wrapped into canWipeWorkspace, + // but the check is too simple right now. Change this + // once actual state-diffing is in place. + $scope.$evalAsync(() => { + kbnUrl.changePath('/home/'); }); } }); From a289d7dd96731b58c7d3f605b71bacfe401ce293 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 29 Aug 2019 15:36:33 +0200 Subject: [PATCH 17/37] improve onClick typing on breadcrumb --- .../core/public/kibana-plugin-public.chromebreadcrumb.md | 2 +- .../public/kibana-plugin-public.chromebreadcrumb.onclick.md | 2 +- src/core/public/chrome/chrome_service.tsx | 4 ++-- src/core/public/public.api.md | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md b/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md index f395defd5eecd..330702a45f633 100644 --- a/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md +++ b/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md @@ -17,6 +17,6 @@ export interface ChromeBreadcrumb | --- | --- | --- | | [data-test-subj](./kibana-plugin-public.chromebreadcrumb.data-test-subj.md) | string | | | [href](./kibana-plugin-public.chromebreadcrumb.href.md) | string | | -| [onClick](./kibana-plugin-public.chromebreadcrumb.onclick.md) | () => void | | +| [onClick](./kibana-plugin-public.chromebreadcrumb.onclick.md) | MouseEventHandler<HTMLButtonElement> | | | [text](./kibana-plugin-public.chromebreadcrumb.text.md) | string | | diff --git a/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.onclick.md b/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.onclick.md index 806b209b5cb35..1e0ae36e893a1 100644 --- a/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.onclick.md +++ b/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.onclick.md @@ -7,5 +7,5 @@ Signature: ```typescript -onClick?: () => void; +onClick?: MouseEventHandler; ``` diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index 24c47abd55c24..d829a27260d27 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React from 'react'; +import React, { MouseEventHandler } from 'react'; import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; import * as Url from 'url'; @@ -63,7 +63,7 @@ export interface ChromeBreadcrumb { text: string; href?: string; 'data-test-subj'?: string; - onClick?: () => void; + onClick?: MouseEventHandler; } /** @public */ diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 71260a856ad07..060188608b860 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -5,6 +5,7 @@ ```ts import { IconType } from '@elastic/eui'; +import { MouseEventHandler } from 'react'; import { Observable } from 'rxjs'; import React from 'react'; import * as Rx from 'rxjs'; @@ -63,7 +64,7 @@ export interface ChromeBreadcrumb { // (undocumented) href?: string; // (undocumented) - onClick?: () => void; + onClick?: MouseEventHandler; // (undocumented) text: string; } From be1e59a621d05e5ded2d78cb8d14d539ad176d29 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 29 Aug 2019 15:40:59 +0200 Subject: [PATCH 18/37] fix newline error and use new types in dashboard app controller --- .../kibana/public/dashboard/dashboard_app_controller.tsx | 1 - .../ui/public/saved_objects/show_saved_object_save_modal.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index 081ad3fa3a95d..802f3cc7bbc09 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -33,7 +33,6 @@ import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter'; import { docTitle } from 'ui/doc_title/doc_title'; -// @ts-ignore import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; import { showShareContextMenu, ShareContextMenuExtensionsRegistryProvider } from 'ui/share'; diff --git a/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx b/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx index 9c03a3de34d75..4de4978424299 100644 --- a/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx +++ b/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx @@ -50,4 +50,4 @@ export function showSaveModal(saveModal: React.ReactElement{element}, container); -} \ No newline at end of file +} From c5ac49b5c759b215a9534a95b9a95a719a398702 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 30 Aug 2019 12:32:00 +0200 Subject: [PATCH 19/37] fix translation errors --- x-pack/legacy/plugins/graph/public/app.js | 2 +- .../public/components/graph_save_modal.scss | 2 +- .../public/components/graph_save_modal.tsx | 17 +++++++++++++++-- .../translations/translations/ja-JP.json | 5 ----- .../translations/translations/zh-CN.json | 5 ----- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 2eb98ca0477ea..3774a7a410f91 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -876,7 +876,7 @@ app.controller('graphuiPlugin', function ( defaultMessage: 'Save', }), description: i18n.translate('xpack.graph.topNavMenu.saveWorkspace.enabledAriaLabel', { - defaultMessage: 'Save Workspace', + defaultMessage: 'Save workspace', }), tooltip: () => { if ($scope.allSavingDisabled) { diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss index def32603643a2..c9a49bf2e11eb 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss @@ -1,6 +1,6 @@ // Import the EUI global scope so we can use EUI constants @import 'src/legacy/ui/public/styles/_styling_constants'; -.gphSaveModal__warning { +.gphSaveModal__callout { width: $euiSizeXXL * 10; } \ No newline at end of file diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx index d53f5a64cd73c..17157af75e853 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx @@ -51,7 +51,7 @@ export function GraphSaveModal({ <> @@ -74,7 +74,7 @@ export function GraphSaveModal({

{i18n.translate('xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning', { @@ -98,6 +98,19 @@ export function GraphSaveModal({ }} /> )} + {savePolicy === 'config' && hasData && ( + <> + +

+ {i18n.translate('xpack.graph.topNavMenu.save.saveConfigurationOnlyText', { + defaultMessage: + 'The data in this workspace will be cleared and only the configuration will be saved.', + })} +

+
+ + + )} } /> diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 50eff336fcffe..87b8d534ff408 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4643,16 +4643,11 @@ "xpack.graph.topNavMenu.newWorkspaceAriaLabel": "新規ワークスペース", "xpack.graph.topNavMenu.newWorkspaceLabel": "新規", "xpack.graph.topNavMenu.newWorkspaceTooltip": "新規ワークスペースを作成します", - "xpack.graph.topNavMenu.save.confirmButtonAriaLabel": "ワークスペースを保存", - "xpack.graph.topNavMenu.save.confirmButtonLabel": "保存", "xpack.graph.topNavMenu.save.descriptionInputLabel": "説明", "xpack.graph.topNavMenu.save.descriptionInputPlaceholder": "メモ…", - "xpack.graph.topNavMenu.save.nameInputLabel": "名前", - "xpack.graph.topNavMenu.save.nameInputPlaceholder": "グラフワークスペース名", "xpack.graph.topNavMenu.save.saveConfigurationOnlyText": "このワークスペースのデータは消去され、構成のみが保存されます", "xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning": "このワークスペースのデータは消去され、構成のみが保存されます", "xpack.graph.topNavMenu.save.saveGraphContentCheckboxLabel": "Graph コンテンツを保存", - "xpack.graph.topNavMenu.save.saveWorkspaceTitle": "ワークスペースの保存", "xpack.graph.topNavMenu.saveWorkspace.disabledTooltip": "現在の保存ポリシーでは、保存されたワークスペースへの変更が許可されていません", "xpack.graph.topNavMenu.saveWorkspace.enabledAriaLabel": "ワークスペースを保存", "xpack.graph.topNavMenu.saveWorkspace.enabledLabel": "保存", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 1dfb90dd7389c..8f1a00e825ddd 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4786,16 +4786,11 @@ "xpack.graph.topNavMenu.newWorkspaceAriaLabel": "新建工作空间", "xpack.graph.topNavMenu.newWorkspaceLabel": "新建", "xpack.graph.topNavMenu.newWorkspaceTooltip": "新建工作空间", - "xpack.graph.topNavMenu.save.confirmButtonAriaLabel": "保存工作空间", - "xpack.graph.topNavMenu.save.confirmButtonLabel": "保存", "xpack.graph.topNavMenu.save.descriptionInputLabel": "描述", "xpack.graph.topNavMenu.save.descriptionInputPlaceholder": "任何备注......", - "xpack.graph.topNavMenu.save.nameInputLabel": "名称", - "xpack.graph.topNavMenu.save.nameInputPlaceholder": "Graph 工作空间名称", "xpack.graph.topNavMenu.save.saveConfigurationOnlyText": "将清除此工作空间的数据,仅保存配置", "xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning": "将清除此工作空间的数据,仅保存配置", "xpack.graph.topNavMenu.save.saveGraphContentCheckboxLabel": "保存 Graph 内容", - "xpack.graph.topNavMenu.save.saveWorkspaceTitle": "保存工作空间", "xpack.graph.topNavMenu.saveWorkspace.disabledTooltip": "当前保存策略不允许对已保存的工作空间做任何更改", "xpack.graph.topNavMenu.saveWorkspace.enabledAriaLabel": "保存工作空间", "xpack.graph.topNavMenu.saveWorkspace.enabledLabel": "保存", From 8d6cec17e6d7224f778afd334444e7289e7bf6ea Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 30 Aug 2019 14:54:55 +0200 Subject: [PATCH 20/37] fix i18n translation for real --- .../legacy/plugins/graph/public/components/graph_save_modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx index 17157af75e853..5421c43256215 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx @@ -51,7 +51,7 @@ export function GraphSaveModal({ <> From 173804acc089f70a5156b4cd98a5cb5fc7a7dd5c Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 2 Sep 2019 09:39:00 +0200 Subject: [PATCH 21/37] code review --- .../saved_object_save_modal.test.tsx.snap | 7 +-- .../components/saved_object_save_modal.tsx | 61 +++++++++++------- .../public/angular/directives/graph_save.js | 17 ----- .../angular/templates/save_workspace.html | 62 ------------------- x-pack/legacy/plugins/graph/public/app.js | 7 +-- .../public/components/graph_save_modal.scss | 2 +- .../public/components/graph_save_modal.tsx | 60 +++++++++--------- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 9 files changed, 76 insertions(+), 142 deletions(-) delete mode 100644 x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js delete mode 100644 x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html diff --git a/src/legacy/ui/public/saved_objects/components/__snapshots__/saved_object_save_modal.test.tsx.snap b/src/legacy/ui/public/saved_objects/components/__snapshots__/saved_object_save_modal.test.tsx.snap index aabf9d8d1d1e7..5ac85fc9d32e8 100644 --- a/src/legacy/ui/public/saved_objects/components/__snapshots__/saved_object_save_modal.test.tsx.snap +++ b/src/legacy/ui/public/saved_objects/components/__snapshots__/saved_object_save_modal.test.tsx.snap @@ -6,7 +6,6 @@ exports[`SavedObjectSaveModal should render matching snapshot 1`] = ` onSubmit={[Function]} > - + Save diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx index 05685817bb0da..d62d5226d6e71 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx @@ -35,6 +35,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import React, { Fragment } from 'react'; import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; export interface OnSaveProps { newTitle: string; @@ -52,6 +53,7 @@ interface Props { confirmButtonLabel?: React.ReactNode; options?: React.ReactNode; description?: string; + className?: string; } interface State { @@ -79,7 +81,7 @@ export class SavedObjectSaveModal extends React.Component {
@@ -136,22 +138,7 @@ export class SavedObjectSaveModal extends React.Component { /> - - {this.props.confirmButtonLabel ? ( - this.props.confirmButtonLabel - ) : ( - - )} - + {this.renderConfirmButton()}
@@ -204,6 +191,34 @@ export class SavedObjectSaveModal extends React.Component { this.saveSavedObject(); }; + private renderConfirmButton = () => { + const { isLoading, title, hasTitleDuplicate } = this.state; + + let confirmLabel: string | React.ReactNode = hasTitleDuplicate + ? i18n.translate('common.ui.savedObjects.saveModal.confirmSaveButtonLabel', { + defaultMessage: 'Confirm save', + }) + : i18n.translate('common.ui.savedObjects.saveModal.saveButtonLabel', { + defaultMessage: 'Save', + }); + + if (this.props.confirmButtonLabel) { + confirmLabel = this.props.confirmButtonLabel; + } + + return ( + + {confirmLabel} + + ); + }; + private renderDuplicateTitleCallout = () => { if (!this.state.hasTitleDuplicate) { return; @@ -230,10 +245,14 @@ export class SavedObjectSaveModal extends React.Component { objectType: this.props.objectType, confirmSaveLabel: ( - + {this.props.confirmButtonLabel + ? this.props.confirmButtonLabel + : i18n.translate( + 'common.ui.savedObjects.saveModal.duplicateTitleDescription.confirmSaveText', + { + defaultMessage: 'Confirm save', + } + )} ), }} diff --git a/x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js b/x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js deleted file mode 100644 index e8a2889feda24..0000000000000 --- a/x-pack/legacy/plugins/graph/public/angular/directives/graph_save.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from '../templates/save_workspace.html'; -const app = uiModules.get('app/graph'); - -app.directive('graphSave', function () { - return { - replace: true, - restrict: 'E', - template, - }; -}); diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html b/x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html deleted file mode 100644 index 76c492397b42c..0000000000000 --- a/x-pack/legacy/plugins/graph/public/angular/templates/save_workspace.html +++ /dev/null @@ -1,62 +0,0 @@ -
-
-
- - -
- -
- - -
-
- - {{ ::'xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning' | i18n: { - defaultMessage: 'The data in this workspace will be cleared and only the configuration will be saved', - } }} -
-
- -
- - - -
- - - {{ ::'xpack.graph.topNavMenu.save.saveConfigurationOnlyText' | i18n: { - defaultMessage: 'The data in this workspace will be cleared and only the configuration will be saved', - } }} - -
- -
diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index e43e451050d6c..4dc7faaff4771 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -61,7 +61,6 @@ import { save } from './services/save'; import settingsTemplate from './angular/templates/settings.html'; -import './angular/directives/graph_save'; import './angular/directives/graph_settings'; const app = uiModules.get('app/graph'); @@ -1174,7 +1173,6 @@ app.controller('graphuiPlugin', function ( $scope.savedWorkspace.numLinks = links.length; return $scope.savedWorkspace.save(saveOptions).then(function (id) { - $scope.closeMenus(); if (id) { const title = i18n.translate('xpack.graph.saveWorkspace.successNotificationTitle', { defaultMessage: 'Saved "{workspaceTitle}"', @@ -1192,8 +1190,9 @@ app.controller('graphuiPlugin', function ( text, 'data-test-subj': 'saveGraphSuccess', }); - if ($scope.savedWorkspace.id === $route.current.params.id) return; - kbnUrl.change(getEditPath($scope.savedWorkspace)); + if ($scope.savedWorkspace.id !== $route.current.params.id) { + kbnUrl.change(getEditPath($scope.savedWorkspace)); + } } return { id }; }, fatalError); diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss index c9a49bf2e11eb..334f9b6826732 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.scss @@ -1,6 +1,6 @@ // Import the EUI global scope so we can use EUI constants @import 'src/legacy/ui/public/styles/_styling_constants'; -.gphSaveModal__callout { +.gphSaveModal { width: $euiSizeXXL * 10; } \ No newline at end of file diff --git a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx index 5421c43256215..47c84ea169cd5 100644 --- a/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx +++ b/x-pack/legacy/plugins/graph/public/components/graph_save_modal.tsx @@ -9,7 +9,14 @@ import { SavedObjectSaveModal, OnSaveProps, } from 'ui/saved_objects/components/saved_object_save_modal'; -import { EuiFormRow, EuiTextArea, EuiCallOut, EuiCheckbox, EuiSpacer } from '@elastic/eui'; +import { + EuiFormRow, + EuiTextArea, + EuiCallOut, + EuiCheckbox, + EuiSpacer, + EuiSwitch, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { GraphSavePolicy } from '../types/config'; @@ -46,7 +53,10 @@ export function GraphSaveModal({ onClose={onClose} title={title} showCopyOnSave={showCopyOnSave} - objectType="graph-workspace" + objectType={i18n.translate('xpack.graph.topNavMenu.save.objectType', { + defaultMessage: 'workspace', + })} + className="gphSaveModal" options={ <> - {savePolicy === 'configAndDataWithConsent' && hasData && !dataConsent && ( - <> - -

- {i18n.translate('xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning', { - defaultMessage: - 'The data in this workspace will be cleared and only the configuration will be saved.', - })} -

-
- - - )} {savePolicy === 'configAndDataWithConsent' && hasData && ( - { - setDataConsent(e.target.checked); - }} - /> + > + { + setDataConsent(e.target.checked); + }} + /> +
)} {savePolicy === 'config' && hasData && ( <> - +

{i18n.translate('xpack.graph.topNavMenu.save.saveConfigurationOnlyText', { defaultMessage: diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f273735f56d1c..2818bc115de72 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4646,7 +4646,6 @@ "xpack.graph.topNavMenu.save.descriptionInputLabel": "説明", "xpack.graph.topNavMenu.save.descriptionInputPlaceholder": "メモ…", "xpack.graph.topNavMenu.save.saveConfigurationOnlyText": "このワークスペースのデータは消去され、構成のみが保存されます", - "xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning": "このワークスペースのデータは消去され、構成のみが保存されます", "xpack.graph.topNavMenu.save.saveGraphContentCheckboxLabel": "Graph コンテンツを保存", "xpack.graph.topNavMenu.saveWorkspace.disabledTooltip": "現在の保存ポリシーでは、保存されたワークスペースへの変更が許可されていません", "xpack.graph.topNavMenu.saveWorkspace.enabledAriaLabel": "ワークスペースを保存", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 465a3fac4730e..34398a96858c3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4789,7 +4789,6 @@ "xpack.graph.topNavMenu.save.descriptionInputLabel": "描述", "xpack.graph.topNavMenu.save.descriptionInputPlaceholder": "任何备注......", "xpack.graph.topNavMenu.save.saveConfigurationOnlyText": "将清除此工作空间的数据,仅保存配置", - "xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning": "将清除此工作空间的数据,仅保存配置", "xpack.graph.topNavMenu.save.saveGraphContentCheckboxLabel": "保存 Graph 内容", "xpack.graph.topNavMenu.saveWorkspace.disabledTooltip": "当前保存策略不允许对已保存的工作空间做任何更改", "xpack.graph.topNavMenu.saveWorkspace.enabledAriaLabel": "保存工作空间", From 6f1bbcac4b6c91fe311a639b4600f9fb0e0e9735 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 2 Sep 2019 10:21:07 +0200 Subject: [PATCH 22/37] fix i18n phrases --- .../saved_objects/saved_object_save_modal.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx index a19b71d035faf..88a07e2492fbf 100644 --- a/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx +++ b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx @@ -87,7 +87,7 @@ export class SavedObjectSaveModal extends React.Component { @@ -109,7 +109,7 @@ export class SavedObjectSaveModal extends React.Component { fullWidth label={ } @@ -133,7 +133,7 @@ export class SavedObjectSaveModal extends React.Component { @@ -195,10 +195,10 @@ export class SavedObjectSaveModal extends React.Component { const { isLoading, title, hasTitleDuplicate } = this.state; let confirmLabel: string | React.ReactNode = hasTitleDuplicate - ? i18n.translate('common.ui.savedObjects.saveModal.confirmSaveButtonLabel', { + ? i18n.translate('kibana-react.savedObjects.saveModal.confirmSaveButtonLabel', { defaultMessage: 'Confirm save', }) - : i18n.translate('common.ui.savedObjects.saveModal.saveButtonLabel', { + : i18n.translate('kibana-react.savedObjects.saveModal.saveButtonLabel', { defaultMessage: 'Save', }); @@ -229,7 +229,7 @@ export class SavedObjectSaveModal extends React.Component { @@ -239,7 +239,7 @@ export class SavedObjectSaveModal extends React.Component { >

{ {this.props.confirmButtonLabel ? this.props.confirmButtonLabel : i18n.translate( - 'common.ui.savedObjects.saveModal.duplicateTitleDescription.confirmSaveText', + 'kibana-react.savedObjects.saveModal.duplicateTitleDescription.confirmSaveText', { defaultMessage: 'Confirm save', } @@ -277,7 +277,7 @@ export class SavedObjectSaveModal extends React.Component { onChange={this.onCopyOnSaveChange} label={ From 7c94337dffc7c53cdcf7ca3f263a569d8cc82546 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 2 Sep 2019 10:30:50 +0200 Subject: [PATCH 23/37] remove fragments --- .../public/saved_objects/saved_object_save_modal.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx index 88a07e2492fbf..ffc2aadc482f8 100644 --- a/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx +++ b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx @@ -33,7 +33,7 @@ import { EuiSwitch, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { Fragment } from 'react'; +import React from 'react'; import { EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -225,7 +225,7 @@ export class SavedObjectSaveModal extends React.Component { } return ( - + <> {

- + ); }; @@ -270,7 +270,7 @@ export class SavedObjectSaveModal extends React.Component { } return ( - + <> { } /> - + ); }; } From 7c0e664c901649e0f1de49d98c4d08040eac3911 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 3 Sep 2019 18:44:15 +0200 Subject: [PATCH 24/37] fix graph functional tests wip --- .../graph/public/angular/templates/index.html | 10 +- x-pack/legacy/plugins/graph/public/app.js | 1 + x-pack/test/functional/apps/graph/graph.js | 169 ---------- x-pack/test/functional/apps/graph/graph.ts | 176 +++++++++++ .../apps/graph/{index.js => index.ts} | 4 +- .../functional/page_objects/graph_page.js | 153 --------- .../functional/page_objects/graph_page.ts | 293 ++++++++++++++++++ 7 files changed, 478 insertions(+), 328 deletions(-) delete mode 100644 x-pack/test/functional/apps/graph/graph.js create mode 100644 x-pack/test/functional/apps/graph/graph.ts rename x-pack/test/functional/apps/graph/{index.js => index.ts} (76%) delete mode 100644 x-pack/test/functional/page_objects/graph_page.js create mode 100644 x-pack/test/functional/page_objects/graph_page.ts diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/index.html b/x-pack/legacy/plugins/graph/public/angular/templates/index.html index 5aaa944d45b27..407b3eb0144be 100644 --- a/x-pack/legacy/plugins/graph/public/angular/templates/index.html +++ b/x-pack/legacy/plugins/graph/public/angular/templates/index.html @@ -326,7 +326,7 @@ @@ -350,13 +350,13 @@
@@ -389,7 +389,7 @@ type="button" class="kuiButton kuiButton--basic kuiButton--small gphVertexSelect__button" ng-disabled="workspace.nodes.length === 0" ng-click="setDetail(null);workspace.selectInvert()" i18n-id="xpack.graph.sidebar.selections.invertSelectionButtonLabel" - i18n-default-message="invert" + i18n-default-message="invert" data-test-subj="graphInvertSelection" >
diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index 4dc7faaff4771..dd9b326127380 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -862,6 +862,7 @@ app.controller('graphuiPlugin', function ( canWipeWorkspace(function () { kbnUrl.change('/workspace/', {}); }); }, + testId: 'graphNewButton', }); // if saving is disabled using uiCapabilities, we don't want to render the save diff --git a/x-pack/test/functional/apps/graph/graph.js b/x-pack/test/functional/apps/graph/graph.js deleted file mode 100644 index b7e5e44450973..0000000000000 --- a/x-pack/test/functional/apps/graph/graph.js +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -// import { indexBy } from 'lodash'; -export default function ({ getService, getPageObjects }) { - - const PageObjects = getPageObjects(['settings', 'common', 'graph', 'header']); - const log = getService('log'); - const esArchiver = getService('esArchiver'); - const browser = getService('browser'); - const retry = getService('retry'); - - - describe.skip('graph', function () { // eslint-disable-line jest/no-disabled-tests - before(async () => { - await browser.setWindowSize(1600, 1000); - log.debug('load graph/secrepo data'); - await esArchiver.loadIfNeeded('graph/secrepo'); - await esArchiver.load('empty_kibana'); - log.debug('create secrepo index pattern'); - await PageObjects.settings.createIndexPattern('secrepo', '@timestamp'); - log.debug('navigateTo graph'); - await PageObjects.common.navigateToApp('graph'); - }); - - const graphName = 'my Graph workspace name ' + new Date().getTime(); - - const expectedText = [ 'blog', - '/wordpress/wp-admin/', - '202.136.75.194', - '82.173.74.216', - '187.131.21.37', - 'wp', - '107.152.98.141', - 'login.php', - '181.113.155.46', - 'admin', - 'wordpress', - '/test/wp-admin/', - 'test', - '/wp-login.php', - '/blog/wp-admin/' - ]; - - - // the line width with abotu 15 decimal places of accuracy looks like it will cause problems - const expectedLineStyle = [ 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:4.90799126435544px', - 'stroke-width:2.0042343137225673px', - 'stroke-width:5.645417023048188px', - 'stroke-width:2px', - 'stroke-width:10px', - 'stroke-width:2.377140951428095px', - 'stroke-width:2.073923530343478px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px', - 'stroke-width:2px' - ]; - - async function buildGraph() { - log.debug('select index pattern secrepo*'); - await PageObjects.graph.selectIndexPattern('secrepo*'); - // select fields url.parts, url, params and src - await PageObjects.graph.addField('url.parts'); - await PageObjects.graph.addField('url'); - await PageObjects.graph.addField('params'); - await PageObjects.graph.addField('src'); - await PageObjects.graph.query('admin'); - await PageObjects.common.sleep(8000); - } - - it('should show correct data circle text', async function () { - await buildGraph(); - const circlesText = await PageObjects.graph.getGraphCircleText(); - log.debug('circle count = ' + circlesText.length); - log.debug('circle values = ' + circlesText); - expect(circlesText.length).to.equal(expectedText.length); - expect(circlesText).to.eql(expectedText); - }); - - it('should show correct number of connecting lines', async function () { - const lineStyle = await PageObjects.graph.getGraphConnectingLines(); - log.debug('line count = ' + lineStyle.length); - log.debug('line style = ' + lineStyle); - expect(lineStyle.length).to.eql(expectedLineStyle.length); - expect(lineStyle).to.eql(expectedLineStyle); - }); - - it('should save Graph workspace', async function () { - const graphExists = await PageObjects.graph.saveGraph(graphName); - expect(graphExists).to.eql(true); - }); - - // open the same graph workspace again and make sure the results are the same - it.skip('should open Graph workspace', async function () { - await PageObjects.graph.openGraph(graphName); - const circlesText = await PageObjects.graph.getGraphCircleText(); - log.debug('circle count = ' + circlesText.length); - log.debug('circle values = ' + circlesText); - expect(circlesText.length).to.equal(expectedText.length); - }); - - it.skip('should delete graph', async function () { - const alertText = await PageObjects.graph.deleteGraph(graphName); - log.debug('alertText = ' + alertText); - }); - - - it('should show venn when clicking a line', async function () { - - await retry.tryForTime(120000, async function () { - // This test can fail after 60000ms defined as mochaOpts.timeout in - // kibana/src/functional_test_runner/lib/config/schema.js - log.debug('build the same graph until we can click the line with stroke-width:2.0042343137225673px'); - await PageObjects.graph.newGraph(); - await buildGraph(); - log.debug('click the line with stroke-width:2.0042343137225673px'); - await PageObjects.graph.clickGraphConnectingLine('stroke-width:2.0042343137225673px'); - }); - - const vennTerm1 = await PageObjects.graph.getVennTerm1(); - log.debug('vennTerm1 = ' + vennTerm1); - - const vennTerm2 = await PageObjects.graph.getVennTerm2(); - log.debug('vennTerm2 = ' + vennTerm2); - - const smallVennTerm1 = await PageObjects.graph.getSmallVennTerm1(); - log.debug('smallVennTerm1 = ' + smallVennTerm1); - - const smallVennTerm12 = await PageObjects.graph.getSmallVennTerm12(); - log.debug('smallVennTerm12 = ' + smallVennTerm12); - - const smallVennTerm2 = await PageObjects.graph.getSmallVennTerm2(); - log.debug('smallVennTerm2 = ' + smallVennTerm2); - - const vennEllipse1 = await PageObjects.graph.getVennEllipse1(); - log.debug('JSON.stringify(vennEllipse1) = ' + JSON.stringify(vennEllipse1)); - - const vennEllipse2 = await PageObjects.graph.getVennEllipse2(); - log.debug('JSON.stringify(vennEllipse2) = ' + JSON.stringify(vennEllipse2)); - - expect(vennTerm1).to.be('/blog/wp-admin/'); - expect(vennTerm2).to.be('admin'); - expect(smallVennTerm1).to.be('5'); - expect(smallVennTerm12).to.be(' (5) '); - expect(smallVennTerm2).to.be('21'); - expect(vennEllipse1).to.eql({ 'cx': '3.8470077339232853', 'cy': '2.5854414729132054', 'rx': '1.2615662610100802' }); - expect(vennEllipse2).to.eql({ 'cx': '5.170882945826411', 'cy': '2.5854414729132054', 'rx': '2.5854414729132054' }); - }); - - - }); -} diff --git a/x-pack/test/functional/apps/graph/graph.ts b/x-pack/test/functional/apps/graph/graph.ts new file mode 100644 index 0000000000000..37f1fb91db1bc --- /dev/null +++ b/x-pack/test/functional/apps/graph/graph.ts @@ -0,0 +1,176 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['settings', 'common', 'graph', 'header']); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + + describe.only('graph', function() { + before(async () => { + await browser.setWindowSize(1600, 1000); + log.debug('load graph/secrepo data'); + await esArchiver.loadIfNeeded('graph/secrepo'); + await esArchiver.load('empty_kibana'); + log.debug('create secrepo index pattern'); + await PageObjects.settings.createIndexPattern('secrepo', '@timestamp'); + log.debug('navigateTo graph'); + await PageObjects.common.navigateToApp('graph'); + await PageObjects.graph.createWorkspace(); + }); + + const graphName = 'my Graph workspace name ' + new Date().getTime(); + + const expectedNodes = [ + 'blog', + '/wordpress/wp-admin/', + '202.136.75.194', + '190.154.27.54', + '187.131.21.37', + 'wp', + '80.5.27.16', + 'login.php', + '181.113.155.46', + 'admin', + 'wordpress', + '/test/wp-admin/', + 'test', + '/wp-login.php', + '/blog/wp-admin/', + ]; + + const expectedConnectionWidth: Record> = { + '/blog/wp-admin/': { wp: 2, blog: 5.51581 }, + wp: { + blog: 2, + '202.136.75.194': 2, + 'login.php': 2, + admin: 2, + '/test/wp-admin/': 2, + '/wp-login.php': 2, + '80.5.27.16': 2, + '/wordpress/wp-admin/': 2, + '190.154.27.54': 2, + '187.131.21.37': 2, + '181.113.155.46': 2, + }, + admin: { test: 2, blog: 2, '/blog/wp-admin/': 2 }, + '/test/wp-admin/': { admin: 2 }, + test: { wp: 2, '/test/wp-admin/': 8.54514 }, + wordpress: { wp: 2, admin: 2.0311 }, + '/wordpress/wp-admin/': { wordpress: 9.70794, admin: 2.30771 }, + }; + + async function buildGraph() { + log.debug('select index pattern secrepo*'); + await PageObjects.graph.selectIndexPattern('secrepo*'); + // select fields url.parts, url, params and src + await PageObjects.graph.addField('url.parts'); + await PageObjects.graph.addField('url'); + await PageObjects.graph.addField('params'); + await PageObjects.graph.addField('src'); + await PageObjects.graph.query('admin'); + await PageObjects.common.sleep(8000); + } + + it('should show correct node labels', async function() { + await buildGraph(); + const { nodes } = await PageObjects.graph.getGraphObjects(); + const circlesText = nodes.map(({ label }) => label); + expect(circlesText.length).to.equal(expectedNodes.length); + expect(circlesText).to.eql(expectedNodes); + }); + + it('should show correct connections', async function() { + const epsilon = Number.EPSILON; + const expectedConnectionCount = Object.values(expectedConnectionWidth) + .map(connections => Object.values(connections).length) + .reduce((acc, n) => acc + n, 0); + const { edges } = await PageObjects.graph.getGraphObjects(); + expect(edges.length).to.be(expectedConnectionCount); + edges.forEach(edge => { + const from = edge.sourceNode.label!; + const to = edge.targetNode.label!; + // fuzzy matching to take floating point rounding issues into account + expect(expectedConnectionWidth[from][to]).to.be.within( + edge.width - epsilon, + edge.width + epsilon + ); + }); + }); + + it('should save Graph workspace', async function() { + const graphExists = await PageObjects.graph.saveGraph(graphName); + expect(graphExists).to.eql(true); + }); + + // open the same graph workspace again and make sure the results are the same + it('should open Graph workspace', async function() { + await PageObjects.graph.openGraph(graphName); + const { nodes } = await PageObjects.graph.getGraphObjects(); + const circlesText = nodes.map(({ label }) => label); + expect(circlesText.length).to.equal(expectedNodes.length); + expect(circlesText).to.eql(expectedNodes); + }); + + it('should create new Graph workspace', async function() { + await PageObjects.graph.newGraph(); + const { nodes, edges } = await PageObjects.graph.getGraphObjects(); + expect(nodes).to.be.empty(); + expect(edges).to.be.empty(); + }); + + it('should show venn when clicking a line', async function() { + await buildGraph(); + const { edges } = await PageObjects.graph.getGraphObjects(); + + const wordpressAdminEdge = edges.find( + ({ sourceNode, targetNode }) => + sourceNode.label === 'wordpress' && targetNode.label === 'admin' + )!; + + await PageObjects.graph.selectEdge(wordpressAdminEdge); + + const vennTerm1 = await PageObjects.graph.getVennTerm1(); + log.debug('vennTerm1 = ' + vennTerm1); + + const vennTerm2 = await PageObjects.graph.getVennTerm2(); + log.debug('vennTerm2 = ' + vennTerm2); + + const smallVennTerm1 = await PageObjects.graph.getSmallVennTerm1(); + log.debug('smallVennTerm1 = ' + smallVennTerm1); + + const smallVennTerm12 = await PageObjects.graph.getSmallVennTerm12(); + log.debug('smallVennTerm12 = ' + smallVennTerm12); + + const smallVennTerm2 = await PageObjects.graph.getSmallVennTerm2(); + log.debug('smallVennTerm2 = ' + smallVennTerm2); + + const vennEllipse1 = await PageObjects.graph.getVennEllipse1(); + log.debug('JSON.stringify(vennEllipse1) = ' + JSON.stringify(vennEllipse1)); + + const vennEllipse2 = await PageObjects.graph.getVennEllipse2(); + log.debug('JSON.stringify(vennEllipse2) = ' + JSON.stringify(vennEllipse2)); + + expect(vennTerm1).to.be('wordpress'); + expect(vennTerm2).to.be('admin'); + expect(smallVennTerm1).to.be('5'); + expect(smallVennTerm12).to.be(' (5) '); + expect(smallVennTerm2).to.be('21'); + }); + + it('should delete graph', async function() { + await PageObjects.graph.goToListingPage(); + expect(await PageObjects.graph.getWorkspaceCount()).to.equal(1); + await PageObjects.graph.deleteGraph(graphName); + expect(await PageObjects.graph.getWorkspaceCount()).to.equal(0); + }); + }); +} diff --git a/x-pack/test/functional/apps/graph/index.js b/x-pack/test/functional/apps/graph/index.ts similarity index 76% rename from x-pack/test/functional/apps/graph/index.js rename to x-pack/test/functional/apps/graph/index.ts index 1eafe341ba2db..4eabb0701d933 100644 --- a/x-pack/test/functional/apps/graph/index.js +++ b/x-pack/test/functional/apps/graph/index.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export default function ({ loadTestFile }) { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { describe('graph app', function () { this.tags('ciGroup1'); diff --git a/x-pack/test/functional/page_objects/graph_page.js b/x-pack/test/functional/page_objects/graph_page.js deleted file mode 100644 index b4990c1653f09..0000000000000 --- a/x-pack/test/functional/page_objects/graph_page.js +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export function GraphPageProvider({ getService, getPageObjects }) { - const find = getService('find'); - const log = getService('log'); - const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['common', 'header', 'settings']); - const retry = getService('retry'); - - - class GraphPage { - - async selectIndexPattern(pattern) { - await find.clickDisplayedByCssSelector('.gphIndexSelect'); - await find.clickByCssSelector('.gphIndexSelect > option[label="' + pattern + '"]'); - } - - async clickAddField() { - await retry.try(async () => { - await find.clickByCssSelector('#addVertexFieldButton'); - // make sure the fieldSelectionList is not hidden - await testSubjects.exists('fieldSelectionList'); - }); - } - - async selectField(field) { - await find.clickDisplayedByCssSelector('select[id="fieldList"] > option[label="' + field + '"]'); - await find.clickDisplayedByCssSelector('button[ng-click="addFieldToSelection()"]'); - } - - async addField(field) { - log.debug('click Add Field icon'); - await this.clickAddField(); - log.debug('select field ' + field); - await this.selectField(field); - } - - async query(str) { - await find.setValue('input.kuiLocalSearchInput', str); - await find.clickDisplayedByCssSelector('button.kuiLocalSearchButton'); - } - - - async getGraphCircleText() { - const chartTypes = await find.allByCssSelector('text.gphNode__label'); - - async function getCircleText(circle) { - return circle.getVisibleText(); - } - - const getChartTypesPromises = chartTypes.map(getCircleText); - return Promise.all(getChartTypesPromises); - } - - async getGraphConnectingLines() { - const chartTypes = await find.allByCssSelector('line.edge'); - - async function getLineStyle(line) { - return line.getAttribute('style'); - } - - const getChartTypesPromises = chartTypes.map(getLineStyle); - return Promise.all(getChartTypesPromises); - } - - // click the line which matches the style - async clickGraphConnectingLine(style) { - await find.clickByCssSelector('line.edge[style="' + style + '"]'); - } - - async newGraph() { - log.debug('Click New Workspace'); - await find.clickByCssSelector('[aria-label="New Workspace"]'); - await PageObjects.common.sleep(1000); - const modal = await find.byCssSelector('#kibana-body'); - const page = await modal.getVisibleText(); - if (page.includes('This will clear the workspace - are you sure?')) { - return testSubjects.click('confirmModalConfirmButton'); - } - } - - async saveGraph(name) { - await find.clickByCssSelector('[aria-label="Save Workspace"]'); - await find.setValue('#workspaceTitle', name); - await find.clickByCssSelector('button[aria-label="Save workspace"]'); - - // Confirm that the Graph has been saved. - return await testSubjects.exists('saveGraphSuccess'); - } - - async openGraph(name) { - await find.clickByCssSelector('[aria-label="Load Saved Workspace"]'); - await find.setValue('input[name="filter"]', name); - await PageObjects.common.sleep(1000); - await find.clickByLinkText(name); - await PageObjects.common.sleep(5000); - } - - async deleteGraph() { - await find.clickByCssSelector('[aria-label="Delete Saved Workspace"]'); - await testSubjects.click('confirmModalConfirmButton'); - } - - - async getVennTerm1() { - const el = await find.byCssSelector('span.vennTerm1'); - return await el.getVisibleText(); - } - - async getVennTerm2() { - const el = await find.byCssSelector('span.vennTerm2'); - return await el.getVisibleText(); - } - - async getSmallVennTerm1() { - const el = await find.byCssSelector('small.vennTerm1'); - return await el.getVisibleText(); - } - - async getSmallVennTerm12() { - const el = await find.byCssSelector('small.vennTerm12'); - return await el.getVisibleText(); - } - - async getSmallVennTerm2() { - const el = await find.byCssSelector('small.vennTerm2'); - return await el.getVisibleText(); - } - - async getVennEllipse1() { - const el = await find.byCssSelector('ellipse.venn1'); - const cx = await el.getAttribute('cx'); - const cy = await el.getAttribute('cy'); - const rx = await el.getAttribute('rx'); - return { cx: cx, cy: cy, rx: rx }; - } - - async getVennEllipse2() { - const el = await find.byCssSelector('ellipse.venn2'); - const cx = await el.getAttribute('cx'); - const cy = await el.getAttribute('cy'); - const rx = await el.getAttribute('rx'); - return { cx: cx, cy: cy, rx: rx }; - } - - - } - return new GraphPage(); -} diff --git a/x-pack/test/functional/page_objects/graph_page.ts b/x-pack/test/functional/page_objects/graph_page.ts new file mode 100644 index 0000000000000..7ea61d52af0b1 --- /dev/null +++ b/x-pack/test/functional/page_objects/graph_page.ts @@ -0,0 +1,293 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper'; +import { FtrProviderContext } from '../ftr_provider_context'; + +interface Node { + circle: WebElementWrapper; + label: string; +} + +interface Edge { + sourceNode: Node; + targetNode: Node; + width: number; + element: WebElementWrapper; + } + +export function GraphPageProvider({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const log = getService('log'); + const testSubjects = getService('testSubjects'); + const browser = getService('browser'); + const PageObjects = getPageObjects(['common', 'header', 'settings']); + const retry = getService('retry'); + + class GraphPage { + async selectIndexPattern(pattern: string) { + await find.clickDisplayedByCssSelector('.gphIndexSelect'); + await find.clickByCssSelector('.gphIndexSelect > option[label="' + pattern + '"]'); + } + + async clickAddField() { + await retry.try(async () => { + await find.clickByCssSelector('#addVertexFieldButton'); + // make sure the fieldSelectionList is not hidden + await testSubjects.exists('fieldSelectionList'); + }); + } + + async selectField(field: string) { + await find.clickDisplayedByCssSelector( + 'select[id="fieldList"] > option[label="' + field + '"]' + ); + await find.clickDisplayedByCssSelector('button[ng-click="addFieldToSelection()"]'); + } + + async addField(field: string) { + log.debug('click Add Field icon'); + await this.clickAddField(); + log.debug('select field ' + field); + await this.selectField(field); + } + + async query(str: string) { + await find.setValue('input.kuiLocalSearchInput', str); + await find.clickDisplayedByCssSelector('button.kuiLocalSearchButton'); + } + + private getPositionAsString(x: string, y: string) { + return `${x}-${y}`; + } + + private async getCirclePosition(element: WebElementWrapper) { + const x = await element.getAttribute('cx'); + const y = await element.getAttribute('cy'); + return this.getPositionAsString(x, y); + } + + private async getLinePositions(element: WebElementWrapper) { + const x1 = await element.getAttribute('x1'); + const y1 = await element.getAttribute('y1'); + const x2 = await element.getAttribute('x2'); + const y2 = await element.getAttribute('y2'); + return [this.getPositionAsString(x1, y1), this.getPositionAsString(x2, y2)]; + } + + async selectEdge(edge: Edge) { + // TODO: select all nodes, then delesect everything but source and target + // by clicking the icons in the side bar. + // Then click the edge itself + await edge.sourceNode.circle.click(); + await testSubjects.click('graphLinkedSelection'); + await testSubjects.click('graphInvertSelection'); + await testSubjects.click('graphRemoveSelection'); + await edge.targetNode.circle.click(); + await testSubjects.click('graphLinkedSelection'); + await testSubjects.click('graphInvertSelection'); + await testSubjects.click('graphRemoveSelection'); + await edge.element.click(); + } + + async selectLinkedNodes() { + await testSubjects.click('graphLinkedSelection'); + } + + async getGraphObjects() { + if (await testSubjects.exists('graphPauseLayout')) { + await testSubjects.click('graphPauseLayout'); + } + const graphElements = await find.allByCssSelector( + '#svgRootGroup line, #svgRootGroup circle, text.gphNode__label' + ); + const nodes: Node[] = []; + const nodePositionMap: Record = {}; + const edges: Edge[] = []; + + // find all nodes and save their positions + for (const element of graphElements) { + const tagName: string = await element.getTagName(); + // check the position of the circle element + if (tagName === 'circle') { + nodes.push({ circle: element, label: '' }); + const position = await this.getCirclePosition(element); + nodePositionMap[position] = nodes.length - 1; + } + // get the label for the node from the text element + if (tagName === 'text') { + const text = await element.getVisibleText(); + nodes[nodes.length - 1].label = text; + } + } + + // find all edges + for (const element of graphElements) { + const tagName: string = await element.getTagName(); + if (tagName === 'line') { + const [sourcePosition, targetPosition] = await this.getLinePositions(element); + const lineStyle = await element.getAttribute('style'); + // grep out the width of the connection from the style attribute + const strokeWidth = Number(/stroke-width: (\d+(\.\d+)?)px/.exec(lineStyle)![1]); + edges.push({ + element, + width: strokeWidth, + // look up source and target node by matching start and end coordinates + // of the edges and the nodes + sourceNode: nodes[nodePositionMap[sourcePosition]], + targetNode: nodes[nodePositionMap[targetPosition]], + }); + } + } + + if (await testSubjects.exists('graphResumeLayout')) { + await testSubjects.click('graphResumeLayout'); + } + + return { + nodes, + edges, + }; + } + + async getGraphCircleText() { + const chartTypes = await find.allByCssSelector('text.gphNode__label'); + + async function getCircleText(circle: WebElementWrapper) { + return circle.getVisibleText(); + } + + const getChartTypesPromises = chartTypes.map(getCircleText); + return Promise.all(getChartTypesPromises); + } + + async getGraphConnectingLines() { + const chartTypes = await find.allByCssSelector('line.edge'); + + async function getLineStyle(line: WebElementWrapper) { + return line.getAttribute('style'); + } + + const getChartTypesPromises = chartTypes.map(getLineStyle); + return Promise.all(getChartTypesPromises); + } + + // click the line which matches the style + async clickGraphConnectingLine(style: string) { + await find.clickByCssSelector('line.edge[style="' + style + '"]'); + } + + async createWorkspace() { + await testSubjects.click('graphCreateWorkspacePromptButton'); + } + + async newGraph() { + log.debug('Click New Workspace'); + await testSubjects.click('graphNewButton'); + await PageObjects.common.sleep(1000); + await PageObjects.common.clickConfirmOnModal(); + } + + async saveGraph(name: string) { + await testSubjects.click('graphSaveButton'); + await testSubjects.setValue('savedObjectTitle', name); + await testSubjects.click('confirmSaveSavedObjectButton'); + + // Confirm that the Graph has been saved. + return await testSubjects.exists('saveGraphSuccess'); + } + + async getSearchFilter() { + const searchFilter = await find.allByCssSelector('.euiFieldSearch'); + return searchFilter[0]; + } + + async searchForWorkspaceWithName(name: string) { + await retry.try(async () => { + const searchFilter = await this.getSearchFilter(); + await searchFilter.clearValue(); + await searchFilter.click(); + await searchFilter.type(name); + await PageObjects.common.pressEnterKey(); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading', 5000); + }); + + await PageObjects.header.waitUntilLoadingHasFinished(); + } + + async goToListingPage() { + await testSubjects.click('graphHomeBreadcrumb'); + } + + async openGraph(name: string) { + await this.goToListingPage(); + await this.searchForWorkspaceWithName(name); + await find.clickByLinkText(name); + await PageObjects.common.sleep(5000); + } + + async deleteGraph(name: string) { + await this.goToListingPage(); + await this.searchForWorkspaceWithName(name); + await testSubjects.click('checkboxSelectAll'); + await this.clickDeleteSelectedWorkspaces(); + await PageObjects.common.clickConfirmOnModal(); + } + + async getWorkspaceCount() { + const workspaceTitles = await find.allByCssSelector( + '[data-test-subj^="graphListingTitleLink"]' + ); + return workspaceTitles.length; + } + + async clickDeleteSelectedWorkspaces() { + await testSubjects.click('deleteSelectedItems'); + } + + async getVennTerm1() { + const el = await find.byCssSelector('span.vennTerm1'); + return await el.getVisibleText(); + } + + async getVennTerm2() { + const el = await find.byCssSelector('span.vennTerm2'); + return await el.getVisibleText(); + } + + async getSmallVennTerm1() { + const el = await find.byCssSelector('small.vennTerm1'); + return await el.getVisibleText(); + } + + async getSmallVennTerm12() { + const el = await find.byCssSelector('small.vennTerm12'); + return await el.getVisibleText(); + } + + async getSmallVennTerm2() { + const el = await find.byCssSelector('small.vennTerm2'); + return await el.getVisibleText(); + } + + async getVennEllipse1() { + const el = await find.byCssSelector('ellipse.venn1'); + const cx = await el.getAttribute('cx'); + const cy = await el.getAttribute('cy'); + const rx = await el.getAttribute('rx'); + return { cx, cy, rx }; + } + + async getVennEllipse2() { + const el = await find.byCssSelector('ellipse.venn2'); + const cx = await el.getAttribute('cx'); + const cy = await el.getAttribute('cy'); + const rx = await el.getAttribute('rx'); + return { cx, cy, rx }; + } + } + return new GraphPage(); +} From 9f0ce62b3113bd3ed2e09cf9dff177935886ea0d Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 4 Sep 2019 11:29:34 +0200 Subject: [PATCH 25/37] fix functional test --- .../graph/public/angular/templates/index.html | 2 +- x-pack/test/functional/apps/graph/graph.ts | 12 +- .../functional/page_objects/graph_page.ts | 120 ++++++++---------- 3 files changed, 57 insertions(+), 77 deletions(-) diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/index.html b/x-pack/legacy/plugins/graph/public/angular/templates/index.html index 407b3eb0144be..3bc3eaadf9090 100644 --- a/x-pack/legacy/plugins/graph/public/angular/templates/index.html +++ b/x-pack/legacy/plugins/graph/public/angular/templates/index.html @@ -373,7 +373,7 @@ type="button" class="kuiButton kuiButton--basic kuiButton--small gphVertexSelect__button" ng-disabled="workspace.nodes.length === 0" ng-click="setDetail(null);workspace.selectAll()" i18n-id="xpack.graph.sidebar.selections.selectAllButtonLabel" - i18n-default-message="all" + i18n-default-message="all" data-test-subj="graphSelectAll" >