From b8986f8cf50acda6cdb423f113f1fd8f6c1ead37 Mon Sep 17 00:00:00 2001 From: Mike Shriver Date: Tue, 12 Oct 2021 08:17:43 -0400 Subject: [PATCH] Implement MetaFilter with separate field and value Alternative to #221 that is more generic. Similar to the filters used on the run/results page, and could be an abstract replacement for their current filters. Obviously not complete yet, but I wanted to commit what I had because its enough to further the discussion and get some feedback before I proceed further. --- frontend/src/components/classify-failures.js | 87 ++++----------- frontend/src/components/filtertable.js | 109 ++++++++++++++++++- frontend/src/components/index.js | 2 +- 3 files changed, 130 insertions(+), 68 deletions(-) diff --git a/frontend/src/components/classify-failures.js b/frontend/src/components/classify-failures.js index 5f8ea850..61f9f88a 100644 --- a/frontend/src/components/classify-failures.js +++ b/frontend/src/components/classify-failures.js @@ -9,9 +9,6 @@ import { Checkbox, Flex, FlexItem, - Select, - SelectOption, - SelectVariant, TextContent, Text, } from '@patternfly/react-core'; @@ -27,10 +24,11 @@ import { getSpinnerRow, resultToClassificationRow, } from '../utilities'; -import { OPERATIONS } from '../constants'; +import { OPERATIONS, STRING_RESULT_FIELDS } from '../constants'; import { FilterTable, MultiClassificationDropdown, + MetaFilter, } from './index'; @@ -56,10 +54,9 @@ export class ClassifyFailuresTable extends React.Component { isError: false, isFieldOpen: false, isOperationOpen: false, - exceptionSelections: [], - isExceptionOpen: false, - exceptions: [], includeSkipped: false, + exceptions: [], + assignees: [], filters: Object.assign({ 'result': {op: 'in', val: 'failed;error'}, 'run_id': {op: 'eq', val: props.run_id}}, props.filters), @@ -73,39 +70,18 @@ export class ClassifyFailuresTable extends React.Component { this.getResultsForTable(); } - onExceptionToggle = isExpanded => { - this.setState({isExceptionOpen: isExpanded}, this.applyFilter); - } - - applyExceptionFilter = () => { - let { filters, exceptionSelections } = this.state; - if (exceptionSelections.length > 0) { - filters["metadata.exception_name"] = Object.assign({op: 'in', val: exceptionSelections.join(';')}); + applyFilter = (field, value) => { + let {filters} = this.state + if (value.length > 0) { + filters["metadata." + field] = Object.assign({op: 'in', val: value.join(';')}); this.setState({filters}, this.refreshResults); } else { - delete filters["metadata.exception_name"]; + delete filters["metadata." + field]; this.setState({filters}, this.refreshResults); } } - onExceptionSelect = (event, selection) => { - const exceptionSelections = this.state.exceptionSelections; - if (exceptionSelections.includes(selection)) { - this.setState({exceptionSelections: exceptionSelections.filter(item => item !== selection)}, this.applyExceptionFilter); - } - else { - this.setState({exceptionSelections: [...exceptionSelections, selection]}, this.applyExceptionFilter); - } - }; - - onExceptionClear = () => { - this.setState({ - exceptionSelections: [], - isExceptionOpen: false - }, this.applyExceptionFilter); - }; - onCollapse(event, rowIndex, isOpen) { const { rows } = this.state; rows[rowIndex].isOpen = isOpen; @@ -229,17 +205,8 @@ export class ClassifyFailuresTable extends React.Component { }); } - getExceptions() { - fetch(buildUrl(Settings.serverUrl + '/widget/result-aggregator', {group_field: 'metadata.exception_name', run_id: this.props.run_id})) - .then(response => response.json()) - .then(data => { - this.setState({exceptions: data}) - }) - } - componentDidMount() { this.getResultsForTable(); - this.getExceptions(); } render() { @@ -248,35 +215,23 @@ export class ClassifyFailuresTable extends React.Component { rows, selectedResults, includeSkipped, - isExceptionOpen, - exceptionSelections, - exceptions, } = this.state; + const { + run_id + } = this.props const pagination = { pageSize: this.state.pageSize, page: this.state.page, totalItems: this.state.totalItems } - // filters for the exception - const exceptionFilters = [ - - - + // filters for the metadata + const resultFilters = [ + , ] return ( @@ -314,7 +269,7 @@ export class ClassifyFailuresTable extends React.Component { onRowSelect={this.onTableRowSelect} variant={TableVariant.compact} activeFilters={this.state.filters} - filters={exceptionFilters} + filters={resultFilters} onRemoveFilter={this.removeFilter} hideFilters={["run_id", "project_id"]} /> diff --git a/frontend/src/components/filtertable.js b/frontend/src/components/filtertable.js index 9877e020..4c4ed38f 100644 --- a/frontend/src/components/filtertable.js +++ b/frontend/src/components/filtertable.js @@ -9,7 +9,10 @@ import { Flex, FlexItem, Pagination, - PaginationVariant + PaginationVariant, + Select, + SelectOption, + SelectVariant, } from '@patternfly/react-core'; import { Table, @@ -17,6 +20,11 @@ import { TableHeader } from '@patternfly/react-table'; +import { Settings } from '../settings'; +import { + buildUrl, +} from '../utilities'; + import { TableEmptyState, TableErrorState } from './tablestates'; export class FilterTable extends React.Component { @@ -160,3 +168,102 @@ export class FilterTable extends React.Component { ); } } + + +export class MetaFilter extends React.Component { + static propTypes = { + fieldOptions: PropTypes.array, // could reference constants directly + run_id: PropTypes.string, // make optional? + applyFunc: PropTypes.func, + }; + + constructor(props) { + super(props); + this.state = { + fieldSelection: null, + isFieldOpen: false, + isValueOpen: false, + valueOptions: [], + valueSelections: [], + }; + } + + onFieldToggle = isExpanded => { + this.setState({isFieldOpen: isExpanded}) + }; + + onValueToggle = isExpanded => { + this.setState({isValueOpen: isExpanded}) + }; + + onFieldSelect = (event, selection) => { + this.setState( + {fieldSelection: selection, isFieldOpen: false}, + this.updateValueOptions + ) + + }; + + onFieldClear = () => { + this.setState( + {fieldSelection: null, valueSelections: [], isFieldOpen: false, isValueOpen: false}, + () => this.props.applyFunc(this.state.fieldSelection, this.state.valueSelections)) + }; + + updateValueOptions = () => { + const {fieldSelection} = this.state + if (fieldSelection !== null) { + fetch(buildUrl(Settings.serverUrl + '/widget/result-aggregator', + {group_field: fieldSelection, run_id: this.props.run_id})) + .then(response => response.json()) + .then(data => { + this.setState({valueOptions: data}) + }) + } + } + + // componentDidMount() { + // this.updateValueOptions(); + // } + + render () { + const {isFieldOpen, fieldSelection, isValueOpen, valueOptions, valueSelections} = this.state + + return ( + + + + + + ) + } +} diff --git a/frontend/src/components/index.js b/frontend/src/components/index.js index 719831b1..1838f057 100644 --- a/frontend/src/components/index.js +++ b/frontend/src/components/index.js @@ -3,7 +3,7 @@ export { ClassifyFailuresTable } from './classify-failures'; export { DeleteModal } from './delete-modal'; export { EmptyObject } from './empty-object'; export { FileUpload } from './fileupload'; -export { FilterTable } from './filtertable'; +export { FilterTable, MetaFilter } from './filtertable'; export { ParamDropdown } from './widget-components'; export { MultiValueInput } from './multivalueinput'; export { NewDashboardModal } from './new-dashboard-modal';