mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
feat(dashboard): Add cross filter from context menu (#23141)
This commit is contained in:
committed by
GitHub
parent
95eb8d79d0
commit
ee1952e488
@@ -279,6 +279,70 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
[groupbyColumnsRaw, groupbyRowsRaw, setDataMask],
|
||||
);
|
||||
|
||||
const getCrossFilterDataMask = useCallback(
|
||||
(value: { [key: string]: string }) => {
|
||||
const isActiveFilterValue = (key: string, val: DataRecordValue) =>
|
||||
!!selectedFilters && selectedFilters[key]?.includes(val);
|
||||
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const [key, val] = Object.entries(value)[0];
|
||||
let values = { ...selectedFilters };
|
||||
if (isActiveFilterValue(key, val)) {
|
||||
values = {};
|
||||
} else {
|
||||
values = { [key]: [val] };
|
||||
}
|
||||
|
||||
const filterKeys = Object.keys(values);
|
||||
const groupby = [...groupbyRowsRaw, ...groupbyColumnsRaw];
|
||||
return {
|
||||
dataMask: {
|
||||
extraFormData: {
|
||||
filters:
|
||||
filterKeys.length === 0
|
||||
? undefined
|
||||
: filterKeys.map(key => {
|
||||
const val = values?.[key];
|
||||
const col =
|
||||
groupby.find(item => {
|
||||
if (isPhysicalColumn(item)) {
|
||||
return item === key;
|
||||
}
|
||||
if (isAdhocColumn(item)) {
|
||||
return item.label === key;
|
||||
}
|
||||
return false;
|
||||
}) ?? '';
|
||||
if (val === null || val === undefined)
|
||||
return {
|
||||
col,
|
||||
op: 'IS NULL' as const,
|
||||
};
|
||||
return {
|
||||
col,
|
||||
op: 'IN' as const,
|
||||
val: val as (string | number | boolean)[],
|
||||
};
|
||||
}),
|
||||
},
|
||||
filterState: {
|
||||
value:
|
||||
values && Object.keys(values).length
|
||||
? Object.values(values)
|
||||
: null,
|
||||
selectedFilters:
|
||||
values && Object.keys(values).length ? values : null,
|
||||
},
|
||||
},
|
||||
isCurrentValueSelected: isActiveFilterValue(key, val),
|
||||
};
|
||||
},
|
||||
[groupbyColumnsRaw, groupbyRowsRaw, selectedFilters],
|
||||
);
|
||||
|
||||
const toggleFilter = useCallback(
|
||||
(
|
||||
e: MouseEvent,
|
||||
@@ -369,18 +433,19 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
e: MouseEvent,
|
||||
colKey: (string | number | boolean)[] | undefined,
|
||||
rowKey: (string | number | boolean)[] | undefined,
|
||||
dataPoint: { [key: string]: string },
|
||||
) => {
|
||||
if (onContextMenu) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const filters: BinaryQueryObjectFilterClause[] = [];
|
||||
const drillToDetailFilters: BinaryQueryObjectFilterClause[] = [];
|
||||
if (colKey && colKey.length > 1) {
|
||||
colKey.forEach((val, i) => {
|
||||
const col = cols[i];
|
||||
const formatter = dateFormatters[col];
|
||||
const formattedVal = formatter?.(val as number) || String(val);
|
||||
if (i > 0) {
|
||||
filters.push({
|
||||
drillToDetailFilters.push({
|
||||
col,
|
||||
op: '==',
|
||||
val,
|
||||
@@ -395,7 +460,7 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
const col = rows[i];
|
||||
const formatter = dateFormatters[col];
|
||||
const formattedVal = formatter?.(val as number) || String(val);
|
||||
filters.push({
|
||||
drillToDetailFilters.push({
|
||||
col,
|
||||
op: '==',
|
||||
val,
|
||||
@@ -404,7 +469,10 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
});
|
||||
});
|
||||
}
|
||||
onContextMenu(e.clientX, e.clientY, filters);
|
||||
onContextMenu(e.clientX, e.clientY, {
|
||||
drillToDetail: drillToDetailFilters,
|
||||
crossFilter: getCrossFilterDataMask(dataPoint),
|
||||
});
|
||||
}
|
||||
},
|
||||
[cols, dateFormatters, onContextMenu, rows, timeGrainSqla],
|
||||
|
||||
@@ -393,6 +393,7 @@ export class TableRenderer extends React.Component {
|
||||
// Iterate through columns. Jump over duplicate values.
|
||||
let i = 0;
|
||||
while (i < visibleColKeys.length) {
|
||||
let handleContextMenu;
|
||||
const colKey = visibleColKeys[i];
|
||||
const colSpan = attrIdx < colKey.length ? colAttrSpans[i][attrIdx] : 1;
|
||||
let colLabelClass = 'pvtColLabel';
|
||||
@@ -402,6 +403,10 @@ export class TableRenderer extends React.Component {
|
||||
!omittedHighlightHeaderGroups.includes(colAttrs[attrIdx])
|
||||
) {
|
||||
colLabelClass += ' hoverable';
|
||||
handleContextMenu = e =>
|
||||
this.props.onContextMenu(e, colKey, undefined, {
|
||||
[attrName]: colKey[attrIdx],
|
||||
});
|
||||
}
|
||||
if (
|
||||
highlightedHeaderCells &&
|
||||
@@ -434,6 +439,7 @@ export class TableRenderer extends React.Component {
|
||||
attrIdx,
|
||||
this.props.tableOptions.clickColumnHeaderCallback,
|
||||
)}
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
{displayHeaderCell(
|
||||
needToggle,
|
||||
@@ -590,12 +596,17 @@ export class TableRenderer extends React.Component {
|
||||
|
||||
const colIncrSpan = colAttrs.length !== 0 ? 1 : 0;
|
||||
const attrValueCells = rowKey.map((r, i) => {
|
||||
let handleContextMenu;
|
||||
let valueCellClassName = 'pvtRowLabel';
|
||||
if (
|
||||
highlightHeaderCellsOnHover &&
|
||||
!omittedHighlightHeaderGroups.includes(rowAttrs[i])
|
||||
) {
|
||||
valueCellClassName += ' hoverable';
|
||||
handleContextMenu = e =>
|
||||
this.props.onContextMenu(e, undefined, rowKey, {
|
||||
[rowAttrs[i]]: r,
|
||||
});
|
||||
}
|
||||
if (
|
||||
highlightedHeaderCells &&
|
||||
@@ -631,6 +642,7 @@ export class TableRenderer extends React.Component {
|
||||
i,
|
||||
this.props.tableOptions.clickRowHeaderCallback,
|
||||
)}
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
{displayHeaderCell(
|
||||
needRowToggle,
|
||||
|
||||
@@ -26,8 +26,8 @@ import {
|
||||
NumberFormatter,
|
||||
QueryFormMetric,
|
||||
QueryFormColumn,
|
||||
BinaryQueryObjectFilterClause,
|
||||
TimeGranularity,
|
||||
ContextMenuFilters,
|
||||
} from '@superset-ui/core';
|
||||
import { ColorFormatters } from '@superset-ui/chart-controls';
|
||||
|
||||
@@ -77,7 +77,7 @@ interface PivotTableCustomizeProps {
|
||||
onContextMenu?: (
|
||||
clientX: number,
|
||||
clientY: number,
|
||||
filters?: BinaryQueryObjectFilterClause[],
|
||||
filters?: ContextMenuFilters,
|
||||
) => void;
|
||||
timeGrainSqla?: TimeGranularity;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user