Add dropdown and filter functionality to the dataset health table

This commit is contained in:
cptran777 2018-08-03 20:04:25 -07:00
parent 8f49e5e7fd
commit a3ac2b014f
4 changed files with 112 additions and 12 deletions

View File

@ -144,7 +144,7 @@ export default class DatasetHealthContainer extends Component {
'currentCategoryFilter', 'currentCategoryFilter',
'currentSeverityFilter' 'currentSeverityFilter'
); );
const newFilterName = filterDatum.name; const newFilterName = filterDatum.name || filterDatum.value.toString();
setProperties(this, { setProperties(this, {
currentCategoryFilter: filterType === 'category' && newFilterName !== currentCategoryFilter ? newFilterName : '', currentCategoryFilter: filterType === 'category' && newFilterName !== currentCategoryFilter ? newFilterName : '',

View File

@ -1,7 +1,9 @@
import Component from '@ember/component'; import Component from '@ember/component';
import { IHealthScore } from 'wherehows-web/typings/api/datasets/health'; 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 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 * 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; isHidden: boolean;
} }
/**
* Properties that define a useful header object to be rendered in the template
*/
interface IHealthTableHeader {
label: string;
class: string;
dropdownOptions: IDropDownOption<string>[] | undefined;
initialDropdown: IDropDownOption<string>;
onDropdownSelect: (type: string, selection: IDropDownOption<string>) => 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<IDropDownOption<string>>;
}
export default class DatasetsHealthScoreTable extends Component { export default class DatasetsHealthScoreTable extends Component {
/** /**
* Sets the class names binded to the html element generated by this 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'; tagName = 'table';
/** /**
* Expected headers for the detailed table * Expected headers for the detailed table. It contains the wording for each header as well as
* @type {Array<string>} * additional properties
* @type {Array<IHealthTableHeader>}
*/ */
headers = ['Category', 'Description', 'Score', 'Severity']; headers = computed('dropdownOptions', function(this: DatasetsHealthScoreTable): Array<IHealthTableHeader> {
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 * Uses the passed in table data to calculate a dropdown list of options available for filtering by category
* @type {Array<string>} * and severity for the table's rows.
* @type {ComputedProperty<IHealthTableDropdowns>}
*/ */
headerClasses = this.headers.map(header => `dataset-health__score-table__${header.toLowerCase()}`); dropdownOptions: ComputedProperty<IHealthTableDropdowns> = 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: <IDropDownOption<string>[]>[{ label: 'Category', value: '' }],
severity: <IDropDownOption<string>[]>[{ label: 'Severity', value: '' }]
}
);
});
/** /**
* Passed in table data, mostly raw detailed information about the compliance score details and the * 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<string>) => 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<string>) => void;
constructor() { constructor() {
super(...arguments); super(...arguments);
setProperties(this, { setProperties(this, {
tableData: this.tableData || [], tableData: this.tableData || [],
currentSeverityFilter: this.currentSeverityFilter || '', 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<string>) {
if (type === 'Category') {
this.onCategorySelect(selection);
} else if (type === 'Severity') {
this.onSeveritySelect(selection);
}
}
} }

View File

@ -7,4 +7,6 @@
{{datasets/health/score-table {{datasets/health/score-table
tableData=tableData tableData=tableData
currentCategoryFilter=currentCategoryFilter currentCategoryFilter=currentCategoryFilter
currentSeverityFilter=currentSeverityFilter}} currentSeverityFilter=currentSeverityFilter
onCategorySelect=(action onFilterSelect "category")
onSeveritySelect=(action onFilterSelect "severity")}}

View File

@ -1,7 +1,18 @@
<thead> <thead>
<tr> <tr>
{{#each headers as |header index|}} {{#each headers as |header|}}
<th class={{get headerClasses index}}>{{header}}</th> <th class={{header.class}}>
{{#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}}
</th>
{{/each}} {{/each}}
</tr> </tr>
</thead> </thead>