Compare commits

...
Author SHA1 Message Date
Evan Rusackas 89f9b4aea2 fix(plugin-chart-table): preserve comparison arrow when a column-specific formatter entry is missing
The per-cell comparison arrow and arrow color are computed twice: once
from the row-level basicColorFormatters, then unconditionally reassigned
from basicColorColumnFormatters when that array is present. Unlike the
sibling backgroundColor assignment (which falls back to the prior value
via `|| backgroundColor`), the arrow and arrowColor reassignments had no
such fallback, so a row missing an entry in basicColorColumnFormatters
lost its arrow entirely and would have flipped its arrow color, even
though a valid value had already been computed from basicColorFormatters.

Falls back to the previously-computed value in both cases, same as
backgroundColor already does.
2026-08-24 16:22:52 -07:00
7e3d092ac4 fix(soft-delete): name the recovery location in the archive confirmation (#43401)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 18:56:20 -04:00
5 changed files with 72 additions and 33 deletions
@@ -1138,7 +1138,8 @@ export default function TableChart<D extends DataRecord = DataRecord>(
?.backgroundColor || backgroundColor;
arrow =
column.label === comparisonLabels[0]
? basicColorColumnFormatters[row.index]?.[column.key]?.mainArrow
? (basicColorColumnFormatters[row.index]?.[column.key]
?.mainArrow ?? arrow)
: '';
}
const rowSurfaceColor =
@@ -1209,15 +1210,18 @@ export default function TableChart<D extends DataRecord = DataRecord>(
basicColorColumnFormatters &&
basicColorColumnFormatters?.length > 0
) {
arrowStyles = css`
color: ${
basicColorColumnFormatters[row.index]?.[column.key]
?.arrowColor === ColorSchemeEnum.Green
? theme.colorSuccess
: theme.colorError
};
margin-right: ${theme.sizeUnit}px;
`;
const columnArrowColor =
basicColorColumnFormatters[row.index]?.[column.key]?.arrowColor;
if (columnArrowColor) {
arrowStyles = css`
color: ${
columnArrowColor === ColorSchemeEnum.Green
? theme.colorSuccess
: theme.colorError
};
margin-right: ${theme.sizeUnit}px;
`;
}
}
const cellProps = {
@@ -2125,8 +2125,18 @@ describe('plugin-chart-table', () => {
'rgba(0, 150, 0, 0.2)',
);
// the row missing a formatter entry still renders its raw value
expect(screen.getAllByTitle('110').length).toBeGreaterThan(0);
// the row missing a formatter entry falls back to the row-level
// comparison arrow instead of losing it: before the fix, this row's
// arrow was silently cleared (and its color, computed the same way,
// would have flipped to the "decrease" color) whenever the
// column-specific lookup for this row was undefined.
const arrowCell = screen
.getAllByTitle('110')
.find(cell => cell.querySelector('span'));
expect(arrowCell).toHaveTextContent('↑110');
expect(getComputedStyle(arrowCell!).background).toContain(
'rgba(0, 150, 0, 0.2)',
);
});
test('preserves client-side search text across temporal table rerenders', async () => {
@@ -96,19 +96,29 @@ test('a malformed window does not leak into the copy', () => {
test('the confirm copy quotes the window when there is one', () => {
withConf({ SOFT_DELETE_RETENTION_DAYS: 30 });
expect(archiveConfirmDescription('chart')).toBe(
'This chart will be moved to Recently Archived. You can recover it there within 30 days.',
'This chart will be moved to Recently Archived in the Settings menu. You can recover it there within 30 days.',
);
expect(archiveConfirmDescription('charts', true)).toBe(
'These charts will be moved to Recently Archived. You can recover them there within 30 days.',
'These charts will be moved to Recently Archived in the Settings menu. You can recover them there within 30 days.',
);
});
test('a one-day window is quoted in the singular', () => {
withConf({ SOFT_DELETE_RETENTION_DAYS: 1 });
expect(archiveConfirmDescription('chart')).toBe(
'This chart will be moved to Recently Archived in the Settings menu. You can recover it there within 1 day.',
);
expect(archiveConfirmDescription('charts', true)).toBe(
'These charts will be moved to Recently Archived in the Settings menu. You can recover them there within 1 day.',
);
});
test('the confirm copy omits the clause when there is no window', () => {
withConf({});
expect(archiveConfirmDescription('dashboard')).toBe(
'This dashboard will be moved to Recently Archived. You can recover it there.',
'This dashboard will be moved to Recently Archived in the Settings menu. You can recover it there.',
);
expect(archiveConfirmDescription('dashboards', true)).toBe(
'These dashboards will be moved to Recently Archived. You can recover them there.',
'These dashboards will be moved to Recently Archived in the Settings menu. You can recover them there.',
);
});
+14 -7
View File
@@ -17,7 +17,7 @@
* under the License.
*/
import { escape } from 'lodash-es';
import { t } from '@apache-superset/core/translation';
import { t, tn } from '@apache-superset/core/translation';
import { isFeatureEnabled, FeatureFlag } from '@superset-ui/core';
import getBootstrapData from 'src/utils/getBootstrapData';
@@ -62,25 +62,32 @@ export function archiveConfirmDescription(
// Each case is a single, complete translation unit (rather than two joined
// fragments) so translators control the whole sentence; only the noun and the
// day count are interpolated, matching Superset's existing `%(...)s` usage.
// The timed variants pluralize on the day count (`tn`) because the retention
// window accepts 1: "within 1 days" is exactly the copy defect this module
// exists to prevent.
const days = getSoftDeleteRetentionDays();
if (days) {
return plural
? t(
'These %(type)s will be moved to Recently Archived. You can recover them there within %(days)s days.',
? tn(
'These %(type)s will be moved to Recently Archived in the Settings menu. You can recover them there within %(days)s day.',
'These %(type)s will be moved to Recently Archived in the Settings menu. You can recover them there within %(days)s days.',
days,
{ type: typeLabel, days },
)
: t(
'This %(type)s will be moved to Recently Archived. You can recover it there within %(days)s days.',
: tn(
'This %(type)s will be moved to Recently Archived in the Settings menu. You can recover it there within %(days)s day.',
'This %(type)s will be moved to Recently Archived in the Settings menu. You can recover it there within %(days)s days.',
days,
{ type: typeLabel, days },
);
}
return plural
? t(
'These %(type)s will be moved to Recently Archived. You can recover them there.',
'These %(type)s will be moved to Recently Archived in the Settings menu. You can recover them there.',
{ type: typeLabel },
)
: t(
'This %(type)s will be moved to Recently Archived. You can recover it there.',
'This %(type)s will be moved to Recently Archived in the Settings menu. You can recover it there.',
{ type: typeLabel },
);
}
+18 -10
View File
@@ -13831,14 +13831,18 @@ msgstr ""
#, python-format
msgid ""
"These %(type)s will be moved to Recently Archived. You can recover them "
"there within %(days)s days."
msgstr ""
"These %(type)s will be moved to Recently Archived in the Settings menu. "
"You can recover them there within %(days)s day."
msgid_plural ""
"These %(type)s will be moved to Recently Archived in the Settings menu. "
"You can recover them there within %(days)s days."
msgstr[0] ""
msgstr[1] ""
#, python-format
msgid ""
"These %(type)s will be moved to Recently Archived. You can recover them "
"there."
"These %(type)s will be moved to Recently Archived in the Settings menu. "
"You can recover them there."
msgstr ""
msgid "These are the datasets this filter will be applied to."
@@ -13846,14 +13850,18 @@ msgstr ""
#, python-format
msgid ""
"This %(type)s will be moved to Recently Archived. You can recover it "
"there within %(days)s days."
msgstr ""
"This %(type)s will be moved to Recently Archived in the Settings menu. "
"You can recover it there within %(days)s day."
msgid_plural ""
"This %(type)s will be moved to Recently Archived in the Settings menu. "
"You can recover it there within %(days)s days."
msgstr[0] ""
msgstr[1] ""
#, python-format
msgid ""
"This %(type)s will be moved to Recently Archived. You can recover it "
"there."
"This %(type)s will be moved to Recently Archived in the Settings menu. "
"You can recover it there."
msgstr ""
msgid ""