xref: /openbmc/phosphor-webui/app/common/components/table/table.js (revision b1f64248b47e6a26ae38f36ebcf67f5955e9e92c)
1window.angular && (function(angular) {
2  'use strict';
3
4  /**
5   *
6   * bmcTable Component
7   *
8   * To use:
9   *
10   * The 'data' attribute should be an array of all row objects in the table.
11   * It will render each item as a <tr> in the table.
12   * Each row object in the data array should also have a 'uiData'
13   * property that should be an array of the properties that will render
14   * as each table cell <td>.
15   * Each row object in the data array can optionally have an
16   * 'actions' property that should be an array of actions to provide the
17   * <bmc-table-actions> component.
18   *
19   * data = [
20   *  { uiData: ['root', 'Admin', 'enabled' ] },
21   *  { uiData: ['user1', 'User', 'disabled' ] }
22   * ]
23   *
24   * The 'header' attribute should be an array of all header objects in the
25   * table. Each object in the header array should have a 'label' property
26   * that will render as a <th> in the table.
27   * If the table is sortable, can optionally add 'sortable' property to header
28   * row object. If a particular column is not sortable, set to false.
29   *
30   * header = [
31   *  { label: 'Username' },
32   *  { label: 'Privilege' }
33   *  { label: 'Account Status', sortable: false }
34   * ]
35   *
36   * The 'sortable' attribute should be a boolean value. Defaults to false.
37   * The 'default-sort' attribute should be the index value of the header
38   * obejct that should be sorted on inital load.
39   *
40   * The 'row-actions-enabled' attribute, should be a boolean value
41   * Can be set to true to render table row actions. Defaults to false.
42   *  Row actions are defined in data.actions.
43   *
44   * The 'size' attribute which can be set to 'small' which will
45   * render a smaller font size in the table.
46   *
47   */
48
49  const TableController = function() {
50    this.sortAscending = true;
51    this.activeSort;
52
53    /**
54     * Sorts table data
55     */
56    const sortData = () => {
57      this.data.sort((a, b) => {
58        const aProp = a.uiData[this.activeSort];
59        const bProp = b.uiData[this.activeSort];
60        if (aProp === bProp) {
61          return 0;
62        } else {
63          if (this.sortAscending) {
64            return aProp < bProp ? -1 : 1;
65          }
66          return aProp > bProp ? -1 : 1;
67        }
68      })
69    };
70
71    /**
72     * Callback when table row action clicked
73     * Emits user desired action and associated row data to
74     * parent controller
75     * @param {string} action : action type
76     * @param {any} row : user object
77     */
78    this.onEmitTableAction = (action, row) => {
79      if (action !== undefined && row !== undefined) {
80        const value = {action, row};
81        this.emitAction({value});
82      }
83    };
84
85    /**
86     * Callback when sortable table header clicked
87     * @param {number} index : index of header item
88     */
89    this.onClickSort = (index) => {
90      if (index === this.activeSort) {
91        // If clicked header is already sorted, reverse
92        // the sort direction
93        this.sortAscending = !this.sortAscending;
94        this.data.reverse();
95      } else {
96        this.sortAscending = true;
97        this.activeSort = index;
98        sortData();
99      }
100    };
101
102    /**
103     * onInit Component lifecycle hook
104     * Checking for undefined values
105     */
106    this.$onInit = () => {
107      this.header = this.header === undefined ? [] : this.header;
108      this.data = this.data == undefined ? [] : this.data;
109      this.sortable = this.sortable === undefined ? false : this.sortable;
110      this.rowActionsEnabled =
111          this.rowActionsEnabled === undefined ? false : this.rowActionsEnabled;
112      this.size = this.size === undefined ? '' : this.size;
113
114      // Check for undefined 'uiData' property for each item in data array
115      this.data = this.data.map((row) => {
116        if (row.uiData === undefined) {
117          row.uiData = [];
118        }
119        return row;
120      })
121      if (this.sortable) {
122        // If sort is enabled, check for undefined 'sortable'
123        // property for each item in header array
124        this.header = this.header.map((column) => {
125          column.sortable =
126              column.sortable === undefined ? true : column.sortable;
127          return column;
128        })
129      }
130      if (this.rowActionsEnabled) {
131        // If table actions are enabled push an empty
132        // string to the header array to account for additional
133        // table actions cell
134        this.header.push({label: '', sortable: false});
135      }
136    };
137
138    /**
139     * onChanges Component lifecycle hook
140     * Check for changes in the data array and apply
141     * default or active sort if one is defined
142     */
143    this.$onChanges = (onChangesObj) => {
144      const dataChange = onChangesObj.data;
145      if (dataChange) {
146        if (this.activeSort !== undefined || this.defaultSort !== undefined) {
147          this.activeSort = this.defaultSort !== undefined ? this.defaultSort :
148                                                             this.activeSort;
149          sortData();
150        }
151      }
152    }
153  };
154
155  /**
156   * Register bmcTable component
157   */
158  angular.module('app.common.components').component('bmcTable', {
159    template: require('./table.html'),
160    controller: TableController,
161    bindings: {
162      data: '<',               // Array
163      header: '<',             // Array
164      rowActionsEnabled: '<',  // boolean
165      size: '<',               // string
166      sortable: '<',           // boolean
167      defaultSort: '<',        // number (index of sort)
168      emitAction: '&'
169    }
170  })
171})(window.angular);
172