From ffef9cc3e785b09d77774eab0455d053ebd3b7d9 Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Thu, 22 Nov 2018 12:52:05 +0100 Subject: [PATCH] New visualization type selection (#23833) * First version of new visualization selection * Extract some components * Remove visualization category * Remove old wizard code * Fix i18n ids * Fix tests * Fix tag cloud tests * Fix broken test method * Fix wrong method call * Fix TSVB navigation in tests * Restructure components * Fix for lab removal * Add tests * Timroes/eui vis type selection (#4) * Added background graphic from welcome screen to modal * Fixed up responsiveness * Change wording * Fix test snapshot * Create VisTypeIcon * Implement suggestions * Change experimental wording * Use regular quotes for i18n engine --- .../development-create-visualization.asciidoc | 4 +- .../input_control_vis/public/register_vis.js | 2 - .../kbn_vislib_vis_types/public/area.js | 2 - .../kbn_vislib_vis_types/public/gauge.js | 2 - .../kbn_vislib_vis_types/public/goal.js | 2 - .../kbn_vislib_vis_types/public/heatmap.js | 2 - .../kbn_vislib_vis_types/public/histogram.js | 2 - .../public/horizontal_bar.js | 2 - .../kbn_vislib_vis_types/public/line.js | 2 - .../kbn_vislib_vis_types/public/pie.js | 2 - .../kibana/public/dashboard/dashboard_app.js | 7 +- .../kibana/public/visualize/index.js | 15 + .../visualize/listing/visualize_listing.html | 7 + .../visualize/listing/visualize_listing.js | 28 +- .../listing/visualize_listing_table.js | 7 +- ...ze_constants.js => visualize_constants.ts} | 0 .../__snapshots__/new_vis_modal.test.tsx.snap | 768 ++++++++++++++++++ .../public/visualize/wizard/_dialog.scss | 99 +++ .../public/visualize/wizard/_index.scss | 1 + .../public/visualize/wizard/_wizard.scss | 16 - .../kibana/public/visualize/wizard/index.ts | 21 + .../visualize/wizard/new_vis_modal.test.tsx | 128 +++ .../public/visualize/wizard/new_vis_modal.tsx | 79 ++ .../public/visualize/wizard/show_new_vis.tsx | 53 ++ .../public/visualize/wizard/step_1.html | 102 --- .../visualize/wizard/type_selection/index.ts | 20 + .../wizard/type_selection/new_vis_help.tsx | 34 + .../wizard/type_selection/type_selection.tsx | 203 +++++ .../wizard/type_selection/vis_help_text.tsx | 55 ++ .../wizard/type_selection/vis_type_icon.tsx | 54 ++ .../kibana/public/visualize/wizard/wizard.js | 159 ---- .../markdown_vis/public/markdown_vis.js | 2 - .../metric_vis/public/metric_vis.js | 2 - .../metrics/public/kbn_vis_types/index.js | 2 - .../region_map/public/region_map_vis.js | 2 - .../table_vis/public/table_vis.js | 2 - .../tagcloud/public/tag_cloud_vis.js | 2 - .../tile_map/public/tile_map_vis.js | 2 - src/core_plugins/timelion/public/vis/index.js | 2 - src/core_plugins/vega/public/vega_type.js | 2 - src/ui/public/vis/vis_category.js | 48 -- src/ui/public/vis/vis_types/base_vis_type.js | 5 +- src/ui/public/vis/vis_types/vis_type.d.ts | 8 + .../functional/apps/visualize/_chart_types.js | 20 +- test/functional/apps/visualize/_tag_cloud.js | 4 +- .../page_objects/visual_builder_page.js | 3 +- .../functional/page_objects/visualize_page.js | 74 +- 47 files changed, 1619 insertions(+), 439 deletions(-) rename src/core_plugins/kibana/public/visualize/{visualize_constants.js => visualize_constants.ts} (100%) create mode 100644 src/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap create mode 100644 src/core_plugins/kibana/public/visualize/wizard/_dialog.scss create mode 100644 src/core_plugins/kibana/public/visualize/wizard/index.ts create mode 100644 src/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx create mode 100644 src/core_plugins/kibana/public/visualize/wizard/new_vis_modal.tsx create mode 100644 src/core_plugins/kibana/public/visualize/wizard/show_new_vis.tsx delete mode 100644 src/core_plugins/kibana/public/visualize/wizard/step_1.html create mode 100644 src/core_plugins/kibana/public/visualize/wizard/type_selection/index.ts create mode 100644 src/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.tsx create mode 100644 src/core_plugins/kibana/public/visualize/wizard/type_selection/type_selection.tsx create mode 100644 src/core_plugins/kibana/public/visualize/wizard/type_selection/vis_help_text.tsx create mode 100644 src/core_plugins/kibana/public/visualize/wizard/type_selection/vis_type_icon.tsx delete mode 100644 src/ui/public/vis/vis_category.js diff --git a/docs/development/visualize/development-create-visualization.asciidoc b/docs/development/visualize/development-create-visualization.asciidoc index a00626493c7631..ec07e90eb3a737 100644 --- a/docs/development/visualize/development-create-visualization.asciidoc +++ b/docs/development/visualize/development-create-visualization.asciidoc @@ -31,7 +31,6 @@ You should also register the visualization with `VisTypesRegistryProvider`. ["source","js"] ----------- -import { CATEGORY } from 'ui/vis/vis_category'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; @@ -43,7 +42,6 @@ const MyNewVisType = (Private) => { title: 'My New Vis', icon: 'my_icon', description: 'Cool new chart', - category: CATEGORY.OTHER ... }); } @@ -59,7 +57,7 @@ The list of common parameters: - *image*: instead of an icon you can provide a SVG image (imported) - *legacyIcon*: (DEPRECATED) provide a class name (e.g. for a font awesome icon) - *description*: description of your visualization as shown in kibana -- *category*: the category your visualization falls into (one of `ui/vis/vis_category` values) +- *hidden*: if set to true, will hide the type from showing up in the visualization wizard - *visConfig*: object holding visualization parameters - *visConfig.defaults*: object holding default visualization configuration - *visualization*: A constructor function for a Visualization. diff --git a/src/core_plugins/input_control_vis/public/register_vis.js b/src/core_plugins/input_control_vis/public/register_vis.js index c975c4bb1afa43..50a8c4312171d2 100644 --- a/src/core_plugins/input_control_vis/public/register_vis.js +++ b/src/core_plugins/input_control_vis/public/register_vis.js @@ -17,7 +17,6 @@ * under the License. */ -import { CATEGORY } from 'ui/vis/vis_category'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import { VisController } from './vis_controller'; @@ -40,7 +39,6 @@ function InputControlVisProvider(Private) { description: i18n.translate('inputControl.register.controlsDescription', { defaultMessage: 'Create interactive controls for easy dashboard manipulation.' }), - category: CATEGORY.OTHER, stage: 'experimental', requiresUpdateStatus: [Status.PARAMS, Status.TIME], feedbackMessage: defaultFeedbackMessage, diff --git a/src/core_plugins/kbn_vislib_vis_types/public/area.js b/src/core_plugins/kbn_vislib_vis_types/public/area.js index 22910838e42586..475b1cb5c1c9a1 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/area.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/area.js @@ -19,7 +19,6 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { CATEGORY } from 'ui/vis/vis_category'; import pointSeriesTemplate from './editors/point_series.html'; export default function PointSeriesVisType(Private, i18n) { @@ -30,7 +29,6 @@ export default function PointSeriesVisType(Private, i18n) { title: i18n('kbnVislibVisTypes.area.areaTitle', { defaultMessage: 'Area' }), icon: 'visArea', description: i18n('kbnVislibVisTypes.area.areaDescription', { defaultMessage: 'Emphasize the quantity beneath a line chart' }), - category: CATEGORY.BASIC, visConfig: { defaults: { type: 'area', diff --git a/src/core_plugins/kbn_vislib_vis_types/public/gauge.js b/src/core_plugins/kbn_vislib_vis_types/public/gauge.js index 708e2afff7265d..ea0e9817b70b41 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/gauge.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/gauge.js @@ -19,7 +19,6 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { CATEGORY } from 'ui/vis/vis_category'; import gaugeTemplate from './editors/gauge.html'; import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; @@ -33,7 +32,6 @@ export default function GaugeVisType(Private, i18n) { description: i18n('kbnVislibVisTypes.gauge.gaugeDescription', { defaultMessage: 'Gauges indicate the status of a metric. Use it to show how a metric\'s value relates to reference threshold values.' }), - category: CATEGORY.DATA, visConfig: { defaults: { type: 'gauge', diff --git a/src/core_plugins/kbn_vislib_vis_types/public/goal.js b/src/core_plugins/kbn_vislib_vis_types/public/goal.js index 0826df5c2de983..78e212ff10b709 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/goal.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/goal.js @@ -19,7 +19,6 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { CATEGORY } from 'ui/vis/vis_category'; import gaugeTemplate from './editors/gauge.html'; import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; @@ -33,7 +32,6 @@ export default function GoalVisType(Private, i18n) { description: i18n('kbnVislibVisTypes.goal.goalDescription', { defaultMessage: 'A goal chart indicates how close you are to your final goal.' }), - category: CATEGORY.DATA, visConfig: { defaults: { addTooltip: true, diff --git a/src/core_plugins/kbn_vislib_vis_types/public/heatmap.js b/src/core_plugins/kbn_vislib_vis_types/public/heatmap.js index f8a5a64a26300b..a62e6df9300727 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/heatmap.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/heatmap.js @@ -19,7 +19,6 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { CATEGORY } from 'ui/vis/vis_category'; import heatmapTemplate from './editors/heatmap.html'; import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; @@ -31,7 +30,6 @@ export default function HeatmapVisType(Private, i18n) { title: i18n('kbnVislibVisTypes.heatmap.heatmapTitle', { defaultMessage: 'Heat Map' }), icon: 'visHeatmap', description: i18n('kbnVislibVisTypes.heatmap.heatmapDescription', { defaultMessage: 'Shade cells within a matrix' }), - category: CATEGORY.BASIC, visConfig: { defaults: { type: 'heatmap', diff --git a/src/core_plugins/kbn_vislib_vis_types/public/histogram.js b/src/core_plugins/kbn_vislib_vis_types/public/histogram.js index 5aa5ad8246bea8..4f3e32ea135ee4 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/histogram.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/histogram.js @@ -19,7 +19,6 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { CATEGORY } from 'ui/vis/vis_category'; import pointSeriesTemplate from './editors/point_series.html'; export default function PointSeriesVisType(Private, i18n) { @@ -32,7 +31,6 @@ export default function PointSeriesVisType(Private, i18n) { description: i18n('kbnVislibVisTypes.histogram.histogramDescription', { defaultMessage: 'Assign a continuous variable to each axis' } ), - category: CATEGORY.BASIC, visConfig: { defaults: { type: 'histogram', diff --git a/src/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js b/src/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js index 3bb14019b217cb..94ee6056ff3394 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js @@ -19,7 +19,6 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { CATEGORY } from 'ui/vis/vis_category'; import pointSeriesTemplate from './editors/point_series.html'; export default function PointSeriesVisType(Private, i18n) { @@ -32,7 +31,6 @@ export default function PointSeriesVisType(Private, i18n) { description: i18n('kbnVislibVisTypes.horizontalBar.horizontalBarDescription', { defaultMessage: 'Assign a continuous variable to each axis' } ), - category: CATEGORY.BASIC, visConfig: { defaults: { type: 'histogram', diff --git a/src/core_plugins/kbn_vislib_vis_types/public/line.js b/src/core_plugins/kbn_vislib_vis_types/public/line.js index 886439eb572445..21855c6bfe3b5e 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/line.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/line.js @@ -19,7 +19,6 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { CATEGORY } from 'ui/vis/vis_category'; import pointSeriesTemplate from './editors/point_series.html'; export default function PointSeriesVisType(Private, i18n) { @@ -30,7 +29,6 @@ export default function PointSeriesVisType(Private, i18n) { title: i18n('kbnVislibVisTypes.line.lineTitle', { defaultMessage: 'Line' }), icon: 'visLine', description: i18n('kbnVislibVisTypes.line.lineDescription', { defaultMessage: 'Emphasize trends' }), - category: CATEGORY.BASIC, visConfig: { defaults: { type: 'line', diff --git a/src/core_plugins/kbn_vislib_vis_types/public/pie.js b/src/core_plugins/kbn_vislib_vis_types/public/pie.js index 366cce5e2c939e..70bfc2f2613c49 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/pie.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/pie.js @@ -19,7 +19,6 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { CATEGORY } from 'ui/vis/vis_category'; import pieTemplate from './editors/pie.html'; export default function HistogramVisType(Private, i18n) { @@ -30,7 +29,6 @@ export default function HistogramVisType(Private, i18n) { title: i18n('kbnVislibVisTypes.pie.pieTitle', { defaultMessage: 'Pie' }), icon: 'visPie', description: i18n('kbnVislibVisTypes.pie.pieDescription', { defaultMessage: 'Compare parts of a whole' }), - category: CATEGORY.BASIC, visConfig: { defaults: { type: 'pie', diff --git a/src/core_plugins/kibana/public/dashboard/dashboard_app.js b/src/core_plugins/kibana/public/dashboard/dashboard_app.js index 919191885a4330..0b0ea54da372f4 100644 --- a/src/core_plugins/kibana/public/dashboard/dashboard_app.js +++ b/src/core_plugins/kibana/public/dashboard/dashboard_app.js @@ -37,7 +37,6 @@ import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter'; import { DocTitleProvider } from 'ui/doc_title'; import { getTopNavConfig } from './top_nav/get_top_nav_config'; import { DashboardConstants, createDashboardEditUrl } from './dashboard_constants'; -import { VisualizeConstants } from '../visualize/visualize_constants'; import { DashboardStateManager } from './dashboard_state_manager'; import { saveDashboard } from './lib'; import { showCloneModal } from './top_nav/show_clone_modal'; @@ -45,6 +44,7 @@ import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; import { DashboardSaveModal } from './top_nav/save_modal'; import { showAddPanel } from './top_nav/show_add_panel'; import { showOptionsPopover } from './top_nav/show_options_popover'; +import { showNewVisModal } from '../visualize/wizard'; import { showShareContextMenu, ShareContextMenuExtensionsRegistryProvider } from 'ui/share'; import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; import * as filterActions from 'ui/doc_table/actions/filter'; @@ -417,10 +417,7 @@ app.directive('dashboardApp', function ($injector) { }; navActions[TopNavIds.ADD] = () => { const addNewVis = () => { - kbnUrl.change( - `${VisualizeConstants.WIZARD_STEP_1_PAGE_PATH}?${DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM}`); - // Function is called outside of angular. Must apply digest cycle to trigger URL update - $scope.$apply(); + showNewVisModal(visTypes, { editorParams: [DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM] }); }; showAddPanel(dashboardStateManager.addNewPanel, addNewVis, visTypes); diff --git a/src/core_plugins/kibana/public/visualize/index.js b/src/core_plugins/kibana/public/visualize/index.js index a4b220c19760d5..274e1bbf338371 100644 --- a/src/core_plugins/kibana/public/visualize/index.js +++ b/src/core_plugins/kibana/public/visualize/index.js @@ -40,6 +40,21 @@ uiRoutes template: visualizeListingTemplate, controller: VisualizeListingController, controllerAs: 'listingController', + resolve: { + createNewVis: () => false, + }, + }) + .when(VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, { + template: visualizeListingTemplate, + controller: VisualizeListingController, + controllerAs: 'listingController', + resolve: { + createNewVis: () => true, + }, + }) + // Old path, will be removed in 7.0 + .when('/visualize/step/1', { + redirectTo: VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, }); FeatureCatalogueRegistryProvider.register(() => { diff --git a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.html b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.html index 60d4622862e029..a81f6036b401c7 100644 --- a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.html +++ b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.html @@ -33,6 +33,13 @@ + + diff --git a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js index 519ed05cc499bd..b8202cbe176625 100644 --- a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js +++ b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js @@ -22,24 +22,44 @@ import 'ui/pager_control'; import 'ui/pager'; import { uiModules } from 'ui/modules'; import { timefilter } from 'ui/timefilter'; +import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; import { VisualizeListingTable } from './visualize_listing_table'; +import { NewVisModal } from '../wizard/new_vis_modal'; + +import { injectI18nProvider } from '@kbn/i18n/react'; const app = uiModules.get('app/visualize', ['ngRoute', 'react']); -app.directive('visualizeListingTable', function (reactDirective) { - return reactDirective(VisualizeListingTable); -}); +app.directive('visualizeListingTable', reactDirective => reactDirective(VisualizeListingTable)); +app.directive('newVisModal', reactDirective => reactDirective(injectI18nProvider(NewVisModal))); -export function VisualizeListingController($injector) { +export function VisualizeListingController($injector, createNewVis) { const Notifier = $injector.get('Notifier'); const Private = $injector.get('Private'); const config = $injector.get('config'); + this.visTypeRegistry = Private(VisTypesRegistryProvider); + timefilter.disableAutoRefreshSelector(); timefilter.disableTimeRangeSelector(); + this.showNewVisModal = false; + + this.createNewVis = () => { + this.showNewVisModal = true; + }; + + this.closeNewVisModal = () => { + this.showNewVisModal = false; + }; + + if (createNewVis) { + // In case the user navigated to the page via the /visualize/new URL we start the dialog immediately + this.createNewVis(); + } + // TODO: Extract this into an external service. const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName; const visualizationService = services.visualizations; diff --git a/src/core_plugins/kibana/public/visualize/listing/visualize_listing_table.js b/src/core_plugins/kibana/public/visualize/listing/visualize_listing_table.js index e1765c58484f70..8eb79f99a2af24 100644 --- a/src/core_plugins/kibana/public/visualize/listing/visualize_listing_table.js +++ b/src/core_plugins/kibana/public/visualize/listing/visualize_listing_table.js @@ -260,14 +260,14 @@ export class VisualizeListingTable extends Component { this.setState({ selectedRowIds: newSelectedIds }); }; - onCreate() { - window.location = '#/visualize/new'; + onCreate = () => { + this.props.onCreateVis(); } renderToolBarActions() { return this.state.selectedRowIds.length > 0 ? : - ; + ; } renderPager() { @@ -323,4 +323,5 @@ export class VisualizeListingTable extends Component { VisualizeListingTable.propTypes = { deleteSelectedItems: PropTypes.func, fetchItems: PropTypes.func, + onCreateVis: PropTypes.func.isRequired, }; diff --git a/src/core_plugins/kibana/public/visualize/visualize_constants.js b/src/core_plugins/kibana/public/visualize/visualize_constants.ts similarity index 100% rename from src/core_plugins/kibana/public/visualize/visualize_constants.js rename to src/core_plugins/kibana/public/visualize/visualize_constants.ts diff --git a/src/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap b/src/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap new file mode 100644 index 00000000000000..ab45dcb8507040 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap @@ -0,0 +1,768 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NewVisModal should render as expected 1`] = ` + + + + + + +
+
+ + + +
+ + +
+ +
+ + New Visualization + +
+
+
+
+
+ +
+ +
+ +
+ +
+ + +
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+ +
+ +
+ + Vis Type 1 + + } + onBlur={[Function]} + onClick={[Function]} + onFocus={[Function]} + onMouseEnter={[Function]} + onMouseLeave={[Function]} + > + + + + Vis with search + + } + onBlur={[Function]} + onClick={[Function]} + onFocus={[Function]} + onMouseEnter={[Function]} + onMouseLeave={[Function]} + > + + +
+
+
+
+
+
+
+
+ +
+ +

+ + Select a visualization type + +

+
+ +
+ + + +
+

+ + Start creating your visualization by selecting a type for that visualization. + +

+
+
+
+
+ +
+ +
+ +
+
+
+ + + + + + +`; diff --git a/src/core_plugins/kibana/public/visualize/wizard/_dialog.scss b/src/core_plugins/kibana/public/visualize/wizard/_dialog.scss new file mode 100644 index 00000000000000..6a95123c2f7cc3 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/_dialog.scss @@ -0,0 +1,99 @@ +.visNewVisDialog { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='166' height='157' viewBox='0 0 166 157'%3E%3Cdefs%3E%3ClinearGradient id='untitled-2-a' x1='0%25' y1='0%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%23FFF' stop-opacity='.2'/%3E%3Cstop offset='100%25' stop-opacity='0'/%3E%3C/linearGradient%3E%3CradialGradient id='untitled-2-b' cx='0%25' cy='0%25' r='127.787%25' fx='0%25' fy='0%25' gradientTransform='matrix(.681 .68098 -.63326 .7323 0 0)'%3E%3Cstop offset='0%25' stop-color='%23BBB' stop-opacity='.1'/%3E%3Cstop offset='100%25' stop-opacity='.5'/%3E%3C/radialGradient%3E%3ClinearGradient id='untitled-2-c' x1='0%25' y1='0%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%23FFF' stop-opacity='.4'/%3E%3Cstop offset='100%25' stop-opacity='0'/%3E%3C/linearGradient%3E%3CradialGradient id='untitled-2-d' cx='0%25' cy='0%25' r='148.851%25' fx='0%25' fy='0%25' gradientTransform='matrix(.6718 .67182 -.74072 .60932 0 0)'%3E%3Cstop offset='0%25' stop-color='%23FFF' stop-opacity='.101'/%3E%3Cstop offset='100%25' stop-opacity='.15'/%3E%3C/radialGradient%3E%3CradialGradient id='untitled-2-e' cx='0%25' cy='0%25' r='127.349%25' fx='0%25' fy='0%25' gradientTransform='matrix(.68331 .68332 -.73013 .63951 0 0)'%3E%3Cstop offset='0%25' stop-color='%23BBB' stop-opacity='.1'/%3E%3Cstop offset='100%25' stop-opacity='.5'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cg opacity='.5' fill='none' fill-rule='evenodd' transform='matrix(-1 0 0 1 166 0)'%3E%3Cg opacity='.65' transform='matrix(0 -1 -1 0 146 157)'%3E%3Cpolygon fill='%23DD0A73' points='0 0 157 146 0 146' opacity='.418'/%3E%3Cpolygon fill='url(%23untitled-2-a)' points='0 0 157 146 0 146' style='mix-blend-mode:overlay'/%3E%3Cpolygon fill='url(%23untitled-2-b)' points='0 0 157 146 0 146' opacity='.618' style='mix-blend-mode:overlay'/%3E%3C/g%3E%3Cg opacity='.65' transform='translate(88 71)'%3E%3Cpath fill='%23017F75' d='M0,86 L78,86 C74.2038079,48.730962 43.6293886,16.7871605 0,0 L0,86 Z' opacity='.409'/%3E%3Cpath fill='url(%23untitled-2-c)' d='M0,86 L78,86 C74.2038079,48.730962 43.6293886,16.7871605 0,0 L0,86 Z' style='mix-blend-mode:overlay'/%3E%3Cpath fill='url(%23untitled-2-d)' d='M0,86 L78,86 C74.2038079,48.730962 43.6293886,16.7871605 0,0 L0,86 Z' opacity='.663' style='mix-blend-mode:overlay'/%3E%3C/g%3E%3Cg opacity='.15' transform='translate(73 79)'%3E%3Cpolygon fill='%23353535' points='0 0 73 78 0 78' opacity='.38'/%3E%3Cpolygon fill='url(%23untitled-2-a)' points='0 0 73 78 0 78' style='mix-blend-mode:overlay'/%3E%3Cpolygon fill='url(%23untitled-2-e)' points='0 0 73 78 0 78' style='mix-blend-mode:overlay'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A"); + background-repeat: no-repeat; + background-position: calc(100% + 1px) calc(100% + 1px); + background-size: 37%; +} + +.visNewVisDialog__body { + display: flex; + padding: $euiSizeM $euiSizeL 0; +} + +.visNewVisDialog__searchWrapper { + flex-shrink: 0; +} + +.visNewVisDialog__typesWrapper { + max-width: $euiSizeXXL * 10; + padding-top: 2px; // Account for search field dropshadow + padding-bottom: $euiSize; + // Add overflow shadows via pseudo elements + position: relative; + &::before, + &::after { + content: ""; + display: block; + position: absolute; + height: $euiSizeXXL; + left: 0; + right: 0; + pointer-events: none; + } + + &::before { + top: -$euiSizeXXL + 2px; // Account for search field dropshadow + @include euiOverflowShadowBottom; + } + + &::after { + bottom: -$euiSizeL; + @include euiOverflowShadowTop; + } +} + +.visNewVisDialog__types { + @include euiScrollBar; + // EUITODO: allow for more (calculated) widths of `EuiKeyPadMenu` + width: auto; + overflow-y: auto; + padding-top: $euiSize; + justify-content: center; + padding-bottom: $euiSize; +} + +.visNewVisDialog__description { + width: $euiSizeXL * 10; +} + +.visNewVisDialog__type:disabled { + opacity: 0.2; + pointer-events: none; +} + +.visNewVisDialog__typeLegacyIcon { + font-size: $euiSizeL; + color: $euiColorSecondary; +} + +.visNewVisDialog__typeImage { + @include size($euiSizeL); +} + +@include euiBreakpoint('xs', 's') { + .visNewVisDialog { + background-image: none; + } + + .visNewVisDialog__typesWrapper { + max-width: none; + } + + .visNewVisDialog__types { + justify-content: flex-start; + } + + .visNewVisDialog__description { + display: none; + } +} + +@include internetExplorerOnly { + .visNewVisDialog { + width: 820px; + } + + .visNewVisDialog__body { + flex-basis: 800px; + } +} diff --git a/src/core_plugins/kibana/public/visualize/wizard/_index.scss b/src/core_plugins/kibana/public/visualize/wizard/_index.scss index 957f378640ea6b..b12194ed5c365f 100644 --- a/src/core_plugins/kibana/public/visualize/wizard/_index.scss +++ b/src/core_plugins/kibana/public/visualize/wizard/_index.scss @@ -1 +1,2 @@ @import './wizard'; +@import './dialog'; diff --git a/src/core_plugins/kibana/public/visualize/wizard/_wizard.scss b/src/core_plugins/kibana/public/visualize/wizard/_wizard.scss index 823a588d61aa12..0e811b500a8699 100644 --- a/src/core_plugins/kibana/public/visualize/wizard/_wizard.scss +++ b/src/core_plugins/kibana/public/visualize/wizard/_wizard.scss @@ -30,19 +30,3 @@ .visWizard__savedObjectFinder { padding: $euiSizeS; /* 1 */ } - -/** - * This preserves backwards-compatibility with any plugins that are still specifying an `icon` - * class. - * - * 1. Size icon correctly to match xxLarge EuiIcon - */ - .visWizard__visTypeIcon { - color: $euiColorFullShade; - font-size: $euiSizeXXL; /* 1 */ -} - -// SASSTODO: Remove img element selector when `.kuiGalleryItem__image img` selector is removed -img.visWizard__visTypeImage { // Use element selector to override base .kuiGalleryItem__image styles - @include size($euiSizeXXL); /* 1 */ -} diff --git a/src/core_plugins/kibana/public/visualize/wizard/index.ts b/src/core_plugins/kibana/public/visualize/wizard/index.ts new file mode 100644 index 00000000000000..7a3fc63af52594 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { NewVisModal } from './new_vis_modal'; +export { showNewVisModal } from './show_new_vis'; diff --git a/src/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx b/src/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx new file mode 100644 index 00000000000000..e4b3b7d3b1adb9 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx @@ -0,0 +1,128 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { I18nProvider } from '@kbn/i18n/react'; +import React from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; + +const settingsGet = jest.fn(); + +jest.mock('ui/chrome', () => ({ + getUiSettingsClient: () => ({ + get: settingsGet, + }), +})); + +import { NewVisModal } from './new_vis_modal'; + +import { VisType } from 'ui/vis'; + +describe('NewVisModal', () => { + const defaultVisTypeParams = { + hidden: false, + visualization: class Controller { + public render = jest.fn(); + public destroy = jest.fn(); + }, + requiresSearch: false, + requestHandler: 'none', + responseHandler: 'none', + }; + const visTypes: VisType[] = [ + { name: 'vis', title: 'Vis Type 1', stage: 'production', ...defaultVisTypeParams }, + { name: 'visExp', title: 'Experimental Vis', stage: 'experimental', ...defaultVisTypeParams }, + { + name: 'visWithSearch', + title: 'Vis with search', + stage: 'production', + ...defaultVisTypeParams, + }, + ]; + + it('should render as expected', () => { + const wrapper = mountWithIntl( + + null} visTypesRegistry={visTypes} /> + + ); + expect(wrapper).toMatchSnapshot(); + }); + + it('should show a button for regular visualizations', () => { + const wrapper = mountWithIntl( + + null} visTypesRegistry={visTypes} /> + + ); + expect(wrapper.find('[data-test-subj="visType-vis"]').exists()).toBe(true); + }); + + describe('open editor', () => { + it('should open the editor for visualizations without search', () => { + window.location.assign = jest.fn(); + const wrapper = mountWithIntl( + + null} visTypesRegistry={visTypes} /> + + ); + const visButton = wrapper.find('button[data-test-subj="visType-vis"]'); + visButton.simulate('click'); + expect(window.location.assign).toBeCalledWith('#/visualize/create?type=vis'); + }); + + it('passes through editor params to the editor URL', () => { + window.location.assign = jest.fn(); + const wrapper = mountWithIntl( + + null} + visTypesRegistry={visTypes} + editorParams={['foo=true', 'bar=42']} + /> + + ); + const visButton = wrapper.find('button[data-test-subj="visType-vis"]'); + visButton.simulate('click'); + expect(window.location.assign).toBeCalledWith('#/visualize/create?type=vis&foo=true&bar=42'); + }); + }); + + describe('experimental visualizations', () => { + it('should not show experimental visualizations if visualize:enableLabs is false', () => { + settingsGet.mockReturnValue(false); + const wrapper = mountWithIntl( + + null} visTypesRegistry={visTypes} /> + + ); + expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(false); + }); + + it('should show experimental visualizations if visualize:enableLabs is true', () => { + settingsGet.mockReturnValue(true); + const wrapper = mountWithIntl( + + null} visTypesRegistry={visTypes} /> + + ); + expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(true); + }); + }); +}); diff --git a/src/core_plugins/kibana/public/visualize/wizard/new_vis_modal.tsx b/src/core_plugins/kibana/public/visualize/wizard/new_vis_modal.tsx new file mode 100644 index 00000000000000..bc206a70fa3619 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/new_vis_modal.tsx @@ -0,0 +1,79 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; + +import { EuiModal, EuiOverlayMask } from '@elastic/eui'; + +import { VisualizeConstants } from '../visualize_constants'; + +import { TypeSelection } from './type_selection'; + +import chrome from 'ui/chrome'; +import { VisType } from 'ui/vis'; + +interface TypeSelectionProps { + isOpen: boolean; + onClose: () => void; + visTypesRegistry: VisType[]; + editorParams?: string[]; +} + +class NewVisModal extends React.Component { + public static defaultProps = { + editorParams: [], + }; + + private readonly isLabsEnabled: boolean; + + constructor(props: TypeSelectionProps) { + super(props); + this.isLabsEnabled = chrome.getUiSettingsClient().get('visualize:enableLabs'); + } + + public render() { + if (!this.props.isOpen) { + return null; + } + + return ( + + + + + + ); + } + + private onVisTypeSelected = (visType: VisType) => { + const baseUrl = + visType.requiresSearch && visType.options.showIndexSelection + ? `#${VisualizeConstants.WIZARD_STEP_2_PAGE_PATH}?` + : `#${VisualizeConstants.CREATE_PATH}?`; + const params = [`type=${encodeURIComponent(visType.name)}`, ...this.props.editorParams!]; + this.props.onClose(); + location.assign(`${baseUrl}${params.join('&')}`); + }; +} + +export { NewVisModal }; diff --git a/src/core_plugins/kibana/public/visualize/wizard/show_new_vis.tsx b/src/core_plugins/kibana/public/visualize/wizard/show_new_vis.tsx new file mode 100644 index 00000000000000..fe4d28900c11e9 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/show_new_vis.tsx @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { I18nProvider } from '@kbn/i18n/react'; +import { VisType } from 'ui/vis'; +import { NewVisModal } from './new_vis_modal'; + +interface ShowNewVisModalParams { + editorParams?: string[]; +} + +export function showNewVisModal( + visTypeRegistry: VisType[], + { editorParams = [] }: ShowNewVisModalParams = {} +) { + const container = document.createElement('div'); + const onClose = () => { + ReactDOM.unmountComponentAtNode(container); + document.body.removeChild(container); + }; + + document.body.appendChild(container); + const element = ( + + + + ); + ReactDOM.render(element, container); +} diff --git a/src/core_plugins/kibana/public/visualize/wizard/step_1.html b/src/core_plugins/kibana/public/visualize/wizard/step_1.html deleted file mode 100644 index ff13e9a7555591..00000000000000 --- a/src/core_plugins/kibana/public/visualize/wizard/step_1.html +++ /dev/null @@ -1,102 +0,0 @@ - - - -
- - -
-
- -
-
- -
-

- Select visualization type -

-
- - - - -
-
diff --git a/src/core_plugins/kibana/public/visualize/wizard/type_selection/index.ts b/src/core_plugins/kibana/public/visualize/wizard/type_selection/index.ts new file mode 100644 index 00000000000000..c4093b4dec3e83 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/type_selection/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { TypeSelection } from './type_selection'; diff --git a/src/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.tsx b/src/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.tsx new file mode 100644 index 00000000000000..5a68be86236862 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.tsx @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +import { EuiText } from '@elastic/eui'; + +export const NewVisHelp = () => ( + +

+ +

+
+); diff --git a/src/core_plugins/kibana/public/visualize/wizard/type_selection/type_selection.tsx b/src/core_plugins/kibana/public/visualize/wizard/type_selection/type_selection.tsx new file mode 100644 index 00000000000000..55554ed7845121 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/type_selection/type_selection.tsx @@ -0,0 +1,203 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { sortByOrder } from 'lodash'; +import React, { ChangeEvent } from 'react'; + +import { + EuiFieldSearch, + EuiFlexGroup, + EuiFlexItem, + EuiKeyPadMenu, + EuiKeyPadMenuItemButton, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { NewVisHelp } from './new_vis_help'; +import { VisHelpText } from './vis_help_text'; +import { VisTypeIcon } from './vis_type_icon'; + +import { memoizeLast } from 'ui/utils/memoize'; +import { VisType } from 'ui/vis'; + +interface VisTypeListEntry extends VisType { + highlighted: boolean; +} + +interface TypeSelectionProps { + onVisTypeSelected: (visType: VisType) => void; + visTypesRegistry: VisType[]; + showExperimental: boolean; +} + +interface TypeSelectionState { + highlightedType: VisType | null; + query: string; +} + +class TypeSelection extends React.Component { + public state = { + highlightedType: null, + query: '', + }; + + private readonly getFilteredVisTypes = memoizeLast(this.filteredVisTypes); + + public render() { + const { query, highlightedType } = this.state; + const visTypes = this.getFilteredVisTypes(this.props.visTypesRegistry, query); + return ( + + + + + + +
+ + + + + + + + + {visTypes.map(this.renderVisType)} + + + + + + {highlightedType ? ( + + ) : ( + + +

+ +

+
+ + +
+ )} +
+
+
+
+ ); + } + + private filteredVisTypes(visTypes: VisType[], query: string): VisTypeListEntry[] { + const types = visTypes.filter(type => { + // Filter out all lab visualizations if lab mode is not enabled + if (!this.props.showExperimental && type.stage === 'experimental') { + return false; + } + + // Filter out hidden visualizations + if (type.hidden) { + return false; + } + + return true; + }); + + let entries: VisTypeListEntry[]; + if (!query) { + entries = types.map(type => ({ ...type, highlighted: false })); + } else { + const q = query.toLowerCase(); + entries = types.map(type => { + const matchesQuery = + type.name.toLowerCase().includes(q) || + type.title.toLowerCase().includes(q) || + (typeof type.description === 'string' && type.description.toLowerCase().includes(q)); + return { ...type, highlighted: matchesQuery }; + }); + } + + return sortByOrder(entries, ['highlighted', 'title'], ['desc', 'asc']); + } + + private renderVisType = (visType: VisTypeListEntry) => { + let stage = {}; + if (visType.stage === 'experimental') { + stage = { + betaBadgeLabel: i18n.translate('kbn.visualize.newVisWizard.experimentalTitle', { + defaultMessage: 'Experimental', + }), + betaBadgeTooltipContent: i18n.translate('kbn.visualize.newVisWizard.experimentalTooltip', { + defaultMessage: 'This visualization is experimental.', + }), + }; + } + const isDisabled = this.state.query !== '' && !visType.highlighted; + return ( + {visType.title}} + onClick={() => this.props.onVisTypeSelected(visType)} + onFocus={() => this.highlightType(visType)} + onMouseEnter={() => this.highlightType(visType)} + onMouseLeave={() => this.highlightType(null)} + onBlur={() => this.highlightType(null)} + className="visNewVisDialog__type" + data-test-subj={`visType-${visType.name}`} + data-vis-stage={visType.stage} + disabled={isDisabled} + {...stage} + > + + + ); + }; + + private highlightType(visType: VisType | null) { + this.setState({ + highlightedType: visType, + }); + } + + private onQueryChange = (ev: ChangeEvent) => { + this.setState({ + query: ev.target.value, + }); + }; +} + +export { TypeSelection }; diff --git a/src/core_plugins/kibana/public/visualize/wizard/type_selection/vis_help_text.tsx b/src/core_plugins/kibana/public/visualize/wizard/type_selection/vis_help_text.tsx new file mode 100644 index 00000000000000..249931733dc22e --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/type_selection/vis_help_text.tsx @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +import { EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; + +import { VisType } from 'ui/vis'; + +interface VisHelpTextProps { + visType: VisType; +} + +export const VisHelpText = ({ visType }: VisHelpTextProps) => { + return ( + + +

{visType.title}

+
+ + {visType.stage === 'experimental' && ( + + + + + + + + + )} + {visType.description} +
+ ); +}; diff --git a/src/core_plugins/kibana/public/visualize/wizard/type_selection/vis_type_icon.tsx b/src/core_plugins/kibana/public/visualize/wizard/type_selection/vis_type_icon.tsx new file mode 100644 index 00000000000000..44c4f4835d5aa9 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/wizard/type_selection/vis_type_icon.tsx @@ -0,0 +1,54 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { EuiIcon } from '@elastic/eui'; +import classnames from 'classnames'; +import React from 'react'; +import { VisType } from 'ui/vis'; + +interface VisTypeIconProps { + visType: VisType; +} + +/** + * This renders the icon for a specific visualization type. + * This currently checks the following: + * - If visType.image is set, use that as the `src` of an image + * - If legacyIcon is set, use that as a classname for a span with kuiIcon (to be removed in 7.0) + * - Otherwise use the visType.icon as an EuiIcon or the 'empty' icon if that's not set + */ +export const VisTypeIcon = ({ visType }: VisTypeIconProps) => { + const legacyIconClass = classnames( + 'kuiIcon', + 'visNewVisDialog__typeLegacyIcon', + visType.legacyIcon + ); + return ( + + {visType.image && ( + + )} + {!visType.image && visType.legacyIcon && } + {!visType.image && + !visType.legacyIcon && ( + + ); +}; diff --git a/src/core_plugins/kibana/public/visualize/wizard/wizard.js b/src/core_plugins/kibana/public/visualize/wizard/wizard.js index c5ba3753d0c934..899c64fb8047a6 100644 --- a/src/core_plugins/kibana/public/visualize/wizard/wizard.js +++ b/src/core_plugins/kibana/public/visualize/wizard/wizard.js @@ -22,175 +22,16 @@ import 'ui/directives/saved_object_finder'; import 'ui/directives/paginated_selectable_list'; import '../../discover/saved_searches/saved_searches'; -import _ from 'lodash'; -import { CATEGORY, CATEGORY_DISPLAY_NAMES } from 'ui/vis/vis_category'; import { DashboardConstants } from '../../dashboard/dashboard_constants'; import { VisualizeConstants } from '../visualize_constants'; import routes from 'ui/routes'; -import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import { uiModules } from 'ui/modules'; -import visualizeWizardStep1Template from './step_1.html'; import visualizeWizardStep2Template from './step_2.html'; import { SavedObjectsClientProvider } from 'ui/saved_objects'; import { timefilter } from 'ui/timefilter'; const module = uiModules.get('app/visualize', ['kibana/courier']); -/******** -/** Wizard Step 1 -/********/ - -// Redirect old route to new route. -routes.when('/visualize/step/1', { - redirectTo: VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, -}); - -routes.when(VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, { - template: visualizeWizardStep1Template, - controller: 'VisualizeWizardStep1', -}); - -module.controller('VisualizeWizardStep1', function ($scope, $route, kbnUrl, Private, config) { - timefilter.disableAutoRefreshSelector(); - timefilter.disableTimeRangeSelector(); - - const addToDashMode = $route.current.params[DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM]; - kbnUrl.removeParam(DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM); - - const visTypes = Private(VisTypesRegistryProvider); - const isLabsEnabled = config.get('visualize:enableLabs'); - $scope.toggleLabView = () => { - $route.current.params.lab = !$route.current.params.lab; - $route.updateParams($route.current.params); - $route.reload(); - }; - - const categoryToVisTypesMap = {}; - - visTypes.forEach(visType => { - - let categoryName = visType.category; - - if (categoryName === CATEGORY.HIDDEN) { - return; - } - - if (!isLabsEnabled && visType.stage === 'experimental') { - return; - } - - // If the specified category doesn't have a value in our display names - // mapping (most likely because the vis specified a random category, not using - // CATEGORY values), just move it to the OTHER category. - if (!CATEGORY_DISPLAY_NAMES[categoryName]) { - categoryName = CATEGORY.OTHER; - } - - // Create category object if it doesn't exist yet. - if (!categoryToVisTypesMap[categoryName]) { - categoryToVisTypesMap[categoryName] = { - label: CATEGORY_DISPLAY_NAMES[categoryName], - list: [], - }; - } - - const categoryVisTypes = categoryToVisTypesMap[categoryName]; - - // Add the visType to the list and sort them by their title. - categoryVisTypes.list = _.sortBy( - categoryVisTypes.list.concat(visType), - type => type.title - ); - }); - - // Sort the categories alphabetically. - const sortedVisTypeCategories = Object.values(categoryToVisTypesMap).sort((a, b) => { - const other = CATEGORY.OTHER.toLowerCase(); - - // Put "other" category at the end of the list. - const labelA = a.label.toLowerCase(); - if (labelA === other) return 1; - - const labelB = b.label.toLowerCase(); - if (labelB === other) return -1; - - if (labelA < labelB) return -1; - if (labelA > labelB) return 1; - return 0; - }); - - $scope.searchTerm = ''; - - $scope.filteredVisTypeCategories = []; - - $scope.$watch('searchTerm', () => { - function getVisTypeCategories() { - const normalizedSearchTerm = $scope.searchTerm.toLowerCase().trim(); - - const filteredVisTypeCategories = sortedVisTypeCategories.map(category => { - // Include entire category if the category matches the search term. - if (category.label.toLowerCase().includes(normalizedSearchTerm)) { - return category; - } - - // Otherwise, return just the vis types in the category which match. - const filteredVisTypes = category.list.filter(visType => { - return visType.title.toLowerCase().includes(normalizedSearchTerm); - }); - - return { - label: category.label, - list: filteredVisTypes, - }; - }); - - return filteredVisTypeCategories.filter(category => category.list.length); - } - - $scope.filteredVisTypeCategories = getVisTypeCategories(); - }); - - $scope.getVisTypeId = type => { - return _.camelCase(type.name); - }; - - $scope.getVisTypeTooltip = type => { - //to not clutter the tooltip, just only notify if labs or experimental. - //labs is more important in this regard. - let prefix = ''; - if (type.stage === 'experimental') { - prefix = '(Experimental)'; - } - return `${prefix} ${type.description}`; - }; - - $scope.getVisTypeTooltipPosition = index => { - // Tooltips should appear on the bottom by default, unless they're on the last row. This is a - // cheap workaround to automatically positioning the tooltip so that it won't disappear off - // the edge of the screen. - if (index === $scope.filteredVisTypeCategories.length - 1) { - return 'top'; - } - - return 'bottom'; - }; - - $scope.getVisTypeUrl = function (visType) { - const baseUrl = - visType.requiresSearch && visType.options.showIndexSelection - ? `#${VisualizeConstants.WIZARD_STEP_2_PAGE_PATH}?` - : `#${VisualizeConstants.CREATE_PATH}?`; - - const params = [`type=${encodeURIComponent(visType.name)}`]; - - if (addToDashMode) { - params.push(DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM); - } - - return baseUrl + params.join('&'); - }; -}); - /******** /** Wizard Step 2 /********/ diff --git a/src/core_plugins/markdown_vis/public/markdown_vis.js b/src/core_plugins/markdown_vis/public/markdown_vis.js index 6c762aa1486369..80cb0b6633a246 100644 --- a/src/core_plugins/markdown_vis/public/markdown_vis.js +++ b/src/core_plugins/markdown_vis/public/markdown_vis.js @@ -19,7 +19,6 @@ import { MarkdownVisWrapper } from './markdown_vis_controller'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; -import { CATEGORY } from 'ui/vis/vis_category'; import markdownVisParamsTemplate from './markdown_vis_params.html'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import { DefaultEditorSize } from 'ui/vis/editor_size'; @@ -41,7 +40,6 @@ function MarkdownVisProvider(Private, i18n) { isAccessible: true, icon: 'visText', description: i18n('markdownVis.markdownDescription', { defaultMessage: 'Create a document using markdown syntax' }), - category: CATEGORY.OTHER, visConfig: { component: MarkdownVisWrapper, defaults: { diff --git a/src/core_plugins/metric_vis/public/metric_vis.js b/src/core_plugins/metric_vis/public/metric_vis.js index 119f28296f9041..f8d8bac235d187 100644 --- a/src/core_plugins/metric_vis/public/metric_vis.js +++ b/src/core_plugins/metric_vis/public/metric_vis.js @@ -19,7 +19,6 @@ import './metric_vis_params'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; -import { CATEGORY } from 'ui/vis/vis_category'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; @@ -41,7 +40,6 @@ function MetricVisProvider(Private, i18n) { title: i18n('metricVis.metricTitle', { defaultMessage: 'Metric' }), icon: 'visMetric', description: i18n('metricVis.metricDescription', { defaultMessage: 'Display a calculation as a single number' }), - category: CATEGORY.DATA, visConfig: { component: MetricVisComponent, defaults: { diff --git a/src/core_plugins/metrics/public/kbn_vis_types/index.js b/src/core_plugins/metrics/public/kbn_vis_types/index.js index ea82789cf61b75..9189a0bb93265f 100644 --- a/src/core_plugins/metrics/public/kbn_vis_types/index.js +++ b/src/core_plugins/metrics/public/kbn_vis_types/index.js @@ -20,7 +20,6 @@ import { MetricsRequestHandlerProvider } from './request_handler'; import { ReactEditorControllerProvider } from './editor_controller'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; -import { CATEGORY } from 'ui/vis/vis_category'; import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; // register the provider with the visTypes registry so that other know it exists @@ -36,7 +35,6 @@ export default function MetricsVisProvider(Private) { name: 'metrics', title: 'Visual Builder', description: 'Build time-series using a visual pipeline interface', - category: CATEGORY.TIME, icon: 'visVisualBuilder', feedbackMessage: defaultFeedbackMessage, visConfig: { diff --git a/src/core_plugins/region_map/public/region_map_vis.js b/src/core_plugins/region_map/public/region_map_vis.js index 24bf400f69df5f..97f0e3f295de64 100644 --- a/src/core_plugins/region_map/public/region_map_vis.js +++ b/src/core_plugins/region_map/public/region_map_vis.js @@ -19,7 +19,6 @@ import './region_map_vis_params'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; -import { CATEGORY } from 'ui/vis/vis_category'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps'; @@ -40,7 +39,6 @@ VisTypesRegistryProvider.register(function RegionMapProvider(Private, regionmaps title: i18n('regionMap.mapVis.regionMapTitle', { defaultMessage: 'Region Map' }), description: i18n('regionMap.mapVis.regionMapDescription', { defaultMessage: 'Show metrics on a thematic map. Use one of the \ provided base maps, or add your own. Darker colors represent higher values.' }), - category: CATEGORY.MAP, icon: 'visMapRegion', visConfig: { defaults: { diff --git a/src/core_plugins/table_vis/public/table_vis.js b/src/core_plugins/table_vis/public/table_vis.js index 0cd14d43253d31..aa6b96648bdfc4 100644 --- a/src/core_plugins/table_vis/public/table_vis.js +++ b/src/core_plugins/table_vis/public/table_vis.js @@ -23,7 +23,6 @@ import './table_vis_params'; import 'ui/agg_table'; import 'ui/agg_table/agg_table_group'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; -import { CATEGORY } from 'ui/vis/vis_category'; import { Schemas } from 'ui/vis/editors/default/schemas'; import tableVisTemplate from './table_vis.html'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; @@ -59,7 +58,6 @@ function TableVisTypeProvider(Private) { description: i18n.translate('tableVis.tableVisDescription', { defaultMessage: 'Display values in a table', }), - category: CATEGORY.DATA, visConfig: { defaults: { perPage: 10, diff --git a/src/core_plugins/tagcloud/public/tag_cloud_vis.js b/src/core_plugins/tagcloud/public/tag_cloud_vis.js index c65f8461137e54..18214965eab268 100644 --- a/src/core_plugins/tagcloud/public/tag_cloud_vis.js +++ b/src/core_plugins/tagcloud/public/tag_cloud_vis.js @@ -19,7 +19,6 @@ import './tag_cloud_vis_params'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; -import { CATEGORY } from 'ui/vis/vis_category'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { TagCloudVisualization } from './tag_cloud_visualization'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; @@ -36,7 +35,6 @@ VisTypesRegistryProvider.register(function (Private, i18n) { description: i18n('tagCloud.vis.tagCloudDescription', { defaultMessage: 'A group of words, sized according to their importance' }), - category: CATEGORY.OTHER, visConfig: { defaults: { scale: 'linear', diff --git a/src/core_plugins/tile_map/public/tile_map_vis.js b/src/core_plugins/tile_map/public/tile_map_vis.js index 1a3349c879ab2c..f76d823b155a53 100644 --- a/src/core_plugins/tile_map/public/tile_map_vis.js +++ b/src/core_plugins/tile_map/public/tile_map_vis.js @@ -21,7 +21,6 @@ import { i18n } from '@kbn/i18n'; import 'plugins/kbn_vislib_vis_types/controls/vislib_basic_options'; import './editors/tile_map_vis_params'; import { supports } from 'ui/utils/supports'; -import { CATEGORY } from 'ui/vis/vis_category'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { CoordinateMapsVisualizationProvider } from './coordinate_maps_visualization'; import { Schemas } from 'ui/vis/editors/default/schemas'; @@ -44,7 +43,6 @@ VisTypesRegistryProvider.register(function TileMapVisType(Private, getAppState, description: i18n.translate('tileMap.vis.mapDescription', { defaultMessage: 'Plot latitude and longitude coordinates on a map', }), - category: CATEGORY.MAP, visConfig: { canDesaturate: !!supports.cssFilters, defaults: { diff --git a/src/core_plugins/timelion/public/vis/index.js b/src/core_plugins/timelion/public/vis/index.js index e3e12d64c753f3..7ae3203e2b79a5 100644 --- a/src/core_plugins/timelion/public/vis/index.js +++ b/src/core_plugins/timelion/public/vis/index.js @@ -18,7 +18,6 @@ */ import { VisFactoryProvider } from 'ui/vis/vis_factory'; -import { CATEGORY } from 'ui/vis/vis_category'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import { TimelionRequestHandlerProvider } from './timelion_request_handler'; import { DefaultEditorSize } from 'ui/vis/editor_size'; @@ -46,7 +45,6 @@ export default function TimelionVisProvider(Private, i18n) { description: i18n('timelion.timelionDescription', { defaultMessage: 'Build time-series using functional expressions', }), - category: CATEGORY.TIME, visConfig: { defaults: { expression: '.es(*)', diff --git a/src/core_plugins/vega/public/vega_type.js b/src/core_plugins/vega/public/vega_type.js index f17852cab9045e..397fca3c5ce55c 100644 --- a/src/core_plugins/vega/public/vega_type.js +++ b/src/core_plugins/vega/public/vega_type.js @@ -20,7 +20,6 @@ import { i18n } from '@kbn/i18n'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; -import { CATEGORY } from 'ui/vis/vis_category'; import { DefaultEditorSize } from 'ui/vis/editor_size'; import { Status } from 'ui/vis/update_status'; import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; @@ -49,7 +48,6 @@ VisTypesRegistryProvider.register((Private) => { defaultMessage: 'Create custom visualizations using Vega and Vega-Lite', }), icon: 'visVega', - category: CATEGORY.OTHER, visConfig: { defaults: { spec: defaultSpec } }, editorConfig: { optionsTemplate: vegaEditorTemplate, diff --git a/src/ui/public/vis/vis_category.js b/src/ui/public/vis/vis_category.js deleted file mode 100644 index 1971c582d4128d..00000000000000 --- a/src/ui/public/vis/vis_category.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * You should always make sure that every CATEGORY on top have a corresponding - * display name in the below object, otherwise they won't be shown properly - * in the vis creation wizard. - */ - -import { i18n } from '@kbn/i18n'; - -const CATEGORY = { - BASIC: 'basic', - DATA: 'data', - GRAPHIC: 'graphic', - MAP: 'map', - OTHER: 'other', - TIME: 'time', - // Hidden is a specific category and doesn't need a display name below - HIDDEN: 'hidden' -}; - -const CATEGORY_DISPLAY_NAMES = { - [CATEGORY.BASIC]: i18n.translate('common.ui.vis.visCategory.basicChartsLabel', { defaultMessage: 'Basic Charts' }), - [CATEGORY.DATA]: i18n.translate('common.ui.vis.visCategory.dataLabel', { defaultMessage: 'Data' }), - [CATEGORY.GRAPHIC]: i18n.translate('common.ui.vis.visCategory.graphicLabel', { defaultMessage: 'Graphic' }), - [CATEGORY.MAP]: i18n.translate('common.ui.vis.visCategory.mapsLabel', { defaultMessage: 'Maps' }), - [CATEGORY.OTHER]: i18n.translate('common.ui.vis.visCategory.otherLabel', { defaultMessage: 'Other' }), - [CATEGORY.TIME]: i18n.translate('common.ui.vis.visCategory.timeSeriesLabel', { defaultMessage: 'Time Series' }) -}; - -export { CATEGORY, CATEGORY_DISPLAY_NAMES }; diff --git a/src/ui/public/vis/vis_types/base_vis_type.js b/src/ui/public/vis/vis_types/base_vis_type.js index 3b360b75df3e1d..ceebabe931edce 100644 --- a/src/ui/public/vis/vis_types/base_vis_type.js +++ b/src/ui/public/vis/vis_types/base_vis_type.js @@ -17,7 +17,6 @@ * under the License. */ -import { CATEGORY } from '../vis_category'; import _ from 'lodash'; import { VisFiltersProvider } from '../vis_filters'; @@ -45,7 +44,6 @@ export function BaseVisTypeProvider(Private) { const _defaults = { // name, title, description, icon, image - category: CATEGORY.OTHER, visualization: null, // must be a class with render/resize/destroy methods visConfig: { defaults: {}, // default configuration @@ -69,7 +67,8 @@ export function BaseVisTypeProvider(Private) { } }, stage: 'production', - feedbackMessage: '' + feedbackMessage: '', + hidden: false, }; _.defaultsDeep(this, opts, _defaults); diff --git a/src/ui/public/vis/vis_types/vis_type.d.ts b/src/ui/public/vis/vis_types/vis_type.d.ts index 5537e50a6d129b..7373770560b4fd 100644 --- a/src/ui/public/vis/vis_types/vis_type.d.ts +++ b/src/ui/public/vis/vis_types/vis_type.d.ts @@ -17,6 +17,7 @@ * under the License. */ +import { IconType } from '@elastic/eui'; import { RequestHandler, ResponseHandler, Vis } from '..'; import { Status } from '../update_status'; @@ -28,11 +29,18 @@ export class VisualizationController { } export interface VisType { + name: string; title: string; + description?: string; visualization: typeof VisualizationController; isAccessible?: boolean; requestHandler: string | RequestHandler; responseHandler: string | ResponseHandler; + icon?: IconType; + image?: string; + stage: 'experimental' | 'production'; + requiresSearch: boolean; + hidden: boolean; // Since we haven't typed everything here yet, we basically "any" the rest // of that interface. This should be removed as soon as this type definition diff --git a/test/functional/apps/visualize/_chart_types.js b/test/functional/apps/visualize/_chart_types.js index 9e07a9741ff931..0912e1536e87ac 100644 --- a/test/functional/apps/visualize/_chart_types.js +++ b/test/functional/apps/visualize/_chart_types.js @@ -32,23 +32,23 @@ export default function ({ getService, getPageObjects }) { it('should show the correct chart types', async function () { const expectedChartTypes = [ 'Area', - 'Heat Map', - 'Horizontal Bar', - 'Line', - 'Pie', - 'Vertical Bar', + 'Controls', + 'Coordinate Map', 'Data Table', 'Gauge', 'Goal', + 'Heat Map', + 'Horizontal Bar', + 'Line', + 'Markdown', 'Metric', - 'Coordinate Map', + 'Pie', 'Region Map', - 'Timelion', - 'Visual Builder', - 'Controls', - 'Markdown', 'Tag Cloud', + 'Timelion', 'Vega', + 'Vertical Bar', + 'Visual Builder', ]; // find all the chart types and make sure there all there diff --git a/test/functional/apps/visualize/_tag_cloud.js b/test/functional/apps/visualize/_tag_cloud.js index bf3471be22fd79..34d6f7849dd53f 100644 --- a/test/functional/apps/visualize/_tag_cloud.js +++ b/test/functional/apps/visualize/_tag_cloud.js @@ -142,8 +142,8 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.openControlsByName(termsField); await PageObjects.settings.setFieldFormat('bytes'); await PageObjects.settings.controlChangeSave(); - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.loadSavedVisualization(vizName1); + await PageObjects.common.navigateToApp('visualize'); + await PageObjects.visualize.loadSavedVisualization(vizName1, { navigateToVisualize: false }); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.header.setAbsoluteRange(fromTime, toTime); await PageObjects.visualize.waitForVisualization(); diff --git a/test/functional/page_objects/visual_builder_page.js b/test/functional/page_objects/visual_builder_page.js index 02ac40b77a66c1..e74a6390657064 100644 --- a/test/functional/page_objects/visual_builder_page.js +++ b/test/functional/page_objects/visual_builder_page.js @@ -34,9 +34,8 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }) { const toTime = '2015-09-22 18:31:44.000'; log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.header.waitUntilLoadingHasFinished(); log.debug('clickVisualBuilderChart'); - await find.clickByPartialLinkText('Visual Builder'); + await PageObjects.visualize.clickVisualBuilder(); log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"'); await PageObjects.header.setAbsoluteRange(fromTime, toTime); await PageObjects.header.waitUntilLoadingHasFinished(); diff --git a/test/functional/page_objects/visualize_page.js b/test/functional/page_objects/visualize_page.js index 2c082dfe4a28ed..a3b99d1812f275 100644 --- a/test/functional/page_objects/visualize_page.js +++ b/test/functional/page_objects/visualize_page.js @@ -45,15 +45,15 @@ export function VisualizePageProvider({ getService, getPageObjects }) { } async navigateToNewVisualization() { - log.debug('navigateToApp visualize new'); - await PageObjects.common.navigateToUrl('visualize', 'new'); + log.debug('navigateToApp visualize'); + await PageObjects.common.navigateToApp('visualize'); + await testSubjects.click('createNewVis'); await this.waitForVisualizationSelectPage(); - await PageObjects.header.waitUntilLoadingHasFinished(); } async waitForVisualizationSelectPage() { await retry.try(async () => { - const visualizeSelectTypePage = await testSubjects.find('visualizeSelectTypePage'); + const visualizeSelectTypePage = await testSubjects.find('visNewDialogTypes'); if (!visualizeSelectTypePage.isDisplayed()) { throw new Error('wait for visualization select page'); } @@ -66,28 +66,23 @@ export function VisualizePageProvider({ getService, getPageObjects }) { } async clickAreaChart() { - await find.clickByPartialLinkText('Area'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('area'); } async clickDataTable() { - await find.clickByPartialLinkText('Data Table'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('table'); } async clickLineChart() { - await find.clickByPartialLinkText('Line'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('line'); } async clickRegionMap() { - await find.clickByPartialLinkText('Region Map'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('region_map'); } async clickMarkdownWidget() { - await find.clickByPartialLinkText('Markdown'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('markdown'); } async clickAddMetric() { @@ -99,38 +94,31 @@ export function VisualizePageProvider({ getService, getPageObjects }) { } async clickMetric() { - await find.clickByPartialLinkText('Metric'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('metric'); } async clickGauge() { - await find.clickByPartialLinkText('Gauge'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('gauge'); } async clickPieChart() { - await find.clickByPartialLinkText('Pie'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('pie'); } async clickTileMap() { - await find.clickByPartialLinkText('Coordinate Map'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('tile_map'); } async clickTagCloud() { - await find.clickByPartialLinkText('Tag Cloud'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('tagcloud'); } async clickVega() { - await find.clickByPartialLinkText('Vega'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('vega'); } async clickVisualBuilder() { - await find.clickByPartialLinkText('Visual Builder'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('metrics'); } async clickEditorSidebarCollapse() { @@ -157,29 +145,23 @@ export function VisualizePageProvider({ getService, getPageObjects }) { } async clickVerticalBarChart() { - await find.clickByPartialLinkText('Vertical Bar'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('histogram'); } async clickHeatmapChart() { - await find.clickByPartialLinkText('Heat Map'); - await PageObjects.header.waitUntilLoadingHasFinished(); + await this.clickVisType('heatmap'); } async clickInputControlVis() { - await find.clickByPartialLinkText('Controls'); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async getChartTypeCount() { - const tags = await find.allByCssSelector('a.wizard-vis-type'); - return tags.length; + await this.clickVisType('input_control_vis'); } async getChartTypes() { - const chartTypes = await testSubjects.findAll('visualizeWizardChartTypeTitle'); + const chartTypeField = await testSubjects.find('visNewDialogTypes'); + const chartTypes = await chartTypeField.findAllByTagName('button'); async function getChartType(chart) { - return await chart.getVisibleText(); + const label = await testSubjects.findDescendant('visTypeTitle', chart); + return await label.getVisibleText(); } const getChartTypesPromises = chartTypes.map(getChartType); return await Promise.all(getChartTypesPromises); @@ -195,7 +177,7 @@ export function VisualizePageProvider({ getService, getPageObjects }) { } async getExperimentalTypeLinks() { - return await remote.findAllByPartialLinkText('(Experimental)'); + return await remote.findAllByCssSelector('[data-vis-stage="experimental"]'); } async isExperimentalInfoShown() { @@ -782,10 +764,10 @@ export function VisualizePageProvider({ getService, getPageObjects }) { }); } - // this starts by clicking the Load Saved Viz button, not from the - // bottom half of the "Create a new visualization Step 1" page - async loadSavedVisualization(vizName) { - await this.clickLoadSavedVisButton(); + async loadSavedVisualization(vizName, { navigateToVisualize = true } = {}) { + if (navigateToVisualize) { + await this.clickLoadSavedVisButton(); + } await this.openSavedVisualization(vizName); }