[ML] Anomaly Explorer - filter addition should be cumulative with existing selectedCells data (#33753)

* Update view by for filter if no selected cells

* keep timerange reference in overview swimlane for filter

* remove obsolete comments

* If AND filter set view by to jobID as others have no results

* clear cellSelection from appstate as well when clearing filter
This commit is contained in:
Melissa Alvarez 2019-03-29 11:32:39 -04:00 committed by GitHub
parent 28f4bfabf8
commit 8cc60c2460
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 22 deletions

View file

@ -62,7 +62,7 @@ function getRemoveFilter({ entityName, entityValue, filter }) {
}
/*
* Component for rendering an entity in the anomalies table, displaying the value
* Component for rendering an entity, displaying the value
* of the entity, such as a partitioning or influencer field value, and optionally links for
* adding or removing a filter on this entity.
*/

View file

@ -6,7 +6,6 @@
.field-value-long {
overflow-wrap: break-word;
// Known issue: IE11 text overflow within a flex container
}
.filter-button {

View file

@ -104,6 +104,7 @@ function getExplorerDefaultState() {
influencersFilterQuery: undefined,
hasResults: false,
influencers: {},
isAndOperator: false,
loading: true,
noInfluencersConfigured: true,
noJobsFound: true,
@ -633,6 +634,7 @@ export const Explorer = injectI18n(injectObservablesAsProps(
filterActive,
filteredFields,
influencersFilterQuery,
isAndOperator,
noInfluencersConfigured,
noJobsFound,
selectedCells,
@ -710,7 +712,9 @@ export const Explorer = injectI18n(injectObservablesAsProps(
currentSwimlaneViewByFieldName: swimlaneViewByFieldName,
filterActive,
filteredFields,
selectedJobs
isAndOperator,
selectedJobs,
selectedCells
});
Object.assign(stateUpdate, viewBySwimlaneOptions);
@ -753,12 +757,14 @@ export const Explorer = injectI18n(injectObservablesAsProps(
// do a sanity check against selectedCells. It can happen that a previously
// selected lane loaded via URL/AppState is not available anymore.
// If filter is active - selectedCell may not be available due to swimlane view by change to filter fieldName
// Ok to keep cellSelection in this case
let clearSelection = false;
if (
selectedCells !== null &&
selectedCells.type === SWIMLANE_TYPE.VIEW_BY
) {
clearSelection = !selectedCells.lanes.some((lane) => {
clearSelection = (filterActive === false) && !selectedCells.lanes.some((lane) => {
return viewBySwimlaneData.points.some((point) => {
return (
point.laneLabel === lane &&
@ -959,18 +965,21 @@ export const Explorer = injectI18n(injectObservablesAsProps(
applyInfluencersFilterQuery = ({
influencersFilterQuery,
isAndOperator,
filteredFields,
queryString,
tableQueryString }) => {
const { swimlaneViewByFieldName, viewBySwimlaneOptions } = this.state;
const { selectedCells, swimlaneViewByFieldName, viewBySwimlaneOptions } = this.state;
let selectedViewByFieldName = swimlaneViewByFieldName;
if (influencersFilterQuery.match_all && Object.keys(influencersFilterQuery.match_all).length === 0) {
this.props.appStateHandler(APP_STATE_ACTION.CLEAR_INFLUENCER_FILTER_SETTINGS);
this.props.appStateHandler(APP_STATE_ACTION.CLEAR_SELECTION);
const stateUpdate = {
filterActive: false,
filteredFields: [],
influencersFilterQuery: undefined,
isAndOperator: false,
maskAll: false,
queryString: '',
tableQueryString: '',
@ -979,25 +988,37 @@ export const Explorer = injectI18n(injectObservablesAsProps(
this.updateExplorer(stateUpdate, false);
} else {
// Set View by dropdown to first relevant fieldName based on incoming filter
for (let i = 0; i < filteredFields.length; i++) {
if (viewBySwimlaneOptions.includes(filteredFields[i])) {
selectedViewByFieldName = filteredFields[i];
this.props.appStateHandler(
APP_STATE_ACTION.SAVE_SWIMLANE_VIEW_BY_FIELD_NAME,
{ swimlaneViewByFieldName: selectedViewByFieldName },
);
break;
// if it's an AND filter set view by swimlane to job ID as the others will have no results
if (isAndOperator && selectedCells === null) {
selectedViewByFieldName = VIEW_BY_JOB_LABEL;
this.props.appStateHandler(
APP_STATE_ACTION.SAVE_SWIMLANE_VIEW_BY_FIELD_NAME,
{ swimlaneViewByFieldName: selectedViewByFieldName },
);
} else {
// Set View by dropdown to first relevant fieldName based on incoming filter if there's no cell selection already
// or if selected cell is from overall swimlane as this won't include an additional influencer filter
for (let i = 0; i < filteredFields.length; i++) {
if (viewBySwimlaneOptions.includes(filteredFields[i]) &&
((selectedCells === null || (selectedCells && selectedCells.type === 'overall')))) {
selectedViewByFieldName = filteredFields[i];
this.props.appStateHandler(
APP_STATE_ACTION.SAVE_SWIMLANE_VIEW_BY_FIELD_NAME,
{ swimlaneViewByFieldName: selectedViewByFieldName },
);
break;
}
}
}
this.props.appStateHandler(APP_STATE_ACTION.SAVE_INFLUENCER_FILTER_SETTINGS,
{ influencersFilterQuery, filterActive: true, filteredFields, queryString, tableQueryString });
{ influencersFilterQuery, filterActive: true, filteredFields, queryString, tableQueryString, isAndOperator });
this.updateExplorer({
filterActive: true,
filteredFields,
influencersFilterQuery,
isAndOperator,
queryString,
tableQueryString,
maskAll: (selectedViewByFieldName === VIEW_BY_JOB_LABEL ||

View file

@ -168,6 +168,14 @@ export const ExplorerSwimlane = injectI18n(class ExplorerSwimlane extends React.
swimlaneCellClick(selectedCells);
}
highlightOverall(times) {
const overallSwimlane = d3.select('.ml-swimlane-overall');
times.forEach(time => {
const overallCell = overallSwimlane.selectAll(`div[data-time="${time}"]`).selectAll('.sl-cell-inner,.sl-cell-inner-dragselect');
overallCell.classed('sl-cell-inner-selected', true);
});
}
highlightSelection(cellsToSelect, laneLabels, times) {
const { swimlaneType } = this.props;
@ -191,11 +199,7 @@ export const ExplorerSwimlane = injectI18n(class ExplorerSwimlane extends React.
if (swimlaneType === 'viewBy') {
// If selecting a cell in the 'view by' swimlane, indicate the corresponding time in the Overall swimlane.
const overallSwimlane = d3.select('.ml-swimlane-overall');
times.forEach(time => {
const overallCell = overallSwimlane.selectAll(`div[data-time="${time}"]`).selectAll('.sl-cell-inner,.sl-cell-inner-dragselect');
overallCell.classed('sl-cell-inner-selected', true);
});
this.highlightOverall(times);
}
}
@ -503,6 +507,9 @@ export const ExplorerSwimlane = injectI18n(class ExplorerSwimlane extends React.
if (cellsToSelect.length > 1 || selectedMaxBucketScore > 0) {
this.highlightSelection(cellsToSelect, selectedLanes, selectedTimes);
} else if (filterActive === true) {
if (selectedTimes) {
this.highlightOverall(selectedTimes);
}
this.maskIrrelevantSwimlanes(maskAll);
} else {
this.clearSelection();

View file

@ -205,6 +205,8 @@ export function getViewBySwimlaneOptions({
currentSwimlaneViewByFieldName,
filterActive,
filteredFields,
isAndOperator,
selectedCells,
selectedJobs
}) {
const selectedJobIds = selectedJobs.map(d => d.id);
@ -289,9 +291,15 @@ export function getViewBySwimlaneOptions({
}
// filter View by options to relevant filter fields
if (filterActive === true && Array.isArray(viewBySwimlaneOptions) && Array.isArray(filteredFields)) {
// If it's an AND filter only show job Id view by as the rest will have no results
if (filterActive === true && isAndOperator === true && selectedCells === null) {
viewBySwimlaneOptions = [VIEW_BY_JOB_LABEL];
} else if (filterActive === true && Array.isArray(viewBySwimlaneOptions) && Array.isArray(filteredFields)) {
const filteredOptions = viewBySwimlaneOptions.filter(option => {
return (filteredFields.includes(option) || option === 'job ID');
return (
filteredFields.includes(option) ||
option === VIEW_BY_JOB_LABEL ||
(selectedCells && selectedCells.viewByFieldName === option));
});
// only replace viewBySwimlaneOptions with filteredOptions if we found a relevant matching field
if (filteredOptions.length > 1) {