Add table search filter clear button

- Adds ability to quickly clear a table input search field
- Uses similar styling to password toggle icon and datepicker
- Button style to be addressed in separate commit to match
  style guide

Signed-off-by: Dixsie Wolmers <dixsie@ibm.com>
Change-Id: I18f2e01c28c00c7e7b2ad1af924070241caf36a5
diff --git a/src/components/Global/Search.vue b/src/components/Global/Search.vue
index 9ebf468..eeb909a 100644
--- a/src/components/Global/Search.vue
+++ b/src/components/Global/Search.vue
@@ -12,12 +12,22 @@
         </b-input-group-prepend>
         <b-form-input
           :id="`searchInput-${_uid}`"
+          ref="searchInput"
           v-model="filter"
           class="search-input"
           type="text"
           :placeholder="placeholder"
           @input="onChangeInput"
-        ></b-form-input>
+        >
+        </b-form-input>
+        <b-button
+          v-if="filter"
+          variant="link"
+          :aria-label="$t('global.ariaLabel.clearSearch')"
+          @click="onClearSearch"
+        >
+          <icon-close :title="$t('global.ariaLabel.clearSearch')" />
+        </b-button>
       </b-input-group>
     </b-form-group>
   </div>
@@ -25,9 +35,11 @@
 
 <script>
 import IconSearch from '@carbon/icons-vue/es/search/16';
+import IconClose from '@carbon/icons-vue/es/close/20';
+
 export default {
   name: 'Search',
-  components: { IconSearch },
+  components: { IconSearch, IconClose },
   props: {
     placeholder: {
       type: String,
@@ -44,6 +56,11 @@
   methods: {
     onChangeInput() {
       this.$emit('changeSearch', this.filter);
+    },
+    onClearSearch() {
+      this.filter = '';
+      this.$emit('clearSearch');
+      this.$refs.searchInput.focus();
     }
   }
 };
@@ -60,4 +77,16 @@
   z-index: 4;
   stroke: gray('400');
 }
+
+.btn {
+  position: absolute;
+  right: 0;
+  top: 0;
+  padding: 0.4rem 1rem;
+  z-index: 4;
+  svg {
+    margin-left: 0;
+    vertical-align: sub;
+  }
+}
 </style>
diff --git a/src/components/Mixins/SearchFilterMixin.js b/src/components/Mixins/SearchFilterMixin.js
new file mode 100644
index 0000000..856975d
--- /dev/null
+++ b/src/components/Mixins/SearchFilterMixin.js
@@ -0,0 +1,17 @@
+const SearchFilterMixin = {
+  data() {
+    return {
+      searchFilter: null
+    };
+  },
+  methods: {
+    onChangeSearchInput(searchValue) {
+      this.searchFilter = searchValue;
+    },
+    onClearSearchInput() {
+      this.searchFilter = null;
+    }
+  }
+};
+
+export default SearchFilterMixin;
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 538b46e..e0da709 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -19,6 +19,7 @@
       "selected": "Selected"
     },
     "ariaLabel": {
+      "clearSearch": "Clear search input",
       "hidePassword": "Hide password",
       "showPassword": "Show password as plain text. Note: this will visually expose your password on the screen.",
       "tooltip": "Tooltip",
diff --git a/src/views/Health/EventLogs/EventLogs.vue b/src/views/Health/EventLogs/EventLogs.vue
index e7d4895..cf5d478 100644
--- a/src/views/Health/EventLogs/EventLogs.vue
+++ b/src/views/Health/EventLogs/EventLogs.vue
@@ -6,6 +6,7 @@
         <search
           :placeholder="$t('pageEventLogs.table.searchLogs')"
           @changeSearch="onChangeSearchInput"
+          @clearSearch="onClearSearchInput"
         />
       </b-col>
       <b-col sm="3" class="d-flex flex-column justify-content-end">
@@ -161,6 +162,7 @@
 import BVToastMixin from '@/components/Mixins/BVToastMixin';
 import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin';
 import TableSortMixin from '@/components/Mixins/TableSortMixin';
+import SearchFilterMixin from '@/components/Mixins/SearchFilterMixin';
 
 export default {
   components: {
@@ -183,7 +185,8 @@
     LoadingBarMixin,
     TableFilterMixin,
     TableDataFormatterMixin,
-    TableSortMixin
+    TableSortMixin,
+    SearchFilterMixin
   ],
   data() {
     return {
@@ -239,7 +242,6 @@
       ],
       filterStartDate: null,
       filterEndDate: null,
-      searchFilter: null,
       searchTotalFilteredRows: 0
     };
   },
@@ -353,9 +355,6 @@
       this.filterStartDate = fromDate;
       this.filterEndDate = toDate;
     },
-    onChangeSearchInput(searchValue) {
-      this.searchFilter = searchValue;
-    },
     onFiltered(filteredItems) {
       this.searchTotalFilteredRows = filteredItems.length;
     },
diff --git a/src/views/Health/HardwareStatus/HardwareStatusTableDimmSlot.vue b/src/views/Health/HardwareStatus/HardwareStatusTableDimmSlot.vue
index 97116ca..ec7c163 100644
--- a/src/views/Health/HardwareStatus/HardwareStatusTableDimmSlot.vue
+++ b/src/views/Health/HardwareStatus/HardwareStatusTableDimmSlot.vue
@@ -2,7 +2,10 @@
   <page-section :section-title="$t('pageHardwareStatus.dimmSlot')">
     <b-row>
       <b-col sm="6" md="5" xl="4">
-        <search @changeSearch="onChangeSearchInput" />
+        <search
+          @changeSearch="onChangeSearchInput"
+          @clearSearch="onClearSearchInput"
+        />
       </b-col>
       <b-col sm="6" md="3" xl="2">
         <table-cell-count
@@ -70,10 +73,11 @@
 import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin';
 import TableSortMixin from '@/components/Mixins/TableSortMixin';
 import Search from '@/components/Global/Search';
+import SearchFilterMixin from '@/components/Mixins/SearchFilterMixin';
 
 export default {
   components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount },
-  mixins: [TableDataFormatterMixin, TableSortMixin],
+  mixins: [TableDataFormatterMixin, TableSortMixin, SearchFilterMixin],
   data() {
     return {
       fields: [
@@ -108,7 +112,6 @@
           sortable: true
         }
       ],
-      searchFilter: null,
       searchTotalFilteredRows: 0
     };
   },
@@ -134,9 +137,6 @@
         return this.sortStatus(a, b, key);
       }
     },
-    onChangeSearchInput(searchValue) {
-      this.searchFilter = searchValue;
-    },
     onFiltered(filteredItems) {
       this.searchTotalFilteredRows = filteredItems.length;
     }
diff --git a/src/views/Health/HardwareStatus/HardwareStatusTableFans.vue b/src/views/Health/HardwareStatus/HardwareStatusTableFans.vue
index 0b6e1a3..98e2fb5 100644
--- a/src/views/Health/HardwareStatus/HardwareStatusTableFans.vue
+++ b/src/views/Health/HardwareStatus/HardwareStatusTableFans.vue
@@ -2,7 +2,10 @@
   <page-section :section-title="$t('pageHardwareStatus.fans')">
     <b-row>
       <b-col sm="6" md="5" xl="4">
-        <search @changeSearch="onChangeSearchInput" />
+        <search
+          @changeSearch="onChangeSearchInput"
+          @clearSearch="onClearSearchInput"
+        />
       </b-col>
       <b-col sm="6" md="3" xl="2">
         <table-cell-count
@@ -69,10 +72,11 @@
 import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin';
 import TableSortMixin from '@/components/Mixins/TableSortMixin';
 import Search from '@/components/Global/Search';
+import SearchFilterMixin from '@/components/Mixins/SearchFilterMixin';
 
 export default {
   components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount },
-  mixins: [TableDataFormatterMixin, TableSortMixin],
+  mixins: [TableDataFormatterMixin, TableSortMixin, SearchFilterMixin],
   data() {
     return {
       fields: [
@@ -107,7 +111,6 @@
           sortable: true
         }
       ],
-      searchFilter: null,
       searchTotalFilteredRows: 0
     };
   },
@@ -133,9 +136,6 @@
         return this.sortStatus(a, b, key);
       }
     },
-    onChangeSearchInput(searchValue) {
-      this.searchFilter = searchValue;
-    },
     onFiltered(filteredItems) {
       this.searchTotalFilteredRows = filteredItems.length;
     }
diff --git a/src/views/Health/HardwareStatus/HardwareStatusTablePowerSupplies.vue b/src/views/Health/HardwareStatus/HardwareStatusTablePowerSupplies.vue
index 77a1e3c..0eb2f60 100644
--- a/src/views/Health/HardwareStatus/HardwareStatusTablePowerSupplies.vue
+++ b/src/views/Health/HardwareStatus/HardwareStatusTablePowerSupplies.vue
@@ -2,7 +2,10 @@
   <page-section :section-title="$t('pageHardwareStatus.powerSupplies')">
     <b-row>
       <b-col sm="6" md="5" xl="4">
-        <search @changeSearch="onChangeSearchInput" />
+        <search
+          @changeSearch="onChangeSearchInput"
+          @clearSearch="onClearSearchInput"
+        />
       </b-col>
       <b-col sm="6" md="3" xl="2">
         <table-cell-count
@@ -92,10 +95,11 @@
 import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin';
 import TableSortMixin from '@/components/Mixins/TableSortMixin';
 import Search from '@/components/Global/Search';
+import SearchFilterMixin from '@/components/Mixins/SearchFilterMixin';
 
 export default {
   components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount },
-  mixins: [TableDataFormatterMixin, TableSortMixin],
+  mixins: [TableDataFormatterMixin, TableSortMixin, SearchFilterMixin],
   data() {
     return {
       fields: [
@@ -130,7 +134,6 @@
           sortable: true
         }
       ],
-      searchFilter: null,
       searchTotalFilteredRows: 0
     };
   },
@@ -156,9 +159,6 @@
         return this.sortStatus(a, b, key);
       }
     },
-    onChangeSearchInput(searchValue) {
-      this.searchFilter = searchValue;
-    },
     onFiltered(filteredItems) {
       this.searchTotalFilteredRows = filteredItems.length;
     }
diff --git a/src/views/Health/HardwareStatus/HardwareStatusTableProcessors.vue b/src/views/Health/HardwareStatus/HardwareStatusTableProcessors.vue
index 6ab1343..5a27cca 100644
--- a/src/views/Health/HardwareStatus/HardwareStatusTableProcessors.vue
+++ b/src/views/Health/HardwareStatus/HardwareStatusTableProcessors.vue
@@ -3,7 +3,10 @@
     <!-- Search -->
     <b-row>
       <b-col sm="6" md="5" xl="4">
-        <search @changeSearch="onChangeSearchInput" />
+        <search
+          @changeSearch="onChangeSearchInput"
+          @clearSearch="onClearSearchInput"
+        />
       </b-col>
       <b-col sm="6" md="3" xl="2">
         <table-cell-count
@@ -99,10 +102,11 @@
 import TableSortMixin from '@/components/Mixins/TableSortMixin';
 import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin';
 import Search from '@/components/Global/Search';
+import SearchFilterMixin from '@/components/Mixins/SearchFilterMixin';
 
 export default {
   components: { PageSection, IconChevron, TableCellCount, StatusIcon, Search },
-  mixins: [TableDataFormatterMixin, TableSortMixin],
+  mixins: [TableDataFormatterMixin, TableSortMixin, SearchFilterMixin],
   data() {
     return {
       fields: [
@@ -137,7 +141,6 @@
           sortable: true
         }
       ],
-      searchFilter: null,
       searchTotalFilteredRows: 0
     };
   },
@@ -158,9 +161,6 @@
     });
   },
   methods: {
-    onChangeSearchInput(searchValue) {
-      this.searchFilter = searchValue;
-    },
     onFiltered(filteredItems) {
       this.searchTotalFilteredRows = filteredItems.length;
     }
diff --git a/src/views/Health/Sensors/Sensors.vue b/src/views/Health/Sensors/Sensors.vue
index 3915ff2..c234359 100644
--- a/src/views/Health/Sensors/Sensors.vue
+++ b/src/views/Health/Sensors/Sensors.vue
@@ -6,6 +6,7 @@
         <search
           :placeholder="$t('pageSensors.searchForSensors')"
           @changeSearch="onChangeSearchInput"
+          @clearSearch="onClearSearchInput"
         />
       </b-col>
       <b-col sm="3" md="3" xl="2">
@@ -107,6 +108,7 @@
 import TableFilterMixin from '@/components/Mixins/TableFilterMixin';
 import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin';
 import TableSortMixin from '@/components/Mixins/TableSortMixin';
+import SearchFilterMixin from '@/components/Mixins/SearchFilterMixin';
 
 export default {
   name: 'Sensors',
@@ -124,7 +126,8 @@
     BVTableSelectableMixin,
     LoadingBarMixin,
     TableDataFormatterMixin,
-    TableSortMixin
+    TableSortMixin,
+    SearchFilterMixin
   ],
   data() {
     return {
@@ -215,9 +218,6 @@
     onFilterChange({ activeFilters }) {
       this.activeFilters = activeFilters;
     },
-    onChangeSearchInput(event) {
-      this.searchFilter = event;
-    },
     onFiltered(filteredItems) {
       this.searchTotalFilteredRows = filteredItems.length;
     }