From a3ac2b014f12b438dae2c44c1d3bc8f33c129770 Mon Sep 17 00:00:00 2001 From: cptran777 Date: Fri, 3 Aug 2018 20:04:25 -0700 Subject: [PATCH] Add dropdown and filter functionality to the dataset health table --- .../datasets/containers/dataset-health.ts | 2 +- .../components/datasets/health/score-table.ts | 103 ++++++++++++++++-- .../datasets/containers/dataset-health.hbs | 4 +- .../datasets/health/score-table.hbs | 15 ++- 4 files changed, 112 insertions(+), 12 deletions(-) diff --git a/wherehows-web/app/components/datasets/containers/dataset-health.ts b/wherehows-web/app/components/datasets/containers/dataset-health.ts index 8f69e25f7a..9a53adff9a 100644 --- a/wherehows-web/app/components/datasets/containers/dataset-health.ts +++ b/wherehows-web/app/components/datasets/containers/dataset-health.ts @@ -144,7 +144,7 @@ export default class DatasetHealthContainer extends Component { 'currentCategoryFilter', 'currentSeverityFilter' ); - const newFilterName = filterDatum.name; + const newFilterName = filterDatum.name || filterDatum.value.toString(); setProperties(this, { currentCategoryFilter: filterType === 'category' && newFilterName !== currentCategoryFilter ? newFilterName : '', diff --git a/wherehows-web/app/components/datasets/health/score-table.ts b/wherehows-web/app/components/datasets/health/score-table.ts index 3b828b65e0..f308a4c99c 100644 --- a/wherehows-web/app/components/datasets/health/score-table.ts +++ b/wherehows-web/app/components/datasets/health/score-table.ts @@ -1,7 +1,9 @@ import Component from '@ember/component'; import { IHealthScore } from 'wherehows-web/typings/api/datasets/health'; -import { computed, setProperties, getProperties } from '@ember/object'; +import { computed, setProperties, getProperties, get } from '@ember/object'; import ComputedProperty from '@ember/object/computed'; +import { IDropDownOption } from 'wherehows-web/typings/app/dataset-compliance'; +import { noop } from 'wherehows-web/utils/helpers/functions'; /** * Adds properties specifically to help the table render each row to the basic health score @@ -11,6 +13,25 @@ interface IRenderedHealthScore extends IHealthScore { isHidden: boolean; } +/** + * Properties that define a useful header object to be rendered in the template + */ +interface IHealthTableHeader { + label: string; + class: string; + dropdownOptions: IDropDownOption[] | undefined; + initialDropdown: IDropDownOption; + onDropdownSelect: (type: string, selection: IDropDownOption) => void; +} + +/** + * Defining a loose interface for the dropdowns object calculated for category and severity based on + * the received table data + */ +interface IHealthTableDropdowns { + [key: string]: Array>; +} + export default class DatasetsHealthScoreTable extends Component { /** * Sets the class names binded to the html element generated by this component @@ -25,16 +46,52 @@ export default class DatasetsHealthScoreTable extends Component { tagName = 'table'; /** - * Expected headers for the detailed table - * @type {Array} + * Expected headers for the detailed table. It contains the wording for each header as well as + * additional properties + * @type {Array} */ - headers = ['Category', 'Description', 'Score', 'Severity']; + headers = computed('dropdownOptions', function(this: DatasetsHealthScoreTable): Array { + const dropdownOptions = get(this, 'dropdownOptions'); + const onDropdownSelect = get(this, 'onDropdownSelect'); + + return ['Category', 'Description', 'Score', 'Severity'].map(header => ({ + label: header, + class: `dataset-health__score-table__${header.toLowerCase()}`, + dropdownOptions: dropdownOptions[header.toLowerCase()], + initialDropdown: { label: header, value: '' }, + onDropdownSelect: onDropdownSelect.bind(this, header) + })); + }); /** - * Classes for each header element based on the actual header column title - * @type {Array} + * Uses the passed in table data to calculate a dropdown list of options available for filtering by category + * and severity for the table's rows. + * @type {ComputedProperty} */ - headerClasses = this.headers.map(header => `dataset-health__score-table__${header.toLowerCase()}`); + dropdownOptions: ComputedProperty = computed('tableData', function( + this: DatasetsHealthScoreTable + ) { + const tableData = get(this, 'tableData'); + const includedOptions = new Set(); + + return tableData.reduce( + (output, row) => { + const { category, severity } = row; + + !includedOptions.has(category) && output.category.push({ label: category, value: category }); + severity && !includedOptions.has(severity) && output.severity.push({ label: severity, value: severity }); + // Ensures we only add each option once, assumes severity will never === category + includedOptions.add(category); + includedOptions.add(severity); + + return output; + }, + { + category: []>[{ label: 'Category', value: '' }], + severity: []>[{ label: 'Severity', value: '' }] + } + ); + }); /** * Passed in table data, mostly raw detailed information about the compliance score details and the @@ -83,13 +140,43 @@ export default class DatasetsHealthScoreTable extends Component { } ); + /** + * Passed in function from the dataset health container that helps us handle when the user clicks on a + * specific category from the dropdown menu in the table header + * @param {IDropDownOption} selection - the selected option from the dropdown + */ + onCategorySelect: (selection: IDropDownOption) => void; + + /** + * Passed in function from the dataset health container that helps us handle when the user clicks on a + * specific severity from the dropdown menu on the table header + * @param {IDropDownOption} selection - the selected option from the dropdown + */ + onSeveritySelect: (section: IDropDownOption) => void; + constructor() { super(...arguments); setProperties(this, { tableData: this.tableData || [], currentSeverityFilter: this.currentSeverityFilter || '', - currentCategoryFilter: this.currentCategoryFilter || '' + currentCategoryFilter: this.currentCategoryFilter || '', + onCategorySelect: this.onCategorySelect || noop, + onSeveritySelect: this.onSeveritySelect || noop }); } + + /** + * This is attached to each header object so that we can pass into the dropdown option for that header + * and allow the user to select from it with a handler + * @param type - lets us know which dropdown called this function + * @param selection - the actual dropdown option passed in from the child component + */ + onDropdownSelect(type: string, selection: IDropDownOption) { + if (type === 'Category') { + this.onCategorySelect(selection); + } else if (type === 'Severity') { + this.onSeveritySelect(selection); + } + } } diff --git a/wherehows-web/app/templates/components/datasets/containers/dataset-health.hbs b/wherehows-web/app/templates/components/datasets/containers/dataset-health.hbs index d7264f2ec2..7459cebafe 100644 --- a/wherehows-web/app/templates/components/datasets/containers/dataset-health.hbs +++ b/wherehows-web/app/templates/components/datasets/containers/dataset-health.hbs @@ -7,4 +7,6 @@ {{datasets/health/score-table tableData=tableData currentCategoryFilter=currentCategoryFilter - currentSeverityFilter=currentSeverityFilter}} + currentSeverityFilter=currentSeverityFilter + onCategorySelect=(action onFilterSelect "category") + onSeveritySelect=(action onFilterSelect "severity")}} diff --git a/wherehows-web/app/templates/components/datasets/health/score-table.hbs b/wherehows-web/app/templates/components/datasets/health/score-table.hbs index 47c08c778c..421a73842a 100644 --- a/wherehows-web/app/templates/components/datasets/health/score-table.hbs +++ b/wherehows-web/app/templates/components/datasets/health/score-table.hbs @@ -1,7 +1,18 @@ - {{#each headers as |header index|}} - {{header}} + {{#each headers as |header|}} + + {{#if header.dropdownOptions}} + {{#nacho/dropdown/hover-dropdown + selectedDropDown=header.initialDropdown + onSelect=header.onDropdownSelect + dropDownItems=header.dropdownOptions as |dd|}} + {{dd.content}} + {{/nacho/dropdown/hover-dropdown}} + {{else}} + {{header.label}} + {{/if}} + {{/each}}