[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:
parent
28f4bfabf8
commit
8cc60c2460
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
.field-value-long {
|
||||
overflow-wrap: break-word;
|
||||
// Known issue: IE11 text overflow within a flex container
|
||||
}
|
||||
|
||||
.filter-button {
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue