refactor(Radio): Upgrade Radio Component to Ant Design 5 (#32004)

This commit is contained in:
Enzo Martellucci 2025-01-31 17:45:06 +01:00 committed by GitHub
parent 1c1494d3e0
commit 468bb5f47a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 293 additions and 199 deletions

View File

@ -19,8 +19,8 @@
import { useCallback, useState, FormEvent } from 'react'; import { useCallback, useState, FormEvent } from 'react';
import { Radio } from 'src/components/Radio'; import { Radio, RadioChangeEvent } from 'src/components/Radio';
import { RadioChangeEvent, AsyncSelect } from 'src/components'; import { AsyncSelect } from 'src/components';
import { Input } from 'src/components/Input'; import { Input } from 'src/components/Input';
import StyledModal from 'src/components/Modal'; import StyledModal from 'src/components/Modal';
import Button from 'src/components/Button'; import Button from 'src/components/Button';

View File

@ -32,33 +32,21 @@ export const useDisplayModeToggle = () => {
<div <div
css={(theme: SupersetTheme) => css` css={(theme: SupersetTheme) => css`
margin-bottom: ${theme.gridUnit * 6}px; margin-bottom: ${theme.gridUnit * 6}px;
.ant-radio-button-wrapper-checked:not(
.ant-radio-button-wrapper-disabled
):focus-within {
box-shadow: none;
}
`} `}
data-test="drill-by-display-toggle" data-test="drill-by-display-toggle"
> >
<Radio.Group <Radio.GroupWrapper
onChange={({ target: { value } }) => { onChange={({ target: { value } }) => {
setDrillByDisplayMode(value); setDrillByDisplayMode(value);
}} }}
defaultValue={DrillByType.Chart} defaultValue={DrillByType.Chart}
> options={[
<Radio.Button { label: t('Chart'), value: DrillByType.Chart },
value={DrillByType.Chart} { label: t('Table'), value: DrillByType.Table },
data-test="drill-by-chart-radio" ]}
> optionType="button"
{t('Chart')} buttonStyle="outline"
</Radio.Button> />
<Radio.Button
value={DrillByType.Table}
data-test="drill-by-table-radio"
>
{t('Table')}
</Radio.Button>
</Radio.Group>
</div> </div>
), ),
[], [],

View File

@ -16,40 +16,139 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { useArgs } from '@storybook/preview-api'; import { Space } from 'src/components/Space';
import { Radio } from './index'; import {
BarChartOutlined,
DotChartOutlined,
LineChartOutlined,
PieChartOutlined,
} from '@ant-design/icons';
import { Radio, RadioProps, RadioGroupWrapperProps } from './index';
export default { export default {
title: 'Radio', title: 'Radio',
component: Radio, component: Radio,
parameters: { tags: ['autodocs'],
controls: { hideNoControlsWarning: true }, };
const RadioArgsType = {
value: {
control: 'text',
description: 'The value of the radio button.',
}, },
argTypes: { disabled: {
theme: { control: 'boolean',
table: { description: 'Whether the radio button is disabled or not.',
disable: true, },
}, checked: {
}, control: 'boolean',
checked: { control: 'boolean' }, description: 'The checked state of the radio button.',
disabled: { control: 'boolean' },
}, },
}; };
export const SupersetRadio = () => { const radioGroupWrapperArgsType = {
const [{ checked, ...rest }, updateArgs] = useArgs(); onChange: { action: 'changed' },
return ( disabled: { control: 'boolean' },
<Radio size: {
checked={checked} control: 'select',
onChange={() => updateArgs({ checked: !checked })} options: ['small', 'middle', 'large'],
{...rest} },
> options: { control: 'object' },
Example 'spaceConfig.direction': {
</Radio> control: 'select',
); options: ['horizontal', 'vertical'],
description: 'Direction of the Space layout',
if: { arg: 'enableSpaceConfig', truthy: true },
},
'spaceConfig.size': {
control: 'select',
options: ['small', 'middle', 'large'],
description: 'Layout size Space',
if: { arg: 'enableSpaceConfig', truthy: true },
},
'spaceConfig.align': {
control: 'select',
options: ['start', 'center', 'end'],
description: 'Alignment of the Space layout',
if: { arg: 'enableSpaceConfig', truthy: true },
},
'spaceConfig.wrap': {
control: 'boolean',
description:
'Controls whether the items inside the Space component should wrap to the next line when space is insufficient',
if: { arg: 'enableSpaceConfig', truthy: true },
},
}; };
SupersetRadio.args = { export const RadioStory = {
args: {
value: 'radio1',
disabled: false,
checked: false,
children: 'Radio',
},
argTypes: RadioArgsType,
};
export const RadioButtonStory = (args: RadioProps) => (
<Radio.Button {...args}>Radio Button</Radio.Button>
);
RadioButtonStory.args = {
value: 'button1',
disabled: false,
checked: false, checked: false,
};
RadioButtonStory.argTypes = RadioArgsType;
export const RadioGroupWithOptionsStory = (args: RadioGroupWrapperProps) => (
<Radio.GroupWrapper {...args} />
);
RadioGroupWithOptionsStory.args = {
spaceConfig: {
direction: 'vertical',
size: 'middle',
align: 'center',
wrap: false,
},
size: 'middle',
options: [
{
value: 1,
label: (
<Space align="center" direction="vertical">
<LineChartOutlined style={{ fontSize: 18 }} />
LineChart
</Space>
),
},
{
value: 2,
label: (
<Space align="center" direction="vertical">
<DotChartOutlined style={{ fontSize: 18 }} />
DotChart
</Space>
),
},
{
value: 3,
label: (
<Space align="center" direction="vertical">
<BarChartOutlined style={{ fontSize: 18 }} />
BarChart
</Space>
),
},
{
value: 4,
label: (
<Space align="center" direction="vertical">
<PieChartOutlined style={{ fontSize: 18 }} />
PieChart
</Space>
),
},
],
disabled: false, disabled: false,
}; };
RadioGroupWithOptionsStory.argTypes = radioGroupWrapperArgsType;

View File

@ -16,46 +16,48 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { styled } from '@superset-ui/core'; import { Radio as Antd5Radio, CheckboxOptionType } from 'antd-v5';
import { Radio as AntdRadio } from 'antd'; import type {
RadioChangeEvent,
RadioProps,
RadioGroupProps,
} from 'antd-v5/lib/radio';
const StyledRadio = styled(AntdRadio)` import { Space, SpaceProps } from 'src/components/Space';
.ant-radio-inner {
top: -1px;
left: 2px;
width: ${({ theme }) => theme.gridUnit * 4}px;
height: ${({ theme }) => theme.gridUnit * 4}px;
border-width: 2px;
border-color: ${({ theme }) => theme.colors.grayscale.light2};
}
.ant-radio.ant-radio-checked { export type RadioGroupWrapperProps = RadioGroupProps & {
.ant-radio-inner { spaceConfig?: {
border-width: ${({ theme }) => theme.gridUnit + 1}px; direction?: SpaceProps['direction'];
border-color: ${({ theme }) => theme.colors.primary.base}; size?: SpaceProps['size'];
} align?: SpaceProps['align'];
wrap?: SpaceProps['wrap'];
};
options: CheckboxOptionType[];
};
.ant-radio-inner::after { const RadioGroup = ({
background-color: ${({ theme }) => theme.colors.grayscale.light5}; spaceConfig,
top: 0; options,
left: 0; ...props
width: ${({ theme }) => theme.gridUnit + 2}px; }: RadioGroupWrapperProps) => {
height: ${({ theme }) => theme.gridUnit + 2}px; const content = options.map((option: CheckboxOptionType) => (
} <Radio key={option.value} value={option.value}>
} {option.label}
</Radio>
.ant-radio:hover, ));
.ant-radio:focus { return (
.ant-radio-inner { <Radio.Group {...props}>
border-color: ${({ theme }) => theme.colors.primary.dark1}; {spaceConfig ? <Space {...spaceConfig}>{content}</Space> : content}
} </Radio.Group>
} );
`; };
const StyledGroup = styled(AntdRadio.Group)` export type {
font-size: inherit; RadioChangeEvent,
`; RadioGroupProps,
RadioProps,
export const Radio = Object.assign(StyledRadio, { CheckboxOptionType,
Group: StyledGroup, };
Button: AntdRadio.Button, export const Radio = Object.assign(Antd5Radio, {
GroupWrapper: RadioGroup,
Button: Antd5Radio.Button,
}); });

View File

@ -19,7 +19,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { css, useTheme } from '@superset-ui/core'; import { css, useTheme } from '@superset-ui/core';
import { Radio } from 'src/components/Radio'; import { Radio } from 'src/components/Radio';
import { Space } from 'src/components/Space';
import Icons from 'src/components/Icons'; import Icons from 'src/components/Icons';
import Popover from 'src/components/Popover'; import Popover from 'src/components/Popover';
@ -56,21 +55,20 @@ function HeaderWithRadioGroup(props: HeaderWithRadioGroupProps) {
> >
{groupTitle} {groupTitle}
</div> </div>
<Radio.Group <Radio.GroupWrapper
spaceConfig={{
direction: 'vertical',
size: 4,
wrap: false,
align: 'start',
}}
value={value} value={value}
onChange={e => { onChange={e => {
onChange(e.target.value); onChange(e.target.value);
setPopoverVisible(false); setPopoverVisible(false);
}} }}
> options={groupOptions}
<Space direction="vertical"> />
{groupOptions.map(option => (
<Radio key={option.value} value={option.value}>
{option.label}
</Radio>
))}
</Space>
</Radio.Group>
</div> </div>
} }
placement="bottomLeft" placement="bottomLeft"

View File

@ -1106,15 +1106,16 @@ const FiltersConfigForm = (
initialValue={sort} initialValue={sort}
label={<StyledLabel>{t('Sort type')}</StyledLabel>} label={<StyledLabel>{t('Sort type')}</StyledLabel>}
> >
<Radio.Group <Radio.GroupWrapper
options={[
{ value: true, label: t('Sort ascending') },
{ value: false, label: t('Sort descending') },
]}
onChange={value => { onChange={value => {
onSortChanged(value.target.value); onSortChanged(value.target.value);
formChanged(); formChanged();
}} }}
> />
<Radio value>{t('Sort ascending')}</Radio>
<Radio value={false}>{t('Sort descending')}</Radio>
</Radio.Group>
</StyledRowFormItem> </StyledRowFormItem>
{hasMetrics && ( {hasMetrics && (
<StyledRowSubFormItem <StyledRowSubFormItem
@ -1181,22 +1182,23 @@ const FiltersConfigForm = (
<StyledLabel>{t('Single value type')}</StyledLabel> <StyledLabel>{t('Single value type')}</StyledLabel>
} }
> >
<Radio.Group <Radio.GroupWrapper
onChange={value => { onChange={value => {
onEnableSingleValueChanged(value.target.value); onEnableSingleValueChanged(value.target.value);
formChanged(); formChanged();
}} }}
> options={[
<Radio value={SingleValueType.Minimum}> {
{t('Minimum')} label: t('Minimum'),
</Radio> value: SingleValueType.Minimum,
<Radio value={SingleValueType.Exact}> },
{t('Exact')} { label: t('Exact'), value: SingleValueType.Exact },
</Radio> {
<Radio value={SingleValueType.Maximum}> label: t('Maximum'),
{t('Maximum')} value: SingleValueType.Maximum,
</Radio> },
</Radio.Group> ]}
/>
</StyledRowFormItem> </StyledRowFormItem>
</CollapsibleControl> </CollapsibleControl>
</CleanFormItem> </CleanFormItem>

View File

@ -30,7 +30,6 @@ import {
import { Global } from '@emotion/react'; import { Global } from '@emotion/react';
import { Column } from 'react-table'; import { Column } from 'react-table';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { Space } from 'src/components/Space';
import { Input } from 'src/components/Input'; import { Input } from 'src/components/Input';
import { import {
BOOL_FALSE_DISPLAY, BOOL_FALSE_DISPLAY,
@ -141,12 +140,21 @@ const FormatPicker = ({
onChange: any; onChange: any;
value: FormatPickerValue; value: FormatPickerValue;
}) => ( }) => (
<Radio.Group value={value} onChange={onChange}> <Radio.GroupWrapper
<Space direction="vertical"> spaceConfig={{
<Radio value={FormatPickerValue.Formatted}>{t('Formatted date')}</Radio> direction: 'vertical',
<Radio value={FormatPickerValue.Original}>{t('Original value')}</Radio> align: 'start',
</Space> size: 15,
</Radio.Group> wrap: false,
}}
size="large"
value={value}
onChange={onChange}
options={[
{ label: t('Formatted date'), value: FormatPickerValue.Formatted },
{ label: t('Original value'), value: FormatPickerValue.Original },
]}
/>
); );
const FormatPickerContainer = styled.div` const FormatPickerContainer = styled.div`

View File

@ -87,12 +87,6 @@ const ContentStyleWrapper = styled.div`
margin: 8px 0; margin: 8px 0;
} }
.vertical-radio {
display: block;
height: 40px;
line-height: 40px;
}
.section-title { .section-title {
font-style: normal; font-style: normal;
font-weight: ${theme.typography.weights.bold}; font-weight: ${theme.typography.weights.bold};

View File

@ -45,16 +45,18 @@ export function CalendarFrame({ onChange, value }: FrameComponentProps) {
<div className="section-title"> <div className="section-title">
{t('Configure Time Range: Previous...')} {t('Configure Time Range: Previous...')}
</div> </div>
<Radio.Group <Radio.GroupWrapper
spaceConfig={{
direction: 'vertical',
size: 15,
align: 'start',
wrap: false,
}}
size="large"
value={value} value={value}
onChange={(e: any) => onChange(e.target.value)} onChange={(e: any) => onChange(e.target.value)}
> options={CALENDAR_RANGE_OPTIONS}
{CALENDAR_RANGE_OPTIONS.map(({ value, label }) => ( />
<Radio key={value} value={value} className="vertical-radio">
{label}
</Radio>
))}
</Radio.Group>
</> </>
); );
} }

View File

@ -41,16 +41,18 @@ export function CommonFrame(props: FrameComponentProps) {
<div className="section-title" data-test={DateFilterTestKey.CommonFrame}> <div className="section-title" data-test={DateFilterTestKey.CommonFrame}>
{t('Configure Time Range: Last...')} {t('Configure Time Range: Last...')}
</div> </div>
<Radio.Group <Radio.GroupWrapper
spaceConfig={{
direction: 'vertical',
size: 15,
align: 'start',
wrap: false,
}}
size="large"
value={commonRange} value={commonRange}
onChange={(e: any) => props.onChange(e.target.value)} onChange={(e: any) => props.onChange(e.target.value)}
> options={COMMON_RANGE_OPTIONS}
{COMMON_RANGE_OPTIONS.map(({ value, label }) => ( />
<Radio key={value} value={value} className="vertical-radio">
{label}
</Radio>
))}
</Radio.Group>
</> </>
); );
} }

View File

@ -41,25 +41,22 @@ export function CurrentCalendarFrame({ onChange, value }: FrameComponentProps) {
<div className="section-title"> <div className="section-title">
{t('Configure Time Range: Current...')} {t('Configure Time Range: Current...')}
</div> </div>
<Radio.Group <Radio.GroupWrapper
value={value} spaceConfig={{
direction: 'vertical',
size: 15,
align: 'start',
wrap: true,
}}
size="large"
onChange={(e: any) => { onChange={(e: any) => {
let newValue = e.target.value; let newValue = e.target.value;
// Sanitization: Trim whitespace
newValue = newValue.trim(); newValue = newValue.trim();
// Validation: Check if the value is non-empty if (newValue === '') return;
if (newValue === '') {
return;
}
onChange(newValue); onChange(newValue);
}} }}
> options={CURRENT_RANGE_OPTIONS}
{CURRENT_RANGE_OPTIONS.map(({ value, label }) => ( />
<Radio key={value} value={value} className="vertical-radio">
{label}
</Radio>
))}
</Radio.Group>
</> </>
); );
} }

View File

@ -238,18 +238,15 @@ export function CustomFrame(props: FrameComponentProps) {
<div className="control-label">{t('Anchor to')}</div> <div className="control-label">{t('Anchor to')}</div>
<Row align="middle"> <Row align="middle">
<Col> <Col>
<Radio.Group <Radio.GroupWrapper
options={[
{ value: 'now', label: t('Now') },
{ value: 'specific', label: t('Date/Time') },
]}
onChange={onAnchorMode} onChange={onAnchorMode}
defaultValue="now" defaultValue="now"
value={anchorMode} value={anchorMode}
> />
<Radio key="now" value="now">
{t('NOW')}
</Radio>
<Radio key="specific" value="specific">
{t('Date/Time')}
</Radio>
</Radio.Group>
</Col> </Col>
{anchorMode !== 'now' && ( {anchorMode !== 'now' && (
<Col> <Col>

View File

@ -42,7 +42,7 @@ describe('CalendarFrame', () => {
const radios = screen.getAllByRole('radio'); const radios = screen.getAllByRole('radio');
expect(radios).toHaveLength(CALENDAR_RANGE_OPTIONS.length); expect(radios).toHaveLength(CALENDAR_RANGE_OPTIONS.length);
CALENDAR_RANGE_OPTIONS.forEach(option => { CALENDAR_RANGE_OPTIONS.forEach(option => {
expect(screen.getByText(option.label)).toBeInTheDocument(); expect(screen.getByText(option.label as string)).toBeInTheDocument();
}); });
}); });
@ -56,7 +56,7 @@ describe('CalendarFrame', () => {
); );
const secondOption = CALENDAR_RANGE_OPTIONS[1]; const secondOption = CALENDAR_RANGE_OPTIONS[1];
const radio = screen.getByLabelText(secondOption.label); const radio = screen.getByLabelText(secondOption.label as string);
fireEvent.click(radio); fireEvent.click(radio);
expect(mockOnChange).toHaveBeenCalledWith(secondOption.value); expect(mockOnChange).toHaveBeenCalledWith(secondOption.value);
@ -85,6 +85,8 @@ describe('CalendarFrame', () => {
const thirdOption = CALENDAR_RANGE_OPTIONS[2]; const thirdOption = CALENDAR_RANGE_OPTIONS[2];
expect(thirdOption.value).toBe(PreviousCalendarQuarter); expect(thirdOption.value).toBe(PreviousCalendarQuarter);
expect(screen.getByLabelText(thirdOption.label)).toBeInTheDocument(); expect(
screen.getByLabelText(thirdOption.label as string),
).toBeInTheDocument();
}); });
}); });

View File

@ -167,8 +167,8 @@ test('renders anchor with now option', async () => {
); );
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading')); await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
expect(screen.getByText('Anchor to')).toBeInTheDocument(); expect(screen.getByText('Anchor to')).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'NOW' })).toBeInTheDocument(); expect(screen.getByLabelText('Now')).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'Date/Time' })).toBeInTheDocument(); expect(screen.getByLabelText('Date/Time')).toBeInTheDocument();
expect(screen.queryByPlaceholderText('Select date')).not.toBeInTheDocument(); expect(screen.queryByPlaceholderText('Select date')).not.toBeInTheDocument();
}); });
@ -180,8 +180,8 @@ test('renders anchor with date/time option', async () => {
); );
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading')); await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
expect(screen.getByText('Anchor to')).toBeInTheDocument(); expect(screen.getByText('Anchor to')).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'NOW' })).toBeInTheDocument(); expect(screen.getByLabelText('Now')).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'Date/Time' })).toBeInTheDocument(); expect(screen.getByLabelText('Date/Time')).toBeInTheDocument();
expect(screen.getByPlaceholderText('Select date')).toBeInTheDocument(); expect(screen.getByPlaceholderText('Select date')).toBeInTheDocument();
}); });

View File

@ -32,6 +32,7 @@ import {
CurrentQuarter, CurrentQuarter,
CurrentDay, CurrentDay,
} from 'src/explore/components/controls/DateFilterControl/types'; } from 'src/explore/components/controls/DateFilterControl/types';
import { CheckboxOptionType } from 'src/components/Radio';
import { extendedDayjs } from 'src/utils/dates'; import { extendedDayjs } from 'src/utils/dates';
export const FRAME_OPTIONS: SelectOptionType[] = [ export const FRAME_OPTIONS: SelectOptionType[] = [
@ -43,7 +44,7 @@ export const FRAME_OPTIONS: SelectOptionType[] = [
{ value: 'No filter', label: t('No filter') }, { value: 'No filter', label: t('No filter') },
]; ];
export const COMMON_RANGE_OPTIONS: SelectOptionType[] = [ export const COMMON_RANGE_OPTIONS: CheckboxOptionType[] = [
{ value: 'Last day', label: t('Last day') }, { value: 'Last day', label: t('Last day') },
{ value: 'Last week', label: t('Last week') }, { value: 'Last week', label: t('Last week') },
{ value: 'Last month', label: t('Last month') }, { value: 'Last month', label: t('Last month') },
@ -51,20 +52,20 @@ export const COMMON_RANGE_OPTIONS: SelectOptionType[] = [
{ value: 'Last year', label: t('Last year') }, { value: 'Last year', label: t('Last year') },
]; ];
export const COMMON_RANGE_VALUES_SET = new Set( export const COMMON_RANGE_VALUES_SET = new Set(
COMMON_RANGE_OPTIONS.map(({ value }) => value), COMMON_RANGE_OPTIONS.map(value => value.value),
); );
export const CALENDAR_RANGE_OPTIONS: SelectOptionType[] = [ export const CALENDAR_RANGE_OPTIONS: CheckboxOptionType[] = [
{ value: PreviousCalendarWeek, label: t('previous calendar week') }, { value: PreviousCalendarWeek, label: t('previous calendar week') },
{ value: PreviousCalendarMonth, label: t('previous calendar month') }, { value: PreviousCalendarMonth, label: t('previous calendar month') },
{ value: PreviousCalendarQuarter, label: t('previous calendar quarter') }, { value: PreviousCalendarQuarter, label: t('previous calendar quarter') },
{ value: PreviousCalendarYear, label: t('previous calendar year') }, { value: PreviousCalendarYear, label: t('previous calendar year') },
]; ];
export const CALENDAR_RANGE_VALUES_SET = new Set( export const CALENDAR_RANGE_VALUES_SET = new Set(
CALENDAR_RANGE_OPTIONS.map(({ value }) => value), CALENDAR_RANGE_OPTIONS.map(value => value.value),
); );
export const CURRENT_RANGE_OPTIONS: SelectOptionType[] = [ export const CURRENT_RANGE_OPTIONS: CheckboxOptionType[] = [
{ value: CurrentDay, label: t('Current day') }, { value: CurrentDay, label: t('Current day') },
{ value: CurrentWeek, label: t('Current week') }, { value: CurrentWeek, label: t('Current week') },
{ value: CurrentMonth, label: t('Current month') }, { value: CurrentMonth, label: t('Current month') },
@ -72,7 +73,7 @@ export const CURRENT_RANGE_OPTIONS: SelectOptionType[] = [
{ value: CurrentYear, label: t('Current year') }, { value: CurrentYear, label: t('Current year') },
]; ];
export const CURRENT_RANGE_VALUES_SET = new Set( export const CURRENT_RANGE_VALUES_SET = new Set(
CURRENT_RANGE_OPTIONS.map(({ value }) => value), CURRENT_RANGE_OPTIONS.map(value => value.value),
); );
const GRAIN_OPTIONS = [ const GRAIN_OPTIONS = [

View File

@ -41,7 +41,7 @@ import TimezoneSelector from 'src/components/TimezoneSelector';
import LabeledErrorBoundInput from 'src/components/Form/LabeledErrorBoundInput'; import LabeledErrorBoundInput from 'src/components/Form/LabeledErrorBoundInput';
import Icons from 'src/components/Icons'; import Icons from 'src/components/Icons';
import { CronError } from 'src/components/CronPicker'; import { CronError } from 'src/components/CronPicker';
import { RadioChangeEvent } from 'src/components'; import { Radio, RadioChangeEvent } from 'src/components/Radio';
import { Input } from 'src/components/Input'; import { Input } from 'src/components/Input';
import withToasts from 'src/components/MessageToasts/withToasts'; import withToasts from 'src/components/MessageToasts/withToasts';
import { ChartState } from 'src/explore/types'; import { ChartState } from 'src/explore/types';
@ -68,8 +68,6 @@ import {
TimezoneHeaderStyle, TimezoneHeaderStyle,
SectionHeaderStyle, SectionHeaderStyle,
StyledMessageContentTitle, StyledMessageContentTitle,
StyledRadio,
StyledRadioGroup,
} from './styles'; } from './styles';
interface ReportProps { interface ReportProps {
@ -257,24 +255,32 @@ function ReportModal({
<h4>{t('Message content')}</h4> <h4>{t('Message content')}</h4>
</StyledMessageContentTitle> </StyledMessageContentTitle>
<div className="inline-container"> <div className="inline-container">
<StyledRadioGroup <Radio.GroupWrapper
spaceConfig={{
direction: 'vertical',
size: 'middle',
align: 'start',
wrap: false,
}}
onChange={(event: RadioChangeEvent) => { onChange={(event: RadioChangeEvent) => {
setCurrentReport({ report_format: event.target.value }); setCurrentReport({ report_format: event.target.value });
}} }}
value={currentReport.report_format || defaultNotificationFormat} value={currentReport.report_format || defaultNotificationFormat}
> options={[
{isTextBasedChart && ( {
<StyledRadio value={NotificationFormats.Text}> label: t('Text embedded in email'),
{t('Text embedded in email')} value: NotificationFormats.Text,
</StyledRadio> },
)} {
<StyledRadio value={NotificationFormats.PNG}> label: t('Image (PNG) embedded in email'),
{t('Image (PNG) embedded in email')} value: NotificationFormats.PNG,
</StyledRadio> },
<StyledRadio value={NotificationFormats.CSV}> {
{t('Formatted CSV attached in email')} label: t('Formatted CSV attached in email'),
</StyledRadio> value: NotificationFormats.CSV,
</StyledRadioGroup> },
]}
/>
</div> </div>
</> </>
); );

View File

@ -108,10 +108,6 @@ export const StyledRadio = styled(Radio)`
line-height: ${({ theme }) => theme.gridUnit * 8}px; line-height: ${({ theme }) => theme.gridUnit * 8}px;
`; `;
export const StyledRadioGroup = styled(Radio.Group)`
margin-left: ${({ theme }) => theme.gridUnit * 0.5}px;
`;
export const antDErrorAlertStyles = (theme: SupersetTheme) => css` export const antDErrorAlertStyles = (theme: SupersetTheme) => css`
margin: ${theme.gridUnit * 4}px; margin: ${theme.gridUnit * 4}px;
margin-top: 0; margin-top: 0;