From f80a1692e74f5353bb824e6b6428084de8516560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 18 Nov 2016 13:50:29 +0100 Subject: [PATCH 01/97] Add app to display discover entry context --- .../kibana/public/context/api/anchor.js | 26 +++++++ .../kibana/public/context/api/context.js | 41 ++++++++++ .../kibana/public/context/api/utils/fields.js | 17 +++++ .../kibana/public/context/api/utils/ids.js | 8 ++ .../public/context/api/utils/queries.js | 29 ++++++++ .../public/context/api/utils/sorting.js | 35 +++++++++ .../kibana/public/context/app.html | 32 ++++++++ src/core_plugins/kibana/public/context/app.js | 74 +++++++++++++++++++ .../kibana/public/context/index.js | 30 ++++++++ .../kibana/public/discover/styles/main.less | 4 + src/core_plugins/kibana/public/kibana.js | 1 + src/ui/public/doc_table/doc_table.html | 5 +- 12 files changed, 300 insertions(+), 2 deletions(-) create mode 100644 src/core_plugins/kibana/public/context/api/anchor.js create mode 100644 src/core_plugins/kibana/public/context/api/context.js create mode 100644 src/core_plugins/kibana/public/context/api/utils/fields.js create mode 100644 src/core_plugins/kibana/public/context/api/utils/ids.js create mode 100644 src/core_plugins/kibana/public/context/api/utils/queries.js create mode 100644 src/core_plugins/kibana/public/context/api/utils/sorting.js create mode 100644 src/core_plugins/kibana/public/context/app.html create mode 100644 src/core_plugins/kibana/public/context/app.js create mode 100644 src/core_plugins/kibana/public/context/index.js diff --git a/src/core_plugins/kibana/public/context/api/anchor.js b/src/core_plugins/kibana/public/context/api/anchor.js new file mode 100644 index 0000000000000..28ceeedceb431 --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/anchor.js @@ -0,0 +1,26 @@ +import _ from 'lodash'; + +import {addComputedFields} from './utils/fields'; +import {createAnchorQuery} from './utils/queries'; + + +async function fetchAnchor(es, indexPattern, uid, sort) { + const indices = await indexPattern.toIndexList(); + const response = await es.search({ + index: indices, + body: addComputedFields(indexPattern, createAnchorQuery(uid, sort)), + }); + + if (_.get(response, ['hits', 'total'], 0) < 1) { + throw new Error('Failed to load anchor row.'); + } + + return _.assign({}, response.hits.hits[0], { + $$_isAnchor: true, + }); +} + + +export { + fetchAnchor, +}; diff --git a/src/core_plugins/kibana/public/context/api/context.js b/src/core_plugins/kibana/public/context/api/context.js new file mode 100644 index 0000000000000..d2699a502757f --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/context.js @@ -0,0 +1,41 @@ +import _ from 'lodash'; + +import {addComputedFields} from './utils/fields'; +import {getDocumentUid} from './utils/ids'; +import {createSuccessorsQuery} from './utils/queries.js'; +import {reverseQuerySort} from './utils/sorting'; + + +async function fetchContext(es, indexPattern, anchorDocument, sort, size) { + const indices = await indexPattern.toIndexList(); + const anchorUid = getDocumentUid(anchorDocument._type, anchorDocument._id); + const successorsQuery = addComputedFields( + indexPattern, + createSuccessorsQuery(anchorUid, anchorDocument.sort, sort, size) + ); + const predecessorsQuery = reverseQuerySort(successorsQuery); + + const response = await es.msearch({ + body: [ + {index: indices}, + predecessorsQuery, + {index: indices}, + successorsQuery, + ], + }); + + const predecessors = _.get(response, ['responses', 0, 'hits', 'hits'], []); + const successors = _.get(response, ['responses', 1, 'hits', 'hits'], []); + + predecessors.reverse(); + + return { + predecessors, + successors, + }; +} + + +export { + fetchContext, +}; diff --git a/src/core_plugins/kibana/public/context/api/utils/fields.js b/src/core_plugins/kibana/public/context/api/utils/fields.js new file mode 100644 index 0000000000000..b9a78aec3a20b --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/utils/fields.js @@ -0,0 +1,17 @@ +import _ from 'lodash'; + + +function addComputedFields(indexPattern, query) { + const computedFields = indexPattern.getComputedFields(); + + return _.assign({}, query, { + script_fields: computedFields.scriptFields, + docvalue_fields: computedFields.docvalueFields, + stored_fields: computedFields.storedFields, + }); +}; + + +export { + addComputedFields, +}; diff --git a/src/core_plugins/kibana/public/context/api/utils/ids.js b/src/core_plugins/kibana/public/context/api/utils/ids.js new file mode 100644 index 0000000000000..42bd4fac8b511 --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/utils/ids.js @@ -0,0 +1,8 @@ +function getDocumentUid(type, id) { + return `${type}#${id}`; +} + + +export { + getDocumentUid, +}; diff --git a/src/core_plugins/kibana/public/context/api/utils/queries.js b/src/core_plugins/kibana/public/context/api/utils/queries.js new file mode 100644 index 0000000000000..410c2d85e50fc --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/utils/queries.js @@ -0,0 +1,29 @@ +function createAnchorQuery(uid, contextSort) { + return { + _source: true, + query: { + terms: { + _uid: [uid], + }, + }, + sort: [contextSort], + }; +}; + +function createSuccessorsQuery(anchorUid, anchorSortValues, contextSort, size) { + return { + _source: true, + query: { + match_all: {}, + }, + size, + sort: [ contextSort, { _uid: 'asc' } ], + search_after: anchorSortValues.concat([ anchorUid ]), + }; +}; + + +export { + createAnchorQuery, + createSuccessorsQuery, +}; diff --git a/src/core_plugins/kibana/public/context/api/utils/sorting.js b/src/core_plugins/kibana/public/context/api/utils/sorting.js new file mode 100644 index 0000000000000..ac2ce09f622bb --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/utils/sorting.js @@ -0,0 +1,35 @@ +import _ from 'lodash'; + + +function reverseQuerySort(query) { + return _.assign({}, query, { + sort: _.get(query, 'sort', []).map(reverseSortDirective), + }); +} + +function reverseSortDirective(sortDirective) { + if (_.isString(sortDirective)) { + return { + [sortDirective]: (sortDirective === '_score' ? 'asc' : 'desc'), + }; + } else if (_.isObject(sortDirective)) { + return _.mapValues(sortDirective, reverseSortDirection); + } else { + return sortDirective; + } +} + +function reverseSortDirection(sortDirection) { + if (_.isObject(sortDirection)) { + return sortDirection.order = reverseSortDirection(sortDirection.order); + } else { + return (sortDirection === 'asc' ? 'desc' : 'asc'); + } +} + + +export { + reverseQuerySort, + reverseSortDirection, + reverseSortDirective, +}; diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html new file mode 100644 index 0000000000000..ae0d21cba24ce --- /dev/null +++ b/src/core_plugins/kibana/public/context/app.html @@ -0,0 +1,32 @@ +
+ + +
+ +
+
+ Context + + +
+
+
+
+ +
+
+
+
+ + +
+
+
+
+
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js new file mode 100644 index 0000000000000..f3e84883544e6 --- /dev/null +++ b/src/core_plugins/kibana/public/context/app.js @@ -0,0 +1,74 @@ +import _ from 'lodash'; + +import uiModules from 'ui/modules'; +import contextAppTemplate from './app.html'; +import {fetchAnchor} from './api/anchor'; +import {fetchContext} from './api/context'; + + +const module = uiModules.get('apps/context', [ + 'kibana', + 'ngRoute', +]); + +module.directive('contextApp', function ContextApp() { + return { + bindToController: true, + controller: ContextAppController, + controllerAs: 'contextApp', + restrict: 'E', + scope: { + anchorUid: '=', + columns: '=', + indexPattern: '=', + size: '=', + sort: '=', + }, + template: contextAppTemplate, + }; +}); + +function ContextAppController($q, es) { + this.anchorRow = null; + this.rows = []; + + this.initialize = () => { + this.actions.reload(); + }; + + this.actions = { + fetchAnchorRow: () => ( + $q.resolve() + .then(() => ( + fetchAnchor(es, this.indexPattern, this.anchorUid, _.zipObject([this.sort])) + )) + .then(anchorRow => ( + this.anchorRow = anchorRow + )) + ), + fetchContextRows: () => ( + $q.resolve(this.anchorRowPromise) + .then(anchorRow => ( + fetchContext(es, this.indexPattern, anchorRow, _.zipObject([this.sort]), this.size) + )) + .then(({predecessors, successors}) => { + this.predecessorRows = predecessors; + this.successorRows = successors; + }) + .then(() => ( + this.rows = [].concat(this.predecessorRows, [this.anchorRow], this.successorRows) + )) + ), + reload: () => { + this.anchorRowPromise = this.actions.fetchAnchorRow(); + this.contextRowsPromise = this.actions.fetchContextRows(); + + $q.all([ + this.anchorRowPromise, + this.contextRowsPromise, + ]); + }, + }; + + this.initialize(); +} diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js new file mode 100644 index 0000000000000..4ab2e84800fba --- /dev/null +++ b/src/core_plugins/kibana/public/context/index.js @@ -0,0 +1,30 @@ +import uiRoutes from 'ui/routes'; +import './app'; +import {getDocumentUid} from './api/utils/ids'; + + +uiRoutes +.when('/context/:indexPattern/:type/:id', { + controller: ContextAppRouteController, + controllerAs: 'contextAppRoute', + resolve: { + indexPattern: function ($route, courier, savedSearches) { + return courier.indexPatterns.get($route.current.params.indexPattern); + }, + }, + template: ( + `` + ), +}); + + +function ContextAppRouteController($routeParams, indexPattern) { + this.anchorUid = getDocumentUid($routeParams.type, $routeParams.id); + this.indexPattern = indexPattern; +} diff --git a/src/core_plugins/kibana/public/discover/styles/main.less b/src/core_plugins/kibana/public/discover/styles/main.less index 22de7213c8e35..2131efbb0b2b4 100644 --- a/src/core_plugins/kibana/public/discover/styles/main.less +++ b/src/core_plugins/kibana/public/discover/styles/main.less @@ -128,6 +128,10 @@ font-size: 9px; } + .discover-table-row--highlight { + font-weight: bold; + } + .shard-failures { color: @discover-shard-failures-color; background-color: @discover-shard-failures-bg !important; diff --git a/src/core_plugins/kibana/public/kibana.js b/src/core_plugins/kibana/public/kibana.js index 5815cc33c2d15..9da35d7ef166e 100644 --- a/src/core_plugins/kibana/public/kibana.js +++ b/src/core_plugins/kibana/public/kibana.js @@ -14,6 +14,7 @@ import 'plugins/kibana/dashboard/index'; import 'plugins/kibana/management/index'; import 'plugins/kibana/doc'; import 'plugins/kibana/dev_tools'; +import 'plugins/kibana/context'; import 'ui/vislib'; import 'ui/agg_response'; import 'ui/agg_types'; diff --git a/src/ui/public/doc_table/doc_table.html b/src/ui/public/doc_table/doc_table.html index 68cd1e0c3d4f2..04a5c87ae13fa 100644 --- a/src/ui/public/doc_table/doc_table.html +++ b/src/ui/public/doc_table/doc_table.html @@ -35,7 +35,8 @@ sorting="sorting" index-pattern="indexPattern" filter="filter" - class="discover-table-row"> + class="discover-table-row" + ng-class="{'discover-table-row--highlight': row['$$_isAnchor']}"> @@ -43,4 +44,4 @@

No results found

-
\ No newline at end of file + From 05aedd8e47c6e70a45278a640168ae351e4b2211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 21 Nov 2016 15:56:20 +0100 Subject: [PATCH 02/97] Extract multi-transclude workaround into directive The `multi-transclude` attribute directive encapsulates the angular 1.4 workaround for multiple transclusion targets pioneered in `kbn-top-nav` for separation of concerns and re-use. There are some differences to the implementation in `kbn-top-nav`: * The directive logic is completely contained inside the link function and therefore won't interfere with other controllers. * The slots are declared as the attribute value. * The transcluded items are appended to the transclusion target's children instead of replacing the whole target. This preserves the attributes present on the target element. --- .../multi_transclude/multi_transclude.js | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/ui/public/multi_transclude/multi_transclude.js diff --git a/src/ui/public/multi_transclude/multi_transclude.js b/src/ui/public/multi_transclude/multi_transclude.js new file mode 100644 index 0000000000000..a47edac86b412 --- /dev/null +++ b/src/ui/public/multi_transclude/multi_transclude.js @@ -0,0 +1,51 @@ +import _ from 'lodash'; +import angular from 'angular'; +import uiModules from 'ui/modules'; + + +const module = uiModules.get('kibana'); + +module.directive('multiTransclude', function MultiTransclude() { + return { + link: linkMultiTransclude, + restrict: 'A', + scope: { + 'multiTransclude': '=', + }, + }; +}); + +function linkMultiTransclude(scope, element, attrs, ctrl, transclude) { + const transclusionSlotNames = scope.multiTransclude; + const transcludes = {}; + + transclude(clone => { + // We expect the transcluded elements to be wrapped in a single div. + const transcludedContentContainer = _.find(clone, item => { + if (item.attributes) { + return _.find(item.attributes, attr => { + return attr.name.indexOf('data-transclude-slots') !== -1; + }); + } + }); + + if (!transcludedContentContainer) { + return; + }; + + const transcludedContent = transcludedContentContainer.children; + _.forEach(transcludedContent, transcludedItem => { + const transclusionSlot = transcludedItem.getAttribute('data-transclude-slot'); + transcludes[transclusionSlot] = transcludedItem; + }); + }); + + // Transclude elements into specified "slots" in the top nav. + transclusionSlotNames.forEach(name => { + const transcludedItem = transcludes[name]; + if (transcludedItem) { + const transclusionSlot = document.querySelector(`[data-transclude-slot="${name}"]`); + angular.element(transclusionSlot).append(transcludedItem); + } + }); +} From 2e422f7e34af378ce375a2e723b961571e8eac23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 21 Nov 2016 16:04:50 +0100 Subject: [PATCH 03/97] Add a generic local-navigation directive The new directive `` creates a navigation that uses the ui-framework block styles of the same name. It utilizes the previously introduced `multi-transclude` directive to provide the following transclusion targets: * `primaryLeft`: The left side of the top row, often used for a title or breadcrumbs. * `primaryRight`: The right side of the top row, often used for menus and time pickers. * `secondary`: The bottom row for search bars or tab navigation. While `kbn-top-nav` combined the concerns of rendering the surrounding DOM as well as menu entries and the timepicker, this directive expects those to be implemented separately and just placed in the navigation bar via aforementioned transclusion slots. A slot for dropdown panels has not yet been added. --- .../local_navigation/local_navigation.html | 7 +++++++ .../public/local_navigation/local_navigation.js | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/ui/public/local_navigation/local_navigation.html create mode 100644 src/ui/public/local_navigation/local_navigation.js diff --git a/src/ui/public/local_navigation/local_navigation.html b/src/ui/public/local_navigation/local_navigation.html new file mode 100644 index 0000000000000..f6fa4da02a75d --- /dev/null +++ b/src/ui/public/local_navigation/local_navigation.html @@ -0,0 +1,7 @@ +
+
+
+
+
+
+
diff --git a/src/ui/public/local_navigation/local_navigation.js b/src/ui/public/local_navigation/local_navigation.js new file mode 100644 index 0000000000000..2d645afd4e814 --- /dev/null +++ b/src/ui/public/local_navigation/local_navigation.js @@ -0,0 +1,17 @@ +import _ from 'lodash'; +import angular from 'angular'; +import uiModules from 'ui/modules'; + +import 'ui/multi_transclude'; +import localNavigationTemplate from './local_navigation.html'; + + +const module = uiModules.get('kibana'); + +module.directive('localNavigation', function LocalNavigation() { + return { + restrict: 'E', + template: localNavigationTemplate, + transclude: true, + }; +}); From 89b31e96d755a9f0879425dcd793699f2922d843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 21 Nov 2016 16:07:48 +0100 Subject: [PATCH 04/97] Use `` for context size controls --- .../kibana/public/context/app.html | 24 +++++++++++++------ src/core_plugins/kibana/public/context/app.js | 12 +++++++--- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index ae0d21cba24ce..c16cb451faa31 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -1,17 +1,27 @@
- +
-
-
- Context - - +
+
Context
+
+
+
+ +
+
+
+
+
+ {{ contextApp.size }} surrounding entries +
+
+
- +
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index f3e84883544e6..aa09a68245b16 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -1,5 +1,6 @@ import _ from 'lodash'; +import 'ui/local_navigation'; import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; import {fetchAnchor} from './api/anchor'; @@ -10,6 +11,7 @@ const module = uiModules.get('apps/context', [ 'kibana', 'ngRoute', ]); +const DEFAULT_SIZE_INCREMENT = 5; module.directive('contextApp', function ContextApp() { return { @@ -42,9 +44,7 @@ function ContextAppController($q, es) { .then(() => ( fetchAnchor(es, this.indexPattern, this.anchorUid, _.zipObject([this.sort])) )) - .then(anchorRow => ( - this.anchorRow = anchorRow - )) + .then(anchorRow => this.anchorRow = anchorRow) ), fetchContextRows: () => ( $q.resolve(this.anchorRowPromise) @@ -59,6 +59,8 @@ function ContextAppController($q, es) { this.rows = [].concat(this.predecessorRows, [this.anchorRow], this.successorRows) )) ), + increaseSize: (value = DEFAULT_SIZE_INCREMENT) => this.actions.setSize(this.size + value), + decreaseSize: (value = DEFAULT_SIZE_INCREMENT) => this.actions.setSize(this.size - value), reload: () => { this.anchorRowPromise = this.actions.fetchAnchorRow(); this.contextRowsPromise = this.actions.fetchContextRows(); @@ -68,6 +70,10 @@ function ContextAppController($q, es) { this.contextRowsPromise, ]); }, + setSize: (size) => { + this.size = size; + return this.actions.fetchContextRows(); + }, }; this.initialize(); From 322701683ed394f67456195ebfc53e29f09ac246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 21 Nov 2016 16:09:34 +0100 Subject: [PATCH 05/97] Wire up the context app state to the url --- .../kibana/public/context/index.js | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js index 4ab2e84800fba..9a2ceba834b94 100644 --- a/src/core_plugins/kibana/public/context/index.js +++ b/src/core_plugins/kibana/public/context/index.js @@ -15,16 +15,30 @@ uiRoutes template: ( `` ), }); -function ContextAppRouteController($routeParams, indexPattern) { +function ContextAppRouteController($routeParams, $scope, AppState, indexPattern) { + this.state = new AppState(createDefaultAppState()); + $scope.$watchGroup([ + 'contextAppRoute.state.columns', + 'contextAppRoute.state.size', + 'contextAppRoute.state.sort', + ], () => this.state.save()); this.anchorUid = getDocumentUid($routeParams.type, $routeParams.id); this.indexPattern = indexPattern; } + +function createDefaultAppState() { + return { + columns: ['_source'], + size: 5, + sort: ['@timestamp', 'desc'], + }; +} From ef78a3c856b6148764670213f1b332f61efeb907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 21 Nov 2016 16:12:38 +0100 Subject: [PATCH 06/97] Add basic link from discover to the context app The doc viewer panel now contains a (hideous) link which displays the context of the entry using the same column configuration. The location and styling is hopefully subject to future improvement --- src/ui/public/doc_table/components/table_row.js | 13 ++++++++++++- .../doc_table/components/table_row/details.html | 5 ++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ui/public/doc_table/components/table_row.js b/src/ui/public/doc_table/components/table_row.js index 530ddb996c3a9..f75ccb8e518eb 100644 --- a/src/ui/public/doc_table/components/table_row.js +++ b/src/ui/public/doc_table/components/table_row.js @@ -1,5 +1,6 @@ import _ from 'lodash'; import $ from 'jquery'; +import rison from 'rison-node'; import 'ui/highlight'; import 'ui/highlight/highlight_tags'; import 'ui/doc_viewer'; @@ -24,7 +25,7 @@ let MIN_LINE_LENGTH = 20; * * ``` */ -module.directive('kbnTableRow', function ($compile) { +module.directive('kbnTableRow', function ($compile, $httpParamSerializer) { let cellTemplate = _.template(noWhiteSpace(require('ui/doc_table/components/table_row/cell.html'))); let truncateByHeightTemplate = _.template(noWhiteSpace(require('ui/partials/truncate_by_height.html'))); @@ -87,6 +88,16 @@ module.directive('kbnTableRow', function ($compile) { createSummaryRow($scope.row, $scope.row._id); }); + $scope.getContextAppHref = () => { + return `#/context/${$scope.indexPattern.id}/${$scope.row._type}/${$scope.row._id}?${ + $httpParamSerializer({ + _a: rison.encode({ + columns: $scope.columns, + }), + }) + }`; + }; + // create a tr element that lists the value for each *column* function createSummaryRow(row) { let indexPattern = $scope.indexPattern; diff --git a/src/ui/public/doc_table/components/table_row/details.html b/src/ui/public/doc_table/components/table_row/details.html index d83d868dbefeb..a5f968e093666 100644 --- a/src/ui/public/doc_table/components/table_row/details.html +++ b/src/ui/public/doc_table/components/table_row/details.html @@ -2,5 +2,8 @@ Link to /{{row._index}}/{{row._type}}/{{row._id | uriescape}} + + View surrounding entries + - \ No newline at end of file + From be355d8d46342990dc922bbd51c3635ef69d9b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 22 Nov 2016 20:14:30 +0100 Subject: [PATCH 07/97] Change context view breadcrumbs The breadcrumbs indicate that the context view is a sub-view of the normal "Discover" view by including a link to it as the first item. --- src/core_plugins/kibana/public/context/app.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index c16cb451faa31..8e56c6a8a1cce 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -4,9 +4,15 @@
-
Context
-
-
+
+ Discover +
+
+ Context of + + in + +
From b2db703119e341758bbbd506e10d7905351228af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 22 Nov 2016 20:16:53 +0100 Subject: [PATCH 08/97] Return promises from the reload and init actions --- src/core_plugins/kibana/public/context/app.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index aa09a68245b16..8b071effe6200 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -33,10 +33,12 @@ module.directive('contextApp', function ContextApp() { function ContextAppController($q, es) { this.anchorRow = null; this.rows = []; + this.initialized = false; - this.initialize = () => { - this.actions.reload(); - }; + this.initialize = () => ( + this.actions.reload() + .then(() => this.initialized = true) + ); this.actions = { fetchAnchorRow: () => ( @@ -65,7 +67,7 @@ function ContextAppController($q, es) { this.anchorRowPromise = this.actions.fetchAnchorRow(); this.contextRowsPromise = this.actions.fetchContextRows(); - $q.all([ + return $q.all([ this.anchorRowPromise, this.contextRowsPromise, ]); From c3d74594a85021569b8d0cd1aa6d5fa279e3eefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 22 Nov 2016 20:19:11 +0100 Subject: [PATCH 09/97] Make the context size display editable --- .../kibana/public/context/app.html | 15 ++++++++--- .../local_navigation/local_navigation.js | 1 + .../local_navigation/local_navigation.less | 27 +++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/ui/public/local_navigation/local_navigation.less diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 8e56c6a8a1cce..710f4355b3a03 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -17,13 +17,20 @@
-
+
-
- {{ contextApp.size }} surrounding entries +
+ +
surrounding entries
-
+
diff --git a/src/ui/public/local_navigation/local_navigation.js b/src/ui/public/local_navigation/local_navigation.js index 2d645afd4e814..8d949f755b45d 100644 --- a/src/ui/public/local_navigation/local_navigation.js +++ b/src/ui/public/local_navigation/local_navigation.js @@ -3,6 +3,7 @@ import angular from 'angular'; import uiModules from 'ui/modules'; import 'ui/multi_transclude'; +import './local_navigation.less'; import localNavigationTemplate from './local_navigation.html'; diff --git a/src/ui/public/local_navigation/local_navigation.less b/src/ui/public/local_navigation/local_navigation.less new file mode 100644 index 0000000000000..9c405233bb5c6 --- /dev/null +++ b/src/ui/public/local_navigation/local_navigation.less @@ -0,0 +1,27 @@ +@import "~ui/styles/variables.less"; + +// This should be moved to the kibana ui framework code as soon as it has been +// integrated into the kibana repo. See elastic/kibana#8867 for the status. +.localMenuItem__input--inPlace { + appearance: textfield; + background: transparent; + border: 0; + border-bottom: 1px dashed; + display: block; + margin-right: 5px; + outline: none; + padding-left: 0; + padding-right: 0; + text-align: center; + width: 2em; + + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + appearance: none; + margin: 0; + } + + &:focus { + background-color: @kibanaGray6; + } +} From 9e5986c0837fd8ad1bfa8751731464ae828598e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 23 Nov 2016 14:44:34 +0100 Subject: [PATCH 10/97] Always sort on the index-pattern's time field To focus on the use case of the app and to rule out numerous edge cases, the context is always interpreted in relation the time field specified in the index pattern. --- src/core_plugins/kibana/public/context/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js index 9a2ceba834b94..3e0001fe5e17c 100644 --- a/src/core_plugins/kibana/public/context/index.js +++ b/src/core_plugins/kibana/public/context/index.js @@ -18,7 +18,7 @@ uiRoutes columns="contextAppRoute.state.columns" index-pattern="contextAppRoute.indexPattern" size="contextAppRoute.state.size" - sort="contextAppRoute.state.sort" + sort="[contextAppRoute.indexPattern.timeFieldName, 'desc']" >` ), }); @@ -29,7 +29,6 @@ function ContextAppRouteController($routeParams, $scope, AppState, indexPattern) $scope.$watchGroup([ 'contextAppRoute.state.columns', 'contextAppRoute.state.size', - 'contextAppRoute.state.sort', ], () => this.state.save()); this.anchorUid = getDocumentUid($routeParams.type, $routeParams.id); this.indexPattern = indexPattern; @@ -39,6 +38,5 @@ function createDefaultAppState() { return { columns: ['_source'], size: 5, - sort: ['@timestamp', 'desc'], }; } From d64f938d82a4c75048e777fc690072b77172ae20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 23 Nov 2016 15:54:36 +0100 Subject: [PATCH 11/97] Improve doc and context link styling in docTable --- .../public/doc_table/components/table_row.js | 1 + .../doc_table/components/table_row.less | 7 ++++++ .../components/table_row/details.html | 22 ++++++++++++++----- 3 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 src/ui/public/doc_table/components/table_row.less diff --git a/src/ui/public/doc_table/components/table_row.js b/src/ui/public/doc_table/components/table_row.js index f75ccb8e518eb..9ae6fd6c5d46d 100644 --- a/src/ui/public/doc_table/components/table_row.js +++ b/src/ui/public/doc_table/components/table_row.js @@ -6,6 +6,7 @@ import 'ui/highlight/highlight_tags'; import 'ui/doc_viewer'; import 'ui/filters/trust_as_html'; import 'ui/filters/short_dots'; +import './table_row.less'; import noWhiteSpace from 'ui/utils/no_white_space'; import openRowHtml from 'ui/doc_table/components/table_row/open.html'; import detailsHtml from 'ui/doc_table/components/table_row/details.html'; diff --git a/src/ui/public/doc_table/components/table_row.less b/src/ui/public/doc_table/components/table_row.less new file mode 100644 index 0000000000000..9d25883fc0c79 --- /dev/null +++ b/src/ui/public/doc_table/components/table_row.less @@ -0,0 +1,7 @@ +.documentTableRow__actions { + float: right; +} + +.documentTableRow__action { + text-decoration: none !important; +} diff --git a/src/ui/public/doc_table/components/table_row/details.html b/src/ui/public/doc_table/components/table_row/details.html index a5f968e093666..7c08fd246bc1e 100644 --- a/src/ui/public/doc_table/components/table_row/details.html +++ b/src/ui/public/doc_table/components/table_row/details.html @@ -1,9 +1,19 @@ - - Link to /{{row._index}}/{{row._type}}/{{row._id | uriescape}} - - - View surrounding entries - + From 2acc12319f4d312c536db70f751f6417406cd3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 23 Nov 2016 15:58:08 +0100 Subject: [PATCH 12/97] Fix font-awesome class name typo --- src/core_plugins/kibana/public/context/app.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 710f4355b3a03..4d5ac3699c5d2 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -30,7 +30,7 @@
surrounding entries
-
+
From 4991bd80711ecb35f721e457b25068291db25045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 23 Nov 2016 16:11:22 +0100 Subject: [PATCH 13/97] Hide context link for non-time-based index patterns The link is hidden for index patterns that do not have a time field defined, because the entry context view is designed to allow viewing the surrounding entries with respect to a time ordering. --- src/ui/public/doc_table/components/table_row/details.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/public/doc_table/components/table_row/details.html b/src/ui/public/doc_table/components/table_row/details.html index 7c08fd246bc1e..589adccffdb0c 100644 --- a/src/ui/public/doc_table/components/table_row/details.html +++ b/src/ui/public/doc_table/components/table_row/details.html @@ -3,6 +3,7 @@ View surrounding entries From e10f458d9da8f2e148398a731bee064824cb11f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 5 Dec 2016 15:57:49 +0100 Subject: [PATCH 14/97] Add setting to configure the default context size The new setting `context:defaultSize` allows for customization of the initial number of rows displayed when viewing an entry's context. --- docs/management/advanced-options.asciidoc | 1 + src/core_plugins/kibana/public/context/index.js | 8 ++++---- src/ui/settings/defaults.js | 6 +++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 42fee52f231c9..4ab562405fdc4 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -74,3 +74,4 @@ Markdown. `notifications:lifetime:error`:: Specifies the duration in milliseconds for error notification displays. The default value is 300000. Set this field to `Infinity` to disable error notifications. `notifications:lifetime:warning`:: Specifies the duration in milliseconds for warning notification displays. The default value is 10000. Set this field to `Infinity` to disable warning notifications. `notifications:lifetime:info`:: Specifies the duration in milliseconds for information notification displays. The default value is 5000. Set this field to `Infinity` to disable information notifications. +`context:defaultSize`:: Specifies the initial number of surrounding entries to display in the context view. The default value is 5. diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js index 3e0001fe5e17c..3067394dae519 100644 --- a/src/core_plugins/kibana/public/context/index.js +++ b/src/core_plugins/kibana/public/context/index.js @@ -24,8 +24,8 @@ uiRoutes }); -function ContextAppRouteController($routeParams, $scope, AppState, indexPattern) { - this.state = new AppState(createDefaultAppState()); +function ContextAppRouteController($routeParams, $scope, AppState, config, indexPattern) { + this.state = new AppState(createDefaultAppState(config)); $scope.$watchGroup([ 'contextAppRoute.state.columns', 'contextAppRoute.state.size', @@ -34,9 +34,9 @@ function ContextAppRouteController($routeParams, $scope, AppState, indexPattern) this.indexPattern = indexPattern; } -function createDefaultAppState() { +function createDefaultAppState(config) { return { columns: ['_source'], - size: 5, + size: parseInt(config.get('context:defaultSize'), 10), }; } diff --git a/src/ui/settings/defaults.js b/src/ui/settings/defaults.js index 1a6845989281a..0e76a7e77ffbf 100644 --- a/src/ui/settings/defaults.js +++ b/src/ui/settings/defaults.js @@ -301,6 +301,10 @@ export default function defaultSettingsProvider() { description: 'The URL can sometimes grow to be too large for some browsers to ' + 'handle. To counter-act this we are testing if storing parts of the URL in ' + 'sessions storage could help. Please let us know how it goes!' - } + }, + 'context:defaultSize': { + value: 5, + description: 'The number of surrounding entries to show in the context view', + }, }; }; From 123d6b033972b862712e3ec53ae6b4f097c40875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 5 Dec 2016 16:01:06 +0100 Subject: [PATCH 15/97] Add setting to configure the context size step The new setting `context:step` allows for customization of the step size the context size is incremented/decremented by when using the buttons in the context view. --- docs/management/advanced-options.asciidoc | 1 + src/core_plugins/kibana/public/context/app.js | 9 +++++---- src/ui/settings/defaults.js | 4 ++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 4ab562405fdc4..0f709247a45c7 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -75,3 +75,4 @@ Markdown. `notifications:lifetime:warning`:: Specifies the duration in milliseconds for warning notification displays. The default value is 10000. Set this field to `Infinity` to disable warning notifications. `notifications:lifetime:info`:: Specifies the duration in milliseconds for information notification displays. The default value is 5000. Set this field to `Infinity` to disable information notifications. `context:defaultSize`:: Specifies the initial number of surrounding entries to display in the context view. The default value is 5. +`context:step`:: Specifies the number to increment or decrement the context size by when using the buttons in the context view. The default value is 5. diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 8b071effe6200..a56f270485aa5 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -11,7 +11,6 @@ const module = uiModules.get('apps/context', [ 'kibana', 'ngRoute', ]); -const DEFAULT_SIZE_INCREMENT = 5; module.directive('contextApp', function ContextApp() { return { @@ -30,7 +29,9 @@ module.directive('contextApp', function ContextApp() { }; }); -function ContextAppController($q, es) { +function ContextAppController($q, config, es) { + const defaultSizeStep = parseInt(config.get('context:step'), 10); + this.anchorRow = null; this.rows = []; this.initialized = false; @@ -61,8 +62,8 @@ function ContextAppController($q, es) { this.rows = [].concat(this.predecessorRows, [this.anchorRow], this.successorRows) )) ), - increaseSize: (value = DEFAULT_SIZE_INCREMENT) => this.actions.setSize(this.size + value), - decreaseSize: (value = DEFAULT_SIZE_INCREMENT) => this.actions.setSize(this.size - value), + increaseSize: (value = defaultSizeStep) => this.actions.setSize(this.size + value), + decreaseSize: (value = defaultSizeStep) => this.actions.setSize(this.size - value), reload: () => { this.anchorRowPromise = this.actions.fetchAnchorRow(); this.contextRowsPromise = this.actions.fetchContextRows(); diff --git a/src/ui/settings/defaults.js b/src/ui/settings/defaults.js index 0e76a7e77ffbf..cf6aae5f79e8a 100644 --- a/src/ui/settings/defaults.js +++ b/src/ui/settings/defaults.js @@ -306,5 +306,9 @@ export default function defaultSettingsProvider() { value: 5, description: 'The number of surrounding entries to show in the context view', }, + 'context:step': { + value: 5, + description: 'The step size to increment or decrement the context size by', + }, }; }; From dbaed4fe3d88d4a9874f842fa37be2211555904e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 5 Dec 2016 16:04:48 +0100 Subject: [PATCH 16/97] Enforce a minimal context size of 0 --- src/core_plugins/kibana/public/context/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index a56f270485aa5..634982999b007 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -11,6 +11,7 @@ const module = uiModules.get('apps/context', [ 'kibana', 'ngRoute', ]); +const MIN_CONTEXT_SIZE = 0; module.directive('contextApp', function ContextApp() { return { @@ -74,7 +75,7 @@ function ContextAppController($q, config, es) { ]); }, setSize: (size) => { - this.size = size; + this.size = Math.max(size, MIN_CONTEXT_SIZE); return this.actions.fetchContextRows(); }, }; From 840278f8b67989230b69be524e3bf296ad060414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 6 Dec 2016 16:21:51 +0100 Subject: [PATCH 17/97] Reimplement the local nav without multi-transclusion The local navigation directive does not require the multi-transclusion workaround anymore. Instead, it comes with a set of child directives (`localNavigationRow` and `localNavigationRowSection`) that can be used to compose a complete local navigation declaratively. --- .../kibana/public/context/app.html | 63 ++++++++++--------- src/core_plugins/kibana/public/context/app.js | 2 +- src/ui/public/local_navigation/index.js | 3 + .../local_navigation/local_navigation.html | 7 --- .../local_navigation/local_navigation.js | 7 +-- .../local_navigation/local_navigation_row.js | 18 ++++++ .../local_navigation_row_section.js | 15 +++++ 7 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 src/ui/public/local_navigation/index.js delete mode 100644 src/ui/public/local_navigation/local_navigation.html create mode 100644 src/ui/public/local_navigation/local_navigation_row.js create mode 100644 src/ui/public/local_navigation/local_navigation_row_section.js diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 4d5ac3699c5d2..aab859ac605e1 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -1,39 +1,42 @@
- -
+ -
-
- Discover + +
+
+ Discover +
+
+ Context of + + in + +
-
- Context of - - in - -
-
+ -
-
-
-
-
- -
surrounding entries
+ +
+
+
+
+
+ +
surrounding entries
+
+
+
+
-
-
-
-
-
+ +
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 634982999b007..795b72d09e2d3 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -1,6 +1,6 @@ import _ from 'lodash'; -import 'ui/local_navigation'; +import 'ui/local_navigation/index'; import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; import {fetchAnchor} from './api/anchor'; diff --git a/src/ui/public/local_navigation/index.js b/src/ui/public/local_navigation/index.js new file mode 100644 index 0000000000000..a44b7f096d1f8 --- /dev/null +++ b/src/ui/public/local_navigation/index.js @@ -0,0 +1,3 @@ +import './local_navigation'; +import './local_navigation_row'; +import './local_navigation_row_section'; diff --git a/src/ui/public/local_navigation/local_navigation.html b/src/ui/public/local_navigation/local_navigation.html deleted file mode 100644 index f6fa4da02a75d..0000000000000 --- a/src/ui/public/local_navigation/local_navigation.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
-
-
-
-
-
diff --git a/src/ui/public/local_navigation/local_navigation.js b/src/ui/public/local_navigation/local_navigation.js index 8d949f755b45d..e683c8f6cadfb 100644 --- a/src/ui/public/local_navigation/local_navigation.js +++ b/src/ui/public/local_navigation/local_navigation.js @@ -1,18 +1,15 @@ -import _ from 'lodash'; -import angular from 'angular'; import uiModules from 'ui/modules'; -import 'ui/multi_transclude'; import './local_navigation.less'; -import localNavigationTemplate from './local_navigation.html'; const module = uiModules.get('kibana'); module.directive('localNavigation', function LocalNavigation() { return { + replace: true, restrict: 'E', - template: localNavigationTemplate, + template: '
', transclude: true, }; }); diff --git a/src/ui/public/local_navigation/local_navigation_row.js b/src/ui/public/local_navigation/local_navigation_row.js new file mode 100644 index 0000000000000..4a05e15ead370 --- /dev/null +++ b/src/ui/public/local_navigation/local_navigation_row.js @@ -0,0 +1,18 @@ +import uiModules from 'ui/modules'; + +import './local_navigation.less'; + + +const module = uiModules.get('kibana'); + +module.directive('localNavigationRow', function LocalNavigationRow() { + return { + replace: true, + restrict: 'E', + scope: { + isSecondary: '=?', + }, + template: `
`, + transclude: true, + }; +}); diff --git a/src/ui/public/local_navigation/local_navigation_row_section.js b/src/ui/public/local_navigation/local_navigation_row_section.js new file mode 100644 index 0000000000000..dc6016ecb12e8 --- /dev/null +++ b/src/ui/public/local_navigation/local_navigation_row_section.js @@ -0,0 +1,15 @@ +import uiModules from 'ui/modules'; + +import './local_navigation.less'; + + +const module = uiModules.get('kibana'); + +module.directive('localNavigationRowSection', function LocalNavigationRowSection() { + return { + replace: true, + restrict: 'E', + template: '
', + transclude: true, + }; +}); From 4730c77e28fb762be52cfdd0af14d3d319237226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 6 Dec 2016 17:35:37 +0100 Subject: [PATCH 18/97] Add visual indicator for first initialization --- src/core_plugins/kibana/public/context/app.html | 5 ++++- src/core_plugins/kibana/public/context/app.js | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index aab859ac605e1..910fd66768008 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -42,7 +42,10 @@
-
+
+

Loading...

+
+
( this.actions.reload() - .then(() => this.initialized = true) + .then(() => this.isInitialized = true) ); this.actions = { From a9c96aa1aa556a2e853a64bd9e2d8151f8315fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 13 Dec 2016 19:55:58 +0100 Subject: [PATCH 19/97] Improve loading ui and behaviour, refactor state The internal context app state is now managed using a unidirectional data flow. On the ui side, controlling the size of the context now happens via buttons at the top and bottom of the doc table or via text inputs in the local navigation bar. Both now allow for independent manipulation of the number of predecessors and successors. The two halves of the table are also loaded independently and have independent loading status indicators. --- .../kibana/public/context/api/anchor.js | 5 +- .../kibana/public/context/api/context.js | 50 +++-- .../kibana/public/context/api/utils/fields.js | 9 +- .../public/context/api/utils/sorting.js | 5 +- .../kibana/public/context/app.html | 54 ++++-- src/core_plugins/kibana/public/context/app.js | 123 +++++++----- .../kibana/public/context/dispatch.js | 107 +++++++++++ .../kibana/public/context/index.js | 2 + .../kibana/public/context/query.js | 181 ++++++++++++++++++ .../kibana/public/context/query_parameters.js | 71 +++++++ .../local_navigation/local_navigation.less | 1 + 11 files changed, 519 insertions(+), 89 deletions(-) create mode 100644 src/core_plugins/kibana/public/context/dispatch.js create mode 100644 src/core_plugins/kibana/public/context/query.js create mode 100644 src/core_plugins/kibana/public/context/query_parameters.js diff --git a/src/core_plugins/kibana/public/context/api/anchor.js b/src/core_plugins/kibana/public/context/api/anchor.js index 28ceeedceb431..b29fa1f62ed7e 100644 --- a/src/core_plugins/kibana/public/context/api/anchor.js +++ b/src/core_plugins/kibana/public/context/api/anchor.js @@ -15,9 +15,10 @@ async function fetchAnchor(es, indexPattern, uid, sort) { throw new Error('Failed to load anchor row.'); } - return _.assign({}, response.hits.hits[0], { + return { + ...response.hits.hits[0], $$_isAnchor: true, - }); + }; } diff --git a/src/core_plugins/kibana/public/context/api/context.js b/src/core_plugins/kibana/public/context/api/context.js index d2699a502757f..2557732e6edac 100644 --- a/src/core_plugins/kibana/public/context/api/context.js +++ b/src/core_plugins/kibana/public/context/api/context.js @@ -6,36 +6,48 @@ import {createSuccessorsQuery} from './utils/queries.js'; import {reverseQuerySort} from './utils/sorting'; -async function fetchContext(es, indexPattern, anchorDocument, sort, size) { - const indices = await indexPattern.toIndexList(); +async function fetchSuccessors(es, indexPattern, anchorDocument, sort, size) { + const successorsQuery = prepareQuery(indexPattern, anchorDocument, sort, size); + const results = await performQuery(es, indexPattern, successorsQuery); + return results; +} + +async function fetchPredecessors(es, indexPattern, anchorDocument, sort, size) { + const successorsQuery = prepareQuery(indexPattern, anchorDocument, sort, size); + const predecessorsQuery = reverseQuerySort(successorsQuery); + const reversedResults = await performQuery(es, indexPattern, predecessorsQuery); + const results = reverseResults(reversedResults); + return results; +} + + +function prepareQuery(indexPattern, anchorDocument, sort, size) { const anchorUid = getDocumentUid(anchorDocument._type, anchorDocument._id); const successorsQuery = addComputedFields( indexPattern, createSuccessorsQuery(anchorUid, anchorDocument.sort, sort, size) ); - const predecessorsQuery = reverseQuerySort(successorsQuery); + return successorsQuery; +} - const response = await es.msearch({ - body: [ - {index: indices}, - predecessorsQuery, - {index: indices}, - successorsQuery, - ], - }); +async function performQuery(es, indexPattern, query) { + const indices = await indexPattern.toIndexList(); - const predecessors = _.get(response, ['responses', 0, 'hits', 'hits'], []); - const successors = _.get(response, ['responses', 1, 'hits', 'hits'], []); + const response = await es.search({ + index: indices, + body: query, + }); - predecessors.reverse(); + return _.get(response, ['hits', 'hits'], []); +} - return { - predecessors, - successors, - }; +function reverseResults(results) { + results.reverse(); + return results; } export { - fetchContext, + fetchPredecessors, + fetchSuccessors, }; diff --git a/src/core_plugins/kibana/public/context/api/utils/fields.js b/src/core_plugins/kibana/public/context/api/utils/fields.js index b9a78aec3a20b..4375ab6b3ef7b 100644 --- a/src/core_plugins/kibana/public/context/api/utils/fields.js +++ b/src/core_plugins/kibana/public/context/api/utils/fields.js @@ -1,15 +1,16 @@ import _ from 'lodash'; -function addComputedFields(indexPattern, query) { +const addComputedFields = _.curry(function addComputedFields(indexPattern, query) { const computedFields = indexPattern.getComputedFields(); - return _.assign({}, query, { + return { + ...query, script_fields: computedFields.scriptFields, docvalue_fields: computedFields.docvalueFields, stored_fields: computedFields.storedFields, - }); -}; + }; +}); export { diff --git a/src/core_plugins/kibana/public/context/api/utils/sorting.js b/src/core_plugins/kibana/public/context/api/utils/sorting.js index ac2ce09f622bb..cea31382abe24 100644 --- a/src/core_plugins/kibana/public/context/api/utils/sorting.js +++ b/src/core_plugins/kibana/public/context/api/utils/sorting.js @@ -2,9 +2,10 @@ import _ from 'lodash'; function reverseQuerySort(query) { - return _.assign({}, query, { + return { + ...query, sort: _.get(query, 'sort', []).map(reverseSortDirective), - }); + }; } function reverseSortDirective(sortDirective) { diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 910fd66768008..e2c1d32303e02 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -9,30 +9,30 @@
Context of - + in - +
-
-
-
-
surrounding entries
-
-
-
+
newer and
+ +
older entries
@@ -42,19 +42,37 @@
-
+ +

Loading...

-
+
+
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 132a03a923dfc..666846c027c1b 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -3,15 +3,33 @@ import _ from 'lodash'; import 'ui/local_navigation/index'; import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; -import {fetchAnchor} from './api/anchor'; -import {fetchContext} from './api/context'; +import { + bindActionCreators, + createDispatchProvider, + createPipeline, + createScopedUpdater, +} from './dispatch'; +import { + QueryParameterActionCreatorsProvider, + selectPredecessorCount, + selectSuccessorCount, + updateQueryParameters, +} from './query_parameters'; +import { + QueryActionCreatorsProvider, + selectIsLoadingAnchorRow, + selectIsLoadingPredecessorRows, + selectIsLoadingSuccessorRows, + selectRows, + updateLoadingStatus, + updateQueryResults +} from './query'; const module = uiModules.get('apps/context', [ 'kibana', 'ngRoute', ]); -const MIN_CONTEXT_SIZE = 0; module.directive('contextApp', function ContextApp() { return { @@ -30,55 +48,72 @@ module.directive('contextApp', function ContextApp() { }; }); -function ContextAppController($q, config, es) { - const defaultSizeStep = parseInt(config.get('context:step'), 10); +function ContextAppController(Private) { + const createDispatch = Private(createDispatchProvider); + const queryParameterActionCreators = Private(QueryParameterActionCreatorsProvider); + const queryActionCreators = Private(QueryActionCreatorsProvider); - this.anchorRow = null; - this.rows = []; - this.isInitialized = false; + this.state = createInitialState( + this.anchorUid, + this.columns, + this.indexPattern, + this.size, + this.sort, + ); + + this.update = createPipeline( + createScopedUpdater('queryParameters', updateQueryParameters), + createScopedUpdater('rows', updateQueryResults), + createScopedUpdater('loadingStatus', updateLoadingStatus), + ); - this.initialize = () => ( - this.actions.reload() - .then(() => this.isInitialized = true) + this.dispatch = createDispatch( + () => this.state, + (state) => this.state = state, + this.update, ); - this.actions = { - fetchAnchorRow: () => ( - $q.resolve() - .then(() => ( - fetchAnchor(es, this.indexPattern, this.anchorUid, _.zipObject([this.sort])) - )) - .then(anchorRow => this.anchorRow = anchorRow) + this.actions = bindActionCreators({ + ...queryParameterActionCreators, + ...queryActionCreators, + }, this.dispatch); + + this.selectors = { + rows: () => selectRows(this.state), + isLoadingAnchorRow: () => selectIsLoadingAnchorRow(this.state), + isLoadingPredecessorRows: () => selectIsLoadingPredecessorRows(this.state), + isLoadingSuccessorRows: () => selectIsLoadingSuccessorRows(this.state), + predecessorCount: (value) => ( + value ? this.actions.fetchGivenPredecessorRows(value) : selectPredecessorCount(this.state) ), - fetchContextRows: () => ( - $q.resolve(this.anchorRowPromise) - .then(anchorRow => ( - fetchContext(es, this.indexPattern, anchorRow, _.zipObject([this.sort]), this.size) - )) - .then(({predecessors, successors}) => { - this.predecessorRows = predecessors; - this.successorRows = successors; - }) - .then(() => ( - this.rows = [].concat(this.predecessorRows, [this.anchorRow], this.successorRows) - )) + successorCount: (value) => ( + value ? this.actions.fetchGivenSuccessorRows(value) : selectSuccessorCount(this.state) ), - increaseSize: (value = defaultSizeStep) => this.actions.setSize(this.size + value), - decreaseSize: (value = defaultSizeStep) => this.actions.setSize(this.size - value), - reload: () => { - this.anchorRowPromise = this.actions.fetchAnchorRow(); - this.contextRowsPromise = this.actions.fetchContextRows(); + }; - return $q.all([ - this.anchorRowPromise, - this.contextRowsPromise, - ]); + this.actions.fetchAllRows(); +} + +function createInitialState(anchorUid, columns, indexPattern, size, sort) { + return { + queryParameters: { + anchorUid, + columns, + indexPattern, + predecessorCount: size, + successorCount: size, + sort, }, - setSize: (size) => { - this.size = Math.max(size, MIN_CONTEXT_SIZE); - return this.actions.fetchContextRows(); + rows: { + anchor: null, + predecessors: [], + successors: [], }, + loadingStatus: { + anchor: 'uninitialized', + predecessors: 'uninitialized', + successors: 'uninitialized', + }, + isInitialized: false, }; - - this.initialize(); } diff --git a/src/core_plugins/kibana/public/context/dispatch.js b/src/core_plugins/kibana/public/context/dispatch.js new file mode 100644 index 0000000000000..11a3d2bc78ec1 --- /dev/null +++ b/src/core_plugins/kibana/public/context/dispatch.js @@ -0,0 +1,107 @@ +import _ from 'lodash'; + + +function createDispatchProvider($q) { + return function createDispatch(getState, setState, update) { + const dispatchWithProcessors = _.compose( + createThunkProcessor, + createPromiseProcessor, + )(dispatch); + + return dispatchWithProcessors; + + function dispatch(action) { + const nextState = update(getState(), action); + setState(nextState); + + return action; + } + + function createThunkProcessor(next) { + return (action) => { + if (_.isFunction(action)) { + return action(dispatchWithProcessors, getState); + } + + return next(action); + }; + } + + function createPromiseProcessor(next) { + return (action) => { + if (_.isFunction(_.get(action, ['payload', 'then']))) { + next({ type: started(action.type) }); + + return $q.resolve(action.payload) + .then( + (result) => dispatchWithProcessors({ type: action.type, payload: result }), + (error) => dispatchWithProcessors({ type: failed(action.type), payload: error, error: true }), + ); + } + + return next(action); + }; + } + + }; +} + +function createPipeline(...updaters) { + return (state, action) => ( + _.flow(...updaters.map((updater) => (state) => updater(state, action)))(state) + ); +} + +function bindActionCreators(actionCreators, dispatch) { + return _.mapValues(actionCreators, (actionCreator) => ( + _.flow(actionCreator, dispatch) + )); +} + +function createScopedUpdater(key, updater) { + return (state, action) => { + const subState = state[key]; + const newSubState = updater(subState, action); + + if (subState !== newSubState) { + return { ...state, [key] : newSubState }; + } + + return state; + }; +} + +function started(type) { + return `${type}:started`; +} + +function failed(type) { + return `${type}:failed`; +} + +function createSelector(dependencies, selector) { + let previousDependencies = []; + let previousResult = null; + + return (...args) => { + const currentDependencies = dependencies.map((dependency) => dependency(...args)); + + if (_.any(_.zipWith(previousDependencies, currentDependencies, (first, second) => first !== second))) { + previousDependencies = currentDependencies; + previousResult = selector(...previousDependencies); + } + + return previousResult; + }; +} + + +export { + bindActionCreators, + createDispatchProvider, + createPipeline, + createScopedUpdater, + createSelector, + failed, + started, +}; diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js index 3067394dae519..86950db552505 100644 --- a/src/core_plugins/kibana/public/context/index.js +++ b/src/core_plugins/kibana/public/context/index.js @@ -26,6 +26,8 @@ uiRoutes function ContextAppRouteController($routeParams, $scope, AppState, config, indexPattern) { this.state = new AppState(createDefaultAppState(config)); + this.state.save(true); + $scope.$watchGroup([ 'contextAppRoute.state.columns', 'contextAppRoute.state.size', diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query.js new file mode 100644 index 0000000000000..7cd7daf6c4acb --- /dev/null +++ b/src/core_plugins/kibana/public/context/query.js @@ -0,0 +1,181 @@ +import _ from 'lodash'; + +import {fetchAnchor} from './api/anchor'; +import {fetchPredecessors, fetchSuccessors} from './api/context'; +import {createSelector, started} from './dispatch'; +import { + QueryParameterActionCreatorsProvider, + selectPredecessorCount, + selectSuccessorCount, +} from './query_parameters'; + + +function QueryActionCreatorsProvider($q, es, Private) { + const { + increasePredecessorCount, + increaseSuccessorCount, + setPredecessorCount, + setSuccessorCount, + } = Private(QueryParameterActionCreatorsProvider); + + return { + fetchAllRows, + fetchAnchorRow, + fetchGivenPredecessorRows, + fetchGivenSuccessorRows, + fetchMorePredecessorRows, + fetchMoreSuccessorRows, + fetchPredecessorRows, + fetchSuccessorRows, + }; + + function fetchAllRows() { + return (dispatch) => ({ + type: 'context/fetch_all_rows', + payload: dispatch(fetchAnchorRow()) + .then(() => $q.all([ + dispatch(fetchPredecessorRows()), + dispatch(fetchSuccessorRows()), + ])), + }); + } + + function fetchAnchorRow() { + return (dispatch, getState) => { + const { queryParameters: { indexPattern, anchorUid, sort } } = getState(); + + return dispatch({ + type: 'context/fetch_anchor_row', + payload: fetchAnchor(es, indexPattern, anchorUid, _.zipObject([sort])), + }); + }; + } + + function fetchPredecessorRows() { + return (dispatch, getState) => { + const state = getState(); + const { + queryParameters: { indexPattern, sort }, + rows: { anchor }, + } = state; + const predecessorCount = selectPredecessorCount(state); + + return dispatch({ + type: 'context/fetch_predecessor_rows', + payload: fetchPredecessors(es, indexPattern, anchor, _.zipObject([sort]), predecessorCount), + }); + }; + } + + function fetchSuccessorRows() { + return (dispatch, getState) => { + const state = getState(); + const { + queryParameters: { indexPattern, sort }, + rows: { anchor }, + } = state; + const successorCount = selectSuccessorCount(state); + + return dispatch({ + type: 'context/fetch_successor_rows', + payload: fetchSuccessors(es, indexPattern, anchor, _.zipObject([sort]), successorCount), + }); + }; + } + + function fetchGivenPredecessorRows(count) { + return (dispatch) => { + dispatch(setPredecessorCount(count)); + return dispatch(fetchPredecessorRows()); + }; + } + + function fetchGivenSuccessorRows(count) { + return (dispatch) => { + dispatch(setSuccessorCount(count)); + return dispatch(fetchSuccessorRows()); + }; + } + function fetchMorePredecessorRows() { + return (dispatch) => { + dispatch(increasePredecessorCount()); + return dispatch(fetchPredecessorRows()); + }; + } + + function fetchMoreSuccessorRows() { + return (dispatch) => { + dispatch(increaseSuccessorCount()); + return dispatch(fetchSuccessorRows()); + }; + } +} + +const selectIsLoadingAnchorRow = createSelector([ + (state) => state.loadingStatus.anchor, +], (anchorLoadingStatus) => ( + _.includes(['loading', 'uninitialized'], anchorLoadingStatus) +)); + +const selectIsLoadingPredecessorRows = createSelector([ + (state) => state.loadingStatus.predecessors, +], (predecessorsLoadingStatus) => ( + _.includes(['loading', 'uninitialized'], predecessorsLoadingStatus) +)); + +const selectIsLoadingSuccessorRows = createSelector([ + (state) => state.loadingStatus.successors, +], (successorsLoadingStatus) => ( + _.includes(['loading', 'uninitialized'], successorsLoadingStatus) +)); + +const selectRows = createSelector([ + (state) => state.rows.predecessors, + (state) => state.rows.anchor, + (state) => state.rows.successors, +], (predecessorRows, anchorRow, successorRows) => ( + [...predecessorRows, ...(anchorRow ? [anchorRow] : []), ...successorRows] +)); + +function updateQueryResults(state, action) { + switch (action.type) { + case 'context/fetch_anchor_row': + return { ...state, anchor: action.payload }; + case 'context/fetch_predecessor_rows': + return { ...state, predecessors: action.payload }; + case 'context/fetch_successor_rows': + return { ...state, successors: action.payload }; + default: + return state; + } +} + +function updateLoadingStatus(state, action) { + switch (action.type) { + case started('context/fetch_anchor_row'): + return { ...state, anchor: 'loading' }; + case 'context/fetch_anchor_row': + return { ...state, anchor: 'loaded' }; + case started('context/fetch_predecessor_rows'): + return { ...state, predecessors: 'loading' }; + case 'context/fetch_predecessor_rows': + return { ...state, predecessors: 'loaded' }; + case started('context/fetch_successor_rows'): + return { ...state, successors: 'loading' }; + case 'context/fetch_successor_rows': + return { ...state, successors: 'loaded' }; + default: + return state; + } +} + + +export { + QueryActionCreatorsProvider, + selectIsLoadingAnchorRow, + selectIsLoadingPredecessorRows, + selectIsLoadingSuccessorRows, + selectRows, + updateLoadingStatus, + updateQueryResults, +}; diff --git a/src/core_plugins/kibana/public/context/query_parameters.js b/src/core_plugins/kibana/public/context/query_parameters.js new file mode 100644 index 0000000000000..ffc7680fadc03 --- /dev/null +++ b/src/core_plugins/kibana/public/context/query_parameters.js @@ -0,0 +1,71 @@ +const MIN_CONTEXT_SIZE = 0; + +function QueryParameterActionCreatorsProvider(config) { + const defaultSizeStep = parseInt(config.get('context:step'), 10); + + return { + increasePredecessorCount, + increaseSuccessorCount, + setPredecessorCount, + setSuccessorCount, + }; + + function increasePredecessorCount(value = defaultSizeStep) { + return { + type: 'context/increase_predecessor_count', + payload: value, + }; + } + + function increaseSuccessorCount(value = defaultSizeStep) { + return { + type: 'context/increase_successor_count', + payload: value, + }; + } + + function setPredecessorCount(value) { + return { + type: 'context/set_predecessor_count', + payload: value, + }; + } + + function setSuccessorCount(value) { + return { + type: 'context/set_successor_count', + payload: value, + }; + } +} + +function selectPredecessorCount(state) { + return state.queryParameters.predecessorCount; +} + +function selectSuccessorCount(state) { + return state.queryParameters.successorCount; +} + +function updateQueryParameters(state, action) { + switch (action.type) { + case 'context/increase_predecessor_count': + return { ...state, predecessorCount: Math.max(MIN_CONTEXT_SIZE, state.predecessorCount + action.payload) }; + case 'context/increase_successor_count': + return { ...state, successorCount: Math.max(MIN_CONTEXT_SIZE, state.successorCount + action.payload) }; + case 'context/set_predecessor_count': + return { ...state, predecessorCount: Math.max(MIN_CONTEXT_SIZE, action.payload) }; + case 'context/set_successor_count': + return { ...state, successorCount: Math.max(MIN_CONTEXT_SIZE, action.payload) }; + default: + return state; + } +} + + +export { + QueryParameterActionCreatorsProvider, + selectPredecessorCount, + selectSuccessorCount, + updateQueryParameters, +}; diff --git a/src/ui/public/local_navigation/local_navigation.less b/src/ui/public/local_navigation/local_navigation.less index 9c405233bb5c6..8bfca4ae01bc9 100644 --- a/src/ui/public/local_navigation/local_navigation.less +++ b/src/ui/public/local_navigation/local_navigation.less @@ -8,6 +8,7 @@ border: 0; border-bottom: 1px dashed; display: block; + margin-left: 5px; margin-right: 5px; outline: none; padding-left: 0; From bb1b056cc372662954b2d51d3a46b015026c2c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 13 Dec 2016 20:16:34 +0100 Subject: [PATCH 20/97] Remove plus icons from "load more" buttons --- src/core_plugins/kibana/public/context/app.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index e2c1d32303e02..02e33bcf45649 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -48,8 +48,8 @@ ng-if="!contextApp.selectors.isLoadingAnchorRow()" ng-disabled="contextApp.selectors.isLoadingPredecessorRows()" > - - Load newer entries + + Load newer entries

Loading...

@@ -70,8 +70,8 @@

Loading...

ng-if="!contextApp.selectors.isLoadingAnchorRow()" ng-disabled="contextApp.selectors.isLoadingSuccessorRows()" > - - Load older entries + + Load older entries
From 366962a4c2177e24fa8a02799be7f5b0e4f81b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 15 Dec 2016 15:54:59 +0100 Subject: [PATCH 21/97] Fix AppState synchronization --- src/core_plugins/kibana/public/context/app.js | 49 ++++++++++++------- .../kibana/public/context/index.js | 9 ++-- .../kibana/public/context/query.js | 9 ++++ .../kibana/public/context/query_parameters.js | 27 ++++++++++ 4 files changed, 74 insertions(+), 20 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 666846c027c1b..5be01d35c1441 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -11,7 +11,9 @@ import { } from './dispatch'; import { QueryParameterActionCreatorsProvider, + QUERY_PARAMETER_KEYS, selectPredecessorCount, + selectQueryParameters, selectSuccessorCount, updateQueryParameters, } from './query_parameters'; @@ -41,25 +43,20 @@ module.directive('contextApp', function ContextApp() { anchorUid: '=', columns: '=', indexPattern: '=', - size: '=', + predecessorCount: '=', + successorCount: '=', sort: '=', }, template: contextAppTemplate, }; }); -function ContextAppController(Private) { +function ContextAppController($scope, Private) { const createDispatch = Private(createDispatchProvider); const queryParameterActionCreators = Private(QueryParameterActionCreatorsProvider); const queryActionCreators = Private(QueryActionCreatorsProvider); - this.state = createInitialState( - this.anchorUid, - this.columns, - this.indexPattern, - this.size, - this.sort, - ); + this.state = createInitialState(); this.update = createPipeline( createScopedUpdater('queryParameters', updateQueryParameters), @@ -91,18 +88,36 @@ function ContextAppController(Private) { ), }; - this.actions.fetchAllRows(); + /** + * Sync query parameters to arguments + */ + $scope.$watchCollection( + () => _.pick(this, QUERY_PARAMETER_KEYS), + (newValues) => { + // break the watch cycle + if (!_.isEqual(newValues, selectQueryParameters(this.state))) { + this.dispatch(queryActionCreators.fetchAllRowsWithNewQueryParameters(newValues)); + } + }, + ); + + $scope.$watchCollection( + () => selectQueryParameters(this.state), + (newValues) => { + _.assign(this, newValues); + }, + ); } -function createInitialState(anchorUid, columns, indexPattern, size, sort) { +function createInitialState() { return { queryParameters: { - anchorUid, - columns, - indexPattern, - predecessorCount: size, - successorCount: size, - sort, + anchorUid: null, + columns: [], + indexPattern: null, + predecessorCount: 0, + successorCount: 0, + sort: [], }, rows: { anchor: null, diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js index 86950db552505..614b9a0aa6280 100644 --- a/src/core_plugins/kibana/public/context/index.js +++ b/src/core_plugins/kibana/public/context/index.js @@ -17,7 +17,8 @@ uiRoutes anchor-uid="contextAppRoute.anchorUid" columns="contextAppRoute.state.columns" index-pattern="contextAppRoute.indexPattern" - size="contextAppRoute.state.size" + predecessor-count="contextAppRoute.state.predecessorCount" + successor-count="contextAppRoute.state.successorCount" sort="[contextAppRoute.indexPattern.timeFieldName, 'desc']" >` ), @@ -30,7 +31,8 @@ function ContextAppRouteController($routeParams, $scope, AppState, config, index $scope.$watchGroup([ 'contextAppRoute.state.columns', - 'contextAppRoute.state.size', + 'contextAppRoute.state.predecessorCount', + 'contextAppRoute.state.successorCount', ], () => this.state.save()); this.anchorUid = getDocumentUid($routeParams.type, $routeParams.id); this.indexPattern = indexPattern; @@ -39,6 +41,7 @@ function ContextAppRouteController($routeParams, $scope, AppState, config, index function createDefaultAppState(config) { return { columns: ['_source'], - size: parseInt(config.get('context:defaultSize'), 10), + predecessorCount: parseInt(config.get('context:defaultSize'), 10), + successorCount: parseInt(config.get('context:defaultSize'), 10), }; } diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query.js index 7cd7daf6c4acb..4fd930c18ee47 100644 --- a/src/core_plugins/kibana/public/context/query.js +++ b/src/core_plugins/kibana/public/context/query.js @@ -15,11 +15,13 @@ function QueryActionCreatorsProvider($q, es, Private) { increasePredecessorCount, increaseSuccessorCount, setPredecessorCount, + setQueryParameters, setSuccessorCount, } = Private(QueryParameterActionCreatorsProvider); return { fetchAllRows, + fetchAllRowsWithNewQueryParameters, fetchAnchorRow, fetchGivenPredecessorRows, fetchGivenSuccessorRows, @@ -40,6 +42,13 @@ function QueryActionCreatorsProvider($q, es, Private) { }); } + function fetchAllRowsWithNewQueryParameters(queryParameters) { + return (dispatch) => { + dispatch(setQueryParameters(queryParameters)); + return dispatch(fetchAllRows()); + }; + } + function fetchAnchorRow() { return (dispatch, getState) => { const { queryParameters: { indexPattern, anchorUid, sort } } = getState(); diff --git a/src/core_plugins/kibana/public/context/query_parameters.js b/src/core_plugins/kibana/public/context/query_parameters.js index ffc7680fadc03..b86eb5272c9c7 100644 --- a/src/core_plugins/kibana/public/context/query_parameters.js +++ b/src/core_plugins/kibana/public/context/query_parameters.js @@ -1,4 +1,15 @@ +import _ from 'lodash'; + + const MIN_CONTEXT_SIZE = 0; +const QUERY_PARAMETER_KEYS = [ + 'anchorUid', + 'columns', + 'indexPattern', + 'predecessorCount', + 'successorCount', + 'sort', +]; function QueryParameterActionCreatorsProvider(config) { const defaultSizeStep = parseInt(config.get('context:step'), 10); @@ -7,6 +18,7 @@ function QueryParameterActionCreatorsProvider(config) { increasePredecessorCount, increaseSuccessorCount, setPredecessorCount, + setQueryParameters, setSuccessorCount, }; @@ -37,12 +49,23 @@ function QueryParameterActionCreatorsProvider(config) { payload: value, }; } + + function setQueryParameters(queryParameters) { + return { + type: 'context/set_query_parameters', + payload: queryParameters, + }; + } } function selectPredecessorCount(state) { return state.queryParameters.predecessorCount; } +function selectQueryParameters(state) { + return state.queryParameters; +} + function selectSuccessorCount(state) { return state.queryParameters.successorCount; } @@ -57,6 +80,8 @@ function updateQueryParameters(state, action) { return { ...state, predecessorCount: Math.max(MIN_CONTEXT_SIZE, action.payload) }; case 'context/set_successor_count': return { ...state, successorCount: Math.max(MIN_CONTEXT_SIZE, action.payload) }; + case 'context/set_query_parameters': + return { ...state, ...(_.pick(action.payload, QUERY_PARAMETER_KEYS)) }; default: return state; } @@ -65,7 +90,9 @@ function updateQueryParameters(state, action) { export { QueryParameterActionCreatorsProvider, + QUERY_PARAMETER_KEYS, selectPredecessorCount, + selectQueryParameters, selectSuccessorCount, updateQueryParameters, }; From 778d3ce189976e61d3b897208ee18865463e2e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 15 Dec 2016 16:00:33 +0100 Subject: [PATCH 22/97] Fix linting errors --- src/core_plugins/kibana/public/context/api/utils/queries.js | 4 ++-- src/ui/public/multi_transclude/multi_transclude.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/utils/queries.js b/src/core_plugins/kibana/public/context/api/utils/queries.js index 410c2d85e50fc..061dfa40b6025 100644 --- a/src/core_plugins/kibana/public/context/api/utils/queries.js +++ b/src/core_plugins/kibana/public/context/api/utils/queries.js @@ -8,7 +8,7 @@ function createAnchorQuery(uid, contextSort) { }, sort: [contextSort], }; -}; +} function createSuccessorsQuery(anchorUid, anchorSortValues, contextSort, size) { return { @@ -20,7 +20,7 @@ function createSuccessorsQuery(anchorUid, anchorSortValues, contextSort, size) { sort: [ contextSort, { _uid: 'asc' } ], search_after: anchorSortValues.concat([ anchorUid ]), }; -}; +} export { diff --git a/src/ui/public/multi_transclude/multi_transclude.js b/src/ui/public/multi_transclude/multi_transclude.js index a47edac86b412..00dbeb8a9e370 100644 --- a/src/ui/public/multi_transclude/multi_transclude.js +++ b/src/ui/public/multi_transclude/multi_transclude.js @@ -31,7 +31,7 @@ function linkMultiTransclude(scope, element, attrs, ctrl, transclude) { if (!transcludedContentContainer) { return; - }; + } const transcludedContent = transcludedContentContainer.children; _.forEach(transcludedContent, transcludedItem => { From 46d84729b570ec591bc2a2557b8339fd1eca6b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 15 Dec 2016 16:10:11 +0100 Subject: [PATCH 23/97] Fix more linting errors --- src/core_plugins/kibana/public/context/api/anchor.js | 4 ++-- src/core_plugins/kibana/public/context/api/context.js | 8 ++++---- src/core_plugins/kibana/public/context/index.js | 2 +- src/core_plugins/kibana/public/context/query.js | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/anchor.js b/src/core_plugins/kibana/public/context/api/anchor.js index b29fa1f62ed7e..e19933a8c0df9 100644 --- a/src/core_plugins/kibana/public/context/api/anchor.js +++ b/src/core_plugins/kibana/public/context/api/anchor.js @@ -1,7 +1,7 @@ import _ from 'lodash'; -import {addComputedFields} from './utils/fields'; -import {createAnchorQuery} from './utils/queries'; +import { addComputedFields } from './utils/fields'; +import { createAnchorQuery } from './utils/queries'; async function fetchAnchor(es, indexPattern, uid, sort) { diff --git a/src/core_plugins/kibana/public/context/api/context.js b/src/core_plugins/kibana/public/context/api/context.js index 2557732e6edac..176e8e5923997 100644 --- a/src/core_plugins/kibana/public/context/api/context.js +++ b/src/core_plugins/kibana/public/context/api/context.js @@ -1,9 +1,9 @@ import _ from 'lodash'; -import {addComputedFields} from './utils/fields'; -import {getDocumentUid} from './utils/ids'; -import {createSuccessorsQuery} from './utils/queries.js'; -import {reverseQuerySort} from './utils/sorting'; +import { addComputedFields } from './utils/fields'; +import { getDocumentUid } from './utils/ids'; +import { createSuccessorsQuery } from './utils/queries.js'; +import { reverseQuerySort } from './utils/sorting'; async function fetchSuccessors(es, indexPattern, anchorDocument, sort, size) { diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js index 614b9a0aa6280..4fca575f58e56 100644 --- a/src/core_plugins/kibana/public/context/index.js +++ b/src/core_plugins/kibana/public/context/index.js @@ -1,6 +1,6 @@ import uiRoutes from 'ui/routes'; import './app'; -import {getDocumentUid} from './api/utils/ids'; +import { getDocumentUid } from './api/utils/ids'; uiRoutes diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query.js index 4fd930c18ee47..eb127de577511 100644 --- a/src/core_plugins/kibana/public/context/query.js +++ b/src/core_plugins/kibana/public/context/query.js @@ -1,8 +1,8 @@ import _ from 'lodash'; -import {fetchAnchor} from './api/anchor'; -import {fetchPredecessors, fetchSuccessors} from './api/context'; -import {createSelector, started} from './dispatch'; +import { fetchAnchor } from './api/anchor'; +import { fetchPredecessors, fetchSuccessors } from './api/context'; +import { createSelector, started } from './dispatch'; import { QueryParameterActionCreatorsProvider, selectPredecessorCount, From 3ae8a5d620a0f39f80c95c2f3aae115371c24c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 16 Dec 2016 13:41:47 +0100 Subject: [PATCH 24/97] Make anchor highlighting more prominent --- .../kibana/public/discover/styles/main.less | 11 ++++++++++- .../public/doc_table/components/table_row/open.html | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/core_plugins/kibana/public/discover/styles/main.less b/src/core_plugins/kibana/public/discover/styles/main.less index 2131efbb0b2b4..f11715f53de9f 100644 --- a/src/core_plugins/kibana/public/discover/styles/main.less +++ b/src/core_plugins/kibana/public/discover/styles/main.less @@ -129,7 +129,16 @@ } .discover-table-row--highlight { - font-weight: bold; + font-weight: 900; + outline: 3px solid @table-border-color; + + td { + border-top: none !important; + } + } + + .discover-table-togglefield--highlight { + background: @table-border-color; } .shard-failures { diff --git a/src/ui/public/doc_table/components/table_row/open.html b/src/ui/public/doc_table/components/table_row/open.html index 5fc93269dce7b..f9e8fa4b3ae6c 100644 --- a/src/ui/public/doc_table/components/table_row/open.html +++ b/src/ui/public/doc_table/components/table_row/open.html @@ -1,6 +1,6 @@ - + - \ No newline at end of file + From ddda2ff3e3adc688001e42c8c5c1f5e4d8aaa546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 16 Dec 2016 13:42:45 +0100 Subject: [PATCH 25/97] Remove obsolete isInitialized from state --- src/core_plugins/kibana/public/context/app.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 5be01d35c1441..2ffcb33705ae7 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -129,6 +129,5 @@ function createInitialState() { predecessors: 'uninitialized', successors: 'uninitialized', }, - isInitialized: false, }; } From 84d0a3dd411899d4faad4e2432b2b0c87c8acd79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 16 Dec 2016 13:46:20 +0100 Subject: [PATCH 26/97] Replace history entry when persisting state --- src/core_plugins/kibana/public/context/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js index 4fca575f58e56..88ecc46869098 100644 --- a/src/core_plugins/kibana/public/context/index.js +++ b/src/core_plugins/kibana/public/context/index.js @@ -33,7 +33,7 @@ function ContextAppRouteController($routeParams, $scope, AppState, config, index 'contextAppRoute.state.columns', 'contextAppRoute.state.predecessorCount', 'contextAppRoute.state.successorCount', - ], () => this.state.save()); + ], () => this.state.save(true)); this.anchorUid = getDocumentUid($routeParams.type, $routeParams.id); this.indexPattern = indexPattern; } From 89f3af2c28db69074285dc4d6984230bef0c83fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 16 Dec 2016 14:11:25 +0100 Subject: [PATCH 27/97] Update to new kui css classes --- .../kibana/public/context/app.html | 20 +++++++++---------- .../local_navigation/local_navigation.js | 2 +- .../local_navigation/local_navigation.less | 2 +- .../local_navigation/local_navigation_row.js | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 02e33bcf45649..768e227df0832 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -3,31 +3,31 @@ -
-
- Discover +
+ -
+
Context of - + in - +
-
-
+
+
newer and
', + template: '
', transclude: true, }; }); diff --git a/src/ui/public/local_navigation/local_navigation.less b/src/ui/public/local_navigation/local_navigation.less index 8bfca4ae01bc9..8c5507cd1fc99 100644 --- a/src/ui/public/local_navigation/local_navigation.less +++ b/src/ui/public/local_navigation/local_navigation.less @@ -2,7 +2,7 @@ // This should be moved to the kibana ui framework code as soon as it has been // integrated into the kibana repo. See elastic/kibana#8867 for the status. -.localMenuItem__input--inPlace { +.kuiLocalMenuItem__input--inPlace { appearance: textfield; background: transparent; border: 0; diff --git a/src/ui/public/local_navigation/local_navigation_row.js b/src/ui/public/local_navigation/local_navigation_row.js index 4a05e15ead370..df1b1e86285fd 100644 --- a/src/ui/public/local_navigation/local_navigation_row.js +++ b/src/ui/public/local_navigation/local_navigation_row.js @@ -12,7 +12,7 @@ module.directive('localNavigationRow', function LocalNavigationRow() { scope: { isSecondary: '=?', }, - template: `
`, + template: `
`, transclude: true, }; }); From cfd057348564b6fce7f969ae7707c572ae018443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 19 Dec 2016 17:12:20 +0100 Subject: [PATCH 28/97] Change the design of the "load more" buttons --- .../kibana/public/context/app.html | 22 ++++++++++++++----- src/core_plugins/kibana/public/context/app.js | 1 + .../kibana/public/context/app.less | 4 ++++ 3 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 src/core_plugins/kibana/public/context/app.less diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 768e227df0832..47c40b3b377b3 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -43,14 +43,19 @@
+
+
+
+

Loading...

@@ -64,14 +69,19 @@

Loading...

>
+
+
+
+
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 2ffcb33705ae7..77fe4684617ff 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -3,6 +3,7 @@ import _ from 'lodash'; import 'ui/local_navigation/index'; import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; +import './app.less'; import { bindActionCreators, createDispatchProvider, diff --git a/src/core_plugins/kibana/public/context/app.less b/src/core_plugins/kibana/public/context/app.less new file mode 100644 index 0000000000000..a1a24e1a86974 --- /dev/null +++ b/src/core_plugins/kibana/public/context/app.less @@ -0,0 +1,4 @@ +.contextLoadingButton { + display: block; + margin: 10px auto; +} From 3ed694159441f59bca67c192ebedce62cda6a3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 19 Dec 2016 18:31:37 +0100 Subject: [PATCH 29/97] Stop abusing the kuiLocalMenu styles for sizePicker --- .../kibana/public/context/app.html | 32 +++++++++---------- .../kibana/public/context/app.less | 23 +++++++++++++ .../local_navigation/local_navigation.less | 28 ---------------- 3 files changed, 38 insertions(+), 45 deletions(-) delete mode 100644 src/ui/public/local_navigation/local_navigation.less diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 47c40b3b377b3..6278285d9a87a 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -17,23 +17,21 @@ -
-
- -
newer and
- -
older entries
-
+
+ +
newer and
+ +
older entries
diff --git a/src/core_plugins/kibana/public/context/app.less b/src/core_plugins/kibana/public/context/app.less index a1a24e1a86974..8a4a768654c56 100644 --- a/src/core_plugins/kibana/public/context/app.less +++ b/src/core_plugins/kibana/public/context/app.less @@ -1,4 +1,27 @@ +@import "~ui/styles/variables.less"; + .contextLoadingButton { display: block; margin: 10px auto; } + +.contextSizePicker { + padding-right: 10px; + height: 100%; +} + + .contextSizePicker__item { + display: inline-block; + } + + .contextSizePicker__sizeInput { + appearance: textfield; + text-align: center; + width: 3em; + + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + appearance: none; + margin: 0; + } + } diff --git a/src/ui/public/local_navigation/local_navigation.less b/src/ui/public/local_navigation/local_navigation.less deleted file mode 100644 index 8c5507cd1fc99..0000000000000 --- a/src/ui/public/local_navigation/local_navigation.less +++ /dev/null @@ -1,28 +0,0 @@ -@import "~ui/styles/variables.less"; - -// This should be moved to the kibana ui framework code as soon as it has been -// integrated into the kibana repo. See elastic/kibana#8867 for the status. -.kuiLocalMenuItem__input--inPlace { - appearance: textfield; - background: transparent; - border: 0; - border-bottom: 1px dashed; - display: block; - margin-left: 5px; - margin-right: 5px; - outline: none; - padding-left: 0; - padding-right: 0; - text-align: center; - width: 2em; - - &::-webkit-outer-spin-button, - &::-webkit-inner-spin-button { - appearance: none; - margin: 0; - } - - &:focus { - background-color: @kibanaGray6; - } -} From d15b35cf537c296a97a718fe543b47b91d2bd3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 19 Dec 2016 18:48:13 +0100 Subject: [PATCH 30/97] Fix imports of removed style file --- src/ui/public/local_navigation/local_navigation.js | 2 -- src/ui/public/local_navigation/local_navigation_row.js | 2 -- src/ui/public/local_navigation/local_navigation_row_section.js | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/ui/public/local_navigation/local_navigation.js b/src/ui/public/local_navigation/local_navigation.js index 6b7cc8dabb3f2..5f3f213619423 100644 --- a/src/ui/public/local_navigation/local_navigation.js +++ b/src/ui/public/local_navigation/local_navigation.js @@ -1,7 +1,5 @@ import uiModules from 'ui/modules'; -import './local_navigation.less'; - const module = uiModules.get('kibana'); diff --git a/src/ui/public/local_navigation/local_navigation_row.js b/src/ui/public/local_navigation/local_navigation_row.js index df1b1e86285fd..cd528006dd076 100644 --- a/src/ui/public/local_navigation/local_navigation_row.js +++ b/src/ui/public/local_navigation/local_navigation_row.js @@ -1,7 +1,5 @@ import uiModules from 'ui/modules'; -import './local_navigation.less'; - const module = uiModules.get('kibana'); diff --git a/src/ui/public/local_navigation/local_navigation_row_section.js b/src/ui/public/local_navigation/local_navigation_row_section.js index dc6016ecb12e8..c34ca49df75d9 100644 --- a/src/ui/public/local_navigation/local_navigation_row_section.js +++ b/src/ui/public/local_navigation/local_navigation_row_section.js @@ -1,7 +1,5 @@ import uiModules from 'ui/modules'; -import './local_navigation.less'; - const module = uiModules.get('kibana'); From c2d22409ea2348d2f1400072e04a0c491a325c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 20 Dec 2016 12:46:02 +0100 Subject: [PATCH 31/97] Use more popular redux terminology --- src/core_plugins/kibana/public/context/app.js | 14 ++++---- .../kibana/public/context/dispatch.js | 32 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 77fe4684617ff..3a7e5ade93e45 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -7,8 +7,8 @@ import './app.less'; import { bindActionCreators, createDispatchProvider, - createPipeline, - createScopedUpdater, + createReducerPipeline, + scopeReducer, } from './dispatch'; import { QueryParameterActionCreatorsProvider, @@ -59,16 +59,16 @@ function ContextAppController($scope, Private) { this.state = createInitialState(); - this.update = createPipeline( - createScopedUpdater('queryParameters', updateQueryParameters), - createScopedUpdater('rows', updateQueryResults), - createScopedUpdater('loadingStatus', updateLoadingStatus), + this.reducer = createReducerPipeline( + scopeReducer('queryParameters', updateQueryParameters), + scopeReducer('rows', updateQueryResults), + scopeReducer('loadingStatus', updateLoadingStatus), ); this.dispatch = createDispatch( () => this.state, (state) => this.state = state, - this.update, + this.reducer, ); this.actions = bindActionCreators({ diff --git a/src/core_plugins/kibana/public/context/dispatch.js b/src/core_plugins/kibana/public/context/dispatch.js index 11a3d2bc78ec1..4d24fbc65e988 100644 --- a/src/core_plugins/kibana/public/context/dispatch.js +++ b/src/core_plugins/kibana/public/context/dispatch.js @@ -2,40 +2,40 @@ import _ from 'lodash'; function createDispatchProvider($q) { - return function createDispatch(getState, setState, update) { - const dispatchWithProcessors = _.compose( - createThunkProcessor, - createPromiseProcessor, + return function createDispatch(getState, setState, reducer) { + const dispatchWithMiddleware = _.compose( + createThunkMiddleware, + createPromiseMiddleware, )(dispatch); - return dispatchWithProcessors; + return dispatchWithMiddleware; function dispatch(action) { - const nextState = update(getState(), action); + const nextState = reducer(getState(), action); setState(nextState); return action; } - function createThunkProcessor(next) { + function createThunkMiddleware(next) { return (action) => { if (_.isFunction(action)) { - return action(dispatchWithProcessors, getState); + return action(dispatchWithMiddleware, getState); } return next(action); }; } - function createPromiseProcessor(next) { + function createPromiseMiddleware(next) { return (action) => { if (_.isFunction(_.get(action, ['payload', 'then']))) { next({ type: started(action.type) }); return $q.resolve(action.payload) .then( - (result) => dispatchWithProcessors({ type: action.type, payload: result }), - (error) => dispatchWithProcessors({ type: failed(action.type), payload: error, error: true }), + (result) => dispatchWithMiddleware({ type: action.type, payload: result }), + (error) => dispatchWithMiddleware({ type: failed(action.type), payload: error, error: true }), ); } @@ -46,7 +46,7 @@ function createDispatchProvider($q) { }; } -function createPipeline(...updaters) { +function createReducerPipeline(...updaters) { return (state, action) => ( _.flow(...updaters.map((updater) => (state) => updater(state, action)))(state) ); @@ -58,10 +58,10 @@ function bindActionCreators(actionCreators, dispatch) { )); } -function createScopedUpdater(key, updater) { +function scopeReducer(key, reducer) { return (state, action) => { const subState = state[key]; - const newSubState = updater(subState, action); + const newSubState = reducer(subState, action); if (subState !== newSubState) { return { ...state, [key] : newSubState }; @@ -99,8 +99,8 @@ function createSelector(dependencies, selector) { export { bindActionCreators, createDispatchProvider, - createPipeline, - createScopedUpdater, + createReducerPipeline, + scopeReducer, createSelector, failed, started, From ed354e0c4f0981bf6ade245b55f6824879af35fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 20 Dec 2016 13:07:49 +0100 Subject: [PATCH 32/97] Split state management into separate files --- src/core_plugins/kibana/public/context/app.js | 8 +- .../kibana/public/context/dispatch.js | 107 ------------------ .../kibana/public/context/query.js | 3 +- .../redux_lite/action_creator_helpers.js | 23 ++++ .../context/redux_lite/create_dispatch.js | 54 +++++++++ .../context/redux_lite/reducer_helpers.js | 27 +++++ .../context/redux_lite/selector_helpers.js | 23 ++++ 7 files changed, 135 insertions(+), 110 deletions(-) delete mode 100644 src/core_plugins/kibana/public/context/dispatch.js create mode 100644 src/core_plugins/kibana/public/context/redux_lite/action_creator_helpers.js create mode 100644 src/core_plugins/kibana/public/context/redux_lite/create_dispatch.js create mode 100644 src/core_plugins/kibana/public/context/redux_lite/reducer_helpers.js create mode 100644 src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 3a7e5ade93e45..c273779a60c27 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -5,11 +5,15 @@ import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; import './app.less'; import { - bindActionCreators, createDispatchProvider, +} from './redux_lite/create_dispatch'; +import { createReducerPipeline, scopeReducer, -} from './dispatch'; +} from './redux_lite/reducer_helpers'; +import { + bindActionCreators, +} from './redux_lite/action_creator_helpers'; import { QueryParameterActionCreatorsProvider, QUERY_PARAMETER_KEYS, diff --git a/src/core_plugins/kibana/public/context/dispatch.js b/src/core_plugins/kibana/public/context/dispatch.js deleted file mode 100644 index 4d24fbc65e988..0000000000000 --- a/src/core_plugins/kibana/public/context/dispatch.js +++ /dev/null @@ -1,107 +0,0 @@ -import _ from 'lodash'; - - -function createDispatchProvider($q) { - return function createDispatch(getState, setState, reducer) { - const dispatchWithMiddleware = _.compose( - createThunkMiddleware, - createPromiseMiddleware, - )(dispatch); - - return dispatchWithMiddleware; - - function dispatch(action) { - const nextState = reducer(getState(), action); - setState(nextState); - - return action; - } - - function createThunkMiddleware(next) { - return (action) => { - if (_.isFunction(action)) { - return action(dispatchWithMiddleware, getState); - } - - return next(action); - }; - } - - function createPromiseMiddleware(next) { - return (action) => { - if (_.isFunction(_.get(action, ['payload', 'then']))) { - next({ type: started(action.type) }); - - return $q.resolve(action.payload) - .then( - (result) => dispatchWithMiddleware({ type: action.type, payload: result }), - (error) => dispatchWithMiddleware({ type: failed(action.type), payload: error, error: true }), - ); - } - - return next(action); - }; - } - - }; -} - -function createReducerPipeline(...updaters) { - return (state, action) => ( - _.flow(...updaters.map((updater) => (state) => updater(state, action)))(state) - ); -} - -function bindActionCreators(actionCreators, dispatch) { - return _.mapValues(actionCreators, (actionCreator) => ( - _.flow(actionCreator, dispatch) - )); -} - -function scopeReducer(key, reducer) { - return (state, action) => { - const subState = state[key]; - const newSubState = reducer(subState, action); - - if (subState !== newSubState) { - return { ...state, [key] : newSubState }; - } - - return state; - }; -} - -function started(type) { - return `${type}:started`; -} - -function failed(type) { - return `${type}:failed`; -} - -function createSelector(dependencies, selector) { - let previousDependencies = []; - let previousResult = null; - - return (...args) => { - const currentDependencies = dependencies.map((dependency) => dependency(...args)); - - if (_.any(_.zipWith(previousDependencies, currentDependencies, (first, second) => first !== second))) { - previousDependencies = currentDependencies; - previousResult = selector(...previousDependencies); - } - - return previousResult; - }; -} - - -export { - bindActionCreators, - createDispatchProvider, - createReducerPipeline, - scopeReducer, - createSelector, - failed, - started, -}; diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query.js index eb127de577511..45bdf39645777 100644 --- a/src/core_plugins/kibana/public/context/query.js +++ b/src/core_plugins/kibana/public/context/query.js @@ -2,7 +2,8 @@ import _ from 'lodash'; import { fetchAnchor } from './api/anchor'; import { fetchPredecessors, fetchSuccessors } from './api/context'; -import { createSelector, started } from './dispatch'; +import { started } from './redux_lite/action_creator_helpers'; +import { createSelector } from './redux_lite/selector_helpers'; import { QueryParameterActionCreatorsProvider, selectPredecessorCount, diff --git a/src/core_plugins/kibana/public/context/redux_lite/action_creator_helpers.js b/src/core_plugins/kibana/public/context/redux_lite/action_creator_helpers.js new file mode 100644 index 0000000000000..174e032a2f7d2 --- /dev/null +++ b/src/core_plugins/kibana/public/context/redux_lite/action_creator_helpers.js @@ -0,0 +1,23 @@ +import _ from 'lodash'; + + +function bindActionCreators(actionCreators, dispatch) { + return _.mapValues(actionCreators, (actionCreator) => ( + _.flow(actionCreator, dispatch) + )); +} + +function failed(type) { + return `${type}:failed`; +} + +function started(type) { + return `${type}:started`; +} + + +export { + bindActionCreators, + failed, + started, +}; diff --git a/src/core_plugins/kibana/public/context/redux_lite/create_dispatch.js b/src/core_plugins/kibana/public/context/redux_lite/create_dispatch.js new file mode 100644 index 0000000000000..ca8a1984d20ec --- /dev/null +++ b/src/core_plugins/kibana/public/context/redux_lite/create_dispatch.js @@ -0,0 +1,54 @@ +import _ from 'lodash'; + +import { failed, started } from './action_creator_helpers'; + + +function createDispatchProvider($q) { + return function createDispatch(getState, setState, reducer) { + const dispatchWithMiddleware = _.compose( + createThunkMiddleware, + createPromiseMiddleware, + )(dispatch); + + return dispatchWithMiddleware; + + function dispatch(action) { + const nextState = reducer(getState(), action); + setState(nextState); + + return action; + } + + function createThunkMiddleware(next) { + return (action) => { + if (_.isFunction(action)) { + return action(dispatchWithMiddleware, getState); + } + + return next(action); + }; + } + + function createPromiseMiddleware(next) { + return (action) => { + if (_.isFunction(_.get(action, ['payload', 'then']))) { + next({ type: started(action.type) }); + + return $q.resolve(action.payload) + .then( + (result) => dispatchWithMiddleware({ type: action.type, payload: result }), + (error) => dispatchWithMiddleware({ type: failed(action.type), payload: error, error: true }), + ); + } + + return next(action); + }; + } + + }; +} + + +export { + createDispatchProvider, +}; diff --git a/src/core_plugins/kibana/public/context/redux_lite/reducer_helpers.js b/src/core_plugins/kibana/public/context/redux_lite/reducer_helpers.js new file mode 100644 index 0000000000000..1885b4bd157a9 --- /dev/null +++ b/src/core_plugins/kibana/public/context/redux_lite/reducer_helpers.js @@ -0,0 +1,27 @@ +import _ from 'lodash'; + + +function createReducerPipeline(...updaters) { + return (state, action) => ( + _.flow(...updaters.map((updater) => (state) => updater(state, action)))(state) + ); +} + +function scopeReducer(key, reducer) { + return (state, action) => { + const subState = state[key]; + const newSubState = reducer(subState, action); + + if (subState !== newSubState) { + return { ...state, [key] : newSubState }; + } + + return state; + }; +} + + +export { + createReducerPipeline, + scopeReducer, +}; diff --git a/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js b/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js new file mode 100644 index 0000000000000..1d7cf6d21f04e --- /dev/null +++ b/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js @@ -0,0 +1,23 @@ +import _ from 'lodash'; + + +function createSelector(dependencies, selector) { + let previousDependencies = []; + let previousResult = null; + + return (...args) => { + const currentDependencies = dependencies.map((dependency) => dependency(...args)); + + if (_.any(_.zipWith(previousDependencies, currentDependencies, (first, second) => first !== second))) { + previousDependencies = currentDependencies; + previousResult = selector(...previousDependencies); + } + + return previousResult; + }; +} + + +export { + createSelector, +}; From 62f734274177a4d0494c080353c7dfb3af496993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 20 Dec 2016 13:40:24 +0100 Subject: [PATCH 33/97] Add helpers to bind selectors to state --- src/core_plugins/kibana/public/context/app.js | 30 ++++++++++++------- .../context/redux_lite/selector_helpers.js | 14 +++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index c273779a60c27..6e3a9c82d00f7 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -14,6 +14,10 @@ import { import { bindActionCreators, } from './redux_lite/action_creator_helpers'; +import { + bindAngularGetterSetters, + bindSelectors, +} from './redux_lite/selector_helpers'; import { QueryParameterActionCreatorsProvider, QUERY_PARAMETER_KEYS, @@ -81,16 +85,22 @@ function ContextAppController($scope, Private) { }, this.dispatch); this.selectors = { - rows: () => selectRows(this.state), - isLoadingAnchorRow: () => selectIsLoadingAnchorRow(this.state), - isLoadingPredecessorRows: () => selectIsLoadingPredecessorRows(this.state), - isLoadingSuccessorRows: () => selectIsLoadingSuccessorRows(this.state), - predecessorCount: (value) => ( - value ? this.actions.fetchGivenPredecessorRows(value) : selectPredecessorCount(this.state) - ), - successorCount: (value) => ( - value ? this.actions.fetchGivenSuccessorRows(value) : selectSuccessorCount(this.state) - ), + ...bindSelectors({ + rows: selectRows, + isLoadingAnchorRow: selectIsLoadingAnchorRow, + isLoadingPredecessorRows: selectIsLoadingPredecessorRows, + isLoadingSuccessorRows: selectIsLoadingSuccessorRows, + }, () => this.state), + ...bindAngularGetterSetters({ + predecessorCount: [ + this.actions.fetchGivenPredecessorRows, + selectPredecessorCount, + ], + successorCount: [ + this.actions.fetchGivenSuccessorRows, + selectSuccessorCount, + ], + }, () => this.state), }; /** diff --git a/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js b/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js index 1d7cf6d21f04e..56b88e1f26152 100644 --- a/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js +++ b/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js @@ -1,6 +1,18 @@ import _ from 'lodash'; +function bindAngularGetterSetters(getterSetters, getState) { + return _.mapValues(getterSetters, ([actionCreator, selector]) => (value) => + _.isUndefined(value) ? selector(getState()) : actionCreator(value) + ); +} + +function bindSelectors(selectors, getState) { + return _.mapValues(selectors, (selector) => () => + selector(getState()) + ); +} + function createSelector(dependencies, selector) { let previousDependencies = []; let previousResult = null; @@ -19,5 +31,7 @@ function createSelector(dependencies, selector) { export { + bindAngularGetterSetters, + bindSelectors, createSelector, }; From 54727303f763cb2cc18958a49741f256f7b2657d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 20 Dec 2016 15:20:36 +0100 Subject: [PATCH 34/97] Factor out the size picker into a separate directive --- .../kibana/public/context/app.html | 22 +++------ src/core_plugins/kibana/public/context/app.js | 45 ++++++------------- .../kibana/public/context/app.less | 23 ---------- .../components/size_picker/size_picker.html | 16 +++++++ .../components/size_picker/size_picker.js | 39 ++++++++++++++++ .../components/size_picker/size_picker.less | 20 +++++++++ .../context/redux_lite/selector_helpers.js | 7 --- 7 files changed, 94 insertions(+), 78 deletions(-) create mode 100644 src/core_plugins/kibana/public/context/components/size_picker/size_picker.html create mode 100644 src/core_plugins/kibana/public/context/components/size_picker/size_picker.js create mode 100644 src/core_plugins/kibana/public/context/components/size_picker/size_picker.less diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 6278285d9a87a..9333775ceec29 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -17,22 +17,12 @@ -
- -
newer and
- -
older entries
-
+
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 6e3a9c82d00f7..6ef17ee20733f 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -4,20 +4,11 @@ import 'ui/local_navigation/index'; import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; import './app.less'; -import { - createDispatchProvider, -} from './redux_lite/create_dispatch'; -import { - createReducerPipeline, - scopeReducer, -} from './redux_lite/reducer_helpers'; -import { - bindActionCreators, -} from './redux_lite/action_creator_helpers'; -import { - bindAngularGetterSetters, - bindSelectors, -} from './redux_lite/selector_helpers'; +import './components/size_picker'; +import { createDispatchProvider } from './redux_lite/create_dispatch'; +import { createReducerPipeline, scopeReducer } from './redux_lite/reducer_helpers'; +import { bindActionCreators } from './redux_lite/action_creator_helpers'; +import { bindSelectors } from './redux_lite/selector_helpers'; import { QueryParameterActionCreatorsProvider, QUERY_PARAMETER_KEYS, @@ -84,24 +75,14 @@ function ContextAppController($scope, Private) { ...queryActionCreators, }, this.dispatch); - this.selectors = { - ...bindSelectors({ - rows: selectRows, - isLoadingAnchorRow: selectIsLoadingAnchorRow, - isLoadingPredecessorRows: selectIsLoadingPredecessorRows, - isLoadingSuccessorRows: selectIsLoadingSuccessorRows, - }, () => this.state), - ...bindAngularGetterSetters({ - predecessorCount: [ - this.actions.fetchGivenPredecessorRows, - selectPredecessorCount, - ], - successorCount: [ - this.actions.fetchGivenSuccessorRows, - selectSuccessorCount, - ], - }, () => this.state), - }; + this.selectors = bindSelectors({ + isLoadingAnchorRow: selectIsLoadingAnchorRow, + isLoadingPredecessorRows: selectIsLoadingPredecessorRows, + isLoadingSuccessorRows: selectIsLoadingSuccessorRows, + predecessorCount: selectPredecessorCount, + rows: selectRows, + successorCount: selectSuccessorCount, + }, () => this.state); /** * Sync query parameters to arguments diff --git a/src/core_plugins/kibana/public/context/app.less b/src/core_plugins/kibana/public/context/app.less index 8a4a768654c56..a1a24e1a86974 100644 --- a/src/core_plugins/kibana/public/context/app.less +++ b/src/core_plugins/kibana/public/context/app.less @@ -1,27 +1,4 @@ -@import "~ui/styles/variables.less"; - .contextLoadingButton { display: block; margin: 10px auto; } - -.contextSizePicker { - padding-right: 10px; - height: 100%; -} - - .contextSizePicker__item { - display: inline-block; - } - - .contextSizePicker__sizeInput { - appearance: textfield; - text-align: center; - width: 3em; - - &::-webkit-outer-spin-button, - &::-webkit-inner-spin-button { - appearance: none; - margin: 0; - } - } diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html new file mode 100644 index 0000000000000..ad5492f24b651 --- /dev/null +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html @@ -0,0 +1,16 @@ +
+ +
newer and
+ +
older entries
+
diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js new file mode 100644 index 0000000000000..008965c1cc0ce --- /dev/null +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js @@ -0,0 +1,39 @@ +import _ from 'lodash'; + +import uiModules from 'ui/modules'; +import contextSizePickerTemplate from './size_picker.html'; +import './size_picker.less'; + + +const module = uiModules.get('apps/context', [ + 'kibana', + 'ngRoute', +]); + +module.directive('contextSizePicker', function ContextSizePicker() { + return { + bindToController: true, + controller: ContextSizePickerController, + controllerAs: 'vm', + replace: true, + restrict: 'E', + scope: { + predecessorCount: '=', + successorCount: '=', + setPredecessorCount: '=', + setSuccessorCount: '=', + }, + template: contextSizePickerTemplate, + }; +}); + +function ContextSizePickerController() { + const vm = this; + + vm.getOrSetPredecessorCount = (value) => ( + _.isUndefined(value) ? vm.predecessorCount : vm.setPredecessorCount(value) + ); + vm.getOrSetSuccessorCount = (value) => ( + _.isUndefined(value) ? vm.successorCount : vm.setSuccessorCount(value) + ); +} diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less new file mode 100644 index 0000000000000..950f93c1e7b56 --- /dev/null +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less @@ -0,0 +1,20 @@ +.contextSizePicker { + padding-right: 10px; + height: 100%; +} + + .contextSizePicker__item { + display: inline-block; + } + + .contextSizePicker__sizeInput { + appearance: textfield; + text-align: center; + width: 3em; + + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + appearance: none; + margin: 0; + } + } diff --git a/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js b/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js index 56b88e1f26152..0a2f163c30817 100644 --- a/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js +++ b/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js @@ -1,12 +1,6 @@ import _ from 'lodash'; -function bindAngularGetterSetters(getterSetters, getState) { - return _.mapValues(getterSetters, ([actionCreator, selector]) => (value) => - _.isUndefined(value) ? selector(getState()) : actionCreator(value) - ); -} - function bindSelectors(selectors, getState) { return _.mapValues(selectors, (selector) => () => selector(getState()) @@ -31,7 +25,6 @@ function createSelector(dependencies, selector) { export { - bindAngularGetterSetters, bindSelectors, createSelector, }; From b7a789d7e50aeaaf5f57406b67261f886f32bd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 20 Dec 2016 15:38:12 +0100 Subject: [PATCH 35/97] Make "load more" buttons full-width --- src/core_plugins/kibana/public/context/app.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.less b/src/core_plugins/kibana/public/context/app.less index a1a24e1a86974..93109c4d2d61e 100644 --- a/src/core_plugins/kibana/public/context/app.less +++ b/src/core_plugins/kibana/public/context/app.less @@ -1,4 +1,4 @@ .contextLoadingButton { - display: block; - margin: 10px auto; + margin: 10px 0; + width: 100%; } From e9cdb7a84f1408fbba3e633dc64e24f96826b100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 2 Jan 2017 15:40:24 +0100 Subject: [PATCH 36/97] Use tabs in the LocalNav --- src/core_plugins/kibana/public/context/app.html | 12 ++++++------ .../public/local_navigation/local_navigation_row.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 9333775ceec29..b92a6fbc84031 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -7,12 +7,7 @@ -
- Context of - - in - -
+
@@ -25,6 +20,11 @@ /> + +
+
+
+
diff --git a/src/ui/public/local_navigation/local_navigation_row.js b/src/ui/public/local_navigation/local_navigation_row.js index cd528006dd076..e13c303535529 100644 --- a/src/ui/public/local_navigation/local_navigation_row.js +++ b/src/ui/public/local_navigation/local_navigation_row.js @@ -10,7 +10,7 @@ module.directive('localNavigationRow', function LocalNavigationRow() { scope: { isSecondary: '=?', }, - template: `
`, + template: `
`, transclude: true, }; }); From 00d1bbe4ab3f3e832dd68e5895a07b3be56cff1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 2 Jan 2017 15:46:32 +0100 Subject: [PATCH 37/97] Use yellow outline for anchor highlighting --- src/core_plugins/kibana/public/discover/styles/main.less | 4 ++-- src/ui/public/styles/variables/for-theme.less | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core_plugins/kibana/public/discover/styles/main.less b/src/core_plugins/kibana/public/discover/styles/main.less index 34e93b83147cc..8c45ed09b7dc7 100644 --- a/src/core_plugins/kibana/public/discover/styles/main.less +++ b/src/core_plugins/kibana/public/discover/styles/main.less @@ -130,7 +130,7 @@ .discover-table-row--highlight { font-weight: 900; - outline: 3px solid @table-border-color; + outline: 3px solid @discover-table-highlight-bg; td { border-top: none !important; @@ -138,7 +138,7 @@ } .discover-table-togglefield--highlight { - background: @table-border-color; + background: @discover-table-highlight-bg; } .shard-failures { diff --git a/src/ui/public/styles/variables/for-theme.less b/src/ui/public/styles/variables/for-theme.less index 5d41f0f7c0439..b43ea260a6ba5 100644 --- a/src/ui/public/styles/variables/for-theme.less +++ b/src/ui/public/styles/variables/for-theme.less @@ -52,6 +52,8 @@ @discover-field-details-bg: @kibanaGray5; @discover-field-details-progress-bar-bg: @gray; +@discover-table-highlight-bg: #faebcc; + // Field name ================================================================== @field-name-color: @kibanaGray3; From d3a37cb8a7aa395d243db8719e773e31b7e51118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 2 Jan 2017 16:36:07 +0100 Subject: [PATCH 38/97] Use kui classes for links in discover detail links --- src/ui/public/doc_table/components/table_row.less | 3 +++ src/ui/public/doc_table/components/table_row/details.html | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ui/public/doc_table/components/table_row.less b/src/ui/public/doc_table/components/table_row.less index 9d25883fc0c79..ea7f4c0760bb5 100644 --- a/src/ui/public/doc_table/components/table_row.less +++ b/src/ui/public/doc_table/components/table_row.less @@ -1,5 +1,8 @@ +@import (reference) "~ui/styles/variables"; + .documentTableRow__actions { float: right; + font-family: @font-family-base; } .documentTableRow__action { diff --git a/src/ui/public/doc_table/components/table_row/details.html b/src/ui/public/doc_table/components/table_row/details.html index 589adccffdb0c..7fe95a7ced1f7 100644 --- a/src/ui/public/doc_table/components/table_row/details.html +++ b/src/ui/public/doc_table/components/table_row/details.html @@ -1,18 +1,18 @@ From 2cbbf622736cc4fcd6489a1c8e4a761ac16460aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 6 Jan 2017 15:48:17 +0100 Subject: [PATCH 39/97] Add implementation notes --- .../kibana/public/context/NOTES.md | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/core_plugins/kibana/public/context/NOTES.md diff --git a/src/core_plugins/kibana/public/context/NOTES.md b/src/core_plugins/kibana/public/context/NOTES.md new file mode 100644 index 0000000000000..49317292e4385 --- /dev/null +++ b/src/core_plugins/kibana/public/context/NOTES.md @@ -0,0 +1,94 @@ +# Discover Context App Implementation Notes + +The implementation of this app is intended to exhibit certain desirable +properties by adhering to a set of *principles*. This document aims to explain +those and the *concepts* employed to achieve that. + + +## Principles + +**Single Source of Truth**: A good user experience depends on the UI displaying +consistent information across the whole page. To achieve this, there should +always be a single source of truth for the application's state. In this +application this is the `ContextAppController::state` object. + +**Unidirectional Data Flow**: While a single state promotes rendering +consistency, it does little to make the state changes easier to reason about. +To avoid having state mutations scattered all over the code, this app +implements a unidirectional data flow architecture. That means that the state +is treated as immutable throughout the application and a new state may only be +derived via the root reducer (see below). + +**Unit-Testability**: Creating unit tests for large parts of the UI code is +made easy by expressing the state management logic mostly as side-effect-free +functions. The only place where side-effects are allowed are action creators +and the reducer middlewares. Due to the nature of AngularJS a certain amount of +impure code must be employed in some cases, e.g. when dealing with the isolate +scope bindings in `ContextAppController`. + +**Loose Coupling**: An attempt was made to couple the parts that make up this +app as loosely as possible. This means using pure functions whenever possible +and isolating the angular directives diligently. To that end, the app has been +implemented as the independent `ContextApp` directive in [app.js](./app.js). It +does not access the Kibana `AppState` directly but communicates only via its +directive properties. The binding of these attributes to the state and thereby +to the route is performed by the `CreateAppRouteController`in +[index.js](./index.js). Similarly, the `SizePicker` directive only communicates +with its parent via the passed properties. + + +## Concepts + +To adhere to the principles mentioned above, this app borrows some concepts +from the redux architecture that forms a ciruclar unidirectional data flow: + +``` + + |* create initial state + v + +->+ + | v + | |* state + | v + | |* selectors calculate derived (memoized) values + | v + | |* angular templates render values + | v + | |* angular dispatches actions in response to user action/system events + | v + | |* middleware processes the action + | v + | |* reducer derives new state from action + | v + +--+ + +``` + +**State**: The state is the single source of truth at +`ContextAppController::state` and may only be replaced by a new state create +via the root reducer. + +**Reducer**: The reducer at `ContextAppController.reducer` can derive a new +state from the previous state and an action. It must be a pure function and can +be composed from sub-reducers using various utilities like +`createReducerPipeline`, that passes the action and the previous sub-reducer's +result to each sub-reducer in order. The reducer is only called by the dispatch +function located at `ContextAppController::dispatch`. + +**Action**: Actions are objets that describe user or system actions in a +declarative manner. Each action is supposed to be passed to +`ContextAppController::dispatch`, that passes them to each of its middlewares +in turn before calling the root reducer with the final action. Usually, actions +are created using helper functions called action creators to ensure they adhere +to the [Flux Standard Action] schema. + +[Flux Standard Action]: https://github.com/acdlite/flux-standard-action + +**Selector**: To decouple the view from the specific state structure, selectors +encapsulate the knowledge about how to retrieve a particular set of values from +the state in a single location. Additionally, selectors can encapsulate the +logic for calculating derived properties from the state, which should be kept +as flat as possible and free of duplicate information. The performance penalty +for performing these calculations at query time is commonly circumvented by +memoizing the selector results as is the case with `createSelector` from +[redux_lite/selector_helpers.js](./redux_lite/selector_helpers.js). From ce2d015f6f26cd2ff5888476b9b490797dac981a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 6 Jan 2017 16:44:14 +0100 Subject: [PATCH 40/97] Remove superfluous directives --- .../kibana/public/context/app.html | 22 ++++---- src/core_plugins/kibana/public/context/app.js | 1 - src/ui/public/local_navigation/index.js | 3 -- .../local_navigation/local_navigation.js | 13 ----- .../local_navigation/local_navigation_row.js | 16 ------ .../local_navigation_row_section.js | 13 ----- .../multi_transclude/multi_transclude.js | 51 ------------------- 7 files changed, 10 insertions(+), 109 deletions(-) delete mode 100644 src/ui/public/local_navigation/index.js delete mode 100644 src/ui/public/local_navigation/local_navigation.js delete mode 100644 src/ui/public/local_navigation/local_navigation_row.js delete mode 100644 src/ui/public/local_navigation/local_navigation_row_section.js delete mode 100644 src/ui/public/multi_transclude/multi_transclude.js diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index b92a6fbc84031..9c0e79a996a27 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -1,31 +1,29 @@
- - - - +
+
+
- - - +
+
- - - +
+
+
- - +
+
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 6ef17ee20733f..719430b8601ee 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -1,6 +1,5 @@ import _ from 'lodash'; -import 'ui/local_navigation/index'; import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; import './app.less'; diff --git a/src/ui/public/local_navigation/index.js b/src/ui/public/local_navigation/index.js deleted file mode 100644 index a44b7f096d1f8..0000000000000 --- a/src/ui/public/local_navigation/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import './local_navigation'; -import './local_navigation_row'; -import './local_navigation_row_section'; diff --git a/src/ui/public/local_navigation/local_navigation.js b/src/ui/public/local_navigation/local_navigation.js deleted file mode 100644 index 5f3f213619423..0000000000000 --- a/src/ui/public/local_navigation/local_navigation.js +++ /dev/null @@ -1,13 +0,0 @@ -import uiModules from 'ui/modules'; - - -const module = uiModules.get('kibana'); - -module.directive('localNavigation', function LocalNavigation() { - return { - replace: true, - restrict: 'E', - template: '
', - transclude: true, - }; -}); diff --git a/src/ui/public/local_navigation/local_navigation_row.js b/src/ui/public/local_navigation/local_navigation_row.js deleted file mode 100644 index e13c303535529..0000000000000 --- a/src/ui/public/local_navigation/local_navigation_row.js +++ /dev/null @@ -1,16 +0,0 @@ -import uiModules from 'ui/modules'; - - -const module = uiModules.get('kibana'); - -module.directive('localNavigationRow', function LocalNavigationRow() { - return { - replace: true, - restrict: 'E', - scope: { - isSecondary: '=?', - }, - template: `
`, - transclude: true, - }; -}); diff --git a/src/ui/public/local_navigation/local_navigation_row_section.js b/src/ui/public/local_navigation/local_navigation_row_section.js deleted file mode 100644 index c34ca49df75d9..0000000000000 --- a/src/ui/public/local_navigation/local_navigation_row_section.js +++ /dev/null @@ -1,13 +0,0 @@ -import uiModules from 'ui/modules'; - - -const module = uiModules.get('kibana'); - -module.directive('localNavigationRowSection', function LocalNavigationRowSection() { - return { - replace: true, - restrict: 'E', - template: '
', - transclude: true, - }; -}); diff --git a/src/ui/public/multi_transclude/multi_transclude.js b/src/ui/public/multi_transclude/multi_transclude.js deleted file mode 100644 index 00dbeb8a9e370..0000000000000 --- a/src/ui/public/multi_transclude/multi_transclude.js +++ /dev/null @@ -1,51 +0,0 @@ -import _ from 'lodash'; -import angular from 'angular'; -import uiModules from 'ui/modules'; - - -const module = uiModules.get('kibana'); - -module.directive('multiTransclude', function MultiTransclude() { - return { - link: linkMultiTransclude, - restrict: 'A', - scope: { - 'multiTransclude': '=', - }, - }; -}); - -function linkMultiTransclude(scope, element, attrs, ctrl, transclude) { - const transclusionSlotNames = scope.multiTransclude; - const transcludes = {}; - - transclude(clone => { - // We expect the transcluded elements to be wrapped in a single div. - const transcludedContentContainer = _.find(clone, item => { - if (item.attributes) { - return _.find(item.attributes, attr => { - return attr.name.indexOf('data-transclude-slots') !== -1; - }); - } - }); - - if (!transcludedContentContainer) { - return; - } - - const transcludedContent = transcludedContentContainer.children; - _.forEach(transcludedContent, transcludedItem => { - const transclusionSlot = transcludedItem.getAttribute('data-transclude-slot'); - transcludes[transclusionSlot] = transcludedItem; - }); - }); - - // Transclude elements into specified "slots" in the top nav. - transclusionSlotNames.forEach(name => { - const transcludedItem = transcludes[name]; - if (transcludedItem) { - const transclusionSlot = document.querySelector(`[data-transclude-slot="${name}"]`); - angular.element(transclusionSlot).append(transcludedItem); - } - }); -} From a7d32acab74a607aa0a51b23bed7c82c5f9da0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 6 Jan 2017 17:17:48 +0100 Subject: [PATCH 41/97] Factor out loading button component --- .../kibana/public/context/app.html | 24 ++++++++----------- src/core_plugins/kibana/public/context/app.js | 2 +- .../loading_button/loading_button.html | 10 ++++++++ .../loading_button/loading_button.js | 22 +++++++++++++++++ .../loading_button/loading_button.less} | 0 5 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 src/core_plugins/kibana/public/context/components/loading_button/loading_button.html create mode 100644 src/core_plugins/kibana/public/context/components/loading_button/loading_button.js rename src/core_plugins/kibana/public/context/{app.less => components/loading_button/loading_button.less} (100%) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 9c0e79a996a27..26b2b4d917bef 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -28,16 +28,14 @@
- + Load newer entries +
@@ -59,16 +57,14 @@

Loading...

- + Load older entries +
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 719430b8601ee..b440ee1fa2342 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -2,7 +2,7 @@ import _ from 'lodash'; import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; -import './app.less'; +import './components/loading_button'; import './components/size_picker'; import { createDispatchProvider } from './redux_lite/create_dispatch'; import { createReducerPipeline, scopeReducer } from './redux_lite/reducer_helpers'; diff --git a/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html new file mode 100644 index 0000000000000..d066bd7138a40 --- /dev/null +++ b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html @@ -0,0 +1,10 @@ + diff --git a/src/core_plugins/kibana/public/context/components/loading_button/loading_button.js b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.js new file mode 100644 index 0000000000000..5543cb33d2e38 --- /dev/null +++ b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.js @@ -0,0 +1,22 @@ +import uiModules from 'ui/modules'; +import contextLoadingButtonTemplate from './loading_button.html'; +import './loading_button.less'; + + +const module = uiModules.get('apps/context', [ + 'kibana', + 'ngRoute', +]); + +module.directive('contextLoadingButton', function ContextLoadingButton() { + return { + replace: true, + restrict: 'E', + scope: { + disabled: '=', + icon: '=', + }, + template: contextLoadingButtonTemplate, + transclude: true, + }; +}); diff --git a/src/core_plugins/kibana/public/context/app.less b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.less similarity index 100% rename from src/core_plugins/kibana/public/context/app.less rename to src/core_plugins/kibana/public/context/components/loading_button/loading_button.less From fbb61c17f7a054696641d9e37f96962196ca915f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 6 Jan 2017 19:02:15 +0100 Subject: [PATCH 42/97] Add directory structure to notes --- .../kibana/public/context/NOTES.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/core_plugins/kibana/public/context/NOTES.md b/src/core_plugins/kibana/public/context/NOTES.md index 49317292e4385..d46586f9c185b 100644 --- a/src/core_plugins/kibana/public/context/NOTES.md +++ b/src/core_plugins/kibana/public/context/NOTES.md @@ -92,3 +92,36 @@ as flat as possible and free of duplicate information. The performance penalty for performing these calculations at query time is commonly circumvented by memoizing the selector results as is the case with `createSelector` from [redux_lite/selector_helpers.js](./redux_lite/selector_helpers.js). + + +## Directory Structure + +**index.js**: Defines the route and renders the `` directive, +binding it to the `AppState`. + +**app.js**: Defines the `` directive, that is at the root of the +application. Creates the store, reducer and bound actions/selectors. + +**query.js**: Exports the actions, reducers and selectors related to the +query status and results. + +**query_parameters.js**: Exports the actions, reducers and selectors related to +the parameters used to construct the query. + +**components/loading_button**: Defines the `` +directive including its respective styles. + +**components/size_picker**: Defines the `` +directive including its respective styles. + +**api/anchor.js**: Exports `fetchAnchor()` that creates and executes the +query for the anchor document. + +**api/context.js**: Exports `fetchPredecessors()` and `fetchSuccessors()` that +create and execute the queries for the preceeding and succeeding documents. + +**api/utils**: Exports various functions used to create and transform +queries. + +**redux_lite**: Exports various functions to create the dispatcher, action +creators, reducers and selectors. From 82f88c45d35013e3166712ecdfe191c2d76c6b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 10 Jan 2017 11:58:06 +0100 Subject: [PATCH 43/97] Add unit tests --- .../context/api/utils/__tests__/fields.js | 39 +++++++++++ .../context/api/utils/__tests__/sorting.js | 66 +++++++++++++++++++ .../public/context/api/utils/sorting.js | 8 ++- 3 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js create mode 100644 src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js new file mode 100644 index 0000000000000..7f11c3f05b0d1 --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js @@ -0,0 +1,39 @@ +import { expect } from 'chai'; + +import { addComputedFields } from 'plugins/kibana/context/api/utils/fields'; + + +describe('context app', function () { + describe('addComputedFields', function () { + it('should add the `script_fields` property defined in the given index pattern', function () { + const getComputedFields = () => ({ + scriptFields: { + sourcefield1: { + script: '_source.field1', + }, + } + }); + + expect(addComputedFields({ getComputedFields }, {})).to.have.property('script_fields') + .that.is.deep.equal(getComputedFields().scriptFields); + }); + + it('should add the `docvalue_fields` property defined in the given index pattern', function () { + const getComputedFields = () => ({ + docvalueFields: ['field1'], + }); + + expect(addComputedFields({ getComputedFields }, {})).to.have.property('docvalue_fields') + .that.is.deep.equal(getComputedFields().docvalueFields); + }); + + it('should add the `stored_fields` property defined in the given index pattern', function () { + const getComputedFields = () => ({ + storedFields: ['field1'], + }); + + expect(addComputedFields({ getComputedFields }, {})).to.have.property('stored_fields') + .that.is.deep.equal(getComputedFields().storedFields); + }); + }); +}); diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js new file mode 100644 index 0000000000000..31256f205e397 --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js @@ -0,0 +1,66 @@ +import expect from 'expect.js'; + +import { + reverseQuerySort, + reverseSortDirection, + reverseSortDirective +} from 'plugins/kibana/context/api/utils/sorting'; + + +describe('context app', function () { + describe('reverseQuerySort', function () { + it('should reverse all the `sort` property values', function () { + expect(reverseQuerySort({ + sort: [ + { field1: { order: 'desc', mode: 'max' } }, + { field2: 'asc' }, + 'field3', + '_score', + ], + })).to.eql({ + sort: [ + { field1: { order: 'asc', mode: 'max' } }, + { field2: 'desc' }, + { field3: 'desc' }, + { _score: 'asc' }, + ], + }); + }); + }); + + describe('reverseSortDirection', function () { + it('should reverse a direction given as a string', function () { + expect(reverseSortDirection('asc')).to.eql('desc'); + expect(reverseSortDirection('desc')).to.eql('asc'); + }); + + it('should reverse a direction given in an option object', function () { + expect(reverseSortDirection({ order: 'asc' })).to.eql({ order: 'desc' }); + expect(reverseSortDirection({ order: 'desc' })).to.eql({ order: 'asc' }); + }); + + it('should preserve other properties than `order` in an option object', function () { + expect(reverseSortDirection({ + order: 'asc', + other: 'field', + })).to.eql({ + order: 'desc', + other: 'field', + }); + }); + }); + + describe('reverseSortDirective', function () { + it('should return direction `asc` when given just `_score`', function () { + expect(reverseSortDirective('_score')).to.eql({ _score: 'asc' }); + }); + + it('should return direction `desc` when given just a field name', function () { + expect(reverseSortDirective('field1')).to.eql({ field1: 'desc' }); + }); + + it('should reverse direction when given an object', function () { + expect(reverseSortDirective({ field1: 'asc' })).to.eql({ field1: 'desc' }); + }); + }); +}); diff --git a/src/core_plugins/kibana/public/context/api/utils/sorting.js b/src/core_plugins/kibana/public/context/api/utils/sorting.js index cea31382abe24..f45cf2a1c655a 100644 --- a/src/core_plugins/kibana/public/context/api/utils/sorting.js +++ b/src/core_plugins/kibana/public/context/api/utils/sorting.js @@ -13,7 +13,7 @@ function reverseSortDirective(sortDirective) { return { [sortDirective]: (sortDirective === '_score' ? 'asc' : 'desc'), }; - } else if (_.isObject(sortDirective)) { + } else if (_.isPlainObject(sortDirective)) { return _.mapValues(sortDirective, reverseSortDirection); } else { return sortDirective; @@ -21,8 +21,10 @@ function reverseSortDirective(sortDirective) { } function reverseSortDirection(sortDirection) { - if (_.isObject(sortDirection)) { - return sortDirection.order = reverseSortDirection(sortDirection.order); + if (_.isPlainObject(sortDirection)) { + return _.assign({}, sortDirection, { + order: reverseSortDirection(sortDirection.order), + }); } else { return (sortDirection === 'asc' ? 'desc' : 'asc'); } From 2f319796eb4bdebf0d056485896f22b8d8b370c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 11 Jan 2017 13:38:18 +0100 Subject: [PATCH 44/97] Use chai instead of expect.js in tests --- .../context/api/utils/__tests__/sorting.js | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js index 31256f205e397..70666bcc21e01 100644 --- a/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js @@ -1,4 +1,4 @@ -import expect from 'expect.js'; +import { expect } from 'chai'; import { reverseQuerySort, @@ -17,7 +17,7 @@ describe('context app', function () { 'field3', '_score', ], - })).to.eql({ + })).to.deep.equal({ sort: [ { field1: { order: 'asc', mode: 'max' } }, { field2: 'desc' }, @@ -30,37 +30,34 @@ describe('context app', function () { describe('reverseSortDirection', function () { it('should reverse a direction given as a string', function () { - expect(reverseSortDirection('asc')).to.eql('desc'); - expect(reverseSortDirection('desc')).to.eql('asc'); + expect(reverseSortDirection('asc')).to.equal('desc'); + expect(reverseSortDirection('desc')).to.equal('asc'); }); it('should reverse a direction given in an option object', function () { - expect(reverseSortDirection({ order: 'asc' })).to.eql({ order: 'desc' }); - expect(reverseSortDirection({ order: 'desc' })).to.eql({ order: 'asc' }); + expect(reverseSortDirection({ order: 'asc' })).to.deep.equal({ order: 'desc' }); + expect(reverseSortDirection({ order: 'desc' })).to.deep.equal({ order: 'asc' }); }); it('should preserve other properties than `order` in an option object', function () { expect(reverseSortDirection({ order: 'asc', other: 'field', - })).to.eql({ - order: 'desc', - other: 'field', - }); + })).to.have.property('other', 'field'); }); }); describe('reverseSortDirective', function () { it('should return direction `asc` when given just `_score`', function () { - expect(reverseSortDirective('_score')).to.eql({ _score: 'asc' }); + expect(reverseSortDirective('_score')).to.deep.equal({ _score: 'asc' }); }); it('should return direction `desc` when given just a field name', function () { - expect(reverseSortDirective('field1')).to.eql({ field1: 'desc' }); + expect(reverseSortDirective('field1')).to.deep.equal({ field1: 'desc' }); }); it('should reverse direction when given an object', function () { - expect(reverseSortDirective({ field1: 'asc' })).to.eql({ field1: 'desc' }); + expect(reverseSortDirective({ field1: 'asc' })).to.deep.equal({ field1: 'desc' }); }); }); }); From 5cc91aac476ab20c8ce9497fdb0e5c2a80d5dcde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 11 Jan 2017 19:08:09 +0100 Subject: [PATCH 45/97] Add more unit tests --- .../public/context/api/__tests__/anchor.js | 84 +++++++++++++++++++ .../context/api/utils/__tests__/fields.js | 9 +- .../context/api/utils/__tests__/queries.js | 44 ++++++++++ .../context/api/utils/__tests__/sorting.js | 6 +- 4 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 src/core_plugins/kibana/public/context/api/__tests__/anchor.js create mode 100644 src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js diff --git a/src/core_plugins/kibana/public/context/api/__tests__/anchor.js b/src/core_plugins/kibana/public/context/api/__tests__/anchor.js new file mode 100644 index 0000000000000..c56de3c5c7553 --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/__tests__/anchor.js @@ -0,0 +1,84 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; + +import { fetchAnchor } from 'plugins/kibana/context/api/anchor'; + + +describe('context app', function () { + describe('function fetchAnchor', function () { + it('should use the `search` api to query the given index', function () { + const indexPatternStub = createIndexPatternStub('index1'); + const esStub = createEsStub(['hit1']); + + return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' }) + .then(() => { + expect(esStub.search.calledOnce).to.be.true; + expect(esStub.search.firstCall.args) + .to.have.lengthOf(1) + .with.deep.property('[0].index', 'index1'); + }); + }); + + it('should include computed fields in the query', function () { + const indexPatternStub = createIndexPatternStub('index1'); + const esStub = createEsStub(['hit1']); + + return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' }) + .then(() => { + expect(esStub.search.calledOnce).to.be.true; + expect(esStub.search.firstCall.args) + .to.have.lengthOf(1) + .with.deep.property('[0].body') + .that.contains.all.keys(['script_fields', 'docvalue_fields', 'stored_fields']); + }); + }); + + it('should reject with an error when no hits were found', function () { + const indexPatternStub = createIndexPatternStub('index1'); + const esStub = createEsStub([]); + + return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' }) + .then( + () => { + expect(true, 'expected the promise to be rejected').to.be.false; + }, + (error) => { + expect(error).to.be.an('error'); + } + ); + }); + + it('should return the first hit after adding an anchor marker', function () { + const indexPatternStub = createIndexPatternStub('index1'); + const esStub = createEsStub([{ property1: 'value1' }, {}]); + + return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' }) + .then((anchorDocument) => { + expect(anchorDocument).to.have.property('property1', 'value1'); + expect(anchorDocument).to.have.property('$$_isAnchor', true); + }); + }); + }); +}); + + +function createIndexPatternStub(indices) { + return { + getComputedFields: sinon.stub() + .returns({}), + toIndexList: sinon.stub() + .returns(indices), + }; +} + +function createEsStub(hits) { + return { + search: sinon.stub() + .returns({ + hits: { + hits, + total: hits.length, + }, + }), + }; +} diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js index 7f11c3f05b0d1..ad43421752d8b 100644 --- a/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js @@ -4,7 +4,7 @@ import { addComputedFields } from 'plugins/kibana/context/api/utils/fields'; describe('context app', function () { - describe('addComputedFields', function () { + describe('function addComputedFields', function () { it('should add the `script_fields` property defined in the given index pattern', function () { const getComputedFields = () => ({ scriptFields: { @@ -35,5 +35,12 @@ describe('context app', function () { expect(addComputedFields({ getComputedFields }, {})).to.have.property('stored_fields') .that.is.deep.equal(getComputedFields().storedFields); }); + + it('should preserve other properties of the query', function () { + const getComputedFields = () => ({}); + + expect(addComputedFields({ getComputedFields }, { property1: 'value1' })) + .to.have.property('property1', 'value1'); + }); }); }); diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js new file mode 100644 index 0000000000000..51802abd56814 --- /dev/null +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js @@ -0,0 +1,44 @@ +import { expect } from 'chai'; + +import { + createAnchorQuery, + createSuccessorsQuery, +} from 'plugins/kibana/context/api/utils/queries'; + + +describe('context app', function () { + describe('function createAnchorQuery', function () { + it('should return a search definition that searches the given uid', function () { + expect(createAnchorQuery('UID', { '@timestamp': 'desc' })) + .to.have.deep.property('query.terms._uid[0]', 'UID'); + }); + + it('should return a search definition that sorts by the given criteria', function () { + expect(createAnchorQuery('UID', { '@timestamp': 'desc' })) + .to.have.deep.property('sort[0]') + .that.is.deep.equal({ '@timestamp': 'desc' }); + }); + }); + + describe('function createSuccessorsQuery', function () { + it('should return a search definition that includes the given size', function () { + expect(createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10)) + .to.have.property('size', 10); + }); + + it('should return a search definition that sorts by the given criteria and uid', function () { + expect(createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10)) + .to.have.property('sort') + .that.is.deep.equal([ + { '@timestamp': 'desc' }, + { _uid: 'asc' }, + ]); + }); + + it('should return a search definition that search after the given uid', function () { + expect(createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10)) + .to.have.property('search_after') + .that.is.deep.equal([0, 'UID']); + }); + }); +}); diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js index 70666bcc21e01..05619f7b977da 100644 --- a/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js @@ -8,7 +8,7 @@ import { describe('context app', function () { - describe('reverseQuerySort', function () { + describe('function reverseQuerySort', function () { it('should reverse all the `sort` property values', function () { expect(reverseQuerySort({ sort: [ @@ -28,7 +28,7 @@ describe('context app', function () { }); }); - describe('reverseSortDirection', function () { + describe('function reverseSortDirection', function () { it('should reverse a direction given as a string', function () { expect(reverseSortDirection('asc')).to.equal('desc'); expect(reverseSortDirection('desc')).to.equal('asc'); @@ -47,7 +47,7 @@ describe('context app', function () { }); }); - describe('reverseSortDirective', function () { + describe('function reverseSortDirective', function () { it('should return direction `asc` when given just `_score`', function () { expect(reverseSortDirective('_score')).to.deep.equal({ _score: 'asc' }); }); From 02440e8b4b8780e619ecfcf8297c199bf08506a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 12 Jan 2017 14:17:29 +0100 Subject: [PATCH 46/97] Remove redux concepts in response to code review The action, dispatcher and reducer concepts have been removed in favour of action functions that directly modify the state object to match the usual Kibana data flow. --- .../kibana/public/context/app.html | 18 +- src/core_plugins/kibana/public/context/app.js | 50 ++-- .../kibana/public/context/query.js | 214 +++++++----------- .../kibana/public/context/query_parameters.js | 101 +++------ .../redux_lite/action_creator_helpers.js | 23 -- .../context/redux_lite/create_dispatch.js | 54 ----- .../context/redux_lite/reducer_helpers.js | 27 --- .../selectors.js} | 0 8 files changed, 135 insertions(+), 352 deletions(-) delete mode 100644 src/core_plugins/kibana/public/context/redux_lite/action_creator_helpers.js delete mode 100644 src/core_plugins/kibana/public/context/redux_lite/create_dispatch.js delete mode 100644 src/core_plugins/kibana/public/context/redux_lite/reducer_helpers.js rename src/core_plugins/kibana/public/context/{redux_lite/selector_helpers.js => utils/selectors.js} (100%) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 26b2b4d917bef..9d0ca85c51ec5 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -11,8 +11,8 @@
@@ -29,10 +29,10 @@
Load newer entries @@ -40,12 +40,12 @@
-
+

Loading...

-
+
Loading...
Load older entries diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index b440ee1fa2342..e6195cdcff676 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -4,26 +4,17 @@ import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; import './components/loading_button'; import './components/size_picker'; -import { createDispatchProvider } from './redux_lite/create_dispatch'; -import { createReducerPipeline, scopeReducer } from './redux_lite/reducer_helpers'; -import { bindActionCreators } from './redux_lite/action_creator_helpers'; -import { bindSelectors } from './redux_lite/selector_helpers'; +import { bindSelectors } from './utils/selectors'; import { - QueryParameterActionCreatorsProvider, + QueryParameterActionsProvider, QUERY_PARAMETER_KEYS, - selectPredecessorCount, - selectQueryParameters, - selectSuccessorCount, - updateQueryParameters, } from './query_parameters'; import { - QueryActionCreatorsProvider, + QueryActionsProvider, selectIsLoadingAnchorRow, selectIsLoadingPredecessorRows, selectIsLoadingSuccessorRows, selectRows, - updateLoadingStatus, - updateQueryResults } from './query'; @@ -51,36 +42,21 @@ module.directive('contextApp', function ContextApp() { }); function ContextAppController($scope, Private) { - const createDispatch = Private(createDispatchProvider); - const queryParameterActionCreators = Private(QueryParameterActionCreatorsProvider); - const queryActionCreators = Private(QueryActionCreatorsProvider); + const queryParameterActions = Private(QueryParameterActionsProvider); + const queryActions = Private(QueryActionsProvider); this.state = createInitialState(); - this.reducer = createReducerPipeline( - scopeReducer('queryParameters', updateQueryParameters), - scopeReducer('rows', updateQueryResults), - scopeReducer('loadingStatus', updateLoadingStatus), - ); - - this.dispatch = createDispatch( - () => this.state, - (state) => this.state = state, - this.reducer, - ); - - this.actions = bindActionCreators({ - ...queryParameterActionCreators, - ...queryActionCreators, - }, this.dispatch); + this.actions = _.mapValues({ + ...queryParameterActions, + ...queryActions, + }, (action) => (...args) => action(this.state)(...args)); - this.selectors = bindSelectors({ + this.derivedState = bindSelectors({ isLoadingAnchorRow: selectIsLoadingAnchorRow, isLoadingPredecessorRows: selectIsLoadingPredecessorRows, isLoadingSuccessorRows: selectIsLoadingSuccessorRows, - predecessorCount: selectPredecessorCount, rows: selectRows, - successorCount: selectSuccessorCount, }, () => this.state); /** @@ -90,14 +66,14 @@ function ContextAppController($scope, Private) { () => _.pick(this, QUERY_PARAMETER_KEYS), (newValues) => { // break the watch cycle - if (!_.isEqual(newValues, selectQueryParameters(this.state))) { - this.dispatch(queryActionCreators.fetchAllRowsWithNewQueryParameters(newValues)); + if (!_.isEqual(newValues, this.state.queryParameters)) { + this.actions.fetchAllRowsWithNewQueryParameters(newValues); } }, ); $scope.$watchCollection( - () => selectQueryParameters(this.state), + () => this.state.queryParameters, (newValues) => { _.assign(this, newValues); }, diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query.js index 45bdf39645777..4420a142b04aa 100644 --- a/src/core_plugins/kibana/public/context/query.js +++ b/src/core_plugins/kibana/public/context/query.js @@ -2,23 +2,95 @@ import _ from 'lodash'; import { fetchAnchor } from './api/anchor'; import { fetchPredecessors, fetchSuccessors } from './api/context'; -import { started } from './redux_lite/action_creator_helpers'; -import { createSelector } from './redux_lite/selector_helpers'; -import { - QueryParameterActionCreatorsProvider, - selectPredecessorCount, - selectSuccessorCount, -} from './query_parameters'; +import { createSelector } from './utils/selectors'; +import { QueryParameterActionsProvider } from './query_parameters'; -function QueryActionCreatorsProvider($q, es, Private) { +function QueryActionsProvider($q, es, Private) { const { increasePredecessorCount, increaseSuccessorCount, setPredecessorCount, setQueryParameters, setSuccessorCount, - } = Private(QueryParameterActionCreatorsProvider); + } = Private(QueryParameterActionsProvider); + + + const fetchAnchorRow = (state) => () => { + const { queryParameters: { indexPattern, anchorUid, sort } } = state; + + state.loadingStatus.anchor = 'loading'; + + return fetchAnchor(es, indexPattern, anchorUid, _.zipObject([sort])) + .then((anchorDocument) => { + state.loadingStatus.anchor = 'loaded'; + state.rows.anchor = anchorDocument; + return anchorDocument; + }); + }; + + const fetchPredecessorRows = (state) => () => { + const { + queryParameters: { indexPattern, predecessorCount, sort }, + rows: { anchor }, + } = state; + + state.loadingStatus.predecessors = 'loading'; + + return fetchPredecessors(es, indexPattern, anchor, _.zipObject([sort]), predecessorCount) + .then((predecessorDocuments) => { + state.loadingStatus.predecessors = 'loaded'; + state.rows.predecessors = predecessorDocuments; + return predecessorDocuments; + }); + }; + + const fetchSuccessorRows = (state) => () => { + const { + queryParameters: { indexPattern, sort, successorCount }, + rows: { anchor }, + } = state; + + return fetchSuccessors(es, indexPattern, anchor, _.zipObject([sort]), successorCount) + .then((successorDocuments) => { + state.loadingStatus.successors = 'loaded'; + state.rows.successors = successorDocuments; + return successorDocuments; + }); + }; + + const fetchAllRows = (state) => () => ( + fetchAnchorRow(state)() + .then(() => $q.all([ + fetchPredecessorRows(state)(), + fetchSuccessorRows(state)(), + ])) + ); + + const fetchAllRowsWithNewQueryParameters = (state) => (queryParameters) => { + setQueryParameters(state)(queryParameters); + return fetchAllRows(state)(); + }; + + const fetchGivenPredecessorRows = (state) => (count) => { + setPredecessorCount(state)(count); + return fetchPredecessorRows(state)(); + }; + + const fetchGivenSuccessorRows = (state) => (count) => { + setSuccessorCount(state)(count); + return fetchSuccessorRows(state)(); + }; + + const fetchMorePredecessorRows = (state) => () => { + increasePredecessorCount(state)(); + return fetchPredecessorRows(state)(); + }; + + const fetchMoreSuccessorRows = (state) => () => { + increaseSuccessorCount(state)(); + return fetchSuccessorRows(state)(); + }; return { fetchAllRows, @@ -31,94 +103,6 @@ function QueryActionCreatorsProvider($q, es, Private) { fetchPredecessorRows, fetchSuccessorRows, }; - - function fetchAllRows() { - return (dispatch) => ({ - type: 'context/fetch_all_rows', - payload: dispatch(fetchAnchorRow()) - .then(() => $q.all([ - dispatch(fetchPredecessorRows()), - dispatch(fetchSuccessorRows()), - ])), - }); - } - - function fetchAllRowsWithNewQueryParameters(queryParameters) { - return (dispatch) => { - dispatch(setQueryParameters(queryParameters)); - return dispatch(fetchAllRows()); - }; - } - - function fetchAnchorRow() { - return (dispatch, getState) => { - const { queryParameters: { indexPattern, anchorUid, sort } } = getState(); - - return dispatch({ - type: 'context/fetch_anchor_row', - payload: fetchAnchor(es, indexPattern, anchorUid, _.zipObject([sort])), - }); - }; - } - - function fetchPredecessorRows() { - return (dispatch, getState) => { - const state = getState(); - const { - queryParameters: { indexPattern, sort }, - rows: { anchor }, - } = state; - const predecessorCount = selectPredecessorCount(state); - - return dispatch({ - type: 'context/fetch_predecessor_rows', - payload: fetchPredecessors(es, indexPattern, anchor, _.zipObject([sort]), predecessorCount), - }); - }; - } - - function fetchSuccessorRows() { - return (dispatch, getState) => { - const state = getState(); - const { - queryParameters: { indexPattern, sort }, - rows: { anchor }, - } = state; - const successorCount = selectSuccessorCount(state); - - return dispatch({ - type: 'context/fetch_successor_rows', - payload: fetchSuccessors(es, indexPattern, anchor, _.zipObject([sort]), successorCount), - }); - }; - } - - function fetchGivenPredecessorRows(count) { - return (dispatch) => { - dispatch(setPredecessorCount(count)); - return dispatch(fetchPredecessorRows()); - }; - } - - function fetchGivenSuccessorRows(count) { - return (dispatch) => { - dispatch(setSuccessorCount(count)); - return dispatch(fetchSuccessorRows()); - }; - } - function fetchMorePredecessorRows() { - return (dispatch) => { - dispatch(increasePredecessorCount()); - return dispatch(fetchPredecessorRows()); - }; - } - - function fetchMoreSuccessorRows() { - return (dispatch) => { - dispatch(increaseSuccessorCount()); - return dispatch(fetchSuccessorRows()); - }; - } } const selectIsLoadingAnchorRow = createSelector([ @@ -147,45 +131,11 @@ const selectRows = createSelector([ [...predecessorRows, ...(anchorRow ? [anchorRow] : []), ...successorRows] )); -function updateQueryResults(state, action) { - switch (action.type) { - case 'context/fetch_anchor_row': - return { ...state, anchor: action.payload }; - case 'context/fetch_predecessor_rows': - return { ...state, predecessors: action.payload }; - case 'context/fetch_successor_rows': - return { ...state, successors: action.payload }; - default: - return state; - } -} - -function updateLoadingStatus(state, action) { - switch (action.type) { - case started('context/fetch_anchor_row'): - return { ...state, anchor: 'loading' }; - case 'context/fetch_anchor_row': - return { ...state, anchor: 'loaded' }; - case started('context/fetch_predecessor_rows'): - return { ...state, predecessors: 'loading' }; - case 'context/fetch_predecessor_rows': - return { ...state, predecessors: 'loaded' }; - case started('context/fetch_successor_rows'): - return { ...state, successors: 'loading' }; - case 'context/fetch_successor_rows': - return { ...state, successors: 'loaded' }; - default: - return state; - } -} - export { - QueryActionCreatorsProvider, + QueryActionsProvider, selectIsLoadingAnchorRow, selectIsLoadingPredecessorRows, selectIsLoadingSuccessorRows, selectRows, - updateLoadingStatus, - updateQueryResults, }; diff --git a/src/core_plugins/kibana/public/context/query_parameters.js b/src/core_plugins/kibana/public/context/query_parameters.js index b86eb5272c9c7..99a9494cf137b 100644 --- a/src/core_plugins/kibana/public/context/query_parameters.js +++ b/src/core_plugins/kibana/public/context/query_parameters.js @@ -11,9 +11,38 @@ const QUERY_PARAMETER_KEYS = [ 'sort', ]; -function QueryParameterActionCreatorsProvider(config) { +function QueryParameterActionsProvider(config) { const defaultSizeStep = parseInt(config.get('context:step'), 10); + const setPredecessorCount = (state) => (predecessorCount) => ( + state.queryParameters.predecessorCount = Math.max( + MIN_CONTEXT_SIZE, + predecessorCount, + ) + ); + + const increasePredecessorCount = (state) => (value = defaultSizeStep) => ( + setPredecessorCount(state)(state.queryParameters.predecessorCount + value) + ); + + const setSuccessorCount = (state) => (successorCount) => ( + state.queryParameters.successorCount = Math.max( + MIN_CONTEXT_SIZE, + successorCount, + ) + ); + + const increaseSuccessorCount = (state) => (value = defaultSizeStep) => ( + setSuccessorCount(state)(state.queryParameters.successorCount + value) + ); + + const setQueryParameters = (state) => (queryParameters) => ( + state.queryParameters = { + ...state.queryParameters, + ...(_.pick(queryParameters, QUERY_PARAMETER_KEYS)), + } + ); + return { increasePredecessorCount, increaseSuccessorCount, @@ -21,78 +50,10 @@ function QueryParameterActionCreatorsProvider(config) { setQueryParameters, setSuccessorCount, }; - - function increasePredecessorCount(value = defaultSizeStep) { - return { - type: 'context/increase_predecessor_count', - payload: value, - }; - } - - function increaseSuccessorCount(value = defaultSizeStep) { - return { - type: 'context/increase_successor_count', - payload: value, - }; - } - - function setPredecessorCount(value) { - return { - type: 'context/set_predecessor_count', - payload: value, - }; - } - - function setSuccessorCount(value) { - return { - type: 'context/set_successor_count', - payload: value, - }; - } - - function setQueryParameters(queryParameters) { - return { - type: 'context/set_query_parameters', - payload: queryParameters, - }; - } -} - -function selectPredecessorCount(state) { - return state.queryParameters.predecessorCount; -} - -function selectQueryParameters(state) { - return state.queryParameters; -} - -function selectSuccessorCount(state) { - return state.queryParameters.successorCount; -} - -function updateQueryParameters(state, action) { - switch (action.type) { - case 'context/increase_predecessor_count': - return { ...state, predecessorCount: Math.max(MIN_CONTEXT_SIZE, state.predecessorCount + action.payload) }; - case 'context/increase_successor_count': - return { ...state, successorCount: Math.max(MIN_CONTEXT_SIZE, state.successorCount + action.payload) }; - case 'context/set_predecessor_count': - return { ...state, predecessorCount: Math.max(MIN_CONTEXT_SIZE, action.payload) }; - case 'context/set_successor_count': - return { ...state, successorCount: Math.max(MIN_CONTEXT_SIZE, action.payload) }; - case 'context/set_query_parameters': - return { ...state, ...(_.pick(action.payload, QUERY_PARAMETER_KEYS)) }; - default: - return state; - } } export { - QueryParameterActionCreatorsProvider, + QueryParameterActionsProvider, QUERY_PARAMETER_KEYS, - selectPredecessorCount, - selectQueryParameters, - selectSuccessorCount, - updateQueryParameters, }; diff --git a/src/core_plugins/kibana/public/context/redux_lite/action_creator_helpers.js b/src/core_plugins/kibana/public/context/redux_lite/action_creator_helpers.js deleted file mode 100644 index 174e032a2f7d2..0000000000000 --- a/src/core_plugins/kibana/public/context/redux_lite/action_creator_helpers.js +++ /dev/null @@ -1,23 +0,0 @@ -import _ from 'lodash'; - - -function bindActionCreators(actionCreators, dispatch) { - return _.mapValues(actionCreators, (actionCreator) => ( - _.flow(actionCreator, dispatch) - )); -} - -function failed(type) { - return `${type}:failed`; -} - -function started(type) { - return `${type}:started`; -} - - -export { - bindActionCreators, - failed, - started, -}; diff --git a/src/core_plugins/kibana/public/context/redux_lite/create_dispatch.js b/src/core_plugins/kibana/public/context/redux_lite/create_dispatch.js deleted file mode 100644 index ca8a1984d20ec..0000000000000 --- a/src/core_plugins/kibana/public/context/redux_lite/create_dispatch.js +++ /dev/null @@ -1,54 +0,0 @@ -import _ from 'lodash'; - -import { failed, started } from './action_creator_helpers'; - - -function createDispatchProvider($q) { - return function createDispatch(getState, setState, reducer) { - const dispatchWithMiddleware = _.compose( - createThunkMiddleware, - createPromiseMiddleware, - )(dispatch); - - return dispatchWithMiddleware; - - function dispatch(action) { - const nextState = reducer(getState(), action); - setState(nextState); - - return action; - } - - function createThunkMiddleware(next) { - return (action) => { - if (_.isFunction(action)) { - return action(dispatchWithMiddleware, getState); - } - - return next(action); - }; - } - - function createPromiseMiddleware(next) { - return (action) => { - if (_.isFunction(_.get(action, ['payload', 'then']))) { - next({ type: started(action.type) }); - - return $q.resolve(action.payload) - .then( - (result) => dispatchWithMiddleware({ type: action.type, payload: result }), - (error) => dispatchWithMiddleware({ type: failed(action.type), payload: error, error: true }), - ); - } - - return next(action); - }; - } - - }; -} - - -export { - createDispatchProvider, -}; diff --git a/src/core_plugins/kibana/public/context/redux_lite/reducer_helpers.js b/src/core_plugins/kibana/public/context/redux_lite/reducer_helpers.js deleted file mode 100644 index 1885b4bd157a9..0000000000000 --- a/src/core_plugins/kibana/public/context/redux_lite/reducer_helpers.js +++ /dev/null @@ -1,27 +0,0 @@ -import _ from 'lodash'; - - -function createReducerPipeline(...updaters) { - return (state, action) => ( - _.flow(...updaters.map((updater) => (state) => updater(state, action)))(state) - ); -} - -function scopeReducer(key, reducer) { - return (state, action) => { - const subState = state[key]; - const newSubState = reducer(subState, action); - - if (subState !== newSubState) { - return { ...state, [key] : newSubState }; - } - - return state; - }; -} - - -export { - createReducerPipeline, - scopeReducer, -}; diff --git a/src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js b/src/core_plugins/kibana/public/context/utils/selectors.js similarity index 100% rename from src/core_plugins/kibana/public/context/redux_lite/selector_helpers.js rename to src/core_plugins/kibana/public/context/utils/selectors.js From cb4d95f5f58425af7ca242a4bfe51a3310583cd5 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 12 Jan 2017 09:10:23 -0800 Subject: [PATCH 47/97] Improve Context Log highlighting and UI. (#1) * Move ContextLoadingButtons out of header into Context view. * Add kuiButton--iconText modifier to ContextLoadingButtons. * Use style guide color for highlighted Context Log row. * Add Bar component. Use Bar to surface entry-pagination controls in Context Log. Refactor ContextSizePicker directive to surface a single input without surrounding text. * Use Panel component to present loading feedback and table within Context app. * Remove fading effect from truncated doc table rows, because it doesn't work with highlighted backgrounds. It also looks weird. * Move bar component to match #9803 * Update ui framework css bundle * Move getter/setter into the size-picker * Remove superfluous loading-button styling --- .../kibana/public/context/app.html | 140 ++++++++++-------- src/core_plugins/kibana/public/context/app.js | 3 +- .../loading_button/loading_button.html | 2 +- .../loading_button/loading_button.js | 1 - .../loading_button/loading_button.less | 4 - .../components/size_picker/size_picker.html | 22 +-- .../components/size_picker/size_picker.js | 17 +-- .../components/size_picker/size_picker.less | 28 ++-- .../kibana/public/discover/styles/main.less | 7 +- .../doc_table/components/table_row/open.html | 2 +- src/ui/public/styles/table.less | 6 +- src/ui/public/styles/truncate.less | 34 ----- src/ui/public/styles/variables/for-theme.less | 1 - ui_framework/components/bar/_bar.scss | 7 + ui_framework/components/bar/_index.scss | 1 + ui_framework/components/index.scss | 39 +++++ ui_framework/components/tool_bar/_index.scss | 3 - .../components/tool_bar/_tool_bar.scss | 23 +-- .../components/tool_bar/_tool_bar_footer.scss | 28 +--- ui_framework/dist/ui_framework.css | 63 +++++++- .../doc_site/src/services/routes/Routes.js | 6 + ui_framework/doc_site/src/views/bar/bar.html | 18 +++ .../doc_site/src/views/bar/bar_example.jsx | 31 ++++ .../src/views/bar/bar_one_section.html | 12 ++ .../src/views/bar/bar_three_sections.html | 38 +++++ 25 files changed, 326 insertions(+), 210 deletions(-) delete mode 100644 src/core_plugins/kibana/public/context/components/loading_button/loading_button.less create mode 100644 ui_framework/components/bar/_bar.scss create mode 100644 ui_framework/components/bar/_index.scss create mode 100644 ui_framework/doc_site/src/views/bar/bar.html create mode 100644 ui_framework/doc_site/src/views/bar/bar_example.jsx create mode 100644 ui_framework/doc_site/src/views/bar/bar_one_section.html create mode 100644 ui_framework/doc_site/src/views/bar/bar_three_sections.html diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 9d0ca85c51ec5..f8b4ad92c7b90 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -1,71 +1,91 @@ -
-
-
-
-
-
- Discover -
-
+
+
+
+
+ -
-
- +
-
-
-
-
+
+
+
+
+
-
-
-
- - Load newer entries - -
+
+ +
+
+ + Load newer entries +
-
-
-
-

Loading...

-
-
- - -
-
+ +
+
Limit to
+ +
newer entries
-
-
- - Load older entries - -
+
+ + +
+
+ Loading… +
+
+ + +
+
+ +
+
+ + +
+
+ + Load older entries + +
+ +
+
Limit to
+ +
older entries
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index e6195cdcff676..44a8891d22d1d 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -3,7 +3,7 @@ import _ from 'lodash'; import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; import './components/loading_button'; -import './components/size_picker'; +import './components/size_picker/size_picker'; import { bindSelectors } from './utils/selectors'; import { QueryParameterActionsProvider, @@ -17,7 +17,6 @@ import { selectRows, } from './query'; - const module = uiModules.get('apps/context', [ 'kibana', 'ngRoute', diff --git a/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html index d066bd7138a40..7759ffb705625 100644 --- a/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html +++ b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html @@ -1,5 +1,5 @@
+ diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js index 008965c1cc0ce..3bdfc71675183 100644 --- a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js @@ -1,13 +1,10 @@ import _ from 'lodash'; - import uiModules from 'ui/modules'; import contextSizePickerTemplate from './size_picker.html'; import './size_picker.less'; - const module = uiModules.get('apps/context', [ 'kibana', - 'ngRoute', ]); module.directive('contextSizePicker', function ContextSizePicker() { @@ -18,22 +15,18 @@ module.directive('contextSizePicker', function ContextSizePicker() { replace: true, restrict: 'E', scope: { - predecessorCount: '=', - successorCount: '=', - setPredecessorCount: '=', - setSuccessorCount: '=', + count: '=', + setCount: '=', }, template: contextSizePickerTemplate, }; }); + function ContextSizePickerController() { const vm = this; - vm.getOrSetPredecessorCount = (value) => ( - _.isUndefined(value) ? vm.predecessorCount : vm.setPredecessorCount(value) - ); - vm.getOrSetSuccessorCount = (value) => ( - _.isUndefined(value) ? vm.successorCount : vm.setSuccessorCount(value) + vm.getOrSetCount = (count) => ( + _.isUndefined(count) ? vm.count : vm.setCount(count) ); } diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less index 950f93c1e7b56..ff0d1a0c99190 100644 --- a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less @@ -1,20 +1,14 @@ +/** + * 1. Hide increment and decrement buttons for type="number" input. + */ .contextSizePicker { - padding-right: 10px; - height: 100%; -} + appearance: textfield; + text-align: center; + width: 3em; - .contextSizePicker__item { - display: inline-block; - } - - .contextSizePicker__sizeInput { - appearance: textfield; - text-align: center; - width: 3em; - - &::-webkit-outer-spin-button, - &::-webkit-inner-spin-button { - appearance: none; - margin: 0; - } + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + appearance: none; /* 1 */ + margin: 0; /* 1 */ } +} diff --git a/src/core_plugins/kibana/public/discover/styles/main.less b/src/core_plugins/kibana/public/discover/styles/main.less index 8c45ed09b7dc7..436a2ea6871fe 100644 --- a/src/core_plugins/kibana/public/discover/styles/main.less +++ b/src/core_plugins/kibana/public/discover/styles/main.less @@ -129,18 +129,13 @@ } .discover-table-row--highlight { - font-weight: 900; - outline: 3px solid @discover-table-highlight-bg; + background-color: #E2F1F6; td { border-top: none !important; } } - .discover-table-togglefield--highlight { - background: @discover-table-highlight-bg; - } - .shard-failures { color: @discover-shard-failures-color; background-color: @discover-shard-failures-bg !important; diff --git a/src/ui/public/doc_table/components/table_row/open.html b/src/ui/public/doc_table/components/table_row/open.html index f9e8fa4b3ae6c..5313be2ed284f 100644 --- a/src/ui/public/doc_table/components/table_row/open.html +++ b/src/ui/public/doc_table/components/table_row/open.html @@ -1,4 +1,4 @@ - + diff --git a/src/ui/public/styles/table.less b/src/ui/public/styles/table.less index 68969e18abd19..2ffa21eb5f485 100644 --- a/src/ui/public/styles/table.less +++ b/src/ui/public/styles/table.less @@ -21,6 +21,10 @@ kbn-table, .kbn-table, tbody[kbn-rows] { } } + /** + * 1. Use alpha so this will stand out against non-white backgrounds, e.g. the highlighted + * row in the Context Log. + */ dl.source { margin-bottom: 0; line-height: 2em; @@ -31,7 +35,7 @@ kbn-table, .kbn-table, tbody[kbn-rows] { } dt { - background: @table-dt-bg; + background-color: rgba(108, 135, 142, 0.15); /* 1 */ color: @table-dt-color; padding: @padding-xs-vertical @padding-xs-horizontal; margin-right: @padding-xs-horizontal; diff --git a/src/ui/public/styles/truncate.less b/src/ui/public/styles/truncate.less index f6b136e941993..30ba5fea2a4ea 100644 --- a/src/ui/public/styles/truncate.less +++ b/src/ui/public/styles/truncate.less @@ -1,37 +1,3 @@ -@import (reference) "~ui/styles/variables"; - -@truncate1: fade(@truncate-color, 0%); -@truncate2: fade(@truncate-color, 1%); -@truncate3: fade(@truncate-color, 99%); -@truncate4: fade(@truncate-color, 100%); - .truncate-by-height { - position: relative; overflow: hidden; - - &:before { - content: " "; - width: 100%; - height: 15px; // copied into index.html! - position: absolute; - left: 0; - - // FF3.6+ - background: -moz-linear-gradient(top, @truncate1 0%, @truncate2 1%, @truncate3 99%, @truncate4 100%); - - // Chrome,Safari4+ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, @truncate1), color-stop(1%, @truncate2), color-stop(99%, @truncate3), color-stop(100%, @truncate4)); - - // Chrome10+,Safari5.1+ - background: -webkit-linear-gradient(top, @truncate1 0%, @truncate2 1%, @truncate3 99%, @truncate4 100%); - - // Opera 11.10+ - background: -o-linear-gradient(top, @truncate1 0%, @truncate2 1%, @truncate3 99%, @truncate4 100%); - - // IE10+ - background: -ms-linear-gradient(top, @truncate1 0%, @truncate2 1%, @truncate3 99%, @truncate4 100%); - - // W3C - background: linear-gradient(to bottom, @truncate1 0%, @truncate2 1%, @truncate3 99%, @truncate4 100%); - } } diff --git a/src/ui/public/styles/variables/for-theme.less b/src/ui/public/styles/variables/for-theme.less index b43ea260a6ba5..89dbd407060b0 100644 --- a/src/ui/public/styles/variables/for-theme.less +++ b/src/ui/public/styles/variables/for-theme.less @@ -82,7 +82,6 @@ // table ======================================================================= -@table-dt-bg: @gray-lighter; @table-dt-color: @brand-primary; @table-cell-hover-bg: white; @table-cell-hover-hover-bg: @gray-lighter; diff --git a/ui_framework/components/bar/_bar.scss b/ui_framework/components/bar/_bar.scss new file mode 100644 index 0000000000000..32453b9700067 --- /dev/null +++ b/ui_framework/components/bar/_bar.scss @@ -0,0 +1,7 @@ +.kuiBar { + @include bar; +} + +.kuiBarSection { + @include barSection; +} diff --git a/ui_framework/components/bar/_index.scss b/ui_framework/components/bar/_index.scss new file mode 100644 index 0000000000000..eb595631d700c --- /dev/null +++ b/ui_framework/components/bar/_index.scss @@ -0,0 +1 @@ +@import "bar"; diff --git a/ui_framework/components/index.scss b/ui_framework/components/index.scss index c3d49afbc5934..a86e0c33c2028 100644 --- a/ui_framework/components/index.scss +++ b/ui_framework/components/index.scss @@ -44,6 +44,10 @@ $tableBorder: 2px solid $panelColor; // Timing $formTransitionTiming: 0.1s linear; +// Bar +$toolBarSectionSpacing: 50px; +$toolBarItsemSpacing: 10px; + @mixin darkTheme() { .theme-dark & { @content; @@ -94,6 +98,40 @@ $formTransitionTiming: 0.1s linear; } } +@mixin bar { + display: flex; + align-items: center; + justify-content: space-between; +} + +/** + * 1. Put 10px of space between each child. + * 2. If there is only one child, align it to the right. If you wanted it aligned right, you + * wouldn't use the Bar in the first place. + */ +@mixin barSection { + display: flex; + align-items: center; + margin-left: $toolBarSectionSpacing * 0.5; + margin-right: $toolBarSectionSpacing * 0.5; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + + &:only-child { + margin-left: auto; /* 1 */ + } + + & > * + * { + margin-left: $toolBarItsemSpacing; /* 1 */ + } +} + * { box-sizing: border-box; } @@ -102,6 +140,7 @@ body { font-family: $font; } +@import "bar/index"; @import "button/index"; @import "form/index"; @import "icon/index"; diff --git a/ui_framework/components/tool_bar/_index.scss b/ui_framework/components/tool_bar/_index.scss index 70749aad66ba9..e1fabe167ea5c 100644 --- a/ui_framework/components/tool_bar/_index.scss +++ b/ui_framework/components/tool_bar/_index.scss @@ -1,6 +1,3 @@ -$toolBarSectionSpacing: 50px; -$toolBarItsemSpacing: 10px; - @import "tool_bar"; @import "tool_bar_footer"; @import "tool_bar_search"; diff --git a/ui_framework/components/tool_bar/_tool_bar.scss b/ui_framework/components/tool_bar/_tool_bar.scss index f447813f60088..26dc086112c3d 100644 --- a/ui_framework/components/tool_bar/_tool_bar.scss +++ b/ui_framework/components/tool_bar/_tool_bar.scss @@ -1,27 +1,12 @@ .kuiToolBar { - display: flex; - justify-content: space-between; + @include bar; + @include buttonOnReverseBackground; + padding: 10px; height: 50px; background-color: $panelColor; - - @include buttonOnReverseBackground; } -/** - * 1. Put 10px of space between each child. - */ .kuiToolBarSection { - display: flex; - align-items: center; - margin-left: $toolBarSectionSpacing * 0.5; - margin-right: $toolBarSectionSpacing * 0.5; - - &:last-child { - margin-right: 0; - } - - & > * + * { - margin-left: $toolBarItsemSpacing; /* 1 */ - } + @include barSection; } diff --git a/ui_framework/components/tool_bar/_tool_bar_footer.scss b/ui_framework/components/tool_bar/_tool_bar_footer.scss index d0b7f43e2151b..c33b66a7e9452 100644 --- a/ui_framework/components/tool_bar/_tool_bar_footer.scss +++ b/ui_framework/components/tool_bar/_tool_bar_footer.scss @@ -1,34 +1,12 @@ .kuiToolBarFooter { - display: flex; - justify-content: space-between; + @include bar; + padding: 10px; height: 40px; background-color: #ffffff; border: $tableBorder; } -/** - * 1. Put 10px of space between each child. - */ .kuiToolBarFooterSection { - display: flex; - align-items: center; - margin-left: $toolBarSectionSpacing * 0.5; - margin-right: $toolBarSectionSpacing * 0.5; - - &:first-child { - margin-left: 0; - } - - &:last-child { - margin-right: 0; - } - - &:only-child { - margin-left: auto; - } - - & > * + * { - margin-left: $toolBarItsemSpacing; /* 1 */ - } + @include barSection; } diff --git a/ui_framework/dist/ui_framework.css b/ui_framework/dist/ui_framework.css index 9fefcdb09850b..84979617d4673 100644 --- a/ui_framework/dist/ui_framework.css +++ b/ui_framework/dist/ui_framework.css @@ -2,12 +2,53 @@ * 1. Make sure outline doesn't get hidden beneath adjacent elements. * 2. Override inherited styles (possibly from Bootstrap). */ +/** + * 1. Put 10px of space between each child. + * 2. If there is only one child, align it to the right. If you wanted it aligned right, you + * wouldn't use the Bar in the first place. + */ * { box-sizing: border-box; } body { font-family: "Open Sans", Helvetica, Arial, sans-serif; } +.kuiBar { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; } + +.kuiBarSection { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + margin-left: 25px; + margin-right: 25px; } + .kuiBarSection:first-child { + margin-left: 0; } + .kuiBarSection:last-child { + margin-right: 0; } + .kuiBarSection:only-child { + margin-left: auto; + /* 1 */ } + .kuiBarSection > * + * { + margin-left: 10px; + /* 1 */ } + /** * 1. Setting to inline-block guarantees the same height when applied to both * button elements and anchor tags. @@ -877,6 +918,10 @@ body { display: -webkit-flex; display: -ms-flexbox; display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; @@ -891,9 +936,6 @@ body { color: #a7a7a7; background-color: #F3F3F3; } -/** - * 1. Put 10px of space between each child. - */ .kuiToolBarSection { display: -webkit-box; display: -webkit-flex; @@ -905,8 +947,13 @@ body { align-items: center; margin-left: 25px; margin-right: 25px; } + .kuiToolBarSection:first-child { + margin-left: 0; } .kuiToolBarSection:last-child { margin-right: 0; } + .kuiToolBarSection:only-child { + margin-left: auto; + /* 1 */ } .kuiToolBarSection > * + * { margin-left: 10px; /* 1 */ } @@ -916,6 +963,10 @@ body { display: -webkit-flex; display: -ms-flexbox; display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; @@ -925,9 +976,6 @@ body { background-color: #ffffff; border: 2px solid #E4E4E4; } -/** - * 1. Put 10px of space between each child. - */ .kuiToolBarFooterSection { display: -webkit-box; display: -webkit-flex; @@ -944,7 +992,8 @@ body { .kuiToolBarFooterSection:last-child { margin-right: 0; } .kuiToolBarFooterSection:only-child { - margin-left: auto; } + margin-left: auto; + /* 1 */ } .kuiToolBarFooterSection > * + * { margin-left: 10px; /* 1 */ } diff --git a/ui_framework/doc_site/src/services/routes/Routes.js b/ui_framework/doc_site/src/services/routes/Routes.js index cf87e6f52c1f2..5ebd5981b44f7 100644 --- a/ui_framework/doc_site/src/services/routes/Routes.js +++ b/ui_framework/doc_site/src/services/routes/Routes.js @@ -1,6 +1,9 @@ import Slugify from '../string/slugify'; +import BarExample + from '../../views/bar/bar_example.jsx'; + import ButtonExample from '../../views/button/button_example.jsx'; @@ -33,6 +36,9 @@ import ToolBarExample // Component route names should match the component name exactly. const components = [{ + name: 'Bar', + component: BarExample, +}, { name: 'Button', component: ButtonExample, }, { diff --git a/ui_framework/doc_site/src/views/bar/bar.html b/ui_framework/doc_site/src/views/bar/bar.html new file mode 100644 index 0000000000000..47346c625c347 --- /dev/null +++ b/ui_framework/doc_site/src/views/bar/bar.html @@ -0,0 +1,18 @@ +
+
+
+ + +
+
+ +
+
Limit to
+ +
pages
+
+
diff --git a/ui_framework/doc_site/src/views/bar/bar_example.jsx b/ui_framework/doc_site/src/views/bar/bar_example.jsx new file mode 100644 index 0000000000000..1b9663e97f358 --- /dev/null +++ b/ui_framework/doc_site/src/views/bar/bar_example.jsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import { + createExample, +} from '../../services'; + +export default createExample([{ + title: 'Bar', + description: ( +
+

Use the Bar to organize controls in a horizontal layout. This is especially useful for surfacing controls in the corners of a view.

+

Note: Instead of using this component with a Table, try using the ControlledTable, ToolBar, and ToolBarFooter components.

+
+ ), + html: require('./bar.html'), + hasDarkTheme: false, +}, { + title: 'One section', + description: ( +

A Bar with one section will align it to the right, by default. To align it to the left, just add another section and leave it empty, or don't use a Bar at all.

+ ), + html: require('./bar_one_section.html'), + hasDarkTheme: false, +}, { + title: 'Three sections', + description: ( +

Technically the Bar can contain three or more sections, but there's no established use-case for this.

+ ), + html: require('./bar_three_sections.html'), + hasDarkTheme: false, +}]); diff --git a/ui_framework/doc_site/src/views/bar/bar_one_section.html b/ui_framework/doc_site/src/views/bar/bar_one_section.html new file mode 100644 index 0000000000000..120421a1138c3 --- /dev/null +++ b/ui_framework/doc_site/src/views/bar/bar_one_section.html @@ -0,0 +1,12 @@ +
+
+
+ + +
+
+
diff --git a/ui_framework/doc_site/src/views/bar/bar_three_sections.html b/ui_framework/doc_site/src/views/bar/bar_three_sections.html new file mode 100644 index 0000000000000..f9941351a1132 --- /dev/null +++ b/ui_framework/doc_site/src/views/bar/bar_three_sections.html @@ -0,0 +1,38 @@ +
+
+
+ + +
+
+ +
+
+ + +
+
+ +
+
Limit to
+ +
pages
+ +
+ + +
+
+
From 2e5b1922a06daaef64919934092b84e51e754f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 12 Jan 2017 18:18:02 +0100 Subject: [PATCH 48/97] Add missing successor loading status --- src/core_plugins/kibana/public/context/query.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query.js index 4420a142b04aa..01129aca0b604 100644 --- a/src/core_plugins/kibana/public/context/query.js +++ b/src/core_plugins/kibana/public/context/query.js @@ -51,6 +51,8 @@ function QueryActionsProvider($q, es, Private) { rows: { anchor }, } = state; + state.loadingStatus.successors = 'loading'; + return fetchSuccessors(es, indexPattern, anchor, _.zipObject([sort]), successorCount) .then((successorDocuments) => { state.loadingStatus.successors = 'loaded'; From 5c50c1d9b0a6ff405becc0bd82ac14d3f4efcc22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 16 Jan 2017 13:26:54 +0100 Subject: [PATCH 49/97] Factor out initial queryParameters state creation This DRYs up the `QUERY_PARAMETERS_KEYS` and concentrates more of the query parameter concern in `query_parameters.js`. --- src/core_plugins/kibana/public/context/app.js | 10 ++------- .../kibana/public/context/query_parameters.js | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 44a8891d22d1d..9bae19eebdf6c 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -6,6 +6,7 @@ import './components/loading_button'; import './components/size_picker/size_picker'; import { bindSelectors } from './utils/selectors'; import { + createInitialQueryParametersState, QueryParameterActionsProvider, QUERY_PARAMETER_KEYS, } from './query_parameters'; @@ -81,14 +82,7 @@ function ContextAppController($scope, Private) { function createInitialState() { return { - queryParameters: { - anchorUid: null, - columns: [], - indexPattern: null, - predecessorCount: 0, - successorCount: 0, - sort: [], - }, + queryParameters: createInitialQueryParametersState(), rows: { anchor: null, predecessors: [], diff --git a/src/core_plugins/kibana/public/context/query_parameters.js b/src/core_plugins/kibana/public/context/query_parameters.js index 99a9494cf137b..792ae87cc1f49 100644 --- a/src/core_plugins/kibana/public/context/query_parameters.js +++ b/src/core_plugins/kibana/public/context/query_parameters.js @@ -2,14 +2,7 @@ import _ from 'lodash'; const MIN_CONTEXT_SIZE = 0; -const QUERY_PARAMETER_KEYS = [ - 'anchorUid', - 'columns', - 'indexPattern', - 'predecessorCount', - 'successorCount', - 'sort', -]; +const QUERY_PARAMETER_KEYS = Object.keys(createInitialQueryParametersState()); function QueryParameterActionsProvider(config) { const defaultSizeStep = parseInt(config.get('context:step'), 10); @@ -52,8 +45,20 @@ function QueryParameterActionsProvider(config) { }; } +function createInitialQueryParametersState() { + return { + anchorUid: null, + columns: [], + indexPattern: null, + predecessorCount: 0, + successorCount: 0, + sort: [], + }; +} + export { + createInitialQueryParametersState, QueryParameterActionsProvider, QUERY_PARAMETER_KEYS, }; From 43472f5d151d9a6968f5e1c2c140b60eeafeef81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 17 Jan 2017 09:44:19 +0100 Subject: [PATCH 50/97] Add basic acceptance tests (WIP) --- src/ui/public/doc_table/doc_table.html | 5 +-- test/functional/apps/context/_size.js | 19 +++++++++++ test/functional/apps/context/index.js | 25 ++++++++++++++ test/functional/index.js | 1 + test/server_config.js | 4 +++ test/support/page_objects/context_page.js | 41 +++++++++++++++++++++++ test/support/page_objects/index.js | 7 ++++ 7 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 test/functional/apps/context/_size.js create mode 100644 test/functional/apps/context/index.js create mode 100644 test/support/page_objects/context_page.js diff --git a/src/ui/public/doc_table/doc_table.html b/src/ui/public/doc_table/doc_table.html index 04a5c87ae13fa..97deda197188e 100644 --- a/src/ui/public/doc_table/doc_table.html +++ b/src/ui/public/doc_table/doc_table.html @@ -21,7 +21,7 @@ - +
+ ng-class="{'discover-table-row--highlight': row['$$_isAnchor']}" + data-test-subj="docTableRow">
diff --git a/test/functional/apps/context/_size.js b/test/functional/apps/context/_size.js new file mode 100644 index 0000000000000..69a3b409311fd --- /dev/null +++ b/test/functional/apps/context/_size.js @@ -0,0 +1,19 @@ +import { expect } from 'chai'; + +import { bdd, esClient } from '../../../support'; +import PageObjects from '../../../support/page_objects'; + + +bdd.describe('context size', function contextSize() { + bdd.it('should respect the default context size setting', async function () { + await esClient.updateConfigDoc({ 'context:defaultSize': '7' }); + await PageObjects.context.navigateTo('logstash-*', 'apache', 'AU_x3_BrGFA8no6QjjaI'); + + console.log(await PageObjects.context.remote.getCurrentUrl()); + expect(await PageObjects.context.getDocTableHeaderTexts()).to.have.members([ + '', + 'Time', + '@message', + ]); + }); +}); diff --git a/test/functional/apps/context/index.js b/test/functional/apps/context/index.js new file mode 100644 index 0000000000000..109a943efd6e8 --- /dev/null +++ b/test/functional/apps/context/index.js @@ -0,0 +1,25 @@ + +import { + bdd, + remote, + scenarioManager, + defaultTimeout +} from '../../../support'; + +import PageObjects from '../../../support/page_objects'; + +bdd.describe('context app', function () { + this.timeout = defaultTimeout; + + bdd.before(async function () { + await PageObjects.remote.setWindowSize(1200,800); + await scenarioManager.loadIfEmpty('logstashFunctional'); + await PageObjects.common.navigateToApp('discover'); + }); + + bdd.after(function unloadMakelogs() { + return scenarioManager.unload('logstashFunctional'); + }); + + require('./_size.js'); +}); diff --git a/test/functional/index.js b/test/functional/index.js index 65bf202538814..605bb1bee9d5d 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -35,6 +35,7 @@ define(function (require) { 'intern/dojo/node!./apps/visualize', 'intern/dojo/node!./apps/console', 'intern/dojo/node!./apps/dashboard', + 'intern/dojo/node!./apps/context', 'intern/dojo/node!./status_page' ].filter((suite) => { if (!requestedApps) return true; diff --git a/test/server_config.js b/test/server_config.js index 869f281e376f6..5528867bfcf00 100644 --- a/test/server_config.js +++ b/test/server_config.js @@ -34,6 +34,10 @@ module.exports = { pathname: kibanaURL, hash: '/discover', }, + context: { + pathname: kibanaURL, + hash: '/context', + }, visualize: { pathname: kibanaURL, hash: '/visualize', diff --git a/test/support/page_objects/context_page.js b/test/support/page_objects/context_page.js new file mode 100644 index 0000000000000..241a76a09a825 --- /dev/null +++ b/test/support/page_objects/context_page.js @@ -0,0 +1,41 @@ +import rison from 'rison-node'; + +import { config } from '../'; +import PageObjects from './'; +import getUrl from '../../utils/get_url'; + + +const DEFAULT_INITIAL_STATE = { + columns: ['@message'], +}; + +export default class DiscoverPage { + init(remote) { + this.remote = remote; + } + + async navigateTo(indexPattern, anchorType, anchorId, overrideInitialState = {}) { + const initialState = rison.encode({ + ...DEFAULT_INITIAL_STATE, + ...overrideInitialState, + }); + const appUrl = getUrl.noAuth(config.servers.kibana, { + ...config.apps.context, + hash: `${config.apps.context.hash}/${indexPattern}/${anchorType}/${anchorId}?_a=${initialState}`, + }); + + await this.remote.get(appUrl); + await this.remote.refresh(); + } + + async getDocTableHeaderTexts() { + const tableHeaderElements = await this.remote.findAllByCssSelector( + '[data-test-subj="docTable"] thead th' + ); + const tableHeaderTexts = await Promise.all(tableHeaderElements.map( + (tableHeaderElement) => tableHeaderElement.getVisibleText() + )); + + return tableHeaderTexts; + } +} diff --git a/test/support/page_objects/index.js b/test/support/page_objects/index.js index 50f88057df38f..c2303f4f23560 100644 --- a/test/support/page_objects/index.js +++ b/test/support/page_objects/index.js @@ -1,6 +1,7 @@ import Common from './common'; import ConsolePage from './console_page'; +import ContextPage from './context_page'; import DashboardPage from './dashboard_page'; import DiscoverPage from './discover_page'; import HeaderPage from './header_page'; @@ -11,6 +12,7 @@ import MonitoringPage from './monitoring_page'; const common = new Common(); const consolePage = new ConsolePage(); +const contextPage = new ContextPage(); const dashboardPage = new DashboardPage(); const discoverPage = new DiscoverPage(); const headerPage = new HeaderPage(); @@ -31,6 +33,7 @@ class PageObjects { this.remote = remote; common.init(remote); consolePage.init(remote); + contextPage.init(remote); dashboardPage.init(remote); discoverPage.init(remote); headerPage.init(remote); @@ -55,6 +58,10 @@ class PageObjects { return this.assertInitialized() && consolePage; } + get context() { + return this.assertInitialized() && contextPage; + } + get dashboard() { return this.assertInitialized() && dashboardPage; } From 5272a76e7c917f399f3ee1104839b6ecf0c15b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 18 Jan 2017 12:45:06 +0100 Subject: [PATCH 51/97] Add some context size tests --- .../kibana/public/context/app.html | 4 ++ test/functional/apps/context/_size.js | 59 +++++++++++++++---- test/functional/apps/context/index.js | 4 +- test/support/page_objects/context_page.js | 26 ++++++++ 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index f8b4ad92c7b90..f20c0559f9787 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -21,6 +21,7 @@
Limit to
newer entries
@@ -70,6 +72,7 @@
Limit to
older entries
diff --git a/test/functional/apps/context/_size.js b/test/functional/apps/context/_size.js index 69a3b409311fd..2d066d73ab13b 100644 --- a/test/functional/apps/context/_size.js +++ b/test/functional/apps/context/_size.js @@ -4,16 +4,55 @@ import { bdd, esClient } from '../../../support'; import PageObjects from '../../../support/page_objects'; +const TEST_INDEX_PATTERN = 'logstash-*'; +const TEST_ANCHOR_TYPE = 'apache'; +const TEST_ANCHOR_ID = 'AU_x3_BrGFA8no6QjjaI'; +const TEST_DEFAULT_CONTEXT_SIZE = 7; +const TEST_STEP_SIZE = 3; + bdd.describe('context size', function contextSize() { - bdd.it('should respect the default context size setting', async function () { - await esClient.updateConfigDoc({ 'context:defaultSize': '7' }); - await PageObjects.context.navigateTo('logstash-*', 'apache', 'AU_x3_BrGFA8no6QjjaI'); - - console.log(await PageObjects.context.remote.getCurrentUrl()); - expect(await PageObjects.context.getDocTableHeaderTexts()).to.have.members([ - '', - 'Time', - '@message', - ]); + bdd.it('should default to the `context:defaultSize` setting', async function () { + await esClient.updateConfigDoc({ 'context:defaultSize': `${TEST_DEFAULT_CONTEXT_SIZE}` }); + await PageObjects.context.navigateTo(TEST_INDEX_PATTERN, TEST_ANCHOR_TYPE, TEST_ANCHOR_ID); + + await PageObjects.common.try(async function () { + expect(await PageObjects.context.getDocTableBodyRows()).to.have.length(2 * TEST_DEFAULT_CONTEXT_SIZE + 1); + }); + await PageObjects.common.try(async function() { + const predecessorCountPicker = await PageObjects.context.getPredecessorCountPicker(); + expect(await predecessorCountPicker.getProperty('value')).to.equal(`${TEST_DEFAULT_CONTEXT_SIZE}`); + }); + await PageObjects.common.try(async function() { + const successorCountPicker = await PageObjects.context.getSuccessorCountPicker(); + expect(await successorCountPicker.getProperty('value')).to.equal(`${TEST_DEFAULT_CONTEXT_SIZE}`); + }); + }); + + bdd.it('should increase according to the `context:step` setting when clicking the `load newer` button', async function() { + await esClient.updateConfigDoc({ 'context:step': `${TEST_STEP_SIZE}` }); + await PageObjects.context.navigateTo(TEST_INDEX_PATTERN, TEST_ANCHOR_TYPE, TEST_ANCHOR_ID); + await PageObjects.context.waitForDocTable(); + + await (await PageObjects.context.getPredecessorLoadMoreButton()).click(); + await PageObjects.common.try(async function () { + expect(await PageObjects.context.getDocTableBodyRows()).to.have.length( + 2 * TEST_DEFAULT_CONTEXT_SIZE + TEST_STEP_SIZE + 1 + ); + }); + }); + + bdd.it('should increase according to the `context:step` setting when clicking the `load older` button', async function() { + await esClient.updateConfigDoc({ 'context:step': `${TEST_STEP_SIZE}` }); + await PageObjects.context.navigateTo(TEST_INDEX_PATTERN, TEST_ANCHOR_TYPE, TEST_ANCHOR_ID); + await PageObjects.context.waitForDocTable(); + + const successorLoadMoreButton = await PageObjects.context.getSuccessorLoadMoreButton(); + await this.remote.moveMouseTo(successorLoadMoreButton); + await successorLoadMoreButton.click(); + await PageObjects.common.try(async function () { + expect(await PageObjects.context.getDocTableBodyRows()).to.have.length( + 2 * TEST_DEFAULT_CONTEXT_SIZE + TEST_STEP_SIZE + 1 + ); + }); }); }); diff --git a/test/functional/apps/context/index.js b/test/functional/apps/context/index.js index 109a943efd6e8..a5d9c78345f74 100644 --- a/test/functional/apps/context/index.js +++ b/test/functional/apps/context/index.js @@ -1,9 +1,10 @@ import { bdd, + defaultTimeout, + elasticDump, remote, scenarioManager, - defaultTimeout } from '../../../support'; import PageObjects from '../../../support/page_objects'; @@ -14,6 +15,7 @@ bdd.describe('context app', function () { bdd.before(async function () { await PageObjects.remote.setWindowSize(1200,800); await scenarioManager.loadIfEmpty('logstashFunctional'); + await elasticDump.elasticLoad('visualize','.kibana'); await PageObjects.common.navigateToApp('discover'); }); diff --git a/test/support/page_objects/context_page.js b/test/support/page_objects/context_page.js index 241a76a09a825..1e3b94032b46a 100644 --- a/test/support/page_objects/context_page.js +++ b/test/support/page_objects/context_page.js @@ -28,6 +28,10 @@ export default class DiscoverPage { await this.remote.refresh(); } + waitForDocTable() { + return PageObjects.common.try(() => PageObjects.common.findTestSubject('docTable')); + } + async getDocTableHeaderTexts() { const tableHeaderElements = await this.remote.findAllByCssSelector( '[data-test-subj="docTable"] thead th' @@ -38,4 +42,26 @@ export default class DiscoverPage { return tableHeaderTexts; } + + getDocTableBodyRows() { + return this.remote.findAllByCssSelector( + '[data-test-subj="docTableRow"]' + ); + } + + getPredecessorCountPicker() { + return PageObjects.common.findTestSubject('predecessorCountPicker'); + } + + getSuccessorCountPicker() { + return PageObjects.common.findTestSubject('successorCountPicker'); + } + + getPredecessorLoadMoreButton() { + return PageObjects.common.findTestSubject('predecessorLoadMoreButton'); + } + + getSuccessorLoadMoreButton() { + return PageObjects.common.findTestSubject('predecessorLoadMoreButton'); + } } From 2756db18377c6a79ad0cf10f1cff168c6e007321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 18 Jan 2017 13:50:05 +0100 Subject: [PATCH 52/97] Remove selectors in reaction to review feedback --- .../kibana/public/context/app.html | 12 +++--- src/core_plugins/kibana/public/context/app.js | 21 ++++------ .../kibana/public/context/query.js | 41 ++++--------------- .../kibana/public/context/utils/selectors.js | 30 -------------- 4 files changed, 21 insertions(+), 83 deletions(-) delete mode 100644 src/core_plugins/kibana/public/context/utils/selectors.js diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index f20c0559f9787..e6abd2895ac0d 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -22,10 +22,9 @@
Load newer entries @@ -44,7 +43,7 @@
@@ -54,12 +53,12 @@
Load older entries diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 9bae19eebdf6c..8bb1fe3da5ddb 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -4,19 +4,12 @@ import uiModules from 'ui/modules'; import contextAppTemplate from './app.html'; import './components/loading_button'; import './components/size_picker/size_picker'; -import { bindSelectors } from './utils/selectors'; import { createInitialQueryParametersState, QueryParameterActionsProvider, QUERY_PARAMETER_KEYS, } from './query_parameters'; -import { - QueryActionsProvider, - selectIsLoadingAnchorRow, - selectIsLoadingPredecessorRows, - selectIsLoadingSuccessorRows, - selectRows, -} from './query'; +import { QueryActionsProvider } from './query'; const module = uiModules.get('apps/context', [ 'kibana', @@ -52,12 +45,11 @@ function ContextAppController($scope, Private) { ...queryActions, }, (action) => (...args) => action(this.state)(...args)); - this.derivedState = bindSelectors({ - isLoadingAnchorRow: selectIsLoadingAnchorRow, - isLoadingPredecessorRows: selectIsLoadingPredecessorRows, - isLoadingSuccessorRows: selectIsLoadingSuccessorRows, - rows: selectRows, - }, () => this.state); + $scope.$watchGroup([ + () => this.state.rows.predecessors, + () => this.state.rows.anchor, + () => this.state.rows.successors, + ], _.spread(this.actions.setAllRows)); /** * Sync query parameters to arguments @@ -84,6 +76,7 @@ function createInitialState() { return { queryParameters: createInitialQueryParametersState(), rows: { + all: [], anchor: null, predecessors: [], successors: [], diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query.js index 01129aca0b604..c4cad20fbbe80 100644 --- a/src/core_plugins/kibana/public/context/query.js +++ b/src/core_plugins/kibana/public/context/query.js @@ -2,7 +2,6 @@ import _ from 'lodash'; import { fetchAnchor } from './api/anchor'; import { fetchPredecessors, fetchSuccessors } from './api/context'; -import { createSelector } from './utils/selectors'; import { QueryParameterActionsProvider } from './query_parameters'; @@ -94,6 +93,14 @@ function QueryActionsProvider($q, es, Private) { return fetchSuccessorRows(state)(); }; + const setAllRows = (state) => (predecessorRows, anchorRow, successorRows) => ( + state.rows.all = [ + ...(predecessorRows || []), + ...(anchorRow ? [anchorRow] : []), + ...(successorRows || []), + ] + ); + return { fetchAllRows, fetchAllRowsWithNewQueryParameters, @@ -104,40 +111,10 @@ function QueryActionsProvider($q, es, Private) { fetchMoreSuccessorRows, fetchPredecessorRows, fetchSuccessorRows, + setAllRows, }; } -const selectIsLoadingAnchorRow = createSelector([ - (state) => state.loadingStatus.anchor, -], (anchorLoadingStatus) => ( - _.includes(['loading', 'uninitialized'], anchorLoadingStatus) -)); - -const selectIsLoadingPredecessorRows = createSelector([ - (state) => state.loadingStatus.predecessors, -], (predecessorsLoadingStatus) => ( - _.includes(['loading', 'uninitialized'], predecessorsLoadingStatus) -)); - -const selectIsLoadingSuccessorRows = createSelector([ - (state) => state.loadingStatus.successors, -], (successorsLoadingStatus) => ( - _.includes(['loading', 'uninitialized'], successorsLoadingStatus) -)); - -const selectRows = createSelector([ - (state) => state.rows.predecessors, - (state) => state.rows.anchor, - (state) => state.rows.successors, -], (predecessorRows, anchorRow, successorRows) => ( - [...predecessorRows, ...(anchorRow ? [anchorRow] : []), ...successorRows] -)); - - export { QueryActionsProvider, - selectIsLoadingAnchorRow, - selectIsLoadingPredecessorRows, - selectIsLoadingSuccessorRows, - selectRows, }; diff --git a/src/core_plugins/kibana/public/context/utils/selectors.js b/src/core_plugins/kibana/public/context/utils/selectors.js deleted file mode 100644 index 0a2f163c30817..0000000000000 --- a/src/core_plugins/kibana/public/context/utils/selectors.js +++ /dev/null @@ -1,30 +0,0 @@ -import _ from 'lodash'; - - -function bindSelectors(selectors, getState) { - return _.mapValues(selectors, (selector) => () => - selector(getState()) - ); -} - -function createSelector(dependencies, selector) { - let previousDependencies = []; - let previousResult = null; - - return (...args) => { - const currentDependencies = dependencies.map((dependency) => dependency(...args)); - - if (_.any(_.zipWith(previousDependencies, currentDependencies, (first, second) => first !== second))) { - previousDependencies = currentDependencies; - previousResult = selector(...previousDependencies); - } - - return previousResult; - }; -} - - -export { - bindSelectors, - createSelector, -}; From de6983f459859f8c4ed0e1745dd6c75f7cf42612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 19 Jan 2017 16:32:35 +0100 Subject: [PATCH 53/97] Bind SizePickerController to a proper name --- .../context/components/size_picker/size_picker.html | 2 +- .../public/context/components/size_picker/size_picker.js | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html index 95df4b263f912..355ed066d4cd3 100644 --- a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html @@ -1,6 +1,6 @@ diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js index 3bdfc71675183..d8139243192b1 100644 --- a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js @@ -11,7 +11,7 @@ module.directive('contextSizePicker', function ContextSizePicker() { return { bindToController: true, controller: ContextSizePickerController, - controllerAs: 'vm', + controllerAs: 'contextSizePicker', replace: true, restrict: 'E', scope: { @@ -24,9 +24,7 @@ module.directive('contextSizePicker', function ContextSizePicker() { function ContextSizePickerController() { - const vm = this; - - vm.getOrSetCount = (count) => ( - _.isUndefined(count) ? vm.count : vm.setCount(count) + this.getOrSetCount = (count) => ( + _.isUndefined(count) ? this.count : this.setCount(count) ); } From 29e1fa819ef1f6fb5a2f86a62a01ea1801c679f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 20 Jan 2017 14:08:17 +0100 Subject: [PATCH 54/97] Replace object spread syntax with Object.assign --- .../kibana/public/context/api/anchor.js | 11 +++++++---- .../kibana/public/context/api/utils/fields.js | 15 +++++++++------ .../kibana/public/context/api/utils/sorting.js | 11 +++++++---- src/core_plugins/kibana/public/context/app.js | 9 +++++---- .../kibana/public/context/query_parameters.js | 9 +++++---- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/anchor.js b/src/core_plugins/kibana/public/context/api/anchor.js index e19933a8c0df9..5e5226160ddc7 100644 --- a/src/core_plugins/kibana/public/context/api/anchor.js +++ b/src/core_plugins/kibana/public/context/api/anchor.js @@ -15,10 +15,13 @@ async function fetchAnchor(es, indexPattern, uid, sort) { throw new Error('Failed to load anchor row.'); } - return { - ...response.hits.hits[0], - $$_isAnchor: true, - }; + return Object.assign( + {}, + response.hits.hits[0], + { + $$_isAnchor: true, + }, + ); } diff --git a/src/core_plugins/kibana/public/context/api/utils/fields.js b/src/core_plugins/kibana/public/context/api/utils/fields.js index 4375ab6b3ef7b..e0035d61d0d61 100644 --- a/src/core_plugins/kibana/public/context/api/utils/fields.js +++ b/src/core_plugins/kibana/public/context/api/utils/fields.js @@ -4,12 +4,15 @@ import _ from 'lodash'; const addComputedFields = _.curry(function addComputedFields(indexPattern, query) { const computedFields = indexPattern.getComputedFields(); - return { - ...query, - script_fields: computedFields.scriptFields, - docvalue_fields: computedFields.docvalueFields, - stored_fields: computedFields.storedFields, - }; + return Object.assign( + {}, + query, + { + script_fields: computedFields.scriptFields, + docvalue_fields: computedFields.docvalueFields, + stored_fields: computedFields.storedFields, + }, + ); }); diff --git a/src/core_plugins/kibana/public/context/api/utils/sorting.js b/src/core_plugins/kibana/public/context/api/utils/sorting.js index f45cf2a1c655a..b89b41d247a67 100644 --- a/src/core_plugins/kibana/public/context/api/utils/sorting.js +++ b/src/core_plugins/kibana/public/context/api/utils/sorting.js @@ -2,10 +2,13 @@ import _ from 'lodash'; function reverseQuerySort(query) { - return { - ...query, - sort: _.get(query, 'sort', []).map(reverseSortDirective), - }; + return Object.assign( + {}, + query, + { + sort: _.get(query, 'sort', []).map(reverseSortDirective), + } + ); } function reverseSortDirective(sortDirective) { diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 8bb1fe3da5ddb..794572a730944 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -40,10 +40,11 @@ function ContextAppController($scope, Private) { this.state = createInitialState(); - this.actions = _.mapValues({ - ...queryParameterActions, - ...queryActions, - }, (action) => (...args) => action(this.state)(...args)); + this.actions = _.mapValues(Object.assign( + {}, + queryParameterActions, + queryActions, + ), (action) => (...args) => action(this.state)(...args)); $scope.$watchGroup([ () => this.state.rows.predecessors, diff --git a/src/core_plugins/kibana/public/context/query_parameters.js b/src/core_plugins/kibana/public/context/query_parameters.js index 792ae87cc1f49..2d16c458c2b59 100644 --- a/src/core_plugins/kibana/public/context/query_parameters.js +++ b/src/core_plugins/kibana/public/context/query_parameters.js @@ -30,10 +30,11 @@ function QueryParameterActionsProvider(config) { ); const setQueryParameters = (state) => (queryParameters) => ( - state.queryParameters = { - ...state.queryParameters, - ...(_.pick(queryParameters, QUERY_PARAMETER_KEYS)), - } + state.queryParameters = Object.assign( + {}, + state.queryParameters, + _.pick(queryParameters, QUERY_PARAMETER_KEYS), + ) ); return { From 19df62f20378294fba8a9f5ce4f3fc4b8116a338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 20 Jan 2017 14:15:10 +0100 Subject: [PATCH 55/97] Rename setCount to onChangeCount Renaming the `setCount` attribute to `onChangeCount` removes some assumptions about what the supplied function does, thereby decreasing coupling. --- src/core_plugins/kibana/public/context/app.html | 4 ++-- .../public/context/components/size_picker/size_picker.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index e6abd2895ac0d..e78f513bae450 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -35,7 +35,7 @@
newer entries
@@ -85,7 +85,7 @@
older entries
diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js index d8139243192b1..a059a26d7bf36 100644 --- a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js @@ -16,7 +16,7 @@ module.directive('contextSizePicker', function ContextSizePicker() { restrict: 'E', scope: { count: '=', - setCount: '=', + onChangeCount: '=', }, template: contextSizePickerTemplate, }; @@ -25,6 +25,6 @@ module.directive('contextSizePicker', function ContextSizePicker() { function ContextSizePickerController() { this.getOrSetCount = (count) => ( - _.isUndefined(count) ? this.count : this.setCount(count) + _.isUndefined(count) ? this.count : this.onChangeCount(count) ); } From 9e4dcea3effa9296b9e9644c445a07427db844a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 20 Jan 2017 15:59:20 +0100 Subject: [PATCH 56/97] Use Promise.try to catch more errors --- .../kibana/public/context/query.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query.js index c4cad20fbbe80..1134e5d4dbf61 100644 --- a/src/core_plugins/kibana/public/context/query.js +++ b/src/core_plugins/kibana/public/context/query.js @@ -5,7 +5,7 @@ import { fetchPredecessors, fetchSuccessors } from './api/context'; import { QueryParameterActionsProvider } from './query_parameters'; -function QueryActionsProvider($q, es, Private) { +function QueryActionsProvider(es, Private, Promise) { const { increasePredecessorCount, increaseSuccessorCount, @@ -20,7 +20,9 @@ function QueryActionsProvider($q, es, Private) { state.loadingStatus.anchor = 'loading'; - return fetchAnchor(es, indexPattern, anchorUid, _.zipObject([sort])) + return Promise.try(() => ( + fetchAnchor(es, indexPattern, anchorUid, _.zipObject([sort])) + )) .then((anchorDocument) => { state.loadingStatus.anchor = 'loaded'; state.rows.anchor = anchorDocument; @@ -36,7 +38,9 @@ function QueryActionsProvider($q, es, Private) { state.loadingStatus.predecessors = 'loading'; - return fetchPredecessors(es, indexPattern, anchor, _.zipObject([sort]), predecessorCount) + return Promise.try(() => ( + fetchPredecessors(es, indexPattern, anchor, _.zipObject([sort]), predecessorCount) + )) .then((predecessorDocuments) => { state.loadingStatus.predecessors = 'loaded'; state.rows.predecessors = predecessorDocuments; @@ -52,7 +56,9 @@ function QueryActionsProvider($q, es, Private) { state.loadingStatus.successors = 'loading'; - return fetchSuccessors(es, indexPattern, anchor, _.zipObject([sort]), successorCount) + return Promise.try(() => ( + fetchSuccessors(es, indexPattern, anchor, _.zipObject([sort]), successorCount) + )) .then((successorDocuments) => { state.loadingStatus.successors = 'loaded'; state.rows.successors = successorDocuments; @@ -61,8 +67,8 @@ function QueryActionsProvider($q, es, Private) { }; const fetchAllRows = (state) => () => ( - fetchAnchorRow(state)() - .then(() => $q.all([ + Promise.try(fetchAnchorRow(state)) + .then(() => Promise.all([ fetchPredecessorRows(state)(), fetchSuccessorRows(state)(), ])) From e0046e2870124b853d8092d08d0d2983b90e0284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 20 Jan 2017 19:07:05 +0100 Subject: [PATCH 57/97] Track loading errors and notify the user about them --- .../kibana/public/context/api/anchor.js | 2 +- .../kibana/public/context/app.html | 12 +++- src/core_plugins/kibana/public/context/app.js | 3 + .../kibana/public/context/query.js | 66 +++++++++++++------ 4 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/anchor.js b/src/core_plugins/kibana/public/context/api/anchor.js index 5e5226160ddc7..60fdeaa8151a6 100644 --- a/src/core_plugins/kibana/public/context/api/anchor.js +++ b/src/core_plugins/kibana/public/context/api/anchor.js @@ -12,7 +12,7 @@ async function fetchAnchor(es, indexPattern, uid, sort) { }); if (_.get(response, ['hits', 'total'], 0) < 1) { - throw new Error('Failed to load anchor row.'); + throw new Error('Failed to load anchor document.'); } return Object.assign( diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index e78f513bae450..10f81c4b8f74c 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -43,7 +43,7 @@
@@ -51,6 +51,16 @@
+ +
+
+ Failed to load the anchor document +
+
+
(subject, status) => ( + state.loadingStatus[subject] = status + ); const fetchAnchorRow = (state) => () => { const { queryParameters: { indexPattern, anchorUid, sort } } = state; - state.loadingStatus.anchor = 'loading'; + setLoadingStatus(state)('anchor', 'loading'); return Promise.try(() => ( fetchAnchor(es, indexPattern, anchorUid, _.zipObject([sort])) )) - .then((anchorDocument) => { - state.loadingStatus.anchor = 'loaded'; - state.rows.anchor = anchorDocument; - return anchorDocument; - }); + .then( + (anchorDocument) => { + setLoadingStatus(state)('anchor', 'loaded'); + state.rows.anchor = anchorDocument; + return anchorDocument; + }, + (error) => { + setLoadingStatus(state)('anchor', 'failed'); + notifier.error(error); + throw error; + } + ); }; const fetchPredecessorRows = (state) => () => { @@ -36,16 +50,23 @@ function QueryActionsProvider(es, Private, Promise) { rows: { anchor }, } = state; - state.loadingStatus.predecessors = 'loading'; + setLoadingStatus(state)('predecessors', 'loading'); return Promise.try(() => ( fetchPredecessors(es, indexPattern, anchor, _.zipObject([sort]), predecessorCount) )) - .then((predecessorDocuments) => { - state.loadingStatus.predecessors = 'loaded'; - state.rows.predecessors = predecessorDocuments; - return predecessorDocuments; - }); + .then( + (predecessorDocuments) => { + setLoadingStatus(state)('predecessors', 'loaded'); + state.rows.predecessors = predecessorDocuments; + return predecessorDocuments; + }, + (error) => { + setLoadingStatus(state)('predecessors', 'failed'); + notifier.error(error); + throw error; + }, + ); }; const fetchSuccessorRows = (state) => () => { @@ -54,16 +75,23 @@ function QueryActionsProvider(es, Private, Promise) { rows: { anchor }, } = state; - state.loadingStatus.successors = 'loading'; + setLoadingStatus(state)('successors', 'loading'); return Promise.try(() => ( fetchSuccessors(es, indexPattern, anchor, _.zipObject([sort]), successorCount) )) - .then((successorDocuments) => { - state.loadingStatus.successors = 'loaded'; - state.rows.successors = successorDocuments; - return successorDocuments; - }); + .then( + (successorDocuments) => { + setLoadingStatus(state)('successors', 'loaded'); + state.rows.successors = successorDocuments; + return successorDocuments; + }, + (error) => { + setLoadingStatus(state)('successors', 'failed'); + notifier.error(error); + throw error; + }, + ); }; const fetchAllRows = (state) => () => ( From 1b7fb542111c9278913a307586e95b2aae0a856e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 20 Jan 2017 19:21:24 +0100 Subject: [PATCH 58/97] Disable size picker inputs until anchor is loaded --- src/core_plugins/kibana/public/context/app.html | 2 ++ .../public/context/components/size_picker/size_picker.html | 1 + .../kibana/public/context/components/size_picker/size_picker.js | 1 + 3 files changed, 4 insertions(+) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 10f81c4b8f74c..f629eb2d3f009 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -35,6 +35,7 @@
newer entries
@@ -95,6 +96,7 @@
older entries
diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html index 355ed066d4cd3..6d1516279cb5d 100644 --- a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.html @@ -1,5 +1,6 @@ Date: Tue, 24 Jan 2017 14:02:34 +0100 Subject: [PATCH 60/97] Explicitly reset the view value in debounced input --- .../context/components/size_picker/size_picker.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js index 08c21c976046b..0ea2fd17833be 100644 --- a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js @@ -12,8 +12,10 @@ module.directive('contextSizePicker', function ContextSizePicker() { bindToController: true, controller: ContextSizePickerController, controllerAs: 'contextSizePicker', + link: linkContextSizePicker, replace: true, restrict: 'E', + require: 'ngModel', scope: { count: '=', isDisabled: '=', @@ -23,8 +25,16 @@ module.directive('contextSizePicker', function ContextSizePicker() { }; }); +function linkContextSizePicker(scope, element, attrs, ngModel) { + scope.countModel = ngModel; +} + +function ContextSizePickerController($scope) { + $scope.$watch( + () => this.count, + () => $scope.countModel.$rollbackViewValue(), + ); -function ContextSizePickerController() { this.getOrSetCount = (count) => ( _.isUndefined(count) ? this.count : this.onChangeCount(count) ); From 826910ebad1a6572eb19b1c8cd1cefca8169236e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 25 Jan 2017 15:37:16 +0100 Subject: [PATCH 61/97] Replace uses of chai with expect.js in response to review feedback --- .../public/context/api/__tests__/anchor.js | 20 +++++------ .../context/api/utils/__tests__/fields.js | 21 +++++++----- .../context/api/utils/__tests__/queries.js | 33 +++++++++---------- .../context/api/utils/__tests__/sorting.js | 18 +++++----- test/functional/apps/context/_size.js | 2 +- 5 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/__tests__/anchor.js b/src/core_plugins/kibana/public/context/api/__tests__/anchor.js index c56de3c5c7553..02bd4220e6509 100644 --- a/src/core_plugins/kibana/public/context/api/__tests__/anchor.js +++ b/src/core_plugins/kibana/public/context/api/__tests__/anchor.js @@ -1,4 +1,4 @@ -import { expect } from 'chai'; +import expect from 'expect.js'; import sinon from 'sinon'; import { fetchAnchor } from 'plugins/kibana/context/api/anchor'; @@ -12,10 +12,8 @@ describe('context app', function () { return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' }) .then(() => { - expect(esStub.search.calledOnce).to.be.true; - expect(esStub.search.firstCall.args) - .to.have.lengthOf(1) - .with.deep.property('[0].index', 'index1'); + expect(esStub.search.calledOnce).to.be(true); + expect(esStub.search.firstCall.args[0]).to.have.property('index', 'index1'); }); }); @@ -25,11 +23,9 @@ describe('context app', function () { return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' }) .then(() => { - expect(esStub.search.calledOnce).to.be.true; - expect(esStub.search.firstCall.args) - .to.have.lengthOf(1) - .with.deep.property('[0].body') - .that.contains.all.keys(['script_fields', 'docvalue_fields', 'stored_fields']); + expect(esStub.search.calledOnce).to.be(true); + expect(esStub.search.firstCall.args[0].body).to.have.keys([ + 'script_fields', 'docvalue_fields', 'stored_fields']); }); }); @@ -40,10 +36,10 @@ describe('context app', function () { return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' }) .then( () => { - expect(true, 'expected the promise to be rejected').to.be.false; + expect().fail('expected the promise to be rejected'); }, (error) => { - expect(error).to.be.an('error'); + expect(error).to.be.an(Error); } ); }); diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js index ad43421752d8b..4da7475a0e120 100644 --- a/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/fields.js @@ -1,4 +1,4 @@ -import { expect } from 'chai'; +import expect from 'expect.js'; import { addComputedFields } from 'plugins/kibana/context/api/utils/fields'; @@ -14,8 +14,9 @@ describe('context app', function () { } }); - expect(addComputedFields({ getComputedFields }, {})).to.have.property('script_fields') - .that.is.deep.equal(getComputedFields().scriptFields); + const query = addComputedFields({ getComputedFields }, {}); + expect(query).to.have.property('script_fields'); + expect(query.script_fields).to.eql(getComputedFields().scriptFields); }); it('should add the `docvalue_fields` property defined in the given index pattern', function () { @@ -23,8 +24,9 @@ describe('context app', function () { docvalueFields: ['field1'], }); - expect(addComputedFields({ getComputedFields }, {})).to.have.property('docvalue_fields') - .that.is.deep.equal(getComputedFields().docvalueFields); + const query = addComputedFields({ getComputedFields }, {}); + expect(query).to.have.property('docvalue_fields'); + expect(query.docvalue_fields).to.eql(getComputedFields().docvalueFields); }); it('should add the `stored_fields` property defined in the given index pattern', function () { @@ -32,15 +34,16 @@ describe('context app', function () { storedFields: ['field1'], }); - expect(addComputedFields({ getComputedFields }, {})).to.have.property('stored_fields') - .that.is.deep.equal(getComputedFields().storedFields); + const query = addComputedFields({ getComputedFields }, {}); + expect(query).to.have.property('stored_fields'); + expect(query.stored_fields).to.eql(getComputedFields().storedFields); }); it('should preserve other properties of the query', function () { const getComputedFields = () => ({}); - expect(addComputedFields({ getComputedFields }, { property1: 'value1' })) - .to.have.property('property1', 'value1'); + const query = addComputedFields({ getComputedFields }, { property1: 'value1' }); + expect(query).to.have.property('property1', 'value1'); }); }); }); diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js index 51802abd56814..d8dfbb8c5f439 100644 --- a/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js @@ -1,4 +1,4 @@ -import { expect } from 'chai'; +import expect from 'expect.js'; import { createAnchorQuery, @@ -9,36 +9,35 @@ import { describe('context app', function () { describe('function createAnchorQuery', function () { it('should return a search definition that searches the given uid', function () { - expect(createAnchorQuery('UID', { '@timestamp': 'desc' })) - .to.have.deep.property('query.terms._uid[0]', 'UID'); + const query = createAnchorQuery('UID', { '@timestamp': 'desc' }); + expect(query.query.terms._uid[0]).to.eql('UID'); }); it('should return a search definition that sorts by the given criteria', function () { - expect(createAnchorQuery('UID', { '@timestamp': 'desc' })) - .to.have.deep.property('sort[0]') - .that.is.deep.equal({ '@timestamp': 'desc' }); + const query = createAnchorQuery('UID', { '@timestamp': 'desc' }); + expect(query.sort[0]).to.eql({ '@timestamp': 'desc' }); }); }); describe('function createSuccessorsQuery', function () { it('should return a search definition that includes the given size', function () { - expect(createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10)) - .to.have.property('size', 10); + const query = createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10); + expect(query).to.have.property('size', 10); }); it('should return a search definition that sorts by the given criteria and uid', function () { - expect(createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10)) - .to.have.property('sort') - .that.is.deep.equal([ - { '@timestamp': 'desc' }, - { _uid: 'asc' }, - ]); + const query = createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10); + expect(query).to.have.property('sort'); + expect(query.sort).to.eql([ + { '@timestamp': 'desc' }, + { _uid: 'asc' }, + ]); }); it('should return a search definition that search after the given uid', function () { - expect(createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10)) - .to.have.property('search_after') - .that.is.deep.equal([0, 'UID']); + const query = createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10); + expect(query).to.have.property('search_after'); + expect(query.search_after).to.eql([0, 'UID']); }); }); }); diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js index 05619f7b977da..3dfea787df227 100644 --- a/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/sorting.js @@ -1,4 +1,4 @@ -import { expect } from 'chai'; +import expect from 'expect.js'; import { reverseQuerySort, @@ -17,7 +17,7 @@ describe('context app', function () { 'field3', '_score', ], - })).to.deep.equal({ + })).to.eql({ sort: [ { field1: { order: 'asc', mode: 'max' } }, { field2: 'desc' }, @@ -30,13 +30,13 @@ describe('context app', function () { describe('function reverseSortDirection', function () { it('should reverse a direction given as a string', function () { - expect(reverseSortDirection('asc')).to.equal('desc'); - expect(reverseSortDirection('desc')).to.equal('asc'); + expect(reverseSortDirection('asc')).to.eql('desc'); + expect(reverseSortDirection('desc')).to.eql('asc'); }); it('should reverse a direction given in an option object', function () { - expect(reverseSortDirection({ order: 'asc' })).to.deep.equal({ order: 'desc' }); - expect(reverseSortDirection({ order: 'desc' })).to.deep.equal({ order: 'asc' }); + expect(reverseSortDirection({ order: 'asc' })).to.eql({ order: 'desc' }); + expect(reverseSortDirection({ order: 'desc' })).to.eql({ order: 'asc' }); }); it('should preserve other properties than `order` in an option object', function () { @@ -49,15 +49,15 @@ describe('context app', function () { describe('function reverseSortDirective', function () { it('should return direction `asc` when given just `_score`', function () { - expect(reverseSortDirective('_score')).to.deep.equal({ _score: 'asc' }); + expect(reverseSortDirective('_score')).to.eql({ _score: 'asc' }); }); it('should return direction `desc` when given just a field name', function () { - expect(reverseSortDirective('field1')).to.deep.equal({ field1: 'desc' }); + expect(reverseSortDirective('field1')).to.eql({ field1: 'desc' }); }); it('should reverse direction when given an object', function () { - expect(reverseSortDirective({ field1: 'asc' })).to.deep.equal({ field1: 'desc' }); + expect(reverseSortDirective({ field1: 'asc' })).to.eql({ field1: 'desc' }); }); }); }); diff --git a/test/functional/apps/context/_size.js b/test/functional/apps/context/_size.js index 2d066d73ab13b..4328eaf09aeb8 100644 --- a/test/functional/apps/context/_size.js +++ b/test/functional/apps/context/_size.js @@ -1,4 +1,4 @@ -import { expect } from 'chai'; +import expect from 'expect.js'; import { bdd, esClient } from '../../../support'; import PageObjects from '../../../support/page_objects'; From 0566459e750aa410d4811e3fff300b3091850e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 25 Jan 2017 18:43:07 +0100 Subject: [PATCH 62/97] Add docstrings and annotations --- .../public/context/api/utils/sorting.js | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/core_plugins/kibana/public/context/api/utils/sorting.js b/src/core_plugins/kibana/public/context/api/utils/sorting.js index b89b41d247a67..b64f2b03fe026 100644 --- a/src/core_plugins/kibana/public/context/api/utils/sorting.js +++ b/src/core_plugins/kibana/public/context/api/utils/sorting.js @@ -1,6 +1,44 @@ import _ from 'lodash'; +/** + * A sort directive in object or string form. + * + * @typedef {(SortDirectiveString|SortDirectiveObject)} SortDirective + */ +/** + * A sort directive in object form. + * + * @typedef {Object.} SortDirectiveObject + */ + +/** + * A sort order string. + * + * @typedef {('asc'|'desc')} SortDirection + */ + +/** + * A field name. + * + * @typedef {string} FieldName + */ + +/** + * A sort options object + * + * @typedef {Object} SortOptions + * @property {SortDirection} order + */ + + +/** + * Return a copy of a query with the sort direction reversed. + * + * @param {Object} query - The query to reverse the sort direction of. + * + * @returns {Object} + */ function reverseQuerySort(query) { return Object.assign( {}, @@ -11,6 +49,16 @@ function reverseQuerySort(query) { ); } +/** + * Return a copy of the directive with the sort direction reversed. If the + * field name is '_score', it inverts the default sort direction in the same + * way as Elasticsearch itself. + * + * @param {SortDirective} sortDirective - The directive to reverse the + * sort direction of + * + * @returns {SortDirective} + */ function reverseSortDirective(sortDirective) { if (_.isString(sortDirective)) { return { @@ -23,6 +71,13 @@ function reverseSortDirective(sortDirective) { } } +/** + * Return the reversed sort direction. + * + * @param {(SortDirection|SortOptions)} sortDirection + * + * @returns {(SortDirection|SortOptions)} + */ function reverseSortDirection(sortDirection) { if (_.isPlainObject(sortDirection)) { return _.assign({}, sortDirection, { From 4b275b923fa765335341d3716273cbc313aa7d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 25 Jan 2017 19:25:08 +0100 Subject: [PATCH 63/97] Remove the need to separately pass the anchor uid Instead of requiring the anchor uid to be passed separately, the uid is now assumed to be included as the last value in the anchor document's `sort` property. --- .../kibana/public/context/api/context.js | 3 +-- .../public/context/api/utils/__tests__/queries.js | 15 +++++++++------ .../kibana/public/context/api/utils/queries.js | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/context.js b/src/core_plugins/kibana/public/context/api/context.js index 176e8e5923997..b569b6e62b866 100644 --- a/src/core_plugins/kibana/public/context/api/context.js +++ b/src/core_plugins/kibana/public/context/api/context.js @@ -22,10 +22,9 @@ async function fetchPredecessors(es, indexPattern, anchorDocument, sort, size) { function prepareQuery(indexPattern, anchorDocument, sort, size) { - const anchorUid = getDocumentUid(anchorDocument._type, anchorDocument._id); const successorsQuery = addComputedFields( indexPattern, - createSuccessorsQuery(anchorUid, anchorDocument.sort, sort, size) + createSuccessorsQuery(anchorDocument.sort, sort, size) ); return successorsQuery; } diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js index d8dfbb8c5f439..1a28522d22ee0 100644 --- a/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js @@ -13,20 +13,23 @@ describe('context app', function () { expect(query.query.terms._uid[0]).to.eql('UID'); }); - it('should return a search definition that sorts by the given criteria', function () { + it('should return a search definition that sorts by the given criteria and uid', function () { const query = createAnchorQuery('UID', { '@timestamp': 'desc' }); - expect(query.sort[0]).to.eql({ '@timestamp': 'desc' }); + expect(query.sort).to.eql([ + { '@timestamp': 'desc' }, + { _uid: 'asc' }, + ]); }); }); describe('function createSuccessorsQuery', function () { it('should return a search definition that includes the given size', function () { - const query = createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10); + const query = createSuccessorsQuery([0, 'UID'], { '@timestamp' : 'desc' }, 10); expect(query).to.have.property('size', 10); }); it('should return a search definition that sorts by the given criteria and uid', function () { - const query = createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10); + const query = createSuccessorsQuery([0, 'UID'], { '@timestamp' : 'desc' }, 10); expect(query).to.have.property('sort'); expect(query.sort).to.eql([ { '@timestamp': 'desc' }, @@ -34,8 +37,8 @@ describe('context app', function () { ]); }); - it('should return a search definition that search after the given uid', function () { - const query = createSuccessorsQuery('UID', [0], { '@timestamp' : 'desc' }, 10); + it('should return a search definition that searches after the given uid', function () { + const query = createSuccessorsQuery([0, 'UID'], { '@timestamp' : 'desc' }, 10); expect(query).to.have.property('search_after'); expect(query.search_after).to.eql([0, 'UID']); }); diff --git a/src/core_plugins/kibana/public/context/api/utils/queries.js b/src/core_plugins/kibana/public/context/api/utils/queries.js index 061dfa40b6025..35bbde0e68fee 100644 --- a/src/core_plugins/kibana/public/context/api/utils/queries.js +++ b/src/core_plugins/kibana/public/context/api/utils/queries.js @@ -6,11 +6,11 @@ function createAnchorQuery(uid, contextSort) { _uid: [uid], }, }, - sort: [contextSort], + sort: [ contextSort, { _uid: 'asc' } ], }; } -function createSuccessorsQuery(anchorUid, anchorSortValues, contextSort, size) { +function createSuccessorsQuery(anchorSortValues, contextSort, size) { return { _source: true, query: { @@ -18,7 +18,7 @@ function createSuccessorsQuery(anchorUid, anchorSortValues, contextSort, size) { }, size, sort: [ contextSort, { _uid: 'asc' } ], - search_after: anchorSortValues.concat([ anchorUid ]), + search_after: anchorSortValues, }; } From 61d7683d758cefa308482050dafb2a53ba6a60fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 26 Jan 2017 13:16:05 +0100 Subject: [PATCH 64/97] Re-use `KbnUrl` to encode the url components --- .../public/doc_table/components/table_row.js | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/ui/public/doc_table/components/table_row.js b/src/ui/public/doc_table/components/table_row.js index 320781db82500..91757322dc5e7 100644 --- a/src/ui/public/doc_table/components/table_row.js +++ b/src/ui/public/doc_table/components/table_row.js @@ -26,7 +26,7 @@ const MIN_LINE_LENGTH = 20; * * ``` */ -module.directive('kbnTableRow', function ($compile, $httpParamSerializer) { +module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl) { const cellTemplate = _.template(noWhiteSpace(require('ui/doc_table/components/table_row/cell.html'))); const truncateByHeightTemplate = _.template(noWhiteSpace(require('ui/partials/truncate_by_height.html'))); @@ -86,13 +86,17 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer) { }); $scope.getContextAppHref = () => { - return `#/context/${$scope.indexPattern.id}/${$scope.row._type}/${$scope.row._id}?${ - $httpParamSerializer({ - _a: rison.encode({ - columns: $scope.columns, - }), - }) - }`; + const path = kbnUrl.eval('#/context/{{ indexPattern }}/{{ anchorType }}/{{ anchorId }}', { + anchorId: $scope.row._id, + anchorType: $scope.row._type, + indexPattern: $scope.indexPattern.id, + }); + const hash = $httpParamSerializer({ + _a: rison.encode({ + columns: $scope.columns, + }), + }); + return `${path}?${hash}`; }; // create a tr element that lists the value for each *column* From c0021f2a7aa17b1030f9f5931e6b318e9788ebca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 26 Jan 2017 13:45:57 +0100 Subject: [PATCH 65/97] Preserve previous discover state in breadcrumb url --- src/core_plugins/kibana/public/context/app.html | 2 +- src/core_plugins/kibana/public/context/app.js | 10 ++++++++-- src/core_plugins/kibana/public/context/index.js | 11 ++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index f629eb2d3f009..355f72aec0d24 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -3,7 +3,7 @@
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 03dd4ff2a979d..4faec1e903a40 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -32,6 +32,7 @@ module.directive('contextApp', function ContextApp() { predecessorCount: '=', successorCount: '=', sort: '=', + discoverUrl: '=', }, template: contextAppTemplate, }; @@ -41,7 +42,7 @@ function ContextAppController($scope, Private) { const queryParameterActions = Private(QueryParameterActionsProvider); const queryActions = Private(QueryActionsProvider); - this.state = createInitialState(); + this.state = createInitialState(this.discoverUrl); this.actions = _.mapValues(Object.assign( {}, @@ -76,7 +77,7 @@ function ContextAppController($scope, Private) { ); } -function createInitialState() { +function createInitialState(discoverUrl) { return { queryParameters: createInitialQueryParametersState(), rows: { @@ -90,5 +91,10 @@ function createInitialState() { predecessors: 'uninitialized', successors: 'uninitialized', }, + navigation: { + discover: { + url: discoverUrl, + }, + }, }; } diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js index 88ecc46869098..2ccfdb5ffe6d9 100644 --- a/src/core_plugins/kibana/public/context/index.js +++ b/src/core_plugins/kibana/public/context/index.js @@ -16,6 +16,7 @@ uiRoutes ` this.state.save(true)); this.anchorUid = getDocumentUid($routeParams.type, $routeParams.id); this.indexPattern = indexPattern; + this.discoverUrl = chrome.getNavLinkById('kibana:discover').lastSubUrl; } function createDefaultAppState(config) { From a916f490830dd690382a48683e3a47e425daa740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 26 Jan 2017 18:06:53 +0100 Subject: [PATCH 66/97] Start to remove duplication from PageObjects --- test/support/page_objects/index.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/test/support/page_objects/index.js b/test/support/page_objects/index.js index c2303f4f23560..6ecfc78355c9f 100644 --- a/test/support/page_objects/index.js +++ b/test/support/page_objects/index.js @@ -26,21 +26,24 @@ class PageObjects { constructor() { this.isInitialized = false; this.remote = undefined; + this.pageObjects = [ + common, + consolePage, + contextPage, + dashboardPage, + discoverPage, + headerPage, + settingsPage, + shieldPage, + visualizePage, + monitoringPage, + ]; } init(remote) { this.isInitialized = true; this.remote = remote; - common.init(remote); - consolePage.init(remote); - contextPage.init(remote); - dashboardPage.init(remote); - discoverPage.init(remote); - headerPage.init(remote); - settingsPage.init(remote); - shieldPage.init(remote); - visualizePage.init(remote); - monitoringPage.init(remote); + this.pageObjects.map((pageObject) => pageObject.init(remote)); } assertInitialized() { From ef3573e34f33a2609dc93c274f2a48cfa96c8990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 31 Jan 2017 20:03:55 +0100 Subject: [PATCH 67/97] Update implementation notes --- .../kibana/public/context/NOTES.md | 58 +++++-------------- 1 file changed, 14 insertions(+), 44 deletions(-) diff --git a/src/core_plugins/kibana/public/context/NOTES.md b/src/core_plugins/kibana/public/context/NOTES.md index d46586f9c185b..32f02c59b0b30 100644 --- a/src/core_plugins/kibana/public/context/NOTES.md +++ b/src/core_plugins/kibana/public/context/NOTES.md @@ -16,15 +16,15 @@ application this is the `ContextAppController::state` object. consistency, it does little to make the state changes easier to reason about. To avoid having state mutations scattered all over the code, this app implements a unidirectional data flow architecture. That means that the state -is treated as immutable throughout the application and a new state may only be -derived via the root reducer (see below). +is treated as immutable throughout the application except for actions, which +may modify it to cause angular to re-render and watches to trigger. **Unit-Testability**: Creating unit tests for large parts of the UI code is -made easy by expressing the state management logic mostly as side-effect-free -functions. The only place where side-effects are allowed are action creators -and the reducer middlewares. Due to the nature of AngularJS a certain amount of -impure code must be employed in some cases, e.g. when dealing with the isolate -scope bindings in `ContextAppController`. +made easy by expressing the as much of the logic as possible as +side-effect-free functions. The only place where side-effects are allowed are +actions. Due to the nature of AngularJS a certain amount of impure code must be +employed in some cases, e.g. when dealing with the isolate scope bindings in +`ContextAppController`. **Loose Coupling**: An attempt was made to couple the parts that make up this app as loosely as possible. This means using pure functions whenever possible @@ -50,48 +50,21 @@ from the redux architecture that forms a ciruclar unidirectional data flow: | v | |* state | v - | |* selectors calculate derived (memoized) values + | |* angular templates render state | v - | |* angular templates render values + | |* angular calls actions in response to user action/system events | v - | |* angular dispatches actions in response to user action/system events - | v - | |* middleware processes the action - | v - | |* reducer derives new state from action + | |* actions modify state | v +--+ ``` **State**: The state is the single source of truth at -`ContextAppController::state` and may only be replaced by a new state create -via the root reducer. - -**Reducer**: The reducer at `ContextAppController.reducer` can derive a new -state from the previous state and an action. It must be a pure function and can -be composed from sub-reducers using various utilities like -`createReducerPipeline`, that passes the action and the previous sub-reducer's -result to each sub-reducer in order. The reducer is only called by the dispatch -function located at `ContextAppController::dispatch`. - -**Action**: Actions are objets that describe user or system actions in a -declarative manner. Each action is supposed to be passed to -`ContextAppController::dispatch`, that passes them to each of its middlewares -in turn before calling the root reducer with the final action. Usually, actions -are created using helper functions called action creators to ensure they adhere -to the [Flux Standard Action] schema. - -[Flux Standard Action]: https://github.com/acdlite/flux-standard-action - -**Selector**: To decouple the view from the specific state structure, selectors -encapsulate the knowledge about how to retrieve a particular set of values from -the state in a single location. Additionally, selectors can encapsulate the -logic for calculating derived properties from the state, which should be kept -as flat as possible and free of duplicate information. The performance penalty -for performing these calculations at query time is commonly circumvented by -memoizing the selector results as is the case with `createSelector` from -[redux_lite/selector_helpers.js](./redux_lite/selector_helpers.js). +`ContextAppController::state` and may only be modified by actions. + +**Action**: Actions are functions that are called inreponse user or system +actions and may modified the state the are bound to via their closure. ## Directory Structure @@ -122,6 +95,3 @@ create and execute the queries for the preceeding and succeeding documents. **api/utils**: Exports various functions used to create and transform queries. - -**redux_lite**: Exports various functions to create the dispatcher, action -creators, reducers and selectors. From 183991e77dec97eca258b9993dd997fd0e2c0b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 1 Feb 2017 13:30:25 +0100 Subject: [PATCH 68/97] Introduce loading status constants, separate state --- .../kibana/public/context/app.html | 17 +++++---- src/core_plugins/kibana/public/context/app.js | 16 +++++---- .../kibana/public/context/query.js | 36 ++++++++++++++----- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 355f72aec0d24..9c33f77c96180 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -22,7 +22,7 @@
@@ -35,7 +35,7 @@
newer entries
@@ -44,7 +44,10 @@
@@ -54,7 +57,7 @@
@@ -64,7 +67,7 @@
@@ -83,7 +86,7 @@
@@ -96,7 +99,7 @@
older entries
diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index 4faec1e903a40..cdd7a74afb549 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -9,7 +9,11 @@ import { QueryParameterActionsProvider, QUERY_PARAMETER_KEYS, } from './query_parameters'; -import { QueryActionsProvider } from './query'; +import { + createInitialLoadingStatusState, + LOADING_STATUS, + QueryActionsProvider, +} from './query'; const module = uiModules.get('apps/context', [ 'elasticsearch', @@ -50,6 +54,10 @@ function ContextAppController($scope, Private) { queryActions, ), (action) => (...args) => action(this.state)(...args)); + this.constants = { + LOADING_STATUS, + }; + $scope.$watchGroup([ () => this.state.rows.predecessors, () => this.state.rows.anchor, @@ -86,11 +94,7 @@ function createInitialState(discoverUrl) { predecessors: [], successors: [], }, - loadingStatus: { - anchor: 'uninitialized', - predecessors: 'uninitialized', - successors: 'uninitialized', - }, + loadingStatus: createInitialLoadingStatusState(), navigation: { discover: { url: discoverUrl, diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query.js index 7048e0577ca8e..7a5873f4a1bd2 100644 --- a/src/core_plugins/kibana/public/context/query.js +++ b/src/core_plugins/kibana/public/context/query.js @@ -5,6 +5,13 @@ import { fetchPredecessors, fetchSuccessors } from './api/context'; import { QueryParameterActionsProvider } from './query_parameters'; +const LOADING_STATUS = { + FAILED: 'failed', + LOADED: 'loaded', + LOADING: 'loading', + UNINITIALIZED: 'uninitialized', +}; + function QueryActionsProvider(es, Notifier, Private, Promise) { const { increasePredecessorCount, @@ -25,19 +32,19 @@ function QueryActionsProvider(es, Notifier, Private, Promise) { const fetchAnchorRow = (state) => () => { const { queryParameters: { indexPattern, anchorUid, sort } } = state; - setLoadingStatus(state)('anchor', 'loading'); + setLoadingStatus(state)('anchor', LOADING_STATUS.LOADING); return Promise.try(() => ( fetchAnchor(es, indexPattern, anchorUid, _.zipObject([sort])) )) .then( (anchorDocument) => { - setLoadingStatus(state)('anchor', 'loaded'); + setLoadingStatus(state)('anchor', LOADING_STATUS.LOADED); state.rows.anchor = anchorDocument; return anchorDocument; }, (error) => { - setLoadingStatus(state)('anchor', 'failed'); + setLoadingStatus(state)('anchor', LOADING_STATUS.FAILED); notifier.error(error); throw error; } @@ -50,19 +57,19 @@ function QueryActionsProvider(es, Notifier, Private, Promise) { rows: { anchor }, } = state; - setLoadingStatus(state)('predecessors', 'loading'); + setLoadingStatus(state)('predecessors', LOADING_STATUS.LOADING); return Promise.try(() => ( fetchPredecessors(es, indexPattern, anchor, _.zipObject([sort]), predecessorCount) )) .then( (predecessorDocuments) => { - setLoadingStatus(state)('predecessors', 'loaded'); + setLoadingStatus(state)('predecessors', LOADING_STATUS.LOADED); state.rows.predecessors = predecessorDocuments; return predecessorDocuments; }, (error) => { - setLoadingStatus(state)('predecessors', 'failed'); + setLoadingStatus(state)('predecessors', LOADING_STATUS.FAILED); notifier.error(error); throw error; }, @@ -75,19 +82,19 @@ function QueryActionsProvider(es, Notifier, Private, Promise) { rows: { anchor }, } = state; - setLoadingStatus(state)('successors', 'loading'); + setLoadingStatus(state)('successors', LOADING_STATUS.LOADING); return Promise.try(() => ( fetchSuccessors(es, indexPattern, anchor, _.zipObject([sort]), successorCount) )) .then( (successorDocuments) => { - setLoadingStatus(state)('successors', 'loaded'); + setLoadingStatus(state)('successors', LOADING_STATUS.LOADED); state.rows.successors = successorDocuments; return successorDocuments; }, (error) => { - setLoadingStatus(state)('successors', 'failed'); + setLoadingStatus(state)('successors', LOADING_STATUS.FAILED); notifier.error(error); throw error; }, @@ -149,6 +156,17 @@ function QueryActionsProvider(es, Notifier, Private, Promise) { }; } +function createInitialLoadingStatusState() { + return { + anchor: LOADING_STATUS.UNINITIALIZED, + predecessors: LOADING_STATUS.UNINITIALIZED, + successors: LOADING_STATUS.UNINITIALIZED, + }; +} + + export { + createInitialLoadingStatusState, + LOADING_STATUS, QueryActionsProvider, }; From 8997be716d99dea5bce86a2c6ca001537627e63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 1 Feb 2017 13:33:51 +0100 Subject: [PATCH 69/97] Remove obsolete border work-around --- src/core_plugins/kibana/public/discover/styles/main.less | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/core_plugins/kibana/public/discover/styles/main.less b/src/core_plugins/kibana/public/discover/styles/main.less index 436a2ea6871fe..796ccbfc1ae27 100644 --- a/src/core_plugins/kibana/public/discover/styles/main.less +++ b/src/core_plugins/kibana/public/discover/styles/main.less @@ -130,10 +130,6 @@ .discover-table-row--highlight { background-color: #E2F1F6; - - td { - border-top: none !important; - } } .shard-failures { From 61f0423ea4d40712ca5cf1136d1afd265f665e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 1 Feb 2017 17:20:39 +0100 Subject: [PATCH 70/97] Split query and query_parameters files --- .../context/{query.js => query/actions.js} | 31 +++---------------- .../kibana/public/context/query/constants.js | 6 ++++ .../kibana/public/context/query/index.js | 3 ++ .../kibana/public/context/query/state.js | 10 ++++++ .../actions.js} | 23 ++------------ .../context/query_parameters/constants.js | 4 +++ .../public/context/query_parameters/index.js | 3 ++ .../public/context/query_parameters/state.js | 10 ++++++ 8 files changed, 44 insertions(+), 46 deletions(-) rename src/core_plugins/kibana/public/context/{query.js => query/actions.js} (85%) create mode 100644 src/core_plugins/kibana/public/context/query/constants.js create mode 100644 src/core_plugins/kibana/public/context/query/index.js create mode 100644 src/core_plugins/kibana/public/context/query/state.js rename src/core_plugins/kibana/public/context/{query_parameters.js => query_parameters/actions.js} (73%) create mode 100644 src/core_plugins/kibana/public/context/query_parameters/constants.js create mode 100644 src/core_plugins/kibana/public/context/query_parameters/index.js create mode 100644 src/core_plugins/kibana/public/context/query_parameters/state.js diff --git a/src/core_plugins/kibana/public/context/query.js b/src/core_plugins/kibana/public/context/query/actions.js similarity index 85% rename from src/core_plugins/kibana/public/context/query.js rename to src/core_plugins/kibana/public/context/query/actions.js index 7a5873f4a1bd2..83609ef6bceca 100644 --- a/src/core_plugins/kibana/public/context/query.js +++ b/src/core_plugins/kibana/public/context/query/actions.js @@ -1,18 +1,12 @@ import _ from 'lodash'; -import { fetchAnchor } from './api/anchor'; -import { fetchPredecessors, fetchSuccessors } from './api/context'; -import { QueryParameterActionsProvider } from './query_parameters'; +import { fetchAnchor } from '../api/anchor'; +import { fetchPredecessors, fetchSuccessors } from '../api/context'; +import { QueryParameterActionsProvider } from '../query_parameters'; +import { LOADING_STATUS } from './constants'; -const LOADING_STATUS = { - FAILED: 'failed', - LOADED: 'loaded', - LOADING: 'loading', - UNINITIALIZED: 'uninitialized', -}; - -function QueryActionsProvider(es, Notifier, Private, Promise) { +export function QueryActionsProvider(es, Notifier, Private, Promise) { const { increasePredecessorCount, increaseSuccessorCount, @@ -155,18 +149,3 @@ function QueryActionsProvider(es, Notifier, Private, Promise) { setAllRows, }; } - -function createInitialLoadingStatusState() { - return { - anchor: LOADING_STATUS.UNINITIALIZED, - predecessors: LOADING_STATUS.UNINITIALIZED, - successors: LOADING_STATUS.UNINITIALIZED, - }; -} - - -export { - createInitialLoadingStatusState, - LOADING_STATUS, - QueryActionsProvider, -}; diff --git a/src/core_plugins/kibana/public/context/query/constants.js b/src/core_plugins/kibana/public/context/query/constants.js new file mode 100644 index 0000000000000..4d63357f105ba --- /dev/null +++ b/src/core_plugins/kibana/public/context/query/constants.js @@ -0,0 +1,6 @@ +export const LOADING_STATUS = { + FAILED: 'failed', + LOADED: 'loaded', + LOADING: 'loading', + UNINITIALIZED: 'uninitialized', +}; diff --git a/src/core_plugins/kibana/public/context/query/index.js b/src/core_plugins/kibana/public/context/query/index.js new file mode 100644 index 0000000000000..e0231e236fae4 --- /dev/null +++ b/src/core_plugins/kibana/public/context/query/index.js @@ -0,0 +1,3 @@ +export { QueryActionsProvider } from './actions'; +export { LOADING_STATUS } from './constants'; +export { createInitialLoadingStatusState } from './state'; diff --git a/src/core_plugins/kibana/public/context/query/state.js b/src/core_plugins/kibana/public/context/query/state.js new file mode 100644 index 0000000000000..bfce1a2d65465 --- /dev/null +++ b/src/core_plugins/kibana/public/context/query/state.js @@ -0,0 +1,10 @@ +import { LOADING_STATUS } from './constants'; + + +export function createInitialLoadingStatusState() { + return { + anchor: LOADING_STATUS.UNINITIALIZED, + predecessors: LOADING_STATUS.UNINITIALIZED, + successors: LOADING_STATUS.UNINITIALIZED, + }; +} diff --git a/src/core_plugins/kibana/public/context/query_parameters.js b/src/core_plugins/kibana/public/context/query_parameters/actions.js similarity index 73% rename from src/core_plugins/kibana/public/context/query_parameters.js rename to src/core_plugins/kibana/public/context/query_parameters/actions.js index 2d16c458c2b59..ed873ddab9905 100644 --- a/src/core_plugins/kibana/public/context/query_parameters.js +++ b/src/core_plugins/kibana/public/context/query_parameters/actions.js @@ -1,10 +1,11 @@ import _ from 'lodash'; +import { QUERY_PARAMETER_KEYS } from './constants'; + const MIN_CONTEXT_SIZE = 0; -const QUERY_PARAMETER_KEYS = Object.keys(createInitialQueryParametersState()); -function QueryParameterActionsProvider(config) { +export function QueryParameterActionsProvider(config) { const defaultSizeStep = parseInt(config.get('context:step'), 10); const setPredecessorCount = (state) => (predecessorCount) => ( @@ -45,21 +46,3 @@ function QueryParameterActionsProvider(config) { setSuccessorCount, }; } - -function createInitialQueryParametersState() { - return { - anchorUid: null, - columns: [], - indexPattern: null, - predecessorCount: 0, - successorCount: 0, - sort: [], - }; -} - - -export { - createInitialQueryParametersState, - QueryParameterActionsProvider, - QUERY_PARAMETER_KEYS, -}; diff --git a/src/core_plugins/kibana/public/context/query_parameters/constants.js b/src/core_plugins/kibana/public/context/query_parameters/constants.js new file mode 100644 index 0000000000000..2455e304fcb41 --- /dev/null +++ b/src/core_plugins/kibana/public/context/query_parameters/constants.js @@ -0,0 +1,4 @@ +import { createInitialQueryParametersState } from './state'; + + +export const QUERY_PARAMETER_KEYS = Object.keys(createInitialQueryParametersState()); diff --git a/src/core_plugins/kibana/public/context/query_parameters/index.js b/src/core_plugins/kibana/public/context/query_parameters/index.js new file mode 100644 index 0000000000000..cc513198cd55d --- /dev/null +++ b/src/core_plugins/kibana/public/context/query_parameters/index.js @@ -0,0 +1,3 @@ +export { QueryParameterActionsProvider } from './actions'; +export { QUERY_PARAMETER_KEYS } from './constants'; +export { createInitialQueryParametersState } from './state'; diff --git a/src/core_plugins/kibana/public/context/query_parameters/state.js b/src/core_plugins/kibana/public/context/query_parameters/state.js new file mode 100644 index 0000000000000..6289418fba3b4 --- /dev/null +++ b/src/core_plugins/kibana/public/context/query_parameters/state.js @@ -0,0 +1,10 @@ +export function createInitialQueryParametersState() { + return { + anchorUid: null, + columns: [], + indexPattern: null, + predecessorCount: 0, + successorCount: 0, + sort: [], + }; +} From 6af8043346629a1abd20fa9b284c979f57f4bbb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 1 Feb 2017 18:58:52 +0100 Subject: [PATCH 71/97] Extract route template into separate html file --- src/core_plugins/kibana/public/context/index.html | 9 +++++++++ src/core_plugins/kibana/public/context/index.js | 14 +++----------- 2 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 src/core_plugins/kibana/public/context/index.html diff --git a/src/core_plugins/kibana/public/context/index.html b/src/core_plugins/kibana/public/context/index.html new file mode 100644 index 0000000000000..46069537ada7e --- /dev/null +++ b/src/core_plugins/kibana/public/context/index.html @@ -0,0 +1,9 @@ + diff --git a/src/core_plugins/kibana/public/context/index.js b/src/core_plugins/kibana/public/context/index.js index 2ccfdb5ffe6d9..f217b33da5fee 100644 --- a/src/core_plugins/kibana/public/context/index.js +++ b/src/core_plugins/kibana/public/context/index.js @@ -1,6 +1,8 @@ import uiRoutes from 'ui/routes'; + import './app'; import { getDocumentUid } from './api/utils/ids'; +import contextAppRouteTemplate from './index.html'; uiRoutes @@ -12,17 +14,7 @@ uiRoutes return courier.indexPatterns.get($route.current.params.indexPattern); }, }, - template: ( - `` - ), + template: contextAppRouteTemplate, }); From d8a7a4ff1513784cc2b9efb7c44c7ea464b4d94e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 2 Feb 2017 11:28:09 +0100 Subject: [PATCH 72/97] Replace _.spread with argument spread syntax --- src/core_plugins/kibana/public/context/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_plugins/kibana/public/context/app.js b/src/core_plugins/kibana/public/context/app.js index cdd7a74afb549..58167d1504d0d 100644 --- a/src/core_plugins/kibana/public/context/app.js +++ b/src/core_plugins/kibana/public/context/app.js @@ -62,7 +62,7 @@ function ContextAppController($scope, Private) { () => this.state.rows.predecessors, () => this.state.rows.anchor, () => this.state.rows.successors, - ], _.spread(this.actions.setAllRows)); + ], (newValues) => this.actions.setAllRows(...newValues)); /** * Sync query parameters to arguments From a0040cc0096c8bcc5cc305f51083949095c7d832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 2 Feb 2017 11:45:02 +0100 Subject: [PATCH 73/97] Use new kui error info panel --- .../kibana/public/context/app.html | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 9c33f77c96180..ccc6294532077 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -16,7 +16,32 @@
-
+ +
+
+
+ + + Problem with query + +
+ +
+
+ Failed to load the anchor document. +
+
+
+
+ +
@@ -55,16 +80,6 @@
- -
-
- Failed to load the anchor document -
-
-
Date: Thu, 2 Feb 2017 12:37:03 +0100 Subject: [PATCH 74/97] Inline reversing the results --- src/core_plugins/kibana/public/context/api/context.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/context.js b/src/core_plugins/kibana/public/context/api/context.js index b569b6e62b866..0754ce089defe 100644 --- a/src/core_plugins/kibana/public/context/api/context.js +++ b/src/core_plugins/kibana/public/context/api/context.js @@ -16,7 +16,7 @@ async function fetchPredecessors(es, indexPattern, anchorDocument, sort, size) { const successorsQuery = prepareQuery(indexPattern, anchorDocument, sort, size); const predecessorsQuery = reverseQuerySort(successorsQuery); const reversedResults = await performQuery(es, indexPattern, predecessorsQuery); - const results = reverseResults(reversedResults); + const results = reversedResults.slice().reverse(); return results; } @@ -40,11 +40,6 @@ async function performQuery(es, indexPattern, query) { return _.get(response, ['hits', 'hits'], []); } -function reverseResults(results) { - results.reverse(); - return results; -} - export { fetchPredecessors, From e8862c7e804c77dff1f2fae557e5031ac58463e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 2 Feb 2017 15:24:18 +0100 Subject: [PATCH 75/97] Add a link to Discover to the error message --- src/core_plugins/kibana/public/context/app.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index ccc6294532077..ab85ae5326142 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -31,7 +31,9 @@
- Failed to load the anchor document. + Failed to load the anchor document. Please reload or visit + Discover + to select a valid anchor document.
From 0102c18a73cc937e35478e1245767e29497ae83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 2 Feb 2017 15:31:04 +0100 Subject: [PATCH 76/97] Rename disabled property of the loading button --- src/core_plugins/kibana/public/context/app.html | 4 ++-- .../context/components/loading_button/loading_button.html | 4 ++-- .../context/components/loading_button/loading_button.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index ab85ae5326142..bcbde69e7fa3f 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -49,7 +49,7 @@
@@ -103,7 +103,7 @@
diff --git a/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html index 7759ffb705625..a3ca64cbe8e2f 100644 --- a/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html +++ b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.html @@ -1,10 +1,10 @@ diff --git a/src/core_plugins/kibana/public/context/components/loading_button/loading_button.js b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.js index 3570c30c5d6ff..70a0764959685 100644 --- a/src/core_plugins/kibana/public/context/components/loading_button/loading_button.js +++ b/src/core_plugins/kibana/public/context/components/loading_button/loading_button.js @@ -12,7 +12,7 @@ module.directive('contextLoadingButton', function ContextLoadingButton() { replace: true, restrict: 'E', scope: { - disabled: '=', + isDisabled: '=', icon: '=', }, template: contextLoadingButtonTemplate, From 9af7cb6cb580e81d9c15fbd8f3d89d5466d3ae87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 3 Feb 2017 17:14:52 +0100 Subject: [PATCH 77/97] Show end-of-data warnings to the user --- src/core_plugins/kibana/public/context/app.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index bcbde69e7fa3f..c8351f90cc10a 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -55,6 +55,14 @@ > Load newer entries + + + +
@@ -109,6 +117,14 @@ > Load older entries + + + +
From f8dcd35d8c29d4d7f576812a87d632097b80989d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 6 Feb 2017 19:21:29 +0100 Subject: [PATCH 78/97] Rename query to queryBody where appropriate --- .../kibana/public/context/api/anchor.js | 5 ++-- .../kibana/public/context/api/context.js | 24 +++++++++---------- .../kibana/public/context/api/utils/fields.js | 4 ++-- .../public/context/api/utils/queries.js | 8 +++---- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/anchor.js b/src/core_plugins/kibana/public/context/api/anchor.js index 60fdeaa8151a6..90ce76e114e7c 100644 --- a/src/core_plugins/kibana/public/context/api/anchor.js +++ b/src/core_plugins/kibana/public/context/api/anchor.js @@ -1,14 +1,15 @@ import _ from 'lodash'; import { addComputedFields } from './utils/fields'; -import { createAnchorQuery } from './utils/queries'; +import { createAnchorQueryBody } from './utils/queries'; async function fetchAnchor(es, indexPattern, uid, sort) { const indices = await indexPattern.toIndexList(); + const queryBody = addComputedFields(indexPattern, createAnchorQueryBody(uid, sort)); const response = await es.search({ index: indices, - body: addComputedFields(indexPattern, createAnchorQuery(uid, sort)), + body: queryBody, }); if (_.get(response, ['hits', 'total'], 0) < 1) { diff --git a/src/core_plugins/kibana/public/context/api/context.js b/src/core_plugins/kibana/public/context/api/context.js index 0754ce089defe..70f69ad805a64 100644 --- a/src/core_plugins/kibana/public/context/api/context.js +++ b/src/core_plugins/kibana/public/context/api/context.js @@ -2,39 +2,39 @@ import _ from 'lodash'; import { addComputedFields } from './utils/fields'; import { getDocumentUid } from './utils/ids'; -import { createSuccessorsQuery } from './utils/queries.js'; +import { createSuccessorsQueryBody } from './utils/queries.js'; import { reverseQuerySort } from './utils/sorting'; async function fetchSuccessors(es, indexPattern, anchorDocument, sort, size) { - const successorsQuery = prepareQuery(indexPattern, anchorDocument, sort, size); - const results = await performQuery(es, indexPattern, successorsQuery); + const successorsQueryBody = prepareQueryBody(indexPattern, anchorDocument, sort, size); + const results = await performQuery(es, indexPattern, successorsQueryBody); return results; } async function fetchPredecessors(es, indexPattern, anchorDocument, sort, size) { - const successorsQuery = prepareQuery(indexPattern, anchorDocument, sort, size); - const predecessorsQuery = reverseQuerySort(successorsQuery); - const reversedResults = await performQuery(es, indexPattern, predecessorsQuery); + const successorsQueryBody = prepareQueryBody(indexPattern, anchorDocument, sort, size); + const predecessorsQueryBody = reverseQuerySort(successorsQueryBody); + const reversedResults = await performQuery(es, indexPattern, predecessorsQueryBody); const results = reversedResults.slice().reverse(); return results; } -function prepareQuery(indexPattern, anchorDocument, sort, size) { - const successorsQuery = addComputedFields( +function prepareQueryBody(indexPattern, anchorDocument, sort, size) { + const successorsQueryBody = addComputedFields( indexPattern, - createSuccessorsQuery(anchorDocument.sort, sort, size) + createSuccessorsQueryBody(anchorDocument.sort, sort, size) ); - return successorsQuery; + return successorsQueryBody; } -async function performQuery(es, indexPattern, query) { +async function performQuery(es, indexPattern, queryBody) { const indices = await indexPattern.toIndexList(); const response = await es.search({ index: indices, - body: query, + body: queryBody, }); return _.get(response, ['hits', 'hits'], []); diff --git a/src/core_plugins/kibana/public/context/api/utils/fields.js b/src/core_plugins/kibana/public/context/api/utils/fields.js index e0035d61d0d61..bb55aca77ce11 100644 --- a/src/core_plugins/kibana/public/context/api/utils/fields.js +++ b/src/core_plugins/kibana/public/context/api/utils/fields.js @@ -1,12 +1,12 @@ import _ from 'lodash'; -const addComputedFields = _.curry(function addComputedFields(indexPattern, query) { +const addComputedFields = _.curry(function addComputedFields(indexPattern, queryBody) { const computedFields = indexPattern.getComputedFields(); return Object.assign( {}, - query, + queryBody, { script_fields: computedFields.scriptFields, docvalue_fields: computedFields.docvalueFields, diff --git a/src/core_plugins/kibana/public/context/api/utils/queries.js b/src/core_plugins/kibana/public/context/api/utils/queries.js index 35bbde0e68fee..bcb0a07ab2365 100644 --- a/src/core_plugins/kibana/public/context/api/utils/queries.js +++ b/src/core_plugins/kibana/public/context/api/utils/queries.js @@ -1,4 +1,4 @@ -function createAnchorQuery(uid, contextSort) { +function createAnchorQueryBody(uid, contextSort) { return { _source: true, query: { @@ -10,7 +10,7 @@ function createAnchorQuery(uid, contextSort) { }; } -function createSuccessorsQuery(anchorSortValues, contextSort, size) { +function createSuccessorsQueryBody(anchorSortValues, contextSort, size) { return { _source: true, query: { @@ -24,6 +24,6 @@ function createSuccessorsQuery(anchorSortValues, contextSort, size) { export { - createAnchorQuery, - createSuccessorsQuery, + createAnchorQueryBody, + createSuccessorsQueryBody, }; From 41ea1bfd9877c4a3abaa71fee78bc07e42fe5e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 6 Feb 2017 19:27:20 +0100 Subject: [PATCH 79/97] Remove Discover breadcrumb in response to review feedback --- src/core_plugins/kibana/public/context/app.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index c8351f90cc10a..64f0c3198d854 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -2,9 +2,6 @@
-
- Discover -
From a3572d0368461182085d85f584c635d2116ffb95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 8 Feb 2017 12:34:21 +0100 Subject: [PATCH 80/97] Get the config options lazily The config values are read as late as possible. Previously, navigating the the advanced settings, changing a setting and navigating back without page reload would still use the old setting. --- .../kibana/public/context/query_parameters/actions.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core_plugins/kibana/public/context/query_parameters/actions.js b/src/core_plugins/kibana/public/context/query_parameters/actions.js index ed873ddab9905..29633e786f90f 100644 --- a/src/core_plugins/kibana/public/context/query_parameters/actions.js +++ b/src/core_plugins/kibana/public/context/query_parameters/actions.js @@ -6,7 +6,7 @@ import { QUERY_PARAMETER_KEYS } from './constants'; const MIN_CONTEXT_SIZE = 0; export function QueryParameterActionsProvider(config) { - const defaultSizeStep = parseInt(config.get('context:step'), 10); + const getDefaultSizeStep = () => parseInt(config.get('context:step'), 10); const setPredecessorCount = (state) => (predecessorCount) => ( state.queryParameters.predecessorCount = Math.max( @@ -15,7 +15,7 @@ export function QueryParameterActionsProvider(config) { ) ); - const increasePredecessorCount = (state) => (value = defaultSizeStep) => ( + const increasePredecessorCount = (state) => (value = getDefaultSizeStep()) => ( setPredecessorCount(state)(state.queryParameters.predecessorCount + value) ); @@ -26,7 +26,7 @@ export function QueryParameterActionsProvider(config) { ) ); - const increaseSuccessorCount = (state) => (value = defaultSizeStep) => ( + const increaseSuccessorCount = (state) => (value = getDefaultSizeStep()) => ( setSuccessorCount(state)(state.queryParameters.successorCount + value) ); From ce4c07f5f65db5a5427884ca8ae9cf9e6a5203f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 9 Feb 2017 21:41:22 +0100 Subject: [PATCH 81/97] Limit the context size to Elasticsearch's limit --- .../public/context/query_parameters/actions.js | 18 +++++++++++++----- .../context/query_parameters/constants.js | 2 ++ .../public/context/query_parameters/index.js | 6 +++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/core_plugins/kibana/public/context/query_parameters/actions.js b/src/core_plugins/kibana/public/context/query_parameters/actions.js index 29633e786f90f..b7232d0dc4196 100644 --- a/src/core_plugins/kibana/public/context/query_parameters/actions.js +++ b/src/core_plugins/kibana/public/context/query_parameters/actions.js @@ -1,16 +1,19 @@ import _ from 'lodash'; -import { QUERY_PARAMETER_KEYS } from './constants'; +import { + MAX_CONTEXT_SIZE, + MIN_CONTEXT_SIZE, + QUERY_PARAMETER_KEYS, +} from './constants'; -const MIN_CONTEXT_SIZE = 0; - export function QueryParameterActionsProvider(config) { const getDefaultSizeStep = () => parseInt(config.get('context:step'), 10); const setPredecessorCount = (state) => (predecessorCount) => ( - state.queryParameters.predecessorCount = Math.max( + state.queryParameters.predecessorCount = clamp( MIN_CONTEXT_SIZE, + MAX_CONTEXT_SIZE, predecessorCount, ) ); @@ -20,8 +23,9 @@ export function QueryParameterActionsProvider(config) { ); const setSuccessorCount = (state) => (successorCount) => ( - state.queryParameters.successorCount = Math.max( + state.queryParameters.successorCount = clamp( MIN_CONTEXT_SIZE, + MAX_CONTEXT_SIZE, successorCount, ) ); @@ -46,3 +50,7 @@ export function QueryParameterActionsProvider(config) { setSuccessorCount, }; } + +function clamp(minimum, maximum, value) { + return Math.max(Math.min(maximum, value), minimum); +} diff --git a/src/core_plugins/kibana/public/context/query_parameters/constants.js b/src/core_plugins/kibana/public/context/query_parameters/constants.js index 2455e304fcb41..939c4221bc883 100644 --- a/src/core_plugins/kibana/public/context/query_parameters/constants.js +++ b/src/core_plugins/kibana/public/context/query_parameters/constants.js @@ -1,4 +1,6 @@ import { createInitialQueryParametersState } from './state'; +export const MAX_CONTEXT_SIZE = 10000; // Elasticsearch's default maximum size limit +export const MIN_CONTEXT_SIZE = 0; export const QUERY_PARAMETER_KEYS = Object.keys(createInitialQueryParametersState()); diff --git a/src/core_plugins/kibana/public/context/query_parameters/index.js b/src/core_plugins/kibana/public/context/query_parameters/index.js index cc513198cd55d..dcc7b4e641ac7 100644 --- a/src/core_plugins/kibana/public/context/query_parameters/index.js +++ b/src/core_plugins/kibana/public/context/query_parameters/index.js @@ -1,3 +1,7 @@ export { QueryParameterActionsProvider } from './actions'; -export { QUERY_PARAMETER_KEYS } from './constants'; +export { + MAX_CONTEXT_SIZE, + MIN_CONTEXT_SIZE, + QUERY_PARAMETER_KEYS, +} from './constants'; export { createInitialQueryParametersState } from './state'; From 938e841cb18340e7a5d3c78720474d03b295b18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Thu, 9 Feb 2017 21:43:10 +0100 Subject: [PATCH 82/97] Increase the size picker width --- .../public/context/components/size_picker/size_picker.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less index ff0d1a0c99190..bec46e3c221e2 100644 --- a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.less @@ -4,7 +4,7 @@ .contextSizePicker { appearance: textfield; text-align: center; - width: 3em; + width: 5em; &::-webkit-outer-spin-button, &::-webkit-inner-spin-button { From 06245b928a5f62402d7763db9a3de4128cf73970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 10 Feb 2017 00:10:03 +0100 Subject: [PATCH 83/97] Move the size picker to the left --- .../kibana/public/context/app.html | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 64f0c3198d854..cd757dfe3fe62 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -44,13 +44,20 @@
+ + newer entries - Load newer entries + Load more contextApp.state.rows.predecessors.length)" > - +
-
Limit to
- -
newer entries
@@ -106,13 +105,20 @@
+ +
older entries
- Load older entries + Load more contextApp.state.rows.successors.length)" > - +
-
Limit to
- -
older entries
From 44c1e8c7d349c17e7f69183068c4771adc9185e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 10 Feb 2017 13:31:42 +0100 Subject: [PATCH 84/97] Add comment about ngModel getter/setter --- .../public/context/components/size_picker/size_picker.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js index 0ea2fd17833be..1cadca59639d4 100644 --- a/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js +++ b/src/core_plugins/kibana/public/context/components/size_picker/size_picker.js @@ -19,7 +19,11 @@ module.directive('contextSizePicker', function ContextSizePicker() { scope: { count: '=', isDisabled: '=', - onChangeCount: '=', + onChangeCount: '=', // To avoid inconsistent ngModel states this action + // should make sure the new value is propagated back + // to the `count` property. If that propagation + // fails, the user input will be reset to the value + // of `count`. }, template: contextSizePickerTemplate, }; From 519a3a4d7b0a7ebcb25e561cff1fb409dba36568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 10 Feb 2017 13:54:31 +0100 Subject: [PATCH 85/97] Don't disable load-more buttons on failure --- src/core_plugins/kibana/public/context/app.html | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index cd757dfe3fe62..63c3b7af90208 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -53,7 +53,10 @@ newer entries @@ -114,7 +117,10 @@
older entries
From 385685114fe10241e662022a247be54870a3ff40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 10 Feb 2017 14:04:16 +0100 Subject: [PATCH 86/97] Revert button styling of links in discover --- src/ui/public/doc_table/components/table_row.less | 11 ++++++++++- .../doc_table/components/table_row/details.html | 6 ++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/ui/public/doc_table/components/table_row.less b/src/ui/public/doc_table/components/table_row.less index ea7f4c0760bb5..c3cea6388dd15 100644 --- a/src/ui/public/doc_table/components/table_row.less +++ b/src/ui/public/doc_table/components/table_row.less @@ -1,10 +1,19 @@ @import (reference) "~ui/styles/variables"; +/** + * 1. Visually align the actions with the tabs. We can improve this by using flexbox instead, at a later point. + */ .documentTableRow__actions { float: right; font-family: @font-family-base; + padding-top: 8px; /* 1 */ } +/** + * 1. Put some space between the actions. + */ .documentTableRow__action { - text-decoration: none !important; + & + & { + margin-left: 10px; /* 1 */ + } } diff --git a/src/ui/public/doc_table/components/table_row/details.html b/src/ui/public/doc_table/components/table_row/details.html index 7fe95a7ced1f7..6a644f69b1c1b 100644 --- a/src/ui/public/doc_table/components/table_row/details.html +++ b/src/ui/public/doc_table/components/table_row/details.html @@ -1,18 +1,16 @@ From 61be5c174f47e881d5b2fc564f0c793a2d2d6751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 10 Feb 2017 15:19:59 +0100 Subject: [PATCH 87/97] Adjust header content --- src/core_plugins/kibana/public/context/app.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core_plugins/kibana/public/context/app.html b/src/core_plugins/kibana/public/context/app.html index 63c3b7af90208..34ace2adeff09 100644 --- a/src/core_plugins/kibana/public/context/app.html +++ b/src/core_plugins/kibana/public/context/app.html @@ -1,14 +1,14 @@
-
-
+
+ Surrounding Entries in {{ contextApp.state.queryParameters.indexPattern.id }}
-
+
From 21c9098c62b12d98be742a37323f1dba27dd8e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 10 Feb 2017 16:17:54 +0100 Subject: [PATCH 88/97] Fix function names in unit tests --- .../context/api/utils/__tests__/queries.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js b/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js index 1a28522d22ee0..bf6066d5604e3 100644 --- a/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js +++ b/src/core_plugins/kibana/public/context/api/utils/__tests__/queries.js @@ -1,20 +1,20 @@ import expect from 'expect.js'; import { - createAnchorQuery, - createSuccessorsQuery, + createAnchorQueryBody, + createSuccessorsQueryBody, } from 'plugins/kibana/context/api/utils/queries'; describe('context app', function () { - describe('function createAnchorQuery', function () { + describe('function createAnchorQueryBody', function () { it('should return a search definition that searches the given uid', function () { - const query = createAnchorQuery('UID', { '@timestamp': 'desc' }); + const query = createAnchorQueryBody('UID', { '@timestamp': 'desc' }); expect(query.query.terms._uid[0]).to.eql('UID'); }); it('should return a search definition that sorts by the given criteria and uid', function () { - const query = createAnchorQuery('UID', { '@timestamp': 'desc' }); + const query = createAnchorQueryBody('UID', { '@timestamp': 'desc' }); expect(query.sort).to.eql([ { '@timestamp': 'desc' }, { _uid: 'asc' }, @@ -22,14 +22,14 @@ describe('context app', function () { }); }); - describe('function createSuccessorsQuery', function () { + describe('function createSuccessorsQueryBody', function () { it('should return a search definition that includes the given size', function () { - const query = createSuccessorsQuery([0, 'UID'], { '@timestamp' : 'desc' }, 10); + const query = createSuccessorsQueryBody([0, 'UID'], { '@timestamp' : 'desc' }, 10); expect(query).to.have.property('size', 10); }); it('should return a search definition that sorts by the given criteria and uid', function () { - const query = createSuccessorsQuery([0, 'UID'], { '@timestamp' : 'desc' }, 10); + const query = createSuccessorsQueryBody([0, 'UID'], { '@timestamp' : 'desc' }, 10); expect(query).to.have.property('sort'); expect(query.sort).to.eql([ { '@timestamp': 'desc' }, @@ -38,7 +38,7 @@ describe('context app', function () { }); it('should return a search definition that searches after the given uid', function () { - const query = createSuccessorsQuery([0, 'UID'], { '@timestamp' : 'desc' }, 10); + const query = createSuccessorsQueryBody([0, 'UID'], { '@timestamp' : 'desc' }, 10); expect(query).to.have.property('search_after'); expect(query.search_after).to.eql([0, 'UID']); }); From c6ac75bb97346a7bbe076dbb4478a3a11896f76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 14 Feb 2017 14:56:37 +0100 Subject: [PATCH 89/97] Add test for navigation from Discover to Context View --- .../field_chooser/discover_field.html | 6 ++- .../doc_table/components/table_header.html | 6 +-- .../doc_table/components/table_row/cell.html | 2 +- .../components/table_row/details.html | 2 + .../doc_table/components/table_row/open.html | 2 +- src/ui/public/doc_table/doc_table.html | 3 +- .../apps/context/_discover_navigation.js | 54 +++++++++++++++++++ test/functional/apps/context/_size.js | 12 ++--- test/functional/apps/context/index.js | 3 +- test/support/page_objects/context_page.js | 33 +++++------- test/support/page_objects/discover_page.js | 5 +- test/support/page_objects/doc_table.js | 40 ++++++++++++++ test/support/page_objects/index.js | 8 +++ 13 files changed, 139 insertions(+), 37 deletions(-) create mode 100644 test/functional/apps/context/_discover_navigation.js create mode 100644 test/support/page_objects/doc_table.js diff --git a/src/core_plugins/kibana/public/discover/components/field_chooser/discover_field.html b/src/core_plugins/kibana/public/discover/components/field_chooser/discover_field.html index d950b12d422f9..c1fcb430e71b8 100644 --- a/src/core_plugins/kibana/public/discover/components/field_chooser/discover_field.html +++ b/src/core_plugins/kibana/public/discover/components/field_chooser/discover_field.html @@ -1,5 +1,9 @@ -