From c5720283db1b1c94ee49d3825f6a5cafab10b04f Mon Sep 17 00:00:00 2001 From: David Taylor Date: Fri, 20 Jan 2017 16:48:39 -0500 Subject: [PATCH 01/27] pfEmptyState: initial impl., List View integration, unit tests --- src/views/empty-state.component.js | 155 ++++++++++++++++++++++ src/views/empty-state.html | 30 +++++ src/views/listview/list-view.component.js | 24 +++- src/views/listview/list-view.html | 97 +++++++------- styles/angular-patternfly.css | 9 ++ test/views/empty-state.spec.js | 99 ++++++++++++++ 6 files changed, 365 insertions(+), 49 deletions(-) create mode 100644 src/views/empty-state.component.js create mode 100644 src/views/empty-state.html create mode 100644 test/views/empty-state.spec.js diff --git a/src/views/empty-state.component.js b/src/views/empty-state.component.js new file mode 100644 index 000000000..186cc1273 --- /dev/null +++ b/src/views/empty-state.component.js @@ -0,0 +1,155 @@ +/** + * @ngdoc directive + * @name patternfly.views.component:pfEmptyState + * @restrict E + * + * @description + * Component for rendering an empty state. + * + * @param {object} config Optional configuration object + * + * @param {array} actionButtons to display under the icon, title, and informational paragraph. + * + * @example + + +
+
+ +
+
+
+ +
+
+ +
+
+
+ + + angular.module('patternfly.views').controller('ViewCtrl', ['$scope', + function ($scope) { + $scope.eventText = ''; + + $scope.config = { + icon: 'pficon-add-circle-o', + title: 'Empty State Title', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; + + var performAction = function (action) { + $scope.eventText = action.name + " executed. \r\n" + $scope.eventText; + }; + + $scope.actionButtons = [ + { + name: 'Main Action', + title: 'Perform an action', + actionFn: performAction, + type: 'main' + }, + { + name: 'Secondary Action 1', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Secondary Action 2', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Secondary Action 3', + title: 'Perform an action', + actionFn: performAction + } + ]; + } + ]); + +
+*/ +angular.module('patternfly.views').component('pfEmptyState', { + bindings: { + config: ' +
+ +
+

+ {{$ctrl.config.title}} +

+

+ {{$ctrl.config.info}} +

+ +
+ +
+
+ +
+ diff --git a/src/views/listview/list-view.component.js b/src/views/listview/list-view.component.js index 8467bdbb5..192690e27 100644 --- a/src/views/listview/list-view.component.js +++ b/src/views/listview/list-view.component.js @@ -20,6 +20,7 @@ *
  • .useExpandingRows - (boolean) Allow row expansion for each list item. *
  • .selectionMatchProp - (string) Property of the items to use for determining matching, default is 'uuid' *
  • .selectedItems - (array) Current set of selected items + *
  • .itemsAvailable - (boolean) If 'false', displays the {@link patternfly.views.component:pfEmptyState Empty State} component. *
  • .checkDisabled - ( function(item) ) Function to call to determine if an item is disabled, default is none *
  • .onCheckBoxChange - ( function(item) ) Called to notify when a checkbox selection changes, default is none *
  • .onSelect - ( function(item, event) ) Called to notify of item selection, default is none @@ -50,13 +51,16 @@ * @param {function (item))} menuClassForItemFn function(item) Used to specify a class for an item's dropdown kebab * @param {function (action, item))} updateMenuActionForItemFn function(action, item) Used to update a menu action based on the current item * @param {object} customScope Object containing any variables/functions used by the transcluded html, access via customScope. + * @param {object} emptyStateConfig Optional configuration settings for the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component * @example
    Show Expanding Rows +
    @@ -227,6 +234,7 @@ dblClick: false, selectionMatchProp: 'name', selectedItems: [], + itemsAvailable: true, checkDisabled: checkDisabledItem, showSelectBox: true, useExpandingRows: false, @@ -237,6 +245,17 @@ onDblClick: handleDblClick }; + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; + $scope.items = [ { name: "Fred Flintstone", @@ -394,7 +413,8 @@ angular.module('patternfly.views').component('pfListView', { updateMenuActionForItemFn: '=?', actions: '=?', updateActionForItemFn: '=?', - customScope: '=?' + customScope: '=?', + emptyStateConfig: '=?' }, transclude: { expandedContent: '?listExpandedContent' diff --git a/src/views/listview/list-view.html b/src/views/listview/list-view.html index 775db616f..cf544ef86 100644 --- a/src/views/listview/list-view.html +++ b/src/views/listview/list-view.html @@ -1,52 +1,55 @@ -
    -
    -
    -
    - - -
    -
    - -
    - -
    - - +
    -
    -
    +
    -
    -
    + + diff --git a/styles/angular-patternfly.css b/styles/angular-patternfly.css index f41570c51..cee4be31b 100644 --- a/styles/angular-patternfly.css +++ b/styles/angular-patternfly.css @@ -512,3 +512,12 @@ table.dataTable tbody th, table.dataTable tbody td { .table-view-pf-select { width: 13px; } + +.blank-slate-pf { + height:100%; + margin-bottom: 0px; +} + +.blank-slate-pf button { + margin-right: 4px; +} diff --git a/test/views/empty-state.spec.js b/test/views/empty-state.spec.js new file mode 100644 index 000000000..503f28ffb --- /dev/null +++ b/test/views/empty-state.spec.js @@ -0,0 +1,99 @@ +describe('Component: pfEnptyState', function () { + var $scope; + var $compile; + var element; + var performedAction; + var updateCount; + + // load the controller's module + beforeEach(function () { + module('patternfly.views', 'patternfly.utils', 'views/empty-state.html'); + }); + + beforeEach(inject(function (_$compile_, _$rootScope_, _$timeout_) { + $compile = _$compile_; + $scope = _$rootScope_; + $timeout = _$timeout_; + })); + + var compileHTML = function (markup, scope) { + element = angular.element(markup); + $compile(element)(scope); + + scope.$digest(); + }; + + beforeEach(function () { + $scope.config = { + icon: 'pficon-add-circle-o', + title: 'Empty State Title', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; + + var performAction = function (action) { + $scope.eventText = action.name + " executed. \r\n" + $scope.eventText; + }; + + $scope.actionButtons = [ + { + name: 'Main Action', + title: 'Perform main action', + actionFn: performAction, + type: 'main' + }, + { + name: 'Secondary Action 1', + title: 'Perform secondary action 1', + actionFn: performAction + }, + { + name: 'Secondary Action 2', + title: 'Perform secondary action 2', + actionFn: performAction + }, + { + name: 'Secondary Action 3', + title: 'Perform secondary action 3', + actionFn: performAction + } + ]; + }); + + it('should display correct information from config and actionButtons', function () { + compileHTML('', $scope); + + expect(element.find('.pficon-add-circle-o').length).toBe(1); + expect(element.find('#title').text()).toContain('Empty State Title'); + expect(element.find('#info').text()).toContain('This is the Empty State component'); + expect(element.find('#helpLink').text()).toContain('For more information please see'); + expect(element.find('a').text()).toContain('pfExample'); + expect(element.find('a').prop('href')).toContain('#/api/patternfly.views.component:pfEmptyState'); + + var buttons = element.find('button'); + expect(buttons.length).toBe(4); + expect(angular.element(buttons[0]).text()).toContain('Main Action'); + expect(angular.element(buttons[0]).prop('title')).toContain('Perform main action'); + expect(angular.element(buttons[1]).text()).toContain('Secondary Action 1'); + expect(angular.element(buttons[1]).prop('title')).toContain('Perform secondary action 1'); + expect(angular.element(buttons[2]).text()).toContain('Secondary Action 2'); + expect(angular.element(buttons[2]).prop('title')).toContain('Perform secondary action 2'); + expect(angular.element(buttons[3]).text()).toContain('Secondary Action 3'); + expect(angular.element(buttons[3]).prop('title')).toContain('Perform secondary action 3'); + }); + + it('should only display main default title when no config and actionButtons defined', function () { + compileHTML('', $scope); + + expect(element.find('#title').text()).toContain('No Items Available'); + + expect(element.find('.pficon-add-circle-o').length).toBe(0); + expect(element.find('#info').length).toBe(0); + expect(element.find('#helpLink').length).toBe(0); + expect(element.find('button').length).toBe(0); + }); +}); From 447dd047d4ded02ba36a77607954c24db942f8c6 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 23 Jan 2017 11:58:04 -0500 Subject: [PATCH 02/27] pfEmptyState: Implemented for Card and Table views, as well as pfToolbar examples --- .../tableview/examples/table-view-basic.js | 54 ++++++++--- .../examples/table-view-with-toolbar.js | 51 ++++++++-- src/table/tableview/table-view.component.js | 3 +- src/table/tableview/table-view.html | 96 ++++++++++--------- src/toolbars/examples/toolbar.js | 48 +++++++++- src/views/cardview/card-view.component.js | 28 +++++- src/views/cardview/card-view.html | 25 ++--- src/views/empty-state.component.js | 2 +- src/views/listview/list-view.component.js | 5 +- test/views/empty-state.spec.js | 2 +- 10 files changed, 224 insertions(+), 90 deletions(-) diff --git a/src/table/tableview/examples/table-view-basic.js b/src/table/tableview/examples/table-view-basic.js index 4db3c6ed4..d9237cf55 100644 --- a/src/table/tableview/examples/table-view-basic.js +++ b/src/table/tableview/examples/table-view-basic.js @@ -9,8 +9,9 @@ * * @param {object} config Optional configuration object *
      - *
    • .selectionMatchProp - (string) Property of the items to use for determining matching, default is 'uuid' - *
    • .onCheckBoxChange - ( function(item) ) Called to notify when a checkbox selection changes, default is none + *
    • .selectionMatchProp - (string) Property of the items to use for determining matching, default is 'uuid' + *
    • .onCheckBoxChange - ( function(item) ) Called to notify when a checkbox selection changes, default is none + *
    • .itemsAvailable - (boolean) If 'false', displays the {@link patternfly.views.component:pfEmptyState Empty State} component. *
    * @param {object} dtOptions Optional angular-datatables DTOptionsBuilder configuration object. See {@link http://l-lin.github.io/angular-datatables/archives/#/api angular-datatables: DTOptionsBuilder} * @param {array} items Array of items to display in the table view. @@ -31,31 +32,48 @@ *
  • .title - (String) Optional title, used for the tooltip *
  • .actionFn - (function(action)) Function to invoke when the action selected * + * @param {object} emptyStateConfig Optional configuration settings for the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component * @example - +
    -
    - -
    -
    - -
    -
    +
    +
    + +
    + + +
    +
    +
    + +
    +
    + +
    +
    +
    + + + angular.module('patternfly.tableview.demo', ['patternfly.views','patternfly.table']); - angular.module('patternfly.table').controller('TableCtrl', ['$scope', + angular.module('patternfly.tableview.demo').controller('TableCtrl', ['$scope', function ($scope) { $scope.dtOptions = { order: [[2, "asc"]], @@ -123,7 +141,19 @@ $scope.config = { onCheckBoxChange: handleCheckBoxChange, - selectionMatchProp: "name" + selectionMatchProp: "name", + itemsAvailable: true + }; + + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } }; function handleCheckBoxChange (item) { diff --git a/src/table/tableview/examples/table-view-with-toolbar.js b/src/table/tableview/examples/table-view-with-toolbar.js index e5060f2a4..f383f3f41 100644 --- a/src/table/tableview/examples/table-view-with-toolbar.js +++ b/src/table/tableview/examples/table-view-with-toolbar.js @@ -8,8 +8,9 @@ * * @param {object} config Optional configuration object *
      - *
    • .selectionMatchProp - (string) Property of the items to use for determining matching, default is 'uuid' - *
    • .onCheckBoxChange - ( function(item) ) Called to notify when a checkbox selection changes, default is none + *
    • .selectionMatchProp - (string) Property of the items to use for determining matching, default is 'uuid' + *
    • .onCheckBoxChange - ( function(item) ) Called to notify when a checkbox selection changes, default is none + *
    • .itemsAvailable - (boolean) If 'false', displays the {@link patternfly.views.component:pfEmptyState Empty State} component. *
    * @param {object} dtOptions Optional angular-datatables DTOptionsBuilder configuration object. See {@link http://l-lin.github.io/angular-datatables/archives/#/api angular-datatables: DTOptionsBuilder} * @param {array} items Array of items to display in the table view. @@ -30,6 +31,7 @@ *
  • .title - (String) Optional title, used for the tooltip *
  • .actionFn - (function(action)) Function to invoke when the action selected * + * @param {object} emptyStateConfig Optional configuration settings for the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component * @example @@ -39,22 +41,29 @@
    -
    - +
    @@ -379,7 +388,19 @@ $scope.tableConfig = { onCheckBoxChange: handleCheckBoxChange, - selectionMatchProp: "name" + selectionMatchProp: "name", + itemsAvailable: true + }; + + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } }; $scope.tableActionButtons = [ @@ -426,6 +447,18 @@ actionFn: performTableAction } ]; + + $scope.updateItemsAvailable = function () { + if(!$scope.tableConfig.itemsAvailable) { + $scope.toolbarConfig.filterConfig.resultsCount = 0; + $scope.toolbarConfig.filterConfig.totalCount = 0; + $scope.toolbarConfig.filterConfig.selectedCount = 0; + } else { + $scope.toolbarConfig.filterConfig.resultsCount = $scope.items.length; + $scope.toolbarConfig.filterConfig.totalCount = $scope.allItems.length; + handleCheckBoxChange(); + } + }; } ]); diff --git a/src/table/tableview/table-view.component.js b/src/table/tableview/table-view.component.js index 87729c5ee..30d637e01 100644 --- a/src/table/tableview/table-view.component.js +++ b/src/table/tableview/table-view.component.js @@ -5,7 +5,8 @@ angular.module('patternfly.table').component('pfTableView', { colummns: '<', items: '<', actionButtons: ' - - - - {{col.header}} - Actions + + + + + + + + + + + + + + + - - - - - - - - - -
    {{col.header}}Actions
    + + {{ value }} +
    + +
    +
    + +
    - - {{ value }} -
    - -
    -
    - -
    + + + +
    diff --git a/src/toolbars/examples/toolbar.js b/src/toolbars/examples/toolbar.js index b2b0ab3db..5057ec015 100644 --- a/src/toolbars/examples/toolbar.js +++ b/src/toolbars/examples/toolbar.js @@ -80,7 +80,9 @@
    - +
    {{item.name}} @@ -100,7 +102,9 @@
    - +
    {{item.name}}
    @@ -115,9 +119,19 @@
    + items="items" + empty-state-config="emptyStateConfig">
    +
    +
    + +
    + +

    @@ -446,12 +460,25 @@ $scope.listConfig = { selectionMatchProp: 'name', checkDisabled: false, + itemsAvailable: true, onCheckBoxChange: handleCheckBoxChange }; + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; + $scope.tableConfig = { onCheckBoxChange: handleCheckBoxChange, - selectionMatchProp: "name" + selectionMatchProp: "name", + itemsAvailable: true, }; $scope.doAdd = function () { @@ -462,6 +489,19 @@ $scope.actionsText = "Option " + option + " selected\n" + $scope.actionsText; }; + $scope.updateItemsAvailable = function () { + $scope.tableConfig.itemsAvailable = $scope.listConfig.itemsAvailable; + if(!$scope.listConfig.itemsAvailable) { + $scope.toolbarConfig.filterConfig.resultsCount = 0; + $scope.toolbarConfig.filterConfig.totalCount = 0; + $scope.toolbarConfig.filterConfig.selectedCount = 0; + } else { + $scope.toolbarConfig.filterConfig.resultsCount = $scope.items.length; + $scope.toolbarConfig.filterConfig.totalCount = $scope.allItems.length; + handleCheckBoxChange(); + } + }; + function handleCheckBoxChange (item) { var selectedItems = $filter('filter')($scope.allItems, {selected: true}); if (selectedItems) { diff --git a/src/views/cardview/card-view.component.js b/src/views/cardview/card-view.component.js index 25a87d6e4..18e42c891 100644 --- a/src/views/cardview/card-view.component.js +++ b/src/views/cardview/card-view.component.js @@ -21,8 +21,9 @@ *
  • .onSelectionChange - ( function(items) ) Called to notify when item selections change, default is none *
  • .onClick - ( function(item, event) ) Called to notify when an item is clicked, default is none *
  • .onDblClick - ( function(item, event) ) Called to notify when an item is double clicked, default is none + *
  • .itemsAvailable - (boolean) If 'false', displays the {@link patternfly.views.component:pfEmptyState Empty State} component. * - * + * @param {object} emptyStateConfig Optional configuration settings for the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component * @param {Array} items the data to be shown in the cards
    * * @example @@ -40,7 +41,7 @@
    - +
    {{item.name}}
    @@ -88,6 +89,9 @@ +
    @@ -142,6 +146,7 @@ $scope.config = { selectItems: false, + itemsAvailable: true, multiSelect: false, dblClick: false, selectionMatchProp: 'name', @@ -187,6 +192,17 @@ state: "New York" }, ] + + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; } ]); @@ -195,12 +211,13 @@ angular.module('patternfly.views').component('pfCardView', { bindings: { config: '=?', + emptyStateConfig: '=?', items: '=', eventId: '@id' }, transclude: true, templateUrl: 'views/cardview/card-view.html', - controller: function (pfUtils) { + controller: function (pfUtils, $log) { 'use strict'; var ctrl = this; ctrl.defaultConfig = { @@ -303,7 +320,10 @@ angular.module('patternfly.views').component('pfCardView', { }; ctrl.$onInit = function () { - ctrl.config = pfUtils.merge(ctrl.defaultConfig, ctrl.config); + // Setting bound variables to new variables loses it's binding + // ctrl.config = pfUtils.merge(ctrl.defaultConfig, ctrl.config); + // Instead, use _.defaults to update the existing variable + _.defaults(ctrl.config, ctrl.defaultConfig); if (ctrl.config.selectItems && ctrl.config.showSelectBox) { throw new Error('pfCardView - ' + 'Illegal use of pfCardView component! ' + diff --git a/src/views/cardview/card-view.html b/src/views/cardview/card-view.html index 522d808f0..b38190d4a 100755 --- a/src/views/cardview/card-view.html +++ b/src/views/cardview/card-view.html @@ -1,13 +1,16 @@ -
    -
    -
    -
    -
    -
    - + +
    +
    +
    +
    +
    +
    + +
    -
    + + diff --git a/src/views/empty-state.component.js b/src/views/empty-state.component.js index 186cc1273..fbbe3fc3a 100644 --- a/src/views/empty-state.component.js +++ b/src/views/empty-state.component.js @@ -12,7 +12,7 @@ *
  • .title - (string) Text for the main title *
  • .info - (string) Text for the main informational paragraph * - * @param {array} actionButtons to display under the icon, title, and informational paragraph. + * @param {array} actionButtons Buttons to display under the icon, title, and informational paragraph. *