Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ export type ConditionalFormattingConfig = {
colorScheme?: string;
toAllRow?: boolean;
toTextColor?: boolean;
useGradient?: boolean;
};

export type ColorFormatters = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export const getColorFunction = (
targetValueLeft,
targetValueRight,
colorScheme,
useGradient,
}: ConditionalFormattingConfig,
columnValues: number[] | string[],
alpha?: boolean,
Expand Down Expand Up @@ -231,6 +232,13 @@ export const getColorFunction = (
const compareResult = comparatorFunction(value, columnValues);
if (compareResult === false) return undefined;
const { cutoffValue, extremeValue } = compareResult;

// If useGradient is explicitly false, return solid color
if (useGradient === false) {
return colorScheme;
}

// Otherwise apply gradient (default behavior for backward compatibility)
if (alpha === undefined || alpha) {
return addAlpha(
colorScheme,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,3 +532,101 @@ test('correct column string config', () => {
expect(colorFormatters[3].column).toEqual('name');
expect(colorFormatters[3].getColorFromValue('Carlos')).toEqual('#FF0000FF');
});

test('getColorFunction with useGradient false returns solid color', () => {
const colorFunction = getColorFunction(
{
operator: Comparator.GreaterOrEqual,
targetValue: 50,
colorScheme: '#FF0000',
column: 'count',
useGradient: false,
},
countValues,
);
// When useGradient is false, should return solid color without opacity
expect(colorFunction(50)).toEqual('#FF0000');
expect(colorFunction(100)).toEqual('#FF0000');
expect(colorFunction(0)).toBeUndefined();
});

test('getColorFunction with useGradient true returns gradient color', () => {
const colorFunction = getColorFunction(
{
operator: Comparator.GreaterOrEqual,
targetValue: 50,
colorScheme: '#FF0000',
column: 'count',
useGradient: true,
},
countValues,
);
// When useGradient is true, should return gradient color with opacity
expect(colorFunction(50)).toEqual('#FF00000D');
expect(colorFunction(100)).toEqual('#FF0000FF');
expect(colorFunction(0)).toBeUndefined();
});

test('getColorFunction with useGradient undefined defaults to gradient (backward compatibility)', () => {
const colorFunction = getColorFunction(
{
operator: Comparator.GreaterOrEqual,
targetValue: 50,
colorScheme: '#FF0000',
column: 'count',
// useGradient is undefined
},
countValues,
);
// When useGradient is undefined, should default to gradient for backward compatibility
expect(colorFunction(50)).toEqual('#FF00000D');
expect(colorFunction(100)).toEqual('#FF0000FF');
expect(colorFunction(0)).toBeUndefined();
});

test('getColorFunction with useGradient false and None operator returns solid color', () => {
const colorFunction = getColorFunction(
{
operator: Comparator.None,
colorScheme: '#FF0000',
column: 'count',
useGradient: false,
},
countValues,
);
// When useGradient is false, all matching values should return solid color
expect(colorFunction(20)).toBeUndefined();
expect(colorFunction(50)).toEqual('#FF0000');
expect(colorFunction(75)).toEqual('#FF0000');
expect(colorFunction(100)).toEqual('#FF0000');
expect(colorFunction(120)).toBeUndefined();
});

test('getColorFormatters with useGradient flag', () => {
const columnConfig = [
{
operator: Comparator.GreaterThan,
targetValue: 50,
colorScheme: '#FF0000',
column: 'count',
useGradient: false,
},
{
operator: Comparator.GreaterThan,
targetValue: 50,
colorScheme: '#00FF00',
column: 'count',
useGradient: true,
},
];
const colorFormatters = getColorFormatters(columnConfig, mockData);
expect(colorFormatters.length).toEqual(2);

// First formatter with useGradient: false should return solid color
expect(colorFormatters[0].column).toEqual('count');
expect(colorFormatters[0].getColorFromValue(100)).toEqual('#FF0000');

// Second formatter with useGradient: true should return gradient color
expect(colorFormatters[1].column).toEqual('count');
expect(colorFormatters[1].getColorFromValue(100)).toEqual('#00FF00FF');
});
130 changes: 130 additions & 0 deletions superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,136 @@ describe('plugin-chart-table', () => {
'rgba(172, 225, 196, 1)',
);
});

test('render color with useGradient false returns solid color', () => {
render(
ProviderWrapper({
children: (
<TableChart
{...transformProps({
...testData.advanced,
rawFormData: {
...testData.advanced.rawFormData,
conditional_formatting: [
{
colorScheme: '#ACE1C4',
column: 'sum__num',
operator: '>',
targetValue: 2467,
useGradient: false,
},
],
},
})}
/>
),
}),
);

// When useGradient is false, should return solid color (no opacity variation)
// The color should be the same for all matching values
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
'rgb(172, 225, 196)',
);
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe('');
});

test('render color with useGradient true returns gradient color', () => {
render(
ProviderWrapper({
children: (
<TableChart
{...transformProps({
...testData.advanced,
rawFormData: {
...testData.advanced.rawFormData,
conditional_formatting: [
{
colorScheme: '#ACE1C4',
column: 'sum__num',
operator: '>',
targetValue: 2467,
useGradient: true,
},
],
},
})}
/>
),
}),
);

// When useGradient is true, should return gradient color with opacity
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
'rgba(172, 225, 196, 1)',
);
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe('');
});

test('render color with useGradient undefined defaults to gradient (backward compatibility)', () => {
render(
ProviderWrapper({
children: (
<TableChart
{...transformProps({
...testData.advanced,
rawFormData: {
...testData.advanced.rawFormData,
conditional_formatting: [
{
colorScheme: '#ACE1C4',
column: 'sum__num',
operator: '>',
targetValue: 2467,
// useGradient is undefined
},
],
},
})}
/>
),
}),
);

// When useGradient is undefined, should default to gradient for backward compatibility
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
'rgba(172, 225, 196, 1)',
);
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe('');
});

test('render color with useGradient false and None operator returns solid color', () => {
render(
ProviderWrapper({
children: (
<TableChart
{...transformProps({
...testData.advanced,
rawFormData: {
...testData.advanced.rawFormData,
conditional_formatting: [
{
colorScheme: '#ACE1C4',
column: 'sum__num',
operator: 'None',
useGradient: false,
},
],
},
})}
/>
),
}),
);

// When useGradient is false with None operator, all values should have solid color
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
'rgb(172, 225, 196)',
);
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe(
'rgb(172, 225, 196)',
);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,53 @@ test('Not displays the toAllRow and toTextColor flags', () => {
expect(screen.queryByText('To entire row')).not.toBeInTheDocument();
expect(screen.queryByText('To text color')).not.toBeInTheDocument();
});

test('displays Use gradient checkbox', () => {
render(
<FormattingPopoverContent onChange={mockOnChange} columns={columns} />,
);

expect(screen.getByText('Use gradient')).toBeInTheDocument();
});

// Helper function to find the "Use gradient" checkbox
// The checkbox and text are in sibling columns within the same row
const findUseGradientCheckbox = (): HTMLInputElement => {
const useGradientText = screen.getByText('Use gradient');
// Find the common parent row that contains both the text and checkbox
let rowElement: HTMLElement | null = useGradientText.parentElement;
while (rowElement) {
const checkbox = rowElement.querySelector('input[type="checkbox"]');
if (checkbox && rowElement.textContent?.includes('Use gradient')) {
return checkbox as HTMLInputElement;
}
rowElement = rowElement.parentElement;
}
throw new Error('Could not find Use gradient checkbox');
};

test('Use gradient checkbox defaults to checked', () => {
render(
<FormattingPopoverContent onChange={mockOnChange} columns={columns} />,
);

const checkbox = findUseGradientCheckbox();
expect(checkbox).toBeChecked();
});

test('Use gradient checkbox can be toggled', async () => {
render(
<FormattingPopoverContent onChange={mockOnChange} columns={columns} />,
);

const checkbox = findUseGradientCheckbox();
expect(checkbox).toBeChecked();

// Uncheck the checkbox
fireEvent.click(checkbox);
expect(checkbox).not.toBeChecked();

// Check the checkbox again
fireEvent.click(checkbox);
expect(checkbox).toBeChecked();
});
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ export const FormattingPopoverContent = ({
const [toTextColor, setToTextColor] = useState(() =>
Boolean(config?.toTextColor),
);
const [useGradient, setUseGradient] = useState(() =>
config?.useGradient !== undefined ? config.useGradient : true,
);

const useConditionalFormattingFlag = (
flagKey: 'toAllRowCheck' | 'toColorTextCheck',
Expand Down Expand Up @@ -365,6 +368,23 @@ export const FormattingPopoverContent = ({
</FormItem>
</Col>
</Row>
<Row gutter={20}>
<Col span={1}>
<FormItem
name="useGradient"
valuePropName="checked"
initialValue={useGradient}
>
<Checkbox
onChange={event => setUseGradient(event.target.checked)}
checked={useGradient}
/>
</FormItem>
</Col>
<Col>
<FormItem required>{t('Use gradient')}</FormItem>
</Col>
</Row>
<FormItem noStyle shouldUpdate={shouldFormItemUpdate}>
{showOperatorFields ? (
(props: GetFieldValue) => renderOperatorFields(props, columnType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export type ConditionalFormattingConfig = {
colorScheme?: string;
toAllRow?: boolean;
toTextColor?: boolean;
useGradient?: boolean;
};

export type ConditionalFormattingControlProps = ControlComponentProps<
Expand Down
Loading