diff options
-rw-r--r-- | src/assets/styles/_badge.scss | 8 | ||||
-rw-r--r-- | src/assets/styles/_dropdown.scss | 21 | ||||
-rw-r--r-- | src/assets/styles/_obmc-custom.scss | 1 | ||||
-rw-r--r-- | src/components/Global/TableFilter.vue | 93 | ||||
-rw-r--r-- | src/components/Mixins/TableFilterMixin.js | 23 | ||||
-rw-r--r-- | src/locales/en-US.json | 4 | ||||
-rw-r--r-- | src/main.js | 5 | ||||
-rw-r--r-- | src/views/Health/Sensors/Sensors.vue | 49 |
8 files changed, 195 insertions, 9 deletions
diff --git a/src/assets/styles/_badge.scss b/src/assets/styles/_badge.scss index 99c758af..68e7482f 100644 --- a/src/assets/styles/_badge.scss +++ b/src/assets/styles/_badge.scss @@ -3,6 +3,14 @@ // for pill variant because global $enable-rounded // Boostrap setting removes rounded pill style border-radius: 10rem; + fill: currentColor; + font-weight: 400; + .close { + font-size: 1em; + margin-left: $spacer/2; + font-weight: inherit; + color: inherit; + } } .badge-primary { diff --git a/src/assets/styles/_dropdown.scss b/src/assets/styles/_dropdown.scss new file mode 100644 index 00000000..0eb310f6 --- /dev/null +++ b/src/assets/styles/_dropdown.scss @@ -0,0 +1,21 @@ +.dropdown-item { + padding-left: $spacer/2; +} + +.b-dropdown-form { + padding: $spacer/2; + .form-group { + margin-bottom: $spacer/2; + } +} + +.table-filter { + // Adding component style to global stylesheet because + // single-file component scoped styles aren't + // being applied to dynamically appended elements + // The overflow menu should be above the table + .dropdown-menu { + z-index: $zindex-dropdown + 1; + padding: 0; + } +}
\ No newline at end of file diff --git a/src/assets/styles/_obmc-custom.scss b/src/assets/styles/_obmc-custom.scss index 544e5858..4e0e1c55 100644 --- a/src/assets/styles/_obmc-custom.scss +++ b/src/assets/styles/_obmc-custom.scss @@ -42,6 +42,7 @@ @import "./alerts"; @import "./badge"; @import "./buttons"; +@import "./dropdown"; @import "./form-components"; @import "./modal"; @import "./table"; diff --git a/src/components/Global/TableFilter.vue b/src/components/Global/TableFilter.vue new file mode 100644 index 00000000..d466d4e0 --- /dev/null +++ b/src/components/Global/TableFilter.vue @@ -0,0 +1,93 @@ +<template> + <div class="table-filter d-inline-block"> + <p class="d-inline-block mb-0"> + <b-badge v-for="(tag, index) in tags" :key="index" pill> + {{ tag }} + <b-button-close + :disabled="dropdownVisible" + :aria-hidden="true" + @click="removeTag(index)" + /> + </b-badge> + </p> + <b-dropdown + variant="link" + no-caret + right + @hide="dropdownVisible = false" + @show="dropdownVisible = true" + > + <template v-slot:button-content> + <icon-filter /> + {{ $t('global.action.filter') }} + </template> + <b-dropdown-form @change="onChange"> + <b-form-group + v-for="(filter, index) of filters" + :key="index" + :label="filter.label" + > + <b-form-checkbox-group v-model="tags" :options="filter.values"> + </b-form-checkbox-group> + </b-form-group> + </b-dropdown-form> + <b-dropdown-item-button variant="primary" @click="clearAllTags"> + {{ $t('global.action.clearAll') }} + </b-dropdown-item-button> + </b-dropdown> + </div> +</template> + +<script> +import IconFilter from '@carbon/icons-vue/es/settings--adjust/20'; + +export default { + name: 'TableFilter', + components: { IconFilter }, + props: { + filters: { + type: Array, + default: () => [], + validator: prop => { + return prop.every( + filter => + filter.hasOwnProperty('label') && filter.hasOwnProperty('values') + ); + } + } + }, + data() { + return { + tags: [], + dropdownVisible: false + }; + }, + methods: { + removeTag(index) { + this.tags = this.tags.filter((_, i) => i !== index); + this.emitChange(); + }, + clearAllTags() { + this.tags = []; + this.emitChange(); + }, + emitChange() { + this.$emit('filterChange', { + activeFilters: this.tags + }); + }, + onChange() { + this.emitChange(); + } + } +}; +</script> + +<style lang="scss" scoped> +p { + font-size: 1.2rem; +} +.badge { + margin-right: $spacer / 2; +} +</style> diff --git a/src/components/Mixins/TableFilterMixin.js b/src/components/Mixins/TableFilterMixin.js new file mode 100644 index 00000000..25c7497a --- /dev/null +++ b/src/components/Mixins/TableFilterMixin.js @@ -0,0 +1,23 @@ +import { includes } from 'lodash'; + +const TableFilterMixin = { + methods: { + getFilteredTableData(tableData = [], filters = []) { + if (filters.length === 0) return tableData; + // will return all items that match + // any of the filter tags (not all) + return tableData.filter(row => { + let returnRow = false; + for (const filter of filters) { + if (includes(row, filter)) { + returnRow = true; + break; + } + } + return returnRow; + }); + } + } +}; + +export default TableFilterMixin; diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 699b0164..022eefbf 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -2,13 +2,15 @@ "global": { "action": { "add": "Add", + "cancel": "Cancel", + "clearAll": "Clear all", "confirm": "Confirm", "copy": "Copy", - "cancel": "Cancel", "delete": "Delete", "disable": "Disable", "download": "Download", "enable": "Enable", + "filter": "Filter", "replace": "Replace", "save": "Save", "selected": "Selected" diff --git a/src/main.js b/src/main.js index 4b0722e2..5adc5efc 100644 --- a/src/main.js +++ b/src/main.js @@ -8,6 +8,7 @@ import { ButtonPlugin, BVConfigPlugin, CollapsePlugin, + DropdownPlugin, FormPlugin, FormCheckboxPlugin, FormFilePlugin, @@ -66,9 +67,13 @@ Vue.use(BVConfigPlugin, { BFormTags: { tagVariant: 'primary', addButtonVariant: 'link-primary' + }, + BBadge: { + variant: 'primary' } }); Vue.use(CollapsePlugin); +Vue.use(DropdownPlugin); Vue.use(FormPlugin); Vue.use(FormCheckboxPlugin); Vue.use(FormFilePlugin); diff --git a/src/views/Health/Sensors/Sensors.vue b/src/views/Health/Sensors/Sensors.vue index 70d4f90d..ae31cc38 100644 --- a/src/views/Health/Sensors/Sensors.vue +++ b/src/views/Health/Sensors/Sensors.vue @@ -2,13 +2,18 @@ <b-container fluid> <page-title /> <b-row> + <b-col xl="12" class="text-right"> + <table-filter :filters="tableFilters" @filterChange="onFilterChange" /> + </b-col> + </b-row> + <b-row> <b-col xl="12"> <b-table sort-icon-left no-sort-reset sticky-header="75vh" sort-by="status" - :items="allSensors" + :items="filteredSensors" :fields="fields" :sort-desc="true" :sort-compare="sortCompare" @@ -41,6 +46,10 @@ <script> import PageTitle from '../../../components/Global/PageTitle'; import StatusIcon from '../../../components/Global/StatusIcon'; +import TableFilter from '../../../components/Global/TableFilter'; +import TableFilterMixin from '../../../components/Mixins/TableFilterMixin'; + +const SENSOR_STATUS = ['OK', 'Warning', 'Critical']; const valueFormatter = value => { if (value === null || value === undefined) { @@ -51,7 +60,8 @@ const valueFormatter = value => { export default { name: 'Sensors', - components: { PageTitle, StatusIcon }, + components: { PageTitle, StatusIcon, TableFilter }, + mixins: [TableFilterMixin], data() { return { fields: [ @@ -91,12 +101,27 @@ export default { formatter: valueFormatter, label: this.$t('pageSensors.table.upperCritical') } - ] + ], + tableFilters: [ + { + label: this.$t('pageSensors.table.status'), + values: SENSOR_STATUS + } + ], + activeFilters: [] }; }, computed: { allSensors() { return this.$store.getters['sensors/sensors']; + }, + filteredSensors: { + get: function() { + return this.getFilteredTableData(this.allSensors, this.activeFilters); + }, + set: function(newVal) { + return newVal; + } } }, created() { @@ -105,11 +130,11 @@ export default { methods: { statusIcon(status) { switch (status) { - case 'OK': + case SENSOR_STATUS[0]: return 'success'; - case 'Warning': + case SENSOR_STATUS[1]: return 'warning'; - case 'Critical': + case SENSOR_STATUS[2]: return 'danger'; default: return ''; @@ -117,9 +142,17 @@ export default { }, sortCompare(a, b, key) { if (key === 'status') { - const status = ['OK', 'Warning', 'Critical']; - return status.indexOf(a.status) - status.indexOf(b.status); + return ( + SENSOR_STATUS.indexOf(a.status) - SENSOR_STATUS.indexOf(b.status) + ); } + }, + onFilterChange({ activeFilters }) { + this.activeFilters = activeFilters; + this.filteredSensors = this.getFilteredTableData( + this.allSensors, + activeFilters + ); } } }; |