mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* 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
This commit is contained in:
parent
fa213f36b1
commit
d9b1c0b626
47 changed files with 1620 additions and 440 deletions
|
@ -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) <string> 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*: <bool> 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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -33,6 +33,13 @@
|
|||
<visualize-listing-table
|
||||
fetch-items="listingController.fetchItems"
|
||||
delete-selected-items="listingController.deleteSelectedItems"
|
||||
on-create-vis="listingController.createNewVis"
|
||||
></visualize-listing-table>
|
||||
|
||||
<new-vis-modal
|
||||
is-open="listingController.showNewVisModal"
|
||||
on-close="listingController.closeNewVisModal"
|
||||
vis-types-registry="listingController.visTypeRegistry"
|
||||
></new-vis-modal>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ?
|
||||
<KuiListingTableDeleteButton onDelete={this.onDelete} aria-label="Delete selected visualizations"/> :
|
||||
<KuiListingTableCreateButton onCreate={this.onCreate} aria-label="Create new visualization"/>;
|
||||
<KuiListingTableCreateButton onCreate={this.onCreate} data-test-subj="createNewVis" aria-label="Create new visualization"/>;
|
||||
}
|
||||
|
||||
renderPager() {
|
||||
|
@ -323,4 +323,5 @@ export class VisualizeListingTable extends Component {
|
|||
VisualizeListingTable.propTypes = {
|
||||
deleteSelectedItems: PropTypes.func,
|
||||
fetchItems: PropTypes.func,
|
||||
onCreateVis: PropTypes.func.isRequired,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,768 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`NewVisModal should render as expected 1`] = `
|
||||
<I18nProvider
|
||||
intl={
|
||||
Object {
|
||||
"defaultFormats": Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": Symbol(react.fragment),
|
||||
"timeZone": null,
|
||||
}
|
||||
}
|
||||
>
|
||||
<IntlProvider
|
||||
defaultFormats={
|
||||
Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
defaultLocale="en"
|
||||
locale="en"
|
||||
messages={Object {}}
|
||||
textComponent={Symbol(react.fragment)}
|
||||
>
|
||||
<NewVisModal
|
||||
editorParams={Array []}
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
visTypesRegistry={
|
||||
Array [
|
||||
Object {
|
||||
"hidden": false,
|
||||
"name": "vis",
|
||||
"requestHandler": "none",
|
||||
"requiresSearch": false,
|
||||
"responseHandler": "none",
|
||||
"stage": "production",
|
||||
"title": "Vis Type 1",
|
||||
"visualization": [Function],
|
||||
},
|
||||
Object {
|
||||
"hidden": false,
|
||||
"name": "visExp",
|
||||
"requestHandler": "none",
|
||||
"requiresSearch": false,
|
||||
"responseHandler": "none",
|
||||
"stage": "experimental",
|
||||
"title": "Experimental Vis",
|
||||
"visualization": [Function],
|
||||
},
|
||||
Object {
|
||||
"hidden": false,
|
||||
"name": "visWithSearch",
|
||||
"requestHandler": "none",
|
||||
"requiresSearch": false,
|
||||
"responseHandler": "none",
|
||||
"stage": "production",
|
||||
"title": "Vis with search",
|
||||
"visualization": [Function],
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<EuiOverlayMask>
|
||||
<EuiModal
|
||||
className="visNewVisDialog"
|
||||
maxWidth="100vw"
|
||||
onClose={[Function]}
|
||||
>
|
||||
<FocusTrap
|
||||
_createFocusTrap={[Function]}
|
||||
active={true}
|
||||
focusTrapOptions={
|
||||
Object {
|
||||
"fallbackFocus": [Function],
|
||||
"initialFocus": undefined,
|
||||
}
|
||||
}
|
||||
paused={false}
|
||||
tag="div"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="euiModal visNewVisDialog"
|
||||
onKeyDown={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"maxWidth": "100vw",
|
||||
}
|
||||
}
|
||||
tabIndex={0}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
aria-label="Closes this modal window"
|
||||
className="euiModal__closeIcon"
|
||||
color="text"
|
||||
iconSize="m"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this modal window"
|
||||
className="euiButtonIcon euiButtonIcon--text euiModal__closeIcon"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
size="m"
|
||||
type="cross"
|
||||
>
|
||||
<cross
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--medium euiButtonIcon__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={
|
||||
Object {
|
||||
"fill": undefined,
|
||||
}
|
||||
}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--medium euiButtonIcon__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={
|
||||
Object {
|
||||
"fill": undefined,
|
||||
}
|
||||
}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<defs>
|
||||
<path
|
||||
d="M7.293 8l-4.147 4.146a.5.5 0 0 0 .708.708L8 8.707l4.146 4.147a.5.5 0 0 0 .708-.708L8.707 8l4.147-4.146a.5.5 0 0 0-.708-.708L8 7.293 3.854 3.146a.5.5 0 1 0-.708.708L7.293 8z"
|
||||
id="cross-a"
|
||||
/>
|
||||
</defs>
|
||||
<use
|
||||
fillRule="nonzero"
|
||||
xlinkHref="#cross-a"
|
||||
/>
|
||||
</svg>
|
||||
</cross>
|
||||
</EuiIcon>
|
||||
</button>
|
||||
</EuiButtonIcon>
|
||||
<div
|
||||
className="euiModal__flex"
|
||||
>
|
||||
<TypeSelection
|
||||
onVisTypeSelected={[Function]}
|
||||
visTypesRegistry={
|
||||
Array [
|
||||
Object {
|
||||
"hidden": false,
|
||||
"name": "vis",
|
||||
"requestHandler": "none",
|
||||
"requiresSearch": false,
|
||||
"responseHandler": "none",
|
||||
"stage": "production",
|
||||
"title": "Vis Type 1",
|
||||
"visualization": [Function],
|
||||
},
|
||||
Object {
|
||||
"hidden": false,
|
||||
"name": "visExp",
|
||||
"requestHandler": "none",
|
||||
"requiresSearch": false,
|
||||
"responseHandler": "none",
|
||||
"stage": "experimental",
|
||||
"title": "Experimental Vis",
|
||||
"visualization": [Function],
|
||||
},
|
||||
Object {
|
||||
"hidden": false,
|
||||
"name": "visWithSearch",
|
||||
"requestHandler": "none",
|
||||
"requiresSearch": false,
|
||||
"responseHandler": "none",
|
||||
"stage": "production",
|
||||
"title": "Vis with search",
|
||||
"visualization": [Function],
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<div
|
||||
className="euiModalHeader"
|
||||
>
|
||||
<EuiModalHeaderTitle>
|
||||
<div
|
||||
className="euiModalHeader__title"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="New Visualization"
|
||||
id="kbn.visualize.newVisWizard.title"
|
||||
values={Object {}}
|
||||
>
|
||||
New Visualization
|
||||
</FormattedMessage>
|
||||
</div>
|
||||
</EuiModalHeaderTitle>
|
||||
</div>
|
||||
</EuiModalHeader>
|
||||
<div
|
||||
className="visNewVisDialog__body"
|
||||
>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="xl"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterExtraLarge euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem"
|
||||
>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
component="div"
|
||||
direction="column"
|
||||
gutterSize="none"
|
||||
justifyContent="flexStart"
|
||||
responsive={false}
|
||||
wrap={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--directionColumn"
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="visNewVisDialog__searchWrapper"
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero visNewVisDialog__searchWrapper"
|
||||
>
|
||||
<EuiFieldSearch
|
||||
compressed={false}
|
||||
fullWidth={true}
|
||||
incremental={false}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
placeholder="Filter"
|
||||
value=""
|
||||
>
|
||||
<EuiFormControlLayout
|
||||
compressed={false}
|
||||
fullWidth={true}
|
||||
icon="search"
|
||||
isLoading={false}
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<EuiValidatableControl>
|
||||
<input
|
||||
className="euiFieldSearch euiFieldSearch--fullWidth"
|
||||
onChange={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Filter"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</EuiValidatableControl>
|
||||
<EuiFormControlLayoutIcons
|
||||
icon="search"
|
||||
isLoading={false}
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayoutIcons"
|
||||
>
|
||||
<EuiFormControlLayoutCustomIcon
|
||||
type="search"
|
||||
>
|
||||
<span
|
||||
className="euiFormControlLayoutCustomIcon"
|
||||
>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiFormControlLayoutCustomIcon__icon"
|
||||
size="m"
|
||||
type="search"
|
||||
>
|
||||
<search
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--medium euiFormControlLayoutCustomIcon__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={
|
||||
Object {
|
||||
"fill": undefined,
|
||||
}
|
||||
}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--medium euiFormControlLayoutCustomIcon__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={
|
||||
Object {
|
||||
"fill": undefined,
|
||||
}
|
||||
}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<defs>
|
||||
<path
|
||||
d="M11.271 11.978l3.872 3.873a.502.502 0 0 0 .708 0 .502.502 0 0 0 0-.708l-3.565-3.564c2.38-2.747 2.267-6.923-.342-9.532-2.73-2.73-7.17-2.73-9.898 0-2.728 2.729-2.728 7.17 0 9.9a6.955 6.955 0 0 0 4.949 2.05.5.5 0 0 0 0-1 5.96 5.96 0 0 1-4.242-1.757 6.01 6.01 0 0 1 0-8.486c2.337-2.34 6.143-2.34 8.484 0a6.01 6.01 0 0 1 0 8.486.5.5 0 0 0 .034.738z"
|
||||
id="search-a"
|
||||
/>
|
||||
</defs>
|
||||
<use
|
||||
xlinkHref="#search-a"
|
||||
/>
|
||||
</svg>
|
||||
</search>
|
||||
</EuiIcon>
|
||||
</span>
|
||||
</EuiFormControlLayoutCustomIcon>
|
||||
</div>
|
||||
</EuiFormControlLayoutIcons>
|
||||
</div>
|
||||
</div>
|
||||
</EuiFormControlLayout>
|
||||
</EuiFieldSearch>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
className="visNewVisDialog__typesWrapper"
|
||||
component="div"
|
||||
grow={1}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrow1 visNewVisDialog__typesWrapper"
|
||||
>
|
||||
<EuiKeyPadMenu
|
||||
className="visNewVisDialog__types"
|
||||
data-test-subj="visNewDialogTypes"
|
||||
>
|
||||
<div
|
||||
className="euiKeyPadMenu visNewVisDialog__types"
|
||||
data-test-subj="visNewDialogTypes"
|
||||
role="menu"
|
||||
>
|
||||
<EuiKeyPadMenuItemButton
|
||||
className="visNewVisDialog__type"
|
||||
data-test-subj="visType-vis"
|
||||
data-vis-stage="production"
|
||||
disabled={false}
|
||||
key="vis"
|
||||
label={
|
||||
<span
|
||||
data-test-subj="visTypeTitle"
|
||||
>
|
||||
Vis Type 1
|
||||
</span>
|
||||
}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<button
|
||||
className="euiKeyPadMenuItem visNewVisDialog__type"
|
||||
data-test-subj="visType-vis"
|
||||
data-vis-stage="production"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
className="euiKeyPadMenuItem__inner"
|
||||
>
|
||||
<div
|
||||
className="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<Component
|
||||
visType={
|
||||
Object {
|
||||
"hidden": false,
|
||||
"highlighted": false,
|
||||
"name": "vis",
|
||||
"requestHandler": "none",
|
||||
"requiresSearch": false,
|
||||
"responseHandler": "none",
|
||||
"stage": "production",
|
||||
"title": "Vis Type 1",
|
||||
"visualization": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
size="l"
|
||||
type="empty"
|
||||
>
|
||||
<empty
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--large euiIcon--secondary"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--large euiIcon--secondary"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
/>
|
||||
</empty>
|
||||
</EuiIcon>
|
||||
</Component>
|
||||
</div>
|
||||
<p
|
||||
className="euiKeyPadMenuItem__label"
|
||||
>
|
||||
<span
|
||||
data-test-subj="visTypeTitle"
|
||||
>
|
||||
Vis Type 1
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
</EuiKeyPadMenuItemButton>
|
||||
<EuiKeyPadMenuItemButton
|
||||
className="visNewVisDialog__type"
|
||||
data-test-subj="visType-visWithSearch"
|
||||
data-vis-stage="production"
|
||||
disabled={false}
|
||||
key="visWithSearch"
|
||||
label={
|
||||
<span
|
||||
data-test-subj="visTypeTitle"
|
||||
>
|
||||
Vis with search
|
||||
</span>
|
||||
}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<button
|
||||
className="euiKeyPadMenuItem visNewVisDialog__type"
|
||||
data-test-subj="visType-visWithSearch"
|
||||
data-vis-stage="production"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
className="euiKeyPadMenuItem__inner"
|
||||
>
|
||||
<div
|
||||
className="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<Component
|
||||
visType={
|
||||
Object {
|
||||
"hidden": false,
|
||||
"highlighted": false,
|
||||
"name": "visWithSearch",
|
||||
"requestHandler": "none",
|
||||
"requiresSearch": false,
|
||||
"responseHandler": "none",
|
||||
"stage": "production",
|
||||
"title": "Vis with search",
|
||||
"visualization": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
size="l"
|
||||
type="empty"
|
||||
>
|
||||
<empty
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--large euiIcon--secondary"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--large euiIcon--secondary"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
/>
|
||||
</empty>
|
||||
</EuiIcon>
|
||||
</Component>
|
||||
</div>
|
||||
<p
|
||||
className="euiKeyPadMenuItem__label"
|
||||
>
|
||||
<span
|
||||
data-test-subj="visTypeTitle"
|
||||
>
|
||||
Vis with search
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
</EuiKeyPadMenuItemButton>
|
||||
</div>
|
||||
</EuiKeyPadMenu>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</div>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
className="visNewVisDialog__description"
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero visNewVisDialog__description"
|
||||
>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
textTransform="none"
|
||||
>
|
||||
<h2
|
||||
className="euiTitle euiTitle--small"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Select a visualization type"
|
||||
id="kbn.visualize.newVisWizard.selectVisType"
|
||||
values={Object {}}
|
||||
>
|
||||
Select a visualization type
|
||||
</FormattedMessage>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
>
|
||||
<div
|
||||
className="euiSpacer euiSpacer--m"
|
||||
/>
|
||||
</EuiSpacer>
|
||||
<Component>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="m"
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--medium"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Start creating your visualization by selecting a type for that visualization."
|
||||
id="kbn.visualize.newVisWizard.helpText"
|
||||
values={Object {}}
|
||||
>
|
||||
Start creating your visualization by selecting a type for that visualization.
|
||||
</FormattedMessage>
|
||||
</p>
|
||||
</div>
|
||||
</EuiText>
|
||||
</Component>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</div>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
</TypeSelection>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FocusTrap>
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
</NewVisModal>
|
||||
</IntlProvider>
|
||||
</I18nProvider>
|
||||
`;
|
99
src/core_plugins/kibana/public/visualize/wizard/_dialog.scss
Normal file
99
src/core_plugins/kibana/public/visualize/wizard/_dialog.scss
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
@import './wizard';
|
||||
@import './dialog';
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
21
src/core_plugins/kibana/public/visualize/wizard/index.ts
Normal file
21
src/core_plugins/kibana/public/visualize/wizard/index.ts
Normal file
|
@ -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';
|
|
@ -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(
|
||||
<I18nProvider>
|
||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
||||
</I18nProvider>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should show a button for regular visualizations', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<I18nProvider>
|
||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
||||
</I18nProvider>
|
||||
);
|
||||
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(
|
||||
<I18nProvider>
|
||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
||||
</I18nProvider>
|
||||
);
|
||||
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(
|
||||
<I18nProvider>
|
||||
<NewVisModal
|
||||
isOpen={true}
|
||||
onClose={() => null}
|
||||
visTypesRegistry={visTypes}
|
||||
editorParams={['foo=true', 'bar=42']}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
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(
|
||||
<I18nProvider>
|
||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
||||
</I18nProvider>
|
||||
);
|
||||
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(
|
||||
<I18nProvider>
|
||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
||||
</I18nProvider>
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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<TypeSelectionProps> {
|
||||
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 (
|
||||
<EuiOverlayMask>
|
||||
<EuiModal onClose={this.props.onClose} maxWidth={'100vw'} className="visNewVisDialog">
|
||||
<TypeSelection
|
||||
showExperimental={this.isLabsEnabled}
|
||||
onVisTypeSelected={this.onVisTypeSelected}
|
||||
visTypesRegistry={this.props.visTypesRegistry}
|
||||
/>
|
||||
</EuiModal>
|
||||
</EuiOverlayMask>
|
||||
);
|
||||
}
|
||||
|
||||
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 };
|
|
@ -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 = (
|
||||
<I18nProvider>
|
||||
<NewVisModal
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
visTypesRegistry={visTypeRegistry}
|
||||
editorParams={editorParams}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
ReactDOM.render(element, container);
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
<!-- Local nav. -->
|
||||
<kbn-top-nav name="visualize">
|
||||
<!-- Transcluded elements. -->
|
||||
<div data-transclude-slots>
|
||||
<!-- Breadcrumbs. -->
|
||||
<bread-crumbs
|
||||
data-transclude-slot="topLeftCorner"
|
||||
use-links="true"
|
||||
omit-current-page="true"
|
||||
page-title="'New'"
|
||||
></bread-crumbs>
|
||||
</div>
|
||||
</kbn-top-nav>
|
||||
|
||||
<div class="kuiViewContent kuiViewContent--constrainedWidth kuiViewContentItem" data-test-subj="visualizeSelectTypePage">
|
||||
<div class="kuiViewContentItem">
|
||||
<!-- Header -->
|
||||
<div class="visualizeViewContentHeader kuiVerticalRhythm kuiVerticalRhythm--medium">
|
||||
<h1 class="kuiTitle">
|
||||
Select visualization type
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!-- Search -->
|
||||
<div class="kuiSearchInput kuiVerticalRhythm kuiVerticalRhythm--medium fullWidth" role="search">
|
||||
<icon class="kuiSearchInput__icon kuiIcon fa-search"></icon>
|
||||
<input
|
||||
class="kuiSearchInput__input"
|
||||
type="text"
|
||||
placeholder="Search visualization types..."
|
||||
ng-model="searchTerm"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="kuiVerticalRhythm kuiVerticalRhythm--medium"
|
||||
ng-repeat="category in filteredVisTypeCategories"
|
||||
>
|
||||
|
||||
<!-- Title for each category -->
|
||||
<h2 class="kuiSubTitle kuiVerticalRhythm">
|
||||
{{ category.label }}
|
||||
</h2>
|
||||
|
||||
<!-- Gallery of buttons for each vis type -->
|
||||
<div class="kuiGallery kuiVerticalRhythm">
|
||||
<a
|
||||
class="kuiGalleryItem"
|
||||
ng-repeat="type in category.list"
|
||||
ng-href="{{ getVisTypeUrl(type) }}"
|
||||
tooltip="{{ getVisTypeTooltip(type) }}"
|
||||
tooltip-placement="{{ getVisTypeTooltipPosition($parent.$index) }}"
|
||||
aria-describedby="visDescription_{{ ::getVisTypeId(type) }}"
|
||||
data-test-subj="visType-{{::type.name}}"
|
||||
>
|
||||
<div class="kuiGalleryItem__image">
|
||||
<img
|
||||
class="visWizard__visTypeImage"
|
||||
ng-if="type.image"
|
||||
aria-hidden="true"
|
||||
ng-src="{{ type.image }}"
|
||||
/>
|
||||
|
||||
<!-- Allowing legacyIcon to hold a CSS name, will be removed in 7.0 -->
|
||||
<span
|
||||
ng-if="!type.image && !type.icon"
|
||||
aria-hidden="true"
|
||||
class="kuiIcon visWizard__visTypeIcon"
|
||||
ng-class="type.legacyIcon"
|
||||
></span>
|
||||
|
||||
<!-- If there's no image, default to an icon, for BWC. -->
|
||||
<icon
|
||||
ng-if="!type.image && type.icon"
|
||||
aria-hidden="true"
|
||||
class="kuiIcon visWizard__visTypeIcon"
|
||||
type="type.icon"
|
||||
size="'xxl'"
|
||||
></icon>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="kuiGalleryItem__label"
|
||||
data-test-subj="visualizeWizardChartTypeTitle"
|
||||
>
|
||||
{{ type.title }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="kuiGalleryItem__icon kuiIcon fa-flask"
|
||||
ng-if="type.shouldMarkAsExperimentalInUI()"
|
||||
></div>
|
||||
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
aria-hidden="true"
|
||||
id="visDescription_{{ ::getVisTypeId(type) }}"
|
||||
>{{::getVisTypeTooltip(type)}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -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';
|
|
@ -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 = () => (
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="kbn.visualize.newVisWizard.helpText"
|
||||
defaultMessage="Start creating your visualization by selecting a type for that visualization."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
);
|
|
@ -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<TypeSelectionProps, TypeSelectionState> {
|
||||
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 (
|
||||
<React.Fragment>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
<FormattedMessage
|
||||
id="kbn.visualize.newVisWizard.title"
|
||||
defaultMessage="New Visualization"
|
||||
/>
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<div className="visNewVisDialog__body">
|
||||
<EuiFlexGroup gutterSize="xl">
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
||||
<EuiFlexItem grow={false} className="visNewVisDialog__searchWrapper">
|
||||
<EuiFieldSearch
|
||||
placeholder="Filter"
|
||||
value={query}
|
||||
onChange={this.onQueryChange}
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={1} className="visNewVisDialog__typesWrapper">
|
||||
<EuiKeyPadMenu
|
||||
className="visNewVisDialog__types"
|
||||
data-test-subj="visNewDialogTypes"
|
||||
>
|
||||
{visTypes.map(this.renderVisType)}
|
||||
</EuiKeyPadMenu>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem className="visNewVisDialog__description" grow={false}>
|
||||
{highlightedType ? (
|
||||
<VisHelpText visType={highlightedType} />
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="kbn.visualize.newVisWizard.selectVisType"
|
||||
defaultMessage="Select a visualization type"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<NewVisHelp />
|
||||
</React.Fragment>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<EuiKeyPadMenuItemButton
|
||||
key={visType.name}
|
||||
label={<span data-test-subj="visTypeTitle">{visType.title}</span>}
|
||||
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}
|
||||
>
|
||||
<VisTypeIcon visType={visType} />
|
||||
</EuiKeyPadMenuItemButton>
|
||||
);
|
||||
};
|
||||
|
||||
private highlightType(visType: VisType | null) {
|
||||
this.setState({
|
||||
highlightedType: visType,
|
||||
});
|
||||
}
|
||||
|
||||
private onQueryChange = (ev: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
query: ev.target.value,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export { TypeSelection };
|
|
@ -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 (
|
||||
<React.Fragment>
|
||||
<EuiTitle size="s">
|
||||
<h2>{visType.title}</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
{visType.stage === 'experimental' && (
|
||||
<React.Fragment>
|
||||
<EuiText>
|
||||
<em>
|
||||
<FormattedMessage
|
||||
id="kbn.visualize.newVisWizard.experimentalDescription"
|
||||
defaultMessage="This visualization is experimental. The design and implementation
|
||||
are less mature than stable visualizations and might be subject to change."
|
||||
/>
|
||||
</em>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
</React.Fragment>
|
||||
)}
|
||||
<EuiText>{visType.description}</EuiText>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
|
@ -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 (
|
||||
<React.Fragment>
|
||||
{visType.image && (
|
||||
<img src={visType.image} aria-hidden="true" className="visNewVisDialog__typeImage" />
|
||||
)}
|
||||
{!visType.image && visType.legacyIcon && <span className={legacyIconClass} />}
|
||||
{!visType.image &&
|
||||
!visType.legacyIcon && (
|
||||
<EuiIcon type={visType.icon || 'empty'} size="l" color="secondary" aria-hidden="true" />
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
|
@ -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
|
||||
/********/
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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(*)',
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 };
|
|
@ -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);
|
||||
|
|
8
src/ui/public/vis/vis_types/vis_type.d.ts
vendored
8
src/ui/public/vis/vis_types/vis_type.d.ts
vendored
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
'Metric',
|
||||
'Coordinate Map',
|
||||
'Region Map',
|
||||
'Timelion',
|
||||
'Visual Builder',
|
||||
'Controls',
|
||||
'Heat Map',
|
||||
'Horizontal Bar',
|
||||
'Line',
|
||||
'Markdown',
|
||||
'Metric',
|
||||
'Pie',
|
||||
'Region Map',
|
||||
'Tag Cloud',
|
||||
'Timelion',
|
||||
'Vega',
|
||||
'Vertical Bar',
|
||||
'Visual Builder',
|
||||
];
|
||||
|
||||
// find all the chart types and make sure there all there
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue