From 6fad639f25846e19704a90ae96b4e7b1538e5b9b Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 29 Aug 2019 17:19:56 +0200 Subject: [PATCH] [Graph] Listing page and folder restructuring (#44068) --- .../kibana-plugin-public.chromebreadcrumb.md | 1 + ...-plugin-public.chromebreadcrumb.onclick.md | 11 + src/core/public/chrome/chrome_service.tsx | 3 +- src/core/public/public.api.md | 3 + .../{ => angular}/__tests__/workspace.js | 2 +- .../angular_venn_simple.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 | 3 +- .../angular/templates/listing_ng_wrapper.html | 10 + .../templates/save_workspace.html | 0 .../{ => angular}/templates/settings.html | 0 x-pack/legacy/plugins/graph/public/app.js | 204 ++++++------------ .../plugins/graph/public/app_with_autoload.js | 8 - .../plugins/graph/public/breadcrumbs.js | 29 --- .../graph/public/components/graph_listing.tsx | 164 ++++++++++++++ .../graph/public/directives/graph_load.js | 17 -- x-pack/legacy/plugins/graph/public/index.scss | 2 +- .../plugins/graph/public/services/url.ts | 69 ++++++ .../public/templates/load_workspace.html | 15 -- .../plugins/graph/public/types/persistence.ts | 20 ++ .../translations/translations/ja-JP.json | 14 -- .../translations/translations/zh-CN.json | 14 -- .../graph/feature_controls/graph_security.ts | 33 +-- .../graph/feature_controls/graph_spaces.ts | 11 +- 35 files changed, 375 insertions(+), 258 deletions(-) create mode 100644 docs/development/core/public/kibana-plugin-public.chromebreadcrumb.onclick.md rename x-pack/legacy/plugins/graph/public/{ => angular}/__tests__/workspace.js (99%) 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_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 (99%) create mode 100644 x-pack/legacy/plugins/graph/public/angular/templates/listing_ng_wrapper.html 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%) delete mode 100644 x-pack/legacy/plugins/graph/public/app_with_autoload.js delete mode 100644 x-pack/legacy/plugins/graph/public/breadcrumbs.js create mode 100644 x-pack/legacy/plugins/graph/public/components/graph_listing.tsx delete mode 100644 x-pack/legacy/plugins/graph/public/directives/graph_load.js create mode 100644 x-pack/legacy/plugins/graph/public/services/url.ts delete mode 100644 x-pack/legacy/plugins/graph/public/templates/load_workspace.html create mode 100644 x-pack/legacy/plugins/graph/public/types/persistence.ts diff --git a/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md b/docs/development/core/public/kibana-plugin-public.chromebreadcrumb.md index 9a6e0a7cc871..330702a45f63 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) | 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 new file mode 100644 index 000000000000..1e0ae36e893a --- /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?: MouseEventHandler; +``` diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index 2fa6206f0ea0..d829a27260d2 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,6 +63,7 @@ export interface ChromeBreadcrumb { text: string; href?: string; 'data-test-subj'?: string; + onClick?: MouseEventHandler; } /** @public */ diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index a5c31e41e026..060188608b86 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,6 +64,8 @@ export interface ChromeBreadcrumb { // (undocumented) href?: string; // (undocumented) + onClick?: MouseEventHandler; + // (undocumented) text: string; } 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 99% rename from x-pack/legacy/plugins/graph/public/__tests__/workspace.js rename to x-pack/legacy/plugins/graph/public/angular/__tests__/workspace.js index 592872c96ee0..2575edb333af 100644 --- a/x-pack/legacy/plugins/graph/public/__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/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_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 99% rename from x-pack/legacy/plugins/graph/public/templates/index.html rename to x-pack/legacy/plugins/graph/public/angular/templates/index.html index fc536d54547e..4dfc1846bb25 100644 --- a/x-pack/legacy/plugins/graph/public/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/listing_ng_wrapper.html b/x-pack/legacy/plugins/graph/public/angular/templates/listing_ng_wrapper.html new file mode 100644 index 000000000000..09899453dd35 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/angular/templates/listing_ng_wrapper.html @@ -0,0 +1,10 @@ + 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 2f65077226a1..910bdd13c03c 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -30,18 +30,22 @@ 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 { capabilities } from 'ui/capabilities'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import appTemplate from './templates/index.html'; -import { getHomeBreadcrumbs, getWorkspaceBreadcrumbs } from './breadcrumbs'; +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 './angular-venn-simple.js'; -import gws from './graphClientWorkspace.js'; +import { GraphListing } from './components/graph_listing'; + +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, @@ -51,16 +55,14 @@ 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 settingsTemplate from './angular/templates/settings.html'; -import './directives/graph_load'; -import './directives/graph_save'; -import './directives/graph_settings'; +import './angular/directives/graph_save'; +import './angular/directives/graph_settings'; const app = uiModules.get('app/graph'); @@ -85,42 +87,49 @@ 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); + controller($injector, $location, $scope, Private, config, Promise, kbnBaseUrl) { + checkLicense(Promise, kbnBaseUrl); + const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName; + const graphService = services['Graph workspace']; + const kbnUrl = $injector.get('kbnUrl'); - 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 + $scope.listingLimit = config.get('savedObjects:listingLimit'); + $scope.create = () => { + kbnUrl.redirect(getNewPath()); + }; + $scope.find = (search) => { + return graphService.find(search, $scope.listingLimit); + }; + $scope.editItem = (workspace) => { + kbnUrl.redirect(getEditPath(workspace)); + }; + $scope.getViewUrl = (workspace) => getEditUrl(chrome, workspace); + $scope.delete = (workspaces) => { + return graphService.delete(workspaces.map(({ id }) => id)); + }; + $scope.capabilities = capabilities.get().graph; + $scope.initialFilter = ($location.search()).filter || ''; + setBreadcrumbs({ chrome }); } }) - .when('/workspace/:id', { + .when('/workspace/:id?', { template: appTemplate, - k7Breadcrumbs: getWorkspaceBreadcrumbs, + badge: getReadonlyBadge, 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 +157,7 @@ uiRoutes }, SavedWorkspacesProvider: function (Private) { return Private(SavedWorkspacesProvider); - }, - CheckLicense: checkLicense + } } }) .otherwise({ @@ -248,11 +256,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 @@ -344,13 +347,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 +847,7 @@ app.controller('graphuiPlugin', function ( }), run: function () { canWipeWorkspace(function () { - kbnUrl.change('/home', {}); + kbnUrl.change('/workspace/', {}); }); }, }); @@ -882,92 +888,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,9 +906,21 @@ app.controller('graphuiPlugin', function ( }, }); + setBreadcrumbs({ + chrome, + savedWorkspace: $route.current.locals.savedWorkspace, + navigateTo: () => { + // 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/'); + }); + } + }); + $scope.menus = { showSave: false, - showLoad: false, showSettings: false, }; @@ -1261,7 +1193,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 ca01a93fefc0..000000000000 --- 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'); 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 63887e23288f..000000000000 --- 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 000000000000..75dcc61017c7 --- /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/persistence'; + +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/directives/graph_load.js b/x-pack/legacy/plugins/graph/public/directives/graph_load.js deleted file mode 100644 index 369243fff748..000000000000 --- a/x-pack/legacy/plugins/graph/public/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/index.scss b/x-pack/legacy/plugins/graph/public/index.scss index eb166638ca24..3986f64c6f4c 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 000000000000..dd9db8fa1f07 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/services/url.ts @@ -0,0 +1,69 @@ +/* + * 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.home.breadcrumb', { + defaultMessage: 'Graph', + }), + onClick: () => { + options.navigateTo(getHomePath()); + }, + 'data-test-subj': 'graphHomeBreadcrumb', + }, + { + text: options.savedWorkspace + ? options.savedWorkspace.title + : i18n.translate('xpack.graph.newWorkspaceTitle', { + defaultMessage: 'Unsaved workspace', + }), + 'data-test-subj': 'graphCurrentWorkspaceBreadcrumb', + }, + ]); + } else { + options.chrome.breadcrumbs.set([ + { + text: i18n.translate('xpack.graph.home.breadcrumb', { + defaultMessage: 'Graph', + }), + href: `#${getHomePath()}`, + 'data-test-subj': 'graphHomeBreadcrumb', + }, + ]); + } +} diff --git a/x-pack/legacy/plugins/graph/public/templates/load_workspace.html b/x-pack/legacy/plugins/graph/public/templates/load_workspace.html deleted file mode 100644 index 5f8948caf1e9..000000000000 --- a/x-pack/legacy/plugins/graph/public/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/types/persistence.ts b/x-pack/legacy/plugins/graph/public/types/persistence.ts new file mode 100644 index 000000000000..56040aa30db4 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/types/persistence.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +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; + numVertices: number; + version: number; + wsState: string; +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 40906f180be9..98af4d5c21eb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4640,23 +4640,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 725fc957d876..630e8220a1de 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4783,23 +4783,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/test/functional/apps/graph/feature_controls/graph_security.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts index 05f6717106ba..808a21388bcd 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,19 +71,26 @@ 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'); - }); - - it('shows delete button', async () => { - await PageObjects.common.navigateToApp('graph'); - await testSubjects.existOrFail('graphDeleteButton'); + await testSubjects.existOrFail('workspaceLandingPage', { timeout: 10000 }); + await testSubjects.existOrFail('graphCreateWorkspacePromptButton'); }); 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.click('graphCreateWorkspacePromptButton'); + const breadcrumb = await testSubjects.find('graphCurrentWorkspaceBreadcrumb'); + expect(await breadcrumb.getVisibleText()).to.equal('Unsaved workspace'); + }); + + it('shows save button', async () => { + await testSubjects.existOrFail('graphSaveButton'); + }); }); describe('global graph read-only privileges', () => { @@ -129,16 +136,10 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { expect(navLinks).to.eql(['Graph', 'Management']); }); - it(`doesn't show save button`, async () => { + it('does not show a "Create new Workspace" button', async () => { await PageObjects.common.navigateToApp('graph'); - await testSubjects.existOrFail('graphOpenButton'); - await testSubjects.missingOrFail('graphSaveButton'); - }); - - it(`doesn't show delete 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 () => { 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 a0b81a8df702..25c094dc5ec9 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 () => { + it('landing page shows "Create new Workspace" button', async () => { await PageObjects.common.navigateToApp('graph', { basePath: '/s/custom_space', }); - await testSubjects.existOrFail('graphSaveButton'); + await testSubjects.existOrFail('workspaceLandingPage', { timeout: 10000 }); + await testSubjects.existOrFail('graphCreateWorkspacePromptButton'); }); - it('shows delete button', async () => { + it('allows creating a new workspace', async () => { await PageObjects.common.navigateToApp('graph', { basePath: '/s/custom_space', }); - await testSubjects.existOrFail('graphDeleteButton'); + await testSubjects.click('graphCreateWorkspacePromptButton'); + const breadcrumb = await testSubjects.find('graphCurrentWorkspaceBreadcrumb'); + expect(await breadcrumb.getVisibleText()).to.equal('Unsaved workspace'); }); });