You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by en...@apache.org on 2026/10/07 22:08:12 UTC
(superset) branch master updated: feat(pivot-table): collapse row groups by default (#45030)
This is an automated email from the ASF dual-hosted git repository.
EnxDev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 1cf9569807c feat(pivot-table): collapse row groups by default (#45030)
1cf9569807c is described below
commit 1cf9569807c872d4cdb59947ba3871d47e30b483
Author: SBIN2010 <Sb...@mail.ru>
AuthorDate: Thu Oct 8 01:08:05 2026 +0300
feat(pivot-table): collapse row groups by default (#45030)
---
.../src/PivotTableChart.tsx | 3 +
.../src/plugin/controlPanel.tsx | 17 +
.../src/plugin/transformProps.ts | 2 +
.../src/react-pivottable/TableRenderers.tsx | 109 ++++++-
.../plugins/plugin-chart-pivot-table/src/types.ts | 5 +
.../test/react-pivottable/tableRenders.test.tsx | 346 +++++++++++++++++++++
superset/translations/messages.pot | 6 +
7 files changed, 476 insertions(+), 12 deletions(-)
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
index f4b91259a42..b3bcf3a7217 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
@@ -238,6 +238,7 @@ export default function PivotTableChart(props: PivotTableProps) {
colSubTotals,
rowTotals,
rowSubTotals,
+ collapseRows,
valueFormat,
currencyFormat,
currencyCodeColumn,
@@ -630,6 +631,7 @@ export default function PivotTableChart(props: PivotTableProps) {
colSubTotals,
rowTotals,
rowSubTotals,
+ collapseRows,
highlightHeaderCellsOnHover:
emitCrossFilters ||
isFeatureEnabled(FeatureFlag.DrillBy) ||
@@ -648,6 +650,7 @@ export default function PivotTableChart(props: PivotTableProps) {
dateFormatters,
emitCrossFilters,
metricColorFormatters,
+ collapseRows,
rowTotals,
rowSubTotals,
selectedFilters,
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx
index 4107466f80d..99c83109849 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx
@@ -192,6 +192,23 @@ const config: ControlPanelConfig = {
},
},
],
+ [
+ {
+ name: 'collapseRows',
+ config: {
+ type: 'CheckboxControl',
+ label: t('Collapse rows by default'),
+ default: false,
+ renderTrigger: true,
+ description: t(
+ 'Start with row groups collapsed when row subtotals are shown. ' +
+ 'Each group can still be expanded with its arrow.',
+ ),
+ visibility: ({ controls }) =>
+ Boolean(controls?.rowSubTotals?.value),
+ },
+ },
+ ],
[
{
name: 'colTotals',
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts
index f176ce64e21..afd1323098d 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts
@@ -184,6 +184,7 @@ export default function transformProps(chartProps: ChartProps<QueryFormData>) {
colSubTotals,
rowTotals,
rowSubTotals,
+ collapseRows,
valueFormat,
dateFormat,
metricsLayout,
@@ -262,6 +263,7 @@ export default function transformProps(chartProps: ChartProps<QueryFormData>) {
colSubTotals,
rowTotals,
rowSubTotals,
+ collapseRows: Boolean(collapseRows),
valueFormat,
currencyFormat,
currencyCodeColumn,
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.tsx
index 057159bf8ed..5ce79d1ec47 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.tsx
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.tsx
@@ -81,6 +81,8 @@ interface TableOptions {
colTotals?: boolean;
rowSubTotals?: boolean;
colSubTotals?: boolean;
+ /** Collapse every row group until the viewer expands it. Requires row subtotals. */
+ collapseRows?: boolean;
clickCallback?: ClickCallback;
clickColumnHeaderCallback?: HeaderClickCallback;
clickRowHeaderCallback?: HeaderClickCallback;
@@ -161,6 +163,35 @@ const parseLabel = (value: unknown): string | number => {
return String(value);
};
+const NO_COLLAPSIBLE_PREFIXES: ReadonlySet<string> = new Set();
+
+/**
+ * Whether a row prefix is collapsed. An explicit toggle wins. Otherwise the
+ * prefix is collapsed only when default collapse is on and a subtotal row for
+ * that prefix will actually render. Prefixes with no subtotal (for example the
+ * metric pseudo-dimension) stay expanded, so their descendants are not hidden
+ * with no arrow left to open them. The empty prefix is always expanded,
+ * because the ancestor walk visits it for every key.
+ *
+ * `Object.hasOwn` ignores `Object.prototype`, so a group named `constructor`
+ * or `toString` is not treated as collapsed.
+ */
+function isEffectivelyCollapsed(
+ key: string[],
+ collapsed: Record<string, boolean>,
+ collapseByDefault: boolean,
+ collapsiblePrefixes: ReadonlySet<string>,
+): boolean {
+ if (key.length === 0) {
+ return false;
+ }
+ const keyId = flatKey(key);
+ if (Object.hasOwn(collapsed, keyId)) {
+ return Boolean(collapsed[keyId]);
+ }
+ return collapseByDefault && collapsiblePrefixes.has(keyId);
+}
+
function displayCell(value: unknown, allowRenderHtml?: boolean): ReactNode {
if (allowRenderHtml && typeof value === 'string') {
return safeHtmlSpan(value);
@@ -364,6 +395,19 @@ export function TableRenderer(props: TableRendererProps) {
const [activeSortColumn, setActiveSortColumn] = useState<number | null>(null);
const [sortedRowKeys, setSortedRowKeys] = useState<string[][] | null>(null);
+ // Default collapse only applies while row subtotals are on. Without them
+ // there is no subtotal row to show and no arrow to expand a hidden group.
+ const collapseRowsByDefault = Boolean(
+ tableOptions.collapseRows && tableOptions.rowSubTotals,
+ );
+
+ // Manual expands live in this state and would otherwise outlive the
+ // checkbox. Resetting on each flip makes the Explore preview match a chart
+ // that opens with the new setting.
+ useEffect(() => {
+ setCollapsedRows({});
+ }, [collapseRowsByDefault]);
+
const sortCacheRef = useRef(new Map<string, string[][]>());
const clickHandler = useCallback(
@@ -476,13 +520,16 @@ export function TableRenderer(props: TableRendererProps) {
);
const toggleRowKey = useCallback(
- (flatRowKey: string) => (e: MouseEvent<HTMLButtonElement>) => {
- e.stopPropagation();
- setCollapsedRows(state => ({
- ...state,
- [flatRowKey]: !state[flatRowKey],
- }));
- },
+ (flatRowKey: string, collapsedByDefault: boolean) =>
+ (e: MouseEvent<HTMLButtonElement>) => {
+ e.stopPropagation();
+ setCollapsedRows(state => ({
+ ...state,
+ [flatRowKey]: !(Object.hasOwn(state, flatRowKey)
+ ? state[flatRowKey]
+ : collapsedByDefault),
+ }));
+ },
[],
);
@@ -729,17 +776,25 @@ export function TableRenderer(props: TableRendererProps) {
collapsed: Record<string, boolean>,
numAttrs: number,
subtotalDisplay: SubtotalDisplay,
+ collapseByDefault: boolean,
+ collapsiblePrefixes: ReadonlySet<string>,
) =>
keys.filter(
(key: string[]) =>
// Is the key hidden by one of its parents?
- !key.some(
- (_k: string, j: number) => collapsed[flatKey(key.slice(0, j))],
+ !key.some((_k: string, j: number) =>
+ isEffectivelyCollapsed(
+ key.slice(0, j),
+ collapsed,
+ collapseByDefault,
+ collapsiblePrefixes,
+ ),
) &&
// Leaf key.
(key.length === numAttrs ||
// Children hidden. Must show total.
- flatKey(key) in collapsed ||
+ Object.hasOwn(collapsed, flatKey(key)) ||
+ (collapseByDefault && collapsiblePrefixes.has(flatKey(key))) ||
// Don't hide totals.
!subtotalDisplay.hideOnExpand),
),
@@ -788,6 +843,19 @@ export function TableRenderer(props: TableRendererProps) {
const rowKeys = effectiveRowKeys;
+ // Subtotal rows are the only prefixes default collapse may hide children
+ // under. A shorter key that was never produced (metric-only rollups are
+ // omitted for metrics-on-rows) must stay expanded.
+ const collapsibleRowPrefixes = useMemo(() => {
+ const prefixes = new Set<string>();
+ basePivotSettings.rowKeys.forEach(key => {
+ if (key.length > 0 && key.length < basePivotSettings.rowAttrs.length) {
+ prefixes.add(flatKey(key));
+ }
+ });
+ return prefixes;
+ }, [basePivotSettings]);
+
// Need to account for exclusions to compute the effective row
// and column keys.
const visibleRowKeys = visibleKeys(
@@ -795,12 +863,16 @@ export function TableRenderer(props: TableRendererProps) {
collapsedRows,
rowAttrs.length,
rowSubtotalDisplay,
+ collapseRowsByDefault,
+ collapsibleRowPrefixes,
);
const visibleColKeys = visibleKeys(
colKeys,
collapsedCols,
colAttrs.length,
colSubtotalDisplay,
+ false,
+ NO_COLLAPSIBLE_PREFIXES,
);
const pivotSettings: PivotSettings = {
@@ -1270,7 +1342,11 @@ export function TableRenderer(props: TableRendererProps) {
settingsRowSubtotalDisplay.enabled === true &&
i !== settingsRowAttrs.length - 1;
const onArrowClick = needRowToggle
- ? toggleRowKey(flatRowKeySlice)
+ ? toggleRowKey(
+ flatRowKeySlice,
+ collapseRowsByDefault &&
+ collapsibleRowPrefixes.has(flatRowKeySlice),
+ )
: null;
const headerFormatterValue = toDateFormatterInput(r);
@@ -1305,7 +1381,14 @@ export function TableRenderer(props: TableRendererProps) {
>
{displayHeaderCell(
needRowToggle,
- collapsedRows[flatRowKeySlice] ? arrowCollapsed : arrowExpanded,
+ isEffectivelyCollapsed(
+ rowKey.slice(0, i + 1),
+ collapsedRows,
+ collapseRowsByDefault,
+ collapsibleRowPrefixes,
+ )
+ ? arrowCollapsed
+ : arrowExpanded,
onArrowClick,
headerCellFormattedValue,
namesMapping,
@@ -1410,6 +1493,8 @@ export function TableRenderer(props: TableRendererProps) {
clickHeaderHandler,
rows,
collapsedRows,
+ collapseRowsByDefault,
+ collapsibleRowPrefixes,
],
);
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts b/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts
index 87cc8f95cba..14bd084ea70 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts
@@ -100,6 +100,11 @@ interface PivotTableCustomizeProps {
colSubTotals: boolean;
rowTotals: boolean;
rowSubTotals: boolean;
+ /**
+ * When row subtotals are shown, start with every row group collapsed.
+ * Viewers can still expand each group with its arrow.
+ */
+ collapseRows?: boolean;
valueFormat: string;
currencyFormat: Currency;
currencyCodeColumn?: string;
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/test/react-pivottable/tableRenders.test.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/test/react-pivottable/tableRenders.test.tsx
index e77fe7876e3..51f59931efb 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/test/react-pivottable/tableRenders.test.tsx
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/test/react-pivottable/tableRenders.test.tsx
@@ -1221,6 +1221,352 @@ test('TableRenderer ignores customFormatters while showValuesAs is a percentage'
);
});
+const NESTED_ROW_DATA = [
+ {
+ country: 'US',
+ city: 'NYC',
+ shape: 'circle',
+ value: 10,
+ __rows: ['country', 'city'],
+ __columns: ['shape'],
+ },
+ {
+ country: 'US',
+ city: 'LA',
+ shape: 'circle',
+ value: 20,
+ __rows: ['country', 'city'],
+ __columns: ['shape'],
+ },
+ {
+ country: 'UK',
+ city: 'London',
+ shape: 'circle',
+ value: 30,
+ __rows: ['country', 'city'],
+ __columns: ['shape'],
+ },
+ {
+ country: 'US',
+ shape: 'circle',
+ value: 30,
+ __rows: ['country'],
+ __columns: ['shape'],
+ },
+ {
+ country: 'UK',
+ shape: 'circle',
+ value: 30,
+ __rows: ['country'],
+ __columns: ['shape'],
+ },
+];
+
+const THREE_LEVEL_ROW_DATA = [
+ {
+ country: 'US',
+ region: 'West',
+ city: 'LA',
+ value: 1,
+ __rows: ['country', 'region', 'city'],
+ __columns: [],
+ },
+ {
+ country: 'US',
+ region: 'West',
+ city: 'SF',
+ value: 2,
+ __rows: ['country', 'region', 'city'],
+ __columns: [],
+ },
+ {
+ country: 'US',
+ region: 'East',
+ city: 'NYC',
+ value: 4,
+ __rows: ['country', 'region', 'city'],
+ __columns: [],
+ },
+ {
+ country: 'US',
+ region: 'West',
+ value: 3,
+ __rows: ['country', 'region'],
+ __columns: [],
+ },
+ {
+ country: 'US',
+ region: 'East',
+ value: 4,
+ __rows: ['country', 'region'],
+ __columns: [],
+ },
+ { country: 'US', value: 7, __rows: ['country'], __columns: [] },
+];
+
+function clickRowGroupToggle(label: string) {
+ const labelNode = screen.getByText(label);
+ const toggle = labelNode.closest('th')?.querySelector('button.toggle');
+ if (!toggle) {
+ throw new Error(`No row-group toggle for ${label}`);
+ }
+ fireEvent.click(toggle);
+}
+
+test('TableRenderer keeps nested rows expanded when collapseRows is off', () => {
+ const props = buildDefaultProps({
+ data: NESTED_ROW_DATA,
+ rows: ['country', 'city'],
+ cols: ['shape'],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: true, collapseRows: false },
+ });
+ renderWithTheme(<TableRenderer {...props} />);
+
+ expect(screen.getByText('NYC')).toBeInTheDocument();
+ expect(screen.getByText('LA')).toBeInTheDocument();
+ expect(screen.getByText('London')).toBeInTheDocument();
+});
+
+test('TableRenderer collapses row groups by default and still expands one group', () => {
+ const props = buildDefaultProps({
+ data: NESTED_ROW_DATA,
+ rows: ['country', 'city'],
+ cols: ['shape'],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: true, collapseRows: true },
+ });
+ renderWithTheme(<TableRenderer {...props} />);
+
+ expect(screen.getByText('US')).toBeInTheDocument();
+ expect(screen.getByText('UK')).toBeInTheDocument();
+ expect(screen.queryByText('NYC')).not.toBeInTheDocument();
+ expect(screen.queryByText('LA')).not.toBeInTheDocument();
+ expect(screen.queryByText('London')).not.toBeInTheDocument();
+ expect(screen.getAllByText('Subtotal').length).toBeGreaterThan(0);
+
+ clickRowGroupToggle('US');
+
+ expect(screen.getByText('NYC')).toBeInTheDocument();
+ expect(screen.getByText('LA')).toBeInTheDocument();
+ expect(screen.queryByText('London')).not.toBeInTheDocument();
+
+ clickRowGroupToggle('US');
+
+ expect(screen.queryByText('NYC')).not.toBeInTheDocument();
+ expect(screen.queryByText('LA')).not.toBeInTheDocument();
+});
+
+test('TableRenderer keeps the next row level collapsed after expanding a parent', () => {
+ const props = buildDefaultProps({
+ data: THREE_LEVEL_ROW_DATA,
+ rows: ['country', 'region', 'city'],
+ cols: [],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: true, collapseRows: true },
+ });
+ renderWithTheme(<TableRenderer {...props} />);
+
+ expect(screen.queryByText('West')).not.toBeInTheDocument();
+ expect(screen.queryByText('LA')).not.toBeInTheDocument();
+
+ clickRowGroupToggle('US');
+
+ expect(screen.getByText('West')).toBeInTheDocument();
+ expect(screen.getByText('East')).toBeInTheDocument();
+ expect(screen.queryByText('LA')).not.toBeInTheDocument();
+ expect(screen.queryByText('NYC')).not.toBeInTheDocument();
+
+ clickRowGroupToggle('West');
+
+ expect(screen.getByText('LA')).toBeInTheDocument();
+ expect(screen.getByText('SF')).toBeInTheDocument();
+ expect(screen.queryByText('NYC')).not.toBeInTheDocument();
+});
+
+test('TableRenderer resets manual expansions when collapseRows flips', () => {
+ const collapsed = buildDefaultProps({
+ data: NESTED_ROW_DATA,
+ rows: ['country', 'city'],
+ cols: ['shape'],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: true, collapseRows: true },
+ });
+ const expanded = buildDefaultProps({
+ data: NESTED_ROW_DATA,
+ rows: ['country', 'city'],
+ cols: ['shape'],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: true, collapseRows: false },
+ });
+ const { rerender } = renderWithTheme(<TableRenderer {...collapsed} />);
+
+ clickRowGroupToggle('US');
+ expect(screen.getByText('NYC')).toBeInTheDocument();
+
+ rerender(
+ <ThemeProvider theme={supersetTheme}>
+ <TableRenderer {...expanded} />
+ </ThemeProvider>,
+ );
+ expect(screen.getByText('NYC')).toBeInTheDocument();
+
+ rerender(
+ <ThemeProvider theme={supersetTheme}>
+ <TableRenderer {...collapsed} />
+ </ThemeProvider>,
+ );
+ expect(screen.queryByText('NYC')).not.toBeInTheDocument();
+});
+
+test('TableRenderer ignores collapseRows when row subtotals are off', () => {
+ const props = buildDefaultProps({
+ data: NESTED_ROW_DATA,
+ rows: ['country', 'city'],
+ cols: ['shape'],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: false, collapseRows: true },
+ });
+ renderWithTheme(<TableRenderer {...props} />);
+
+ expect(screen.getByText('NYC')).toBeInTheDocument();
+ expect(screen.getByText('London')).toBeInTheDocument();
+});
+
+test('TableRenderer does not treat prototype key names as collapsed', () => {
+ // Leaf keys are `constructor\\0NYC`, so PivotData can store them. The
+ // ancestor walk still looks up the bare prefix `constructor` / `toString`.
+ const data = [
+ {
+ group: 'constructor',
+ city: 'NYC',
+ value: 1,
+ __rows: ['group', 'city'],
+ __columns: [],
+ },
+ {
+ group: 'toString',
+ city: 'LA',
+ value: 2,
+ __rows: ['group', 'city'],
+ __columns: [],
+ },
+ ];
+ const props = buildDefaultProps({
+ data,
+ rows: ['group', 'city'],
+ cols: [],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: true, collapseRows: false },
+ });
+ renderWithTheme(<TableRenderer {...props} />);
+
+ expect(screen.getByText('NYC')).toBeInTheDocument();
+ expect(screen.getByText('LA')).toBeInTheDocument();
+});
+
+test('TableRenderer keeps metrics-on-rows leaves visible when the metric prefix has no subtotal', () => {
+ // Combine metrics off prepends the metric. The metric-only rollup is not
+ // queried, so ['count'] is not a subtotal and must not hide ['count', 'US'].
+ const data = [
+ {
+ Metric: 'count',
+ country: 'US',
+ value: 10,
+ __rows: ['Metric', 'country'],
+ __columns: [],
+ },
+ {
+ Metric: 'count',
+ country: 'UK',
+ value: 4,
+ __rows: ['Metric', 'country'],
+ __columns: [],
+ },
+ ];
+ const props = buildDefaultProps({
+ data,
+ rows: ['Metric', 'country'],
+ cols: [],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: true, colTotals: false, collapseRows: true },
+ });
+ renderWithTheme(<TableRenderer {...props} />);
+
+ expect(screen.getByText('US')).toBeInTheDocument();
+ expect(screen.getByText('UK')).toBeInTheDocument();
+});
+
+test('TableRenderer keeps combined metric rows visible when intermediate rollups are omitted', () => {
+ // Combine metrics on appends the metric and drops intermediate row rollups,
+ // so ['US', 'NYC', 'count'] has no subtotal ancestors to collapse under.
+ const data = [
+ {
+ country: 'US',
+ city: 'NYC',
+ Metric: 'count',
+ value: 10,
+ __rows: ['country', 'city', 'Metric'],
+ __columns: [],
+ },
+ {
+ country: 'US',
+ city: 'LA',
+ Metric: 'count',
+ value: 20,
+ __rows: ['country', 'city', 'Metric'],
+ __columns: [],
+ },
+ ];
+ const props = buildDefaultProps({
+ data,
+ rows: ['country', 'city', 'Metric'],
+ cols: [],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: true, colTotals: false, collapseRows: true },
+ });
+ renderWithTheme(<TableRenderer {...props} />);
+
+ expect(screen.getByText('US')).toBeInTheDocument();
+ expect(screen.getByText('NYC')).toBeInTheDocument();
+ expect(screen.getByText('LA')).toBeInTheDocument();
+});
+
+test('TableRenderer still default-collapses a real subtotal under a metric row', () => {
+ const data = [
+ {
+ Metric: 'count',
+ country: 'US',
+ city: 'NYC',
+ value: 10,
+ __rows: ['Metric', 'country', 'city'],
+ __columns: [],
+ },
+ {
+ Metric: 'count',
+ country: 'US',
+ value: 10,
+ __rows: ['Metric', 'country'],
+ __columns: [],
+ },
+ ];
+ const props = buildDefaultProps({
+ data,
+ rows: ['Metric', 'country', 'city'],
+ cols: [],
+ vals: ['value'],
+ tableOptions: { rowSubTotals: true, colTotals: false, collapseRows: true },
+ });
+ renderWithTheme(<TableRenderer {...props} />);
+
+ expect(screen.getByText('US')).toBeInTheDocument();
+ expect(screen.queryByText('NYC')).not.toBeInTheDocument();
+
+ clickRowGroupToggle('US');
+
+ expect(screen.getByText('NYC')).toBeInTheDocument();
+});
+
/**
* #44724: an aggregation cell must be formatted with the formatter of the metric
* it aggregates, not with the chart-level `defaultFormatter`. `sales` is a
diff --git a/superset/translations/messages.pot b/superset/translations/messages.pot
index c16967c0b2c..f9a48fd10cf 100644
--- a/superset/translations/messages.pot
+++ b/superset/translations/messages.pot
@@ -3496,6 +3496,9 @@ msgstr ""
msgid "Collapse row"
msgstr ""
+msgid "Collapse rows by default"
+msgstr ""
+
msgid "Collapse settings"
msgstr ""
@@ -12860,6 +12863,9 @@ msgstr ""
msgid "Start field selection using current member IDs. This clears any existing query and formatting settings. Saved display titles cannot be safely mapped to member IDs. The saved chart is unchanged until you save."
msgstr ""
+msgid "Start with row groups collapsed when row subtotals are shown. Each group can still be expanded with its arrow."
+msgstr ""
+
msgid "Start y-axis at 0"
msgstr ""