Add landing page with table to Visualize app.

- Update Visualize wizard UI.
- kbnTopNav directive supports no-menu-extensions attribute for excluding extensions, so you can hide the menu items entirely.
This commit is contained in:
CJ Cenizal 2016-12-21 16:08:06 -08:00
parent 5778951aa4
commit 5a5eecbfc6
9 changed files with 310 additions and 61 deletions

View file

@ -33,7 +33,7 @@
</a>
</div>
<!-- Allow searching if there is no linked Saved Searc. -->
<!-- Allow searching if there is no linked Saved Search. -->
<form
ng-if="vis.type.requiresSearch && !$state.linked"
name="queryInput"

View file

@ -1,5 +1,6 @@
import 'plugins/kibana/visualize/styles/main.less';
import 'plugins/kibana/visualize/editor/editor';
import 'plugins/kibana/visualize/landing/landing';
import 'plugins/kibana/visualize/wizard/wizard';
import 'plugins/kibana/visualize/editor/add_bucket_agg';
import 'plugins/kibana/visualize/editor/agg';
@ -24,7 +25,7 @@ uiRoutes
requireDefaultIndex: true
})
.when('/visualize', {
redirectTo: '/visualize/step/1'
redirectTo: '/visualize/landing'
});
// preloading

View file

@ -0,0 +1,11 @@
import routes from 'ui/routes';
import modules from 'ui/modules';
import visualizeLandingTemplate from './visualize_landing.html';
import { VisualizeLandingController } from './visualize_landing';
routes
.when('/visualize/landing', {
template: visualizeLandingTemplate,
controller: VisualizeLandingController,
controllerAs: 'landingController',
});

View file

@ -0,0 +1,147 @@
<!-- Local nav. -->
<kbn-top-nav name="visualize" no-menu-extensions>
<!-- Transcluded elements. -->
<div data-transclude-slots>
<!-- Title. -->
<div
data-transclude-slot="topLeftCorner"
class="kuiLocalTitle"
>
Visualize
</div>
</div>
</kbn-top-nav>
<div class="kuiViewContent kuiViewContent--constrainedWidth">
<!-- ControlledTable -->
<div class="kuiViewContentItem kuiControlledTable kuiVerticalRhythm">
<!-- ToolBar -->
<div class="kuiToolBar">
<div class="kuiToolBarSearch">
<div class="kuiToolBarSearchBox">
<div class="kuiToolBarSearchBox__icon kuiIcon fa-search"></div>
<input
class="kuiToolBarSearchBox__input"
type="text"
placeholder="Search..."
aria-label="Filter"
ng-model="landingController.filter"
>
</div>
</div>
<div class="kuiToolBarSection">
<!-- Bulk delete button -->
<button
class="kuiButton kuiButton--danger kuiButton--iconText"
confirm-click="landingController.deleteSelectedItems()"
confirmation="Are you sure you want to delete the selected visualizations? This action is irreversible!"
aria-label="Delete selected objects"
ng-hide="landingController.getSelectedItemsCount() === 0"
>
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-trash"></span>
Delete
</button>
<!-- Create visualization button -->
<a
class="kuiButton kuiButton--primary kuiButton--iconText"
href="#/visualize/step/1"
aria-label="Create new visualization"
ng-hide="landingController.getSelectedItemsCount() > 0"
>
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-plus"></span>
Create visualization
</a>
</div>
<div class="kuiToolBarSection">
<!-- We need an empty section for the buttons to be positioned consistently. -->
</div>
</div>
<!-- NoResults -->
<div class="kuiPanel kuiPanel--centered" ng-if="!landingController.items.length">
<div class="kuiNoResults">
No visualizations matched your search.
</div>
</div>
<!-- Table -->
<table class="kuiTable" ng-if="landingController.items.length">
<thead>
<tr>
<th class="kuiTableHeaderCell kuiTableHeaderCell--checkBox">
<input
type="checkbox"
class="kuiCheckBox"
ng-checked="landingController.areAllItemsChecked()"
ng-click="landingController.toggleAll()"
>
</th>
<th class="kuiTableHeaderCell">
Visualization
</th>
<th class="kuiTableHeaderCell">
Type
</th>
</tr>
</thead>
<tbody>
<tr
ng-repeat="item in landingController.items track by item.id | orderBy:'title'"
class="kuiTableRow"
>
<td class="kuiTableRowCell kuiTableRowCell--checkBox">
<input
type="checkbox"
class="kuiCheckBox"
ng-click="landingController.toggleItem(item)"
ng-checked="landingController.isItemChecked(item)"
>
</td>
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
<a class="kuiLink" ng-href="{{ item.url }}">
{{ item.title }}
</a>
<button
class="kuiMicroButton kuiTableRowHoverReveal"
ng-click="landingController.edit(item)"
aria-label="Edit"
tooltip="Edit object"
>
<span
aria-hidden="true"
class="kuiIcon fa-code"
></span>
</button>
</div>
</td>
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
{{ item.type.title }}
</div>
</td>
</tr>
</tbody>
</table>
<!-- ToolBarFooter -->
<div class="kuiToolBarFooter">
<div class="kuiToolBarFooterSection">
<div class="kuiToolBarText" ng-hide="landingController.getSelectedItemsCount() === 0">
{{ landingController.getSelectedItemsCount() }} selected
</div>
</div>
<div class="kuiToolBarFooterSection">
<!-- We need an empty section for the buttons to be positioned consistently. -->
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,83 @@
import SavedObjectRegistryProvider from 'ui/saved_objects/saved_object_registry';
export function VisualizeLandingController(
$scope,
kbnUrl,
Notifier,
Private
) {
// TODO: Extract this into an external service.
const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName;
const visualizationService = services.visualizations;
const notify = new Notifier({ location: 'Visualize' });
let selectedItems = [];
const fetchObjects = () => {
visualizationService.find(this.filter)
.then(result => {
this.items = result.hits;
});
};
this.items = [];
this.filter = '';
this.toggleAll = function toggleAll() {
if (this.areAllItemsChecked()) {
selectedItems = [];
} else {
selectedItems = this.items.slice(0);
}
};
this.toggleItem = function toggleItem(item) {
if (this.isItemChecked(item)) {
const index = selectedItems.indexOf(item);
selectedItems.splice(index, 1);
} else {
selectedItems.push(item);
}
};
this.isItemChecked = function isItemChecked(item) {
return selectedItems.indexOf(item) !== -1;
};
this.areAllItemsChecked = function areAllItemsChecked() {
return this.getSelectedItemsCount() === this.items.length;
};
this.getSelectedItemsCount = function getSelectedItemsCount() {
return selectedItems.length;
};
this.deleteSelectedItems = function deleteSelectedItems() {
const selectedIds = selectedItems.map(item => item.id);
visualizationService.delete(selectedIds)
.then(fetchObjects)
.then(() => {
selectedItems = [];
})
.catch(error => notify.error(error));
};
this.open = function open(item) {
kbnUrl.change(item.url.substr(1));
};
this.edit = function edit(item) {
const params = {
// TODO: Get this value from somewhere instead of hardcodign it.
service: 'savedVisualizations',
id: item.id
};
kbnUrl.change('/management/kibana/objects/{{ service }}/{{ id }}', params);
};
$scope.$watch(() => this.filter, () => {
fetchObjects();
});
}

View file

@ -2,56 +2,48 @@
@import (reference) "~ui/styles/bootstrap/list-group";
@import (reference) "~ui/styles/list-group-menu";
/**
* 1. Push down the breadcrumbs a bit.
*/
.vis-wizard {
margin: 0;
padding: 12px 0 0; // 1
.wizard-sub-title {
margin-top: 0px;
margin-bottom: 8px;
padding: 0px 5px;
}
.wizard-sub-title {
margin-top: 0px;
margin-bottom: 8px;
padding: 0px 5px;
.wizard-type {
flex: 1;
// TODO: When we migrate off Bootstrap, we can eliminate these "mixins".
.list-group-item();
.list-group-menu .list-group-menu-item();
border: none;
border-radius: 0;
background-color: @kibanaGray6;
}
.wizard-type-heading {
flex: 0 0 200px;
display: flex;
align-items: center;
font-size: 1.2em;
}
.wizard-type {
flex: 1;
.wizard-type-heading-icon {
flex: 0 0 auto;
margin-right: @padding-base-horizontal;
font-size: 1.5em;
text-align: center;
color: @saved-object-finder-icon-color;
}
// TODO: When we migrate off Bootstrap, we can eliminate these "mixins".
.list-group-item();
.list-group-menu .list-group-menu-item();
.wizard-type-heading-text {
flex: 1 0 auto;
}
border: none;
border-radius: 0;
background-color: @kibanaGray6;
.wizard-type-description {
flex: 1 1 auto;
color: @wizard-vis-type-description-color;
}
.wizard-type-heading {
flex: 0 0 200px;
display: flex;
align-items: center;
font-size: 1.2em;
}
.wizard-type-heading-icon {
flex: 0 0 auto;
margin-right: @padding-base-horizontal;
font-size: 1.5em;
text-align: center;
color: @saved-object-finder-icon-color;
}
.wizard-type-heading-text {
flex: 1 0 auto;
}
.wizard-type-description {
flex: 1 1 auto;
color: @wizard-vis-type-description-color;
}
@media (min-width: @screen-lg) {
.wizard {
padding: 0;

View file

@ -1,10 +1,24 @@
<div class="container-fluid vis-wizard">
<div class="visualizeWizardBreadcrumbs">
<bread-crumbs></bread-crumbs>
<!-- Local nav. -->
<kbn-top-nav name="visualize" no-menu-extensions>
<!-- Transcluded elements. -->
<div data-transclude-slots>
<!-- Breadcrumbs. -->
<bread-crumbs
data-transclude-slot="topLeftCorner"
></bread-crumbs>
</div>
</kbn-top-nav>
<div class="kuiViewContent kuiViewContent--constrainedWidth">
<!-- Header -->
<div class="kuiViewContentItem kuiSubHeader">
<h1 class="kuiTitle">
Select visualization type
</h1>
</div>
<div class="wizard">
<div class="wizard-column">
<h3 class="wizard-sub-title">Create New Visualization</h3>
<div class="wizard-row">
<a
class="wizard-type"
@ -23,13 +37,5 @@
</a>
</div>
</div>
<div class="wizard-column">
<h3 class="wizard-sub-title">Or, Open a Saved Visualization</h3>
<saved-object-finder
title="Saved Visualizations"
type="visualizations"
class="wizard-row"
></saved-object-finder>
</div>
</div>
</div>

View file

@ -1,7 +1,15 @@
<div class="container-fluid vis-wizard">
<div class="visualizeWizardBreadcrumbs">
<bread-crumbs></bread-crumbs>
<!-- Local nav. -->
<kbn-top-nav name="visualize" no-menu-extensions>
<!-- Transcluded elements. -->
<div data-transclude-slots>
<!-- Breadcrumbs. -->
<bread-crumbs
data-transclude-slot="topLeftCorner"
></bread-crumbs>
</div>
</kbn-top-nav>
<div class="kuiViewContent kuiViewContent--constrainedWidth">
<div class="wizard">
<div class="wizard-column wizard-column--small">
<h3 class="wizard-sub-title">From a New Search, Select Index</h3>

View file

@ -99,9 +99,10 @@ module.directive('kbnTopNav', function (Private) {
});
});
const extensions = getNavbarExtensions($attrs.name);
let controls = _.get($scope, $attrs.config, []);
const noMenuExtensions = $attrs.hasOwnProperty('noMenuExtensions');
const extensions = noMenuExtensions ? [] : getNavbarExtensions($attrs.name);
if (controls instanceof KbnTopNavController) {
controls.addItems(extensions);
} else {