Add batch action functionality to table Component

These changes aren't currently implemented on any table.
It will be added to event logs and local user table.

- Create tableCheckbox component to handle custom styles
  and indeterminate state which needs to be set with JS
- Update tableComponent layout to allow transition for
  toolbar. Updated user-accounts layout and styles to
  account for these changes

Tested on Chrome, Safari, Firefox, Edge, IE

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: Ic57a090db1ef66f9d33facfdc425db868ae8d8c6
diff --git a/app/common/components/table/table-checkbox.js b/app/common/components/table/table-checkbox.js
new file mode 100644
index 0000000..b15e4ed
--- /dev/null
+++ b/app/common/components/table/table-checkbox.js
@@ -0,0 +1,67 @@
+window.angular && (function(angular) {
+  'use strict';
+
+  /**
+   *
+   * tableCheckbox Component
+   *
+   */
+
+  const controller = function($element) {
+    // <input> element ref needed to add indeterminate state
+    let inputEl;
+
+    /**
+     * Callback when the input select value changes
+     */
+    this.onSelectChange = () => {
+      const checked = this.ngModel;
+      this.emitChange({checked});
+    };
+
+    /**
+     * onChanges Component lifecycle hook
+     */
+    this.$onChanges = (onChangesObj) => {
+      const indeterminateChange = onChangesObj.indeterminate;
+      if (indeterminateChange && inputEl) {
+        inputEl.prop('indeterminate', this.indeterminate);
+      }
+    };
+
+    /**
+     * postLink Component lifecycle hook
+     */
+    this.$postLink = () => {
+      inputEl = $element.find('input');
+    };
+  };
+
+  /**
+   * Component template
+   */
+  const template = `
+    <div class="bmc-table__checkbox-container">
+      <label class="bmc-table__checkbox"
+             ng-class="{
+              'checked': $ctrl.ngModel,
+              'indeterminate': $ctrl.indeterminate
+            }">
+        <input type="checkbox"
+            class="bmc-table__checkbox-input"
+            ng-model="$ctrl.ngModel"
+            ng-change="$ctrl.onSelectChange()"
+            aria-label="Select row"/>
+        <span class="screen-reader-offscreen">Select row</span>
+      </label>
+    </div>`
+
+  /**
+   * Register tableCheckbox component
+   */
+  angular.module('app.common.components').component('tableCheckbox', {
+    controller: ['$element', controller],
+    template,
+    bindings: {ngModel: '=', indeterminate: '<', emitChange: '&'}
+  })
+})(window.angular);
\ No newline at end of file
diff --git a/app/common/components/table/table-toolbar.js b/app/common/components/table/table-toolbar.js
new file mode 100644
index 0000000..830bbd1
--- /dev/null
+++ b/app/common/components/table/table-toolbar.js
@@ -0,0 +1,112 @@
+window.angular && (function(angular) {
+  'use strict';
+
+  /**
+   *
+   * tableToolbar Component
+   *
+   * To use:
+   * The <table-toolbar> component expects an 'actions' attribute
+   * that should be an array of action objects.
+   * Each action object should have 'type', 'label', and 'file'
+   * properties.
+   *
+   * actions: [
+   *  {type: 'Edit', label: 'Edit', file: 'icon-edit.svg'},
+   *  {type: 'Delete', label: 'Edit', file: 'icon-trashcan.svg'}
+   * ]
+   *
+   * The 'type' property is a string value that will be emitted to the
+   * parent component when clicked.
+   *
+   * The 'label' property is a string value that will render as text in
+   * the button
+   *
+   * The 'file' property is a string value of the filename of the svg icon
+   * to provide <icon> directive.
+   *
+   */
+
+  const controller = function() {
+    /**
+     * Set defaults if properties undefined
+     * @param {[]} actions
+     */
+    const setActions = (actions = []) => {
+      return actions
+          .map((action) => {
+            if (action.type === undefined) {
+              return;
+            }
+            if (action.file === undefined) {
+              action.file = null;
+            }
+            return action;
+          })
+          .filter((action) => action);
+    };
+
+    /**
+     * Callback when button action clicked
+     * Emits the action type to the parent component
+     */
+    this.onClick = (action) => {
+      this.emitAction({action});
+    };
+
+    this.onClickClose = () => {
+      this.emitClose();
+    };
+
+    /**
+     * onInit Component lifecycle hook
+     */
+    this.$onInit = () => {
+      this.actions = setActions(this.actions);
+    };
+  };
+
+  /**
+   * Component template
+   */
+  const template = `
+    <div class="bmc-table__toolbar" ng-if="$ctrl.active">
+      <p class="toolbar__selection-count">{{$ctrl.selectionCount}} selected</p>
+      <div class="toolbar__batch-actions" ng-show="$ctrl.actions.length > 0">
+        <button
+          class="btn  btn-tertiary"
+          type="button"
+          aria-label="{{action.label}}"
+          ng-repeat="action in $ctrl.actions"
+          ng-click="$ctrl.onClick(action.type)">
+          <icon ng-if="action.file !== null"
+                ng-file="{{action.file}}"
+                aria-hidden="true">
+          </icon>
+          {{action.label || action.type}}
+        </button>
+        <button
+          class="btn  btn-tertiary  btn-close"
+          type="button"
+          aria-label="Cancel"
+          ng-click="$ctrl.onClickClose()">
+          Cancel
+        </button>
+      </div>
+    </div>`
+
+  /**
+   * Register tableToolbar component
+   */
+  angular.module('app.common.components').component('tableToolbar', {
+    controller,
+    template,
+    bindings: {
+      actions: '<',
+      selectionCount: '<',
+      active: '<',
+      emitAction: '&',
+      emitClose: '&'
+    }
+  })
+})(window.angular);
\ No newline at end of file
diff --git a/app/common/components/table/table.html b/app/common/components/table/table.html
index 96ca870..7d906a1 100644
--- a/app/common/components/table/table.html
+++ b/app/common/components/table/table.html
@@ -1,94 +1,131 @@
-<table class="bmc-table {{$ctrl.size}}">
-  <thead class="bmc-table__head">
-    <!-- Header row (non-sortable) -->
-    <tr ng-if="!$ctrl.sortable">
-      <th ng-repeat="headerItem in $ctrl.header"
-          class="bmc-table__column-header">
-        {{headerItem.label}}
-      </th>
-    </tr>
-    <!-- Header row (sortable) -->
-    <tr ng-if="$ctrl.sortable">
-      <th ng-repeat="headerItem in $ctrl.header track by $index"
-          class="bmc-table__column-header">
-        <span ng-if="!headerItem.sortable">
-          {{headerItem.label}}
-        </span>
-        <span ng-if="headerItem.sortable"
-              ng-click="$ctrl.onClickSort($index)"
-              class="bmc-table__column-header--sortable">
-          {{headerItem.label}}
-          <!-- Sort icons -->
-          <button class="sort-icon"
-                  type="button"
-                  aria-label="sort {{headerItem.label}}">
-            <icon file="icon-arrow--up.svg"
-                  ng-if="$index === $ctrl.activeSort"
-                  ng-class="{
-                    'sort-icon--descending': !$ctrl.sortAscending,
-                    'sort-icon--ascending' : $ctrl.sortAscending }"
-                  class="sort-icon--active"
-                  aria-hidden="true"></icon>
-            <span ng-if="$index !== $ctrl.activeSort"
-                  class="sort-icon--inactive"
-                  aria-hidden="true">
-                <icon file="icon-arrow--up.svg"></icon>
-                <icon file="icon-arrow--down.svg"></icon>
-            </span>
+<div class="bmc-table__container">
+  <table-toolbar  ng-if="$ctrl.selectable"
+                  selection-count="$ctrl.selectedRows.size"
+                  active="$ctrl.selectedRows.size > 0"
+                  actions="$ctrl.batchActions"
+                  emit-action="$ctrl.onEmitBatchAction(action)"
+                  emit-close="$ctrl.onToolbarClose()"
+                  ng-animate-children="true">
+  </table-toolbar>
+  <table  class="bmc-table {{$ctrl.size}}"
+          ng-class="{
+            'bmc-table--sortable': $ctrl.sortable,
+            'bmc-table--expandable': $ctrl.expandable,
+            'bmc-table--selectable': $ctrl.selectable,
+            'bmc-table--row-actions-enabled': '$ctrl.rowActionsEnabled',
+          }">
+    <thead class="bmc-table__head">
+      <!-- Header row -->
+      <tr>
+        <!-- Expandable empty cell -->
+        <th ng-if="$ctrl.expandable"
+            class="bmc-table__column-header"></th>
+        <!-- Select checkbox -->
+        <th ng-if="$ctrl.selectable"
+            class="bmc-table__column-header">
+          <table-checkbox ng-model="$ctrl.selectHeaderCheckbox"
+                          indeterminate="$ctrl.someSelected"
+                          emit-change="$ctrl.onHeaderSelectChange(checked)">
+          </table-checkbox>
+        </th>
+        <!-- Header items -->
+        <th ng-repeat="headerItem in $ctrl.header track by $index"
+            class="bmc-table__column-header">
+          <span ng-if="!$ctrl.sortable || !headerItem.sortable">
+            {{headerItem.label}}
+          </span>
+          <span ng-if="$ctrl.sortable && headerItem.sortable"
+                ng-click="$ctrl.onClickSort($index)"
+                class="bmc-table__column-header--sortable">
+            {{headerItem.label}}
+            <!-- Sort icons -->
+            <button class="sort-icon"
+                    type="button"
+                    aria-label="sort {{headerItem.label}}">
+              <icon file="icon-arrow--up.svg"
+                    ng-if="$index === $ctrl.activeSort"
+                    ng-class="{
+                      'sort-icon--descending': !$ctrl.sortAscending,
+                      'sort-icon--ascending' : $ctrl.sortAscending }"
+                    class="sort-icon--active"
+                    aria-hidden="true"></icon>
+              <span ng-if="$index !== $ctrl.activeSort"
+                    class="sort-icon--inactive"
+                    aria-hidden="true">
+                  <icon file="icon-arrow--up.svg"></icon>
+                  <icon file="icon-arrow--down.svg"></icon>
+              </span>
+            </button>
+          </span>
+        </th>
+        <!-- Row actions empty cell -->
+        <th ng-if="$ctrl.rowActionsEnabled"
+            class="bmc-table__column-header"></th>
+      </tr>
+    </thead>
+    <tbody class="bmc-table__body">
+      <!-- Data rows -->
+      <tr ng-if="$ctrl.data.length > 0"
+          ng-repeat-start="row in $ctrl.data track by $index"
+          class="bmc-table__row"
+          ng-class="{
+            'bmc-table__row--expanded': $ctrl.expandedRows.has($index),
+            'bmc-table__row--selected': $ctrl.selectedRows.has($index),
+          }">
+        <!-- Row expansion trigger -->
+        <td ng-if="$ctrl.expandable"
+            class="bmc-table__cell">
+          <button type="button"
+                  class="btn  btn--expand"
+                  aria-label="expand row"
+                  ng-click="$ctrl.onClickExpand($index)">
+            <icon file="icon-chevron-right.svg" aria-hidden="true"></icon>
           </button>
-        </span>
-      </th>
-    </tr>
-  </thead>
-  <tbody class="bmc-table__body">
-    <!-- Data rows -->
-    <tr ng-if="$ctrl.data.length > 0"
-        ng-repeat-start="row in $ctrl.data track by $index"
-        class="bmc-table__row"
-        ng-class="{
-          'bmc-table__row--expanded': $ctrl.expandedRows.has($index)
-        }">
-      <!-- Row expansion trigger -->
-      <td ng-if="$ctrl.expandable"
-          class="bmc-table__cell">
-        <button type="button"
-                class="btn  btn--expand"
-                aria-label="expand row"
-                ng-click="$ctrl.onClickExpand($index)">
-          <icon file="icon-chevron-right.svg" aria-hidden="true"></icon>
-        </button>
-      </td>
-      <!-- Row item -->
-      <td ng-repeat="item in row.uiData track by $index"
-          class="bmc-table__cell">
-          <ng-bind-html ng-bind-html="item"></ng-bind-html>
-      </td>
-      <!-- Row Actions -->
-      <td ng-if="$ctrl.rowActionsEnabled"
-          class="bmc-table__cell  bmc-table__row-actions">
-        <table-actions
-          actions="row.actions"
-          emit-action="$ctrl.onEmitTableAction(action, row)">
-        </table-actions>
-      </td>
-    </tr>
-    <!-- Expansion row -->
-    <tr ng-repeat-end
-        ng-if="$ctrl.expandedRows.has($index)"
-        class="bmc-table__expansion-row">
-      <td class="bmc-table__cell"></td>
-      <td class="bmc-table__cell"
-          colspan="{{$ctrl.header.length - 1}}">
-        <ng-bind-html
-          ng-bind-html="row.expandContent || 'No data'">
-        </ng-bind-html>
-      </td>
-    </tr>
-    <!-- Empty table -->
-    <tr ng-if="$ctrl.data.length === 0"
-        class="bmc-table__expansion-row">
-      <td class="bmc-table__cell"
-          colspan="{{$ctrl.header.length}}">No data</td>
-    </tr>
-  </tbody>
-</table>
\ No newline at end of file
+        </td>
+        <!-- Row checkbox -->
+        <td ng-if="$ctrl.selectable"
+            class="bmc-table__cell">
+          <table-checkbox ng-if="row.selectable"
+                          ng-model="row.selected"
+                          emit-change="$ctrl.onRowSelectChange($index)">
+          </table-checkbox>
+        </td>
+        <!-- Row item -->
+        <td ng-repeat="item in row.uiData track by $index"
+            class="bmc-table__cell">
+            <ng-bind-html ng-bind-html="item"></ng-bind-html>
+        </td>
+        <!-- Row Actions -->
+        <td ng-if="$ctrl.rowActionsEnabled"
+            class="bmc-table__cell  bmc-table__row-actions">
+          <table-actions
+            actions="row.actions"
+            emit-action="$ctrl.onEmitRowAction(action, row)">
+          </table-actions>
+        </td>
+      </tr>
+      <!-- Expansion row -->
+      <tr ng-repeat-end
+          ng-if="$ctrl.expandedRows.has($index)"
+          class="bmc-table__expansion-row">
+        <td class="bmc-table__cell"></td>
+        <td class="bmc-table__cell"
+            colspan="{{$ctrl.header.length + $ctrl.sortable +
+                      $ctrl.expandable + $ctrl.rowActionsEnabled}}">
+          <ng-bind-html
+            ng-bind-html="row.expandContent || 'No data'">
+          </ng-bind-html>
+        </td>
+      </tr>
+      <!-- Empty table -->
+      <tr ng-if="$ctrl.data.length === 0"
+          class="bmc-table__expansion-row">
+        <td class="bmc-table__cell"
+            colspan="{{$ctrl.header.length + $ctrl.sortable +
+                      $ctrl.expandable + $ctrl.rowActionsEnabled}}">
+          No data
+        </td>
+      </tr>
+    </tbody>
+  </table>
+</div>
\ No newline at end of file
diff --git a/app/common/components/table/table.js b/app/common/components/table/table.js
index 5db05b6..2063555 100644
--- a/app/common/components/table/table.js
+++ b/app/common/components/table/table.js
@@ -18,9 +18,12 @@
    * Each row object can optionally have an 'expandContent' property
    * that should be a string value and can contain valid HTML. To render
    * the expanded content, set 'expandable' attribute to true.
+   * Each row object can optionally have a 'selectable' property. Defaults
+   * to true if table is selectable. If a particular row should not
+   * be selectable, set to false.
    *
    * data = [
-   *  { uiData: ['root', 'Admin', 'enabled' ] },
+   *  { uiData: ['root', 'Admin', 'enabled' ], selectable: false },
    *  { uiData: ['user1', 'User', 'disabled' ] }
    * ]
    *
@@ -47,6 +50,10 @@
    * The 'expandable' attribute should be a boolean value. If true each
    * row object in data array should contain a 'expandContent' property
    *
+   * The 'selectable' attribute should be a boolean value.
+   * If 'selectable' is true, include 'batch-actions' property that should
+   * be an array of actions to provide <table-toolbar> component.
+   *
    * The 'size' attribute which can be set to 'small' which will
    * render a smaller font size in the table.
    *
@@ -56,6 +63,11 @@
     this.sortAscending = true;
     this.activeSort;
     this.expandedRows = new Set();
+    this.selectedRows = new Set();
+    this.selectHeaderCheckbox = false;
+    this.someSelected = false;
+
+    let selectableRowCount = 0;
 
     /**
      * Sorts table data
@@ -89,17 +101,70 @@
           return column;
         })
       }
-      if (this.rowActionsEnabled) {
-        // If table actions are enabled push an empty
-        // string to the header array to account for additional
-        // table actions cell
-        this.header.push({label: '', sortable: false});
+    };
+
+    /**
+     * Prep data
+     * When data binding changes, make adjustments to account for
+     * optional configurations and undefined values
+     */
+    const prepData = () => {
+      selectableRowCount = 0;
+      this.data.forEach((row) => {
+        if (row.uiData === undefined) {
+          // Check for undefined 'uiData' property for each item in data
+          // array
+          row.uiData = [];
+        }
+        if (this.selectable) {
+          // If table is selectable check row for 'selectable' property
+          row.selectable = row.selectable === undefined ? true : row.selectable;
+          if (row.selectable) {
+            selectableRowCount++;
+            row.selected = false;
+          }
+        }
+      });
+      if (this.sortable) {
+        if (this.activeSort !== undefined || this.defaultSort !== undefined) {
+          // apply default or active sort if one is defined
+          this.activeSort = this.defaultSort !== undefined ? this.defaultSort :
+                                                             this.activeSort;
+          sortData();
+        }
       }
-      if (this.expandable) {
-        // If table is expandable, push an empty string to the
-        // header array to account for additional expansion cell
-        this.header.unshift({label: '', sortable: false});
-      }
+    };
+
+    /**
+     * Select all rows
+     * Sets each selectable row selected property to true
+     * and adds index to selectedRow Set
+     */
+    const selectAllRows = () => {
+      this.selectHeaderCheckbox = true;
+      this.someSelected = false;
+      this.data.forEach((row, index) => {
+        if (!row.selected && row.selectable) {
+          row.selected = true;
+          this.selectedRows.add(index);
+        }
+      })
+    };
+
+    /**
+     * Deselect all rows
+     * Sets each row selected property to false
+     * and clears selectedRow Set
+     */
+    const deselectAllRows = () => {
+      this.selectHeaderCheckbox = false;
+      this.someSelected = false;
+      this.selectedRows.clear();
+      this.data.forEach((row) => {
+        if (row.selectable) {
+          row.selected = false;
+        }
+      })
     };
 
     /**
@@ -109,14 +174,26 @@
      * @param {string} action : action type
      * @param {any} row : user object
      */
-    this.onEmitTableAction = (action, row) => {
+    this.onEmitRowAction = (action, row) => {
       if (action !== undefined && row !== undefined) {
         const value = {action, row};
-        this.emitAction({value});
+        this.emitRowAction({value});
       }
     };
 
     /**
+     * Callback when batch action clicked from toolbar
+     * Emits the action type and the selected row data to
+     * parent controller
+     * @param {string} action : action type
+     */
+    this.onEmitBatchAction = (action) => {
+      const filteredRows = this.data.filter((row) => row.selected);
+      const value = {action, filteredRows};
+      this.emitBatchAction({value});
+    };
+
+    /**
      * Callback when sortable table header clicked
      * @param {number} index : index of header item
      */
@@ -146,6 +223,49 @@
     };
 
     /**
+     * Callback when select checkbox clicked
+     * @param {number} row : index of selected row
+     */
+    this.onRowSelectChange = (row) => {
+      if (this.selectedRows.has(row)) {
+        this.selectedRows.delete(row);
+      } else {
+        this.selectedRows.add(row);
+      }
+      if (this.selectedRows.size === 0) {
+        this.someSelected = false;
+        this.selectHeaderCheckbox = false;
+        deselectAllRows();
+      } else if (this.selectedRows.size === selectableRowCount) {
+        this.someSelected = false;
+        this.selectHeaderCheckbox = true;
+        selectAllRows();
+      } else {
+        this.someSelected = true;
+      }
+    };
+
+    /**
+     * Callback when header select box value changes
+     */
+    this.onHeaderSelectChange = (checked) => {
+      this.selectHeaderCheckbox = checked;
+      if (this.selectHeaderCheckbox) {
+        selectAllRows();
+      } else {
+        deselectAllRows();
+      }
+    };
+
+    /**
+     * Callback when cancel/close button closed
+     * from toolbar
+     */
+    this.onToolbarClose = () => {
+      deselectAllRows();
+    };
+
+    /**
      * onInit Component lifecycle hook
      * Checking for undefined values
      */
@@ -157,32 +277,19 @@
           this.rowActionsEnabled === undefined ? false : this.rowActionsEnabled;
       this.size = this.size === undefined ? '' : this.size;
       this.expandable = this.expandable === undefined ? false : this.expandable;
-
-      // Check for undefined 'uiData' property for each item in data array
-      this.data = this.data.map((row) => {
-        if (row.uiData === undefined) {
-          row.uiData = [];
-        }
-        return row;
-      })
+      this.selectable = this.selectable === undefined ? false : this.selectable;
       prepTable();
     };
 
     /**
      * onChanges Component lifecycle hook
-     * Check for changes in the data array and apply
-     * default or active sort if one is defined
      */
     this.$onChanges = (onChangesObj) => {
       const dataChange = onChangesObj.data;
       if (dataChange) {
-        if (this.activeSort !== undefined || this.defaultSort !== undefined) {
-          this.activeSort = this.defaultSort !== undefined ? this.defaultSort :
-                                                             this.activeSort;
-          sortData();
-        }
+        prepData();
       }
-    }
+    };
   };
 
   /**
@@ -199,7 +306,10 @@
       sortable: '<',           // boolean
       defaultSort: '<',        // number (index of sort)
       expandable: '<',         // boolean
-      emitAction: '&'
+      selectable: '<',         // boolean
+      batchActions: '<',       // Array
+      emitRowAction: '&',
+      emitBatchAction: '&'
     }
   })
 })(window.angular);
diff --git a/app/common/styles/base/variables.scss b/app/common/styles/base/variables.scss
index 0e09f86..55a3eed 100755
--- a/app/common/styles/base/variables.scss
+++ b/app/common/styles/base/variables.scss
@@ -3,4 +3,12 @@
 $duration--moderate-01: 150ms; //Micro-interactions, small expansion, short distance movements
 $duration--moderate-02: 240ms; //Expansion, system communication, toast
 $duration--slow-01: 400ms; //Large expansion, important system notifications
-$duration--slow-02: 700ms; //Background dimming
\ No newline at end of file
+$duration--slow-02: 700ms; //Background dimming
+
+// https://www.carbondesignsystem.com/guidelines/motion/basics/#easing
+$standard-easing--productive: cubic-bezier(0.2, 0, 0.38, 0.9);
+$standard-easing--expressive: cubic-bezier(0.4, 0.14, 0.3, 1);
+$entrance-easing--productive: cubic-bezier(0, 0, 0.38, 0.9);
+$entrance-easing--expressive: cubic-bezier(0, 0, 0.3, 1);
+$exit-easing--productive: cubic-bezier(0.2, 0, 1, 0.9);
+$exit-easing--expressive: cubic-bezier(0.4, 0.14, 1, 1);
\ No newline at end of file
diff --git a/app/common/styles/components/table.scss b/app/common/styles/components/table.scss
index 0178486..5aa4915 100644
--- a/app/common/styles/components/table.scss
+++ b/app/common/styles/components/table.scss
@@ -147,6 +147,10 @@
   }
 }
 
+.bmc-table__container {
+  position: relative;
+}
+
 .bmc-table {
   width: 100%;
   &.small {
@@ -223,4 +227,126 @@
 
 .bmc-table__row-actions {
   text-align: right;
+}
+
+.bmc-table__checkbox-container {
+  position: relative;
+  display: inline-block;
+  width: 1rem;
+  height: 1rem;
+}
+
+.bmc-table__checkbox {
+  margin: 0;
+  line-height: 1;
+  position: absolute;
+  width: 1rem;
+  height: 1rem;
+  top: 0;
+  right: 0;
+  cursor: pointer;
+  &::before {
+    // checkbox border
+    box-sizing: border-box;
+    content: "";
+    width: 1rem;
+    height: 1rem;
+    position: absolute;
+    left: 0;
+    top: 0.15rem;
+    background-color: transparent;
+    border: 2px solid $border-color-02;
+    border-radius: 1px;
+  }
+  &::after {
+    // checkbox check mark
+    content: "";
+    position: absolute;
+    left: 0.2rem;
+    top: 0.15rem;
+    width: 0.6rem;
+    height: 0.3rem;
+    background: none;
+    border-left: 2px solid $primary-light;
+    border-bottom: 2px solid $primary-light;
+    transform: scale(0) rotate(-45deg);
+    transform-origin: bottom right;
+  }
+  &.checked::before ,
+  &.indeterminate::before {
+      background: $primary-accent;
+      border-color: $primary-accent;
+  }
+  &.checked::after {
+    transform: scale(1) rotate(-45deg);
+  }
+  &.indeterminate::after {
+    transform: scale(1) rotate(0deg);
+    border-left: 0 solid $primary-light;
+    border-bottom: 2px solid $primary-light;
+    top: 0.4rem;
+  }
+}
+
+.bmc-table__checkbox-input {
+  position: absolute;
+  opacity: 0;
+  height: 0;
+  width: 0;
+  margin: 0;
+}
+
+.bmc-table__toolbar {
+  position: absolute;
+  left: 0;
+  right: 1px;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  color: $text-03;
+  background-color: $primary-accent;
+  max-height: 40px;
+  padding-left: 1em;
+  padding-top: 0.5em;
+  padding-right: 0;
+  padding-bottom: 0.5em;
+  transform: translateY(-40px);
+  &.ng-animate {
+    transition: transform $duration--moderate-02 $standard-easing--productive;
+  }
+  &.ng-enter {
+    transform: translateY(0);
+  }
+  &.ng-enter.ng-enter-active {
+    transform: translateY(-40px);
+  }
+  &.ng-leave {
+    transform: translateY(-40px);
+  }
+  &.ng-leave.ng-leave-active {
+    transform: translateY(0);
+  }
+  .btn-tertiary {
+    color: $text-03;
+    padding-top:0;
+    padding-bottom:0;
+    position: relative;
+    .icon {
+      fill: $text-03;
+      margin: 0;
+    }
+  }
+}
+
+.toolbar__batch-actions {
+  .btn-close {
+    border-top: none;
+    border-bottom: none;
+    border-left: 2px solid $primary-light;
+    margin-left: 0.5em;
+  }
+}
+
+.toolbar__selection-count {
+  margin: 0;
 }
\ No newline at end of file
diff --git a/app/index.js b/app/index.js
index b121d24..918748a 100644
--- a/app/index.js
+++ b/app/index.js
@@ -68,6 +68,8 @@
 import components_index from './common/components/index.js';
 import table_component from './common/components/table/table.js';
 import table_actions_component from './common/components/table/table-actions.js';
+import table_toolbar_component from './common/components/table/table-toolbar.js';
+import table_checkbox from './common/components/table/table-checkbox.js';
 
 import login_index from './login/index.js';
 import login_controller from './login/controllers/login-controller.js';
diff --git a/app/users/controllers/user-accounts-controller.html b/app/users/controllers/user-accounts-controller.html
index 696a984..9577196 100644
--- a/app/users/controllers/user-accounts-controller.html
+++ b/app/users/controllers/user-accounts-controller.html
@@ -6,7 +6,7 @@
     </div>
   </div>
   <div class="row column">
-    <div class="column small-12">
+    <div class="column small-12 medium-10">
       <div class="local-users__actions">
         <button ng-disabled="accountSettings === null"
                 ng-click="onClickAccountSettingsPolicy()"
@@ -26,13 +26,13 @@
         data="tableData"
         header="tableHeader"
         row-actions-enabled="true"
-        emit-action="onEmitAction(value)"
+        emit-row-action="onEmitRowAction(value)"
         class="local-users__table">
       </bmc-table>
     </div>
   </div>
   <div class="row column">
-    <div class="column small-12">
+    <div class="column small-12 medium-9">
       <!-- Role table -->
       <role-table></role-table>
     </div>
diff --git a/app/users/controllers/user-accounts-controller.js b/app/users/controllers/user-accounts-controller.js
index 7591a53..9f31beb 100644
--- a/app/users/controllers/user-accounts-controller.js
+++ b/app/users/controllers/user-accounts-controller.js
@@ -362,7 +362,7 @@
        * Callback when action emitted from table
        * @param {*} value
        */
-      $scope.onEmitAction = (value) => {
+      $scope.onEmitRowAction = (value) => {
         switch (value.action) {
           case 'Edit':
             initUserModal(value.row);
diff --git a/app/users/styles/user-accounts.scss b/app/users/styles/user-accounts.scss
index fe580cd..fa0c5d7 100644
--- a/app/users/styles/user-accounts.scss
+++ b/app/users/styles/user-accounts.scss
@@ -8,11 +8,6 @@
   justify-content: flex-end;
 }
 
-.local-users__actions,
-.local-users__table .bmc-table {
-  max-width: 900px;
-}
-
 .modal__local-users,
 .modal__local-users-settings {
   .modal-body {
@@ -36,9 +31,6 @@
 
 .role-table {
   margin-top: 30px;
-  .bmc-table {
-    max-width: 750px;
-  }
   .bmc-table__cell:not(:first-of-type) {
     text-align: center;
   }