mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Introduce lab mode for visualizations (#15050)
* apply patch add styling remove cruft split up concept of experimental and labs adjust wording * improve wording * improve wording & punctuation. remove concept of feedback-url * remove duplicate labeling between labs/experimental; resolve some typos * merging isExperimental and isLabs flags to a stage setting * adding the option to override feedback message back (and improving it) * updating the docs * change text labs to lab * visualize:enableLabsVisualizations to visualize:enableLabs * fixing github link
This commit is contained in:
parent
6e1faea678
commit
4e982c0a6d
18 changed files with 133 additions and 22 deletions
|
@ -72,7 +72,10 @@ The list of common parameters:
|
|||
- *options.showQueryBar*: <bool> show or hide query bar (defaults to true)
|
||||
- *options.showFilterBar*: <bool> show or hide filter bar (defaults to true)
|
||||
- *options.showIndexSelection*: <bool> show or hide index selection (defaults to true)
|
||||
- *isExperimental*: <bool> mark visualization as experimental (defaults to false)
|
||||
- *stage*: <string> Set this to "experimental" or "labs" to mark your visualization as experimental.
|
||||
Labs visualizations can also be disabled from the advanced settings. (defaults to "production")
|
||||
- *feedbackMessage*: <string> You can provide a message (which can contain HTML), that will be appended
|
||||
to the experimental notification in visualize, if your visualization is experimental or in lab mode.
|
||||
|
||||
|
||||
Each of the factories have some of the custom parameters, which will be described below.
|
||||
|
|
|
@ -5,6 +5,7 @@ import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
|
|||
import { VisController } from './vis_controller';
|
||||
import { ControlsTab } from './components/editor/controls_tab';
|
||||
import { OptionsTab } from './components/editor/options_tab';
|
||||
import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
|
||||
import image from './images/icon-input-control.svg';
|
||||
|
||||
|
||||
|
@ -18,7 +19,8 @@ function InputControlVisProvider(Private) {
|
|||
image,
|
||||
description: 'Create interactive controls for easy dashboard manipulation.',
|
||||
category: CATEGORY.OTHER,
|
||||
isExperimental: true,
|
||||
stage: 'lab',
|
||||
feedbackMessage: defaultFeedbackMessage,
|
||||
visualization: VisController,
|
||||
visConfig: {
|
||||
defaults: {
|
||||
|
|
|
@ -50,6 +50,13 @@
|
|||
index-patterns="[indexPattern]"
|
||||
></filter-bar>
|
||||
|
||||
<div class="kuiInfoPanel kuiInfoPanel--warning kuiVerticalRhythm" ng-if="vis.type.shouldMarkAsExperimentalInUI">
|
||||
<div class="kuiInfoPanelBody">
|
||||
<div class="kuiInfoPanelBody__message" ng-bind-html="getAdditionalMessage()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<visualize
|
||||
saved-obj="savedVis"
|
||||
ui-state="uiState"
|
||||
|
|
|
@ -295,5 +295,10 @@ function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courie
|
|||
$scope.fetch();
|
||||
};
|
||||
|
||||
|
||||
$scope.getAdditionalMessage = () => {
|
||||
return `This visualization is marked as experimental. ${vis.type.feedbackMessage}`;
|
||||
};
|
||||
|
||||
init();
|
||||
}
|
||||
|
|
|
@ -10,8 +10,9 @@ import { EmbeddableFactory, Embeddable } from 'ui/embeddable';
|
|||
import chrome from 'ui/chrome';
|
||||
|
||||
export class VisualizeEmbeddableFactory extends EmbeddableFactory {
|
||||
constructor($compile, $rootScope, visualizeLoader, timefilter, Notifier, Promise, Private) {
|
||||
constructor($compile, $rootScope, visualizeLoader, timefilter, Notifier, Promise, Private, config) {
|
||||
super();
|
||||
this._config = config;
|
||||
this.$compile = $compile;
|
||||
this.visualizeLoader = visualizeLoader;
|
||||
this.$rootScope = $rootScope;
|
||||
|
@ -30,6 +31,21 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory {
|
|||
visualizeScope.editUrl = this.getEditPath(panel.id);
|
||||
return this.visualizeLoader.get(panel.id)
|
||||
.then(savedObject => {
|
||||
const isLabsEnabled = this._config.get('visualize:enableLabs');
|
||||
|
||||
if (!isLabsEnabled && savedObject.vis.type.stage === 'lab') {
|
||||
domNode.innerHTML = `
|
||||
<div class="disabledLabVisualization">
|
||||
<div class="kuiVerticalRhythm disabledLabVisualization__icon kuiIcon fa-flask" aria-hidden="true"></div>
|
||||
<div class="kuiVerticalRhythm"><em>${savedObject.title}</em> is a lab visualization.</div>
|
||||
<div class="kuiVerticalRhythm">Please turn on lab-mode in the advanced settings to see lab visualizations.</div>
|
||||
</div>
|
||||
`;
|
||||
return new Embeddable({
|
||||
title: savedObject.title
|
||||
});
|
||||
}
|
||||
|
||||
if (!container.getHidePanelTitles()) {
|
||||
visualizeScope.sharedItemTitle = panel.title !== undefined ? panel.title : savedObject.title;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@ export function visualizeEmbeddableFactoryProvider(Private) {
|
|||
timefilter,
|
||||
Notifier,
|
||||
Promise,
|
||||
Private) => {
|
||||
return new VisualizeEmbeddableFactory($compile, $rootScope, savedVisualizations, timefilter, Notifier, Promise, Private);
|
||||
Private,
|
||||
config) => {
|
||||
return new VisualizeEmbeddableFactory($compile, $rootScope, savedVisualizations, timefilter, Notifier, Promise, Private, config);
|
||||
};
|
||||
return Private(VisualizeEmbeddableFactoryProvider);
|
||||
}
|
||||
|
|
|
@ -24,12 +24,13 @@ export function VisualizeListingController($injector) {
|
|||
const notify = new Notifier({ location: 'Visualize' });
|
||||
|
||||
this.fetchItems = (filter) => {
|
||||
const isLabsEnabled = config.get('visualize:enableLabs');
|
||||
return visualizationService.find(filter, config.get('savedObjects:listingLimit'))
|
||||
.then(result => {
|
||||
this.totalItems = result.total;
|
||||
this.showLimitError = result.total > config.get('savedObjects:listingLimit');
|
||||
this.listingLimit = config.get('savedObjects:listingLimit');
|
||||
return result.hits;
|
||||
return result.hits.filter(result => (isLabsEnabled || !result.type.stage === 'lab'));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -150,10 +150,21 @@ export class VisualizeListingTable extends Component {
|
|||
}
|
||||
|
||||
renderRowCells(item) {
|
||||
|
||||
let flaskHolder;
|
||||
if (item.type.shouldMarkAsExperimentalInUI()) {
|
||||
flaskHolder = <span className="kuiIcon fa-flask ng-scope"> </span>;
|
||||
}else{
|
||||
flaskHolder = <span />;
|
||||
}
|
||||
|
||||
return [
|
||||
<a className="kuiLink" href={this.getUrlForItem(item)}>
|
||||
{item.title}
|
||||
</a>,
|
||||
<span>
|
||||
{flaskHolder}
|
||||
<a className="kuiLink" href={this.getUrlForItem(item)}>
|
||||
{item.title}
|
||||
</a>
|
||||
</span>,
|
||||
<span className="kuiStatusText">
|
||||
{this.renderItemTypeIcon(item)}
|
||||
{item.type.title}
|
||||
|
|
12
src/core_plugins/kibana/public/visualize/styles/lab_vis.less
Normal file
12
src/core_plugins/kibana/public/visualize/styles/lab_vis.less
Normal file
|
@ -0,0 +1,12 @@
|
|||
.disabledLabVisualization {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.disabledLabVisualization__icon {
|
||||
font-size: 2em;
|
||||
}
|
|
@ -1,6 +1,16 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/bootstrap/list-group";
|
||||
@import (reference) "~ui/styles/list-group-menu";
|
||||
@import "./lab_vis";
|
||||
|
||||
.visualizeViewContentHeader {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.visualizeViewContentHeader .kuiTitle {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
|
||||
.wizard-sub-title {
|
||||
margin-top: 0px;
|
||||
|
|
|
@ -15,9 +15,11 @@
|
|||
<div class="kuiViewContent kuiViewContent--constrainedWidth">
|
||||
<div class="kuiViewContentItem">
|
||||
<!-- Header -->
|
||||
<h1 class="kuiTitle kuiVerticalRhythm kuiVerticalRhythm--medium">
|
||||
Select visualization type
|
||||
</h1>
|
||||
<div class="visualizeViewContentHeader">
|
||||
<h1 class="kuiTitle kuiVerticalRhythm kuiVerticalRhythm--medium">
|
||||
Select visualization type
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!-- Search -->
|
||||
<div class="kuiSearchInput kuiVerticalRhythm kuiVerticalRhythm--medium fullWidth" role="search">
|
||||
|
@ -29,7 +31,6 @@
|
|||
ng-model="searchTerm"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="kuiVerticalRhythm kuiVerticalRhythm--medium"
|
||||
ng-repeat="category in filteredVisTypeCategories"
|
||||
|
@ -75,7 +76,7 @@
|
|||
|
||||
<div
|
||||
class="kuiGalleryItem__icon kuiIcon fa-flask"
|
||||
ng-if="type.isExperimental"
|
||||
ng-if="type.shouldMarkAsExperimentalInUI()"
|
||||
></div>
|
||||
|
||||
<span
|
||||
|
|
|
@ -31,7 +31,7 @@ routes.when(VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, {
|
|||
controller: 'VisualizeWizardStep1',
|
||||
});
|
||||
|
||||
module.controller('VisualizeWizardStep1', function ($scope, $route, kbnUrl, timefilter, Private) {
|
||||
module.controller('VisualizeWizardStep1', function ($scope, $route, kbnUrl, timefilter, Private, config) {
|
||||
timefilter.enabled = false;
|
||||
|
||||
const visTypeCategoryToHumanReadableMap = {
|
||||
|
@ -47,13 +47,26 @@ module.controller('VisualizeWizardStep1', function ($scope, $route, kbnUrl, time
|
|||
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 => {
|
||||
|
||||
const categoryName = visType.category;
|
||||
|
||||
if (categoryName === CATEGORY.HIDDEN) return;
|
||||
if (categoryName === CATEGORY.HIDDEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLabsEnabled && visType.stage === 'lab') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create category object if it doesn't exist yet.
|
||||
if (!categoryToVisTypesMap[categoryName]) {
|
||||
|
@ -66,7 +79,6 @@ module.controller('VisualizeWizardStep1', function ($scope, $route, kbnUrl, time
|
|||
const categoryVisTypes = categoryToVisTypesMap[categoryName];
|
||||
|
||||
// Add the visType to the list and sort them by their title.
|
||||
// categoryVisTypes.list.push(visType);
|
||||
categoryVisTypes.list = _.sortBy(
|
||||
categoryVisTypes.list.concat(visType),
|
||||
type => type.title
|
||||
|
@ -125,7 +137,14 @@ module.controller('VisualizeWizardStep1', function ($scope, $route, kbnUrl, time
|
|||
};
|
||||
|
||||
$scope.getVisTypeTooltip = type => {
|
||||
const prefix = type.isExperimental ? '(Experimental)' : '';
|
||||
//to not clutter the tooltip, just only notify if labs or experimental.
|
||||
//labs is more important in this regard.
|
||||
let prefix = '';
|
||||
if (type.stage === 'lab') {
|
||||
prefix = '(Lab)';
|
||||
} else if (type.stage === 'experimental') {
|
||||
prefix = '(Experimental)';
|
||||
}
|
||||
return `${prefix} ${type.description}`;
|
||||
};
|
||||
|
||||
|
|
|
@ -122,6 +122,10 @@ export function getUiSettingDefaults() {
|
|||
value: 100,
|
||||
description: 'Never show more than this many bars in date histograms, scale values if needed',
|
||||
},
|
||||
'visualize:enableLabs': {
|
||||
value: true,
|
||||
description: 'Enable lab visualizations in Visualize.'
|
||||
},
|
||||
'visualization:tileMap:maxPrecision': {
|
||||
value: 7,
|
||||
description: 'The maximum geoHash precision displayed on tile maps: 7 is high, 10 is very high, ' +
|
||||
|
|
|
@ -5,6 +5,7 @@ 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
|
||||
import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
|
||||
|
@ -21,7 +22,8 @@ export default function MetricsVisProvider(Private) {
|
|||
description: 'Build time-series using a visual pipeline interface',
|
||||
category: CATEGORY.TIME,
|
||||
image,
|
||||
isExperimental: true,
|
||||
stage: 'experimental',
|
||||
feedbackMessage: defaultFeedbackMessage,
|
||||
visConfig: {
|
||||
defaults: {
|
||||
id: '61ca57f0-469d-11e7-af02-69e470af7417',
|
||||
|
|
|
@ -262,10 +262,16 @@ module.directive('savedObjectFinder', function ($location, $injector, kbnUrl, Pr
|
|||
if (prevSearch === filter) return;
|
||||
|
||||
prevSearch = filter;
|
||||
|
||||
const isLabsEnabled = config.get('visualize:enableLabs');
|
||||
self.service.find(filter)
|
||||
.then(function (hits) {
|
||||
// ensure that we don't display old results
|
||||
// as we can't really cancel requests
|
||||
|
||||
hits.hits = hits.hits.filter((hit) => (isLabsEnabled || !hit.type.stage === 'lab'));
|
||||
hits.total = hits.hits.length;
|
||||
|
||||
// ensure that we don't display old results
|
||||
// as we can't really cancel requests
|
||||
if (currentFilter === filter) {
|
||||
self.hitCount = hits.total;
|
||||
self.hits = _.sortBy(hits.hits, 'title');
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
ng-blur="finder.hitBlur($event)"
|
||||
ng-click="finder.preventClick($event)">
|
||||
<span aria-hidden="true" class="finder-type fa" ng-if="hit.icon" ng-class="hit.icon"></span>
|
||||
<div class="kuiIcon fa-flask ng-scope" ng-if="hit.type.shouldMarkAsExperimentalInUI()"></div>
|
||||
<span>{{hit.title}}</span>
|
||||
<p ng-if="hit.description" ng-bind="hit.description"></p>
|
||||
</a>
|
||||
|
|
2
src/ui/public/vis/default_feedback_message.js
Normal file
2
src/ui/public/vis/default_feedback_message.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
export const defaultFeedbackMessage = `Have feedback? Please create an issue in
|
||||
<a href="https://github.com/elastic/kibana/issues/new" rel="noopener noreferrer" target="_blank">GitHub</a>.`;
|
|
@ -3,6 +3,7 @@ import _ from 'lodash';
|
|||
|
||||
export function VisTypeProvider() {
|
||||
class VisType {
|
||||
|
||||
constructor(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
|
@ -32,13 +33,20 @@ export function VisTypeProvider() {
|
|||
showIndexSelection: true,
|
||||
hierarchicalData: false // we should get rid of this i guess ?
|
||||
},
|
||||
isExperimental: false
|
||||
stage: 'production',
|
||||
feedbackMessage: ''
|
||||
};
|
||||
|
||||
_.defaultsDeep(this, opts, _defaults);
|
||||
|
||||
this.requiresSearch = !(this.requestHandler === 'none');
|
||||
}
|
||||
|
||||
shouldMarkAsExperimentalInUI() {
|
||||
//we are not making a distinction in the UI if a plugin is experimental and/or labs.
|
||||
//we just want to indicate it is special. the current flask icon is sufficient for that.
|
||||
return this.stage === 'experimental' || this.stage === 'lab';
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(VisType.prototype, 'schemas', {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue