mirror of
https://github.com/apache/superset.git
synced 2026-04-07 10:31:50 +00:00
feat: Dynamic imports for the Icons component (#14318)
* Add aria-label and twotone * Enhance LazyIcon * Fix tests and solve ject warnings * Add new line * Revert package-lock to master * Fix failing test * Implement icon overrides * Fix failing storybook * Clean up * Improve var name
This commit is contained in:
19
superset-frontend/images/icons/transparent.svg
Normal file
19
superset-frontend/images/icons/transparent.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"></svg>
|
||||
|
After Width: | Height: | Size: 866 B |
@@ -21,7 +21,7 @@ module.exports = {
|
||||
moduleNameMapper: {
|
||||
'\\.(css|less)$': '<rootDir>/spec/__mocks__/styleMock.js',
|
||||
'\\.(gif|ttf|eot|png)$': '<rootDir>/spec/__mocks__/fileMock.js',
|
||||
'\\.svg$': '<rootDir>/spec/__mocks__/svgrMock.js',
|
||||
'\\.svg$': '<rootDir>/spec/__mocks__/svgrMock.tsx',
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
'^spec/(.*)$': '<rootDir>/spec/$1',
|
||||
},
|
||||
|
||||
@@ -16,5 +16,14 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
export default 'SvgrURL';
|
||||
export const ReactComponent = 'svg';
|
||||
|
||||
import React, { SVGProps } from 'react';
|
||||
|
||||
const SvgrMock = React.forwardRef<SVGSVGElement, SVGProps<SVGSVGElement>>(
|
||||
(props, ref) => <svg ref={ref} {...props} />,
|
||||
);
|
||||
|
||||
SvgrMock.displayName = 'SvgrMock';
|
||||
|
||||
export const ReactComponent = SvgrMock;
|
||||
export default SvgrMock;
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
import { sliceId } from 'spec/fixtures/mockChartQueries';
|
||||
import { dashboardFilters } from 'spec/fixtures/mockDashboardFilters';
|
||||
import { dashboardWithFilter } from 'spec/fixtures/mockDashboardLayout';
|
||||
import Icons from 'src/components/Icons';
|
||||
import { FeatureFlag } from 'src/featureFlags';
|
||||
|
||||
describe('FiltersBadge', () => {
|
||||
@@ -129,7 +130,7 @@ describe('FiltersBadge', () => {
|
||||
).toHaveText('1');
|
||||
// to look at the shape of the wrapper use:
|
||||
// console.log(wrapper.dive().debug())
|
||||
expect(wrapper.dive().find('Icon[name="alert-solid"]')).toExist();
|
||||
expect(wrapper.dive().find(Icons.AlertSolid)).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -216,7 +217,7 @@ describe('FiltersBadge', () => {
|
||||
expect(
|
||||
wrapper.dive().find('[data-test="incompatible-filter-count"]'),
|
||||
).toHaveText('1');
|
||||
expect(wrapper.dive().find('Icon[name="alert-solid"]')).toExist();
|
||||
expect(wrapper.dive().find(Icons.AlertSolid)).toExist();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ import { Menu } from 'src/common/components';
|
||||
import DatasourceModal from 'src/datasource/DatasourceModal';
|
||||
import ChangeDatasourceModal from 'src/datasource/ChangeDatasourceModal';
|
||||
import DatasourceControl from 'src/explore/components/controls/DatasourceControl';
|
||||
import Icon from 'src/components/Icon';
|
||||
import Icons from 'src/components/Icons';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
|
||||
const defaultProps = {
|
||||
@@ -119,8 +119,7 @@ describe('DatasourceControl', () => {
|
||||
|
||||
it('should render health check message', () => {
|
||||
const wrapper = setup();
|
||||
const alert = wrapper.find(Icon);
|
||||
expect(alert.at(1).prop('name')).toBe('alert-solid');
|
||||
expect(wrapper.find(Icons.AlertSolid)).toExist();
|
||||
const tooltip = wrapper.find(Tooltip).at(0);
|
||||
expect(tooltip.prop('title')).toBe(
|
||||
defaultProps.datasource.health_check_message,
|
||||
|
||||
@@ -17,31 +17,37 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import Alert, { AlertProps } from 'src/components/Alert';
|
||||
|
||||
type AlertType = Pick<AlertProps, 'type'>;
|
||||
type AlertTypeValue = AlertType[keyof AlertType];
|
||||
|
||||
test('renders with default props', () => {
|
||||
test('renders with default props', async () => {
|
||||
render(<Alert message="Message" />);
|
||||
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('Message');
|
||||
expect(screen.queryByLabelText(`info icon`)).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('close icon')).toBeInTheDocument();
|
||||
expect(await screen.findByLabelText(`info icon`)).toBeInTheDocument();
|
||||
expect(await screen.findByLabelText('close icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders each type', () => {
|
||||
test('renders each type', async () => {
|
||||
const types: AlertTypeValue[] = ['info', 'error', 'warning', 'success'];
|
||||
types.forEach(type => {
|
||||
for (let i = 0; i < types.length; i += 1) {
|
||||
const type = types[i];
|
||||
render(<Alert type={type} message="Message" />);
|
||||
expect(screen.queryByLabelText(`${type} icon`)).toBeInTheDocument();
|
||||
});
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
expect(await screen.findByLabelText(`${type} icon`)).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
|
||||
test('renders without close button', () => {
|
||||
test('renders without close button', async () => {
|
||||
render(<Alert message="Message" closable={false} />);
|
||||
expect(screen.queryByLabelText('close icon')).not.toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByLabelText('close icon')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('disappear when closed', () => {
|
||||
@@ -50,10 +56,12 @@ test('disappear when closed', () => {
|
||||
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders without icon', () => {
|
||||
test('renders without icon', async () => {
|
||||
const type = 'info';
|
||||
render(<Alert type={type} message="Message" showIcon={false} />);
|
||||
expect(screen.queryByLabelText(`${type} icon`)).not.toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByLabelText(`${type} icon`)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('renders message', () => {
|
||||
|
||||
@@ -22,7 +22,8 @@ import {
|
||||
AlertProps as AntdAlertProps,
|
||||
} from 'src/common/components';
|
||||
import { useTheme } from '@superset-ui/core';
|
||||
import Icon, { IconName } from 'src/components/Icon';
|
||||
import Icon from 'src/components/Icon';
|
||||
import Icons from 'src/components/Icons';
|
||||
|
||||
export type AlertProps = PropsWithChildren<AntdAlertProps>;
|
||||
|
||||
@@ -40,23 +41,23 @@ export default function Alert(props: AlertProps) {
|
||||
const { alert, error, info, success } = colors;
|
||||
|
||||
let baseColor = info;
|
||||
let iconName: IconName = 'info-solid';
|
||||
let AlertIcon = Icons.InfoSolid;
|
||||
if (type === 'error') {
|
||||
baseColor = error;
|
||||
iconName = 'error-solid';
|
||||
AlertIcon = Icons.ErrorSolid;
|
||||
} else if (type === 'warning') {
|
||||
baseColor = alert;
|
||||
iconName = 'alert-solid';
|
||||
AlertIcon = Icons.AlertSolid;
|
||||
} else if (type === 'success') {
|
||||
baseColor = success;
|
||||
iconName = 'circle-check-solid';
|
||||
AlertIcon = Icons.CircleCheckSolid;
|
||||
}
|
||||
|
||||
return (
|
||||
<AntdAlert
|
||||
role="alert"
|
||||
showIcon={showIcon}
|
||||
icon={<Icon name={iconName} aria-label={`${type} icon`} />}
|
||||
icon={<AlertIcon aria-label={`${type} icon`} />}
|
||||
closeText={closable && <Icon name="x-small" aria-label="close icon" />}
|
||||
css={{
|
||||
padding: '6px 10px',
|
||||
|
||||
@@ -19,12 +19,14 @@
|
||||
|
||||
import React from 'react';
|
||||
import * as AntdIcons from '@ant-design/icons/lib/icons';
|
||||
import Icon from './Icon';
|
||||
import { StyledIcon } from './Icon';
|
||||
import IconType from './IconType';
|
||||
|
||||
const AntdEnhancedIcons = Object.keys(AntdIcons)
|
||||
.map(k => ({
|
||||
[k]: (props: IconType) => <Icon component={AntdIcons[k]} {...props} />,
|
||||
[k]: (props: IconType) => (
|
||||
<StyledIcon component={AntdIcons[k]} {...props} />
|
||||
),
|
||||
}))
|
||||
.reduce((l, r) => ({ ...l, ...r }));
|
||||
|
||||
|
||||
@@ -17,34 +17,54 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import AntdIcon from '@ant-design/icons';
|
||||
import { styled } from '@superset-ui/core';
|
||||
import { CustomIconComponentProps } from '@ant-design/icons/lib/components/Icon';
|
||||
import { ReactComponent as TransparentIcon } from 'images/icons/transparent.svg';
|
||||
import IconType from './IconType';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const EnhancedIcon = ({ iconColor, iconSize, ...rest }: IconType) => (
|
||||
const AntdIconComponent = ({ iconColor, iconSize, ...rest }: IconType) => (
|
||||
<AntdIcon viewBox={rest.viewBox || '0 0 24 24'} {...rest} />
|
||||
);
|
||||
|
||||
const Icon = styled(EnhancedIcon)<IconType>`
|
||||
export const StyledIcon = styled(AntdIconComponent)<IconType>`
|
||||
${({ iconColor }) => iconColor && `color: ${iconColor};`};
|
||||
font-size: ${({ iconSize, theme }) =>
|
||||
iconSize ? `${theme.typography.sizes[iconSize]}px` : '24px'};
|
||||
iconSize
|
||||
? `${theme.typography.sizes[iconSize] || theme.typography.sizes.m}px`
|
||||
: '24px'};
|
||||
`;
|
||||
|
||||
export const renderIcon = (
|
||||
SVGComponent:
|
||||
| React.ComponentClass<
|
||||
CustomIconComponentProps | React.SVGProps<SVGSVGElement>,
|
||||
any
|
||||
>
|
||||
| React.FunctionComponent<
|
||||
CustomIconComponentProps | React.SVGProps<SVGSVGElement>
|
||||
>
|
||||
| undefined,
|
||||
props: IconType,
|
||||
) => <Icon component={SVGComponent} {...props} />;
|
||||
interface IconProps extends IconType {
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export const Icon = (props: IconProps) => {
|
||||
const { fileName, ...iconProps } = props;
|
||||
const [, setLoaded] = useState(false);
|
||||
const ImportedSVG = useRef<React.FC<React.SVGProps<SVGSVGElement>>>();
|
||||
const name = fileName.replace('_', '-');
|
||||
|
||||
useEffect(() => {
|
||||
async function importIcon(): Promise<void> {
|
||||
ImportedSVG.current = (
|
||||
await import(
|
||||
`!!@svgr/webpack?-svgo,+titleProp,+ref!images/icons/${fileName}.svg`
|
||||
)
|
||||
).default;
|
||||
setLoaded(true);
|
||||
}
|
||||
importIcon();
|
||||
}, [fileName, ImportedSVG]);
|
||||
|
||||
return (
|
||||
<StyledIcon
|
||||
component={ImportedSVG.current || TransparentIcon}
|
||||
aria-label={name}
|
||||
{...iconProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Icon;
|
||||
|
||||
@@ -22,6 +22,7 @@ import AntdIcon from '@ant-design/icons';
|
||||
type AntdIconType = typeof AntdIcon.defaultProps;
|
||||
type IconType = AntdIconType & {
|
||||
iconColor?: string;
|
||||
twoToneColor?: string;
|
||||
iconSize?: 's' | 'm' | 'l' | 'xl' | 'xxl';
|
||||
};
|
||||
|
||||
|
||||
@@ -78,6 +78,11 @@ InteractiveIcons.argTypes = {
|
||||
defaultValue: null,
|
||||
control: { type: 'select', options: palette },
|
||||
},
|
||||
// @TODO twoToneColor is being ignored
|
||||
twoToneColor: {
|
||||
defaultValue: null,
|
||||
control: { type: 'select', options: palette },
|
||||
},
|
||||
theme: {
|
||||
table: {
|
||||
disable: true,
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { ReactComponent as AlertIcon } from 'images/icons/alert.svg';
|
||||
import { ReactComponent as AlertSolidIcon } from 'images/icons/alert_solid.svg';
|
||||
import { ReactComponent as AlertSolidSmallIcon } from 'images/icons/alert_solid_small.svg';
|
||||
import { ReactComponent as BinocularsIcon } from 'images/icons/binoculars.svg';
|
||||
import { ReactComponent as BoltIcon } from 'images/icons/bolt.svg';
|
||||
import { ReactComponent as BoltSmallIcon } from 'images/icons/bolt_small.svg';
|
||||
import { ReactComponent as BoltSmallRunIcon } from 'images/icons/bolt_small_run.svg';
|
||||
import { ReactComponent as CalendarIcon } from 'images/icons/calendar.svg';
|
||||
import { ReactComponent as CancelIcon } from 'images/icons/cancel.svg';
|
||||
import { ReactComponent as CancelSolidIcon } from 'images/icons/cancel_solid.svg';
|
||||
import { ReactComponent as CancelXIcon } from 'images/icons/cancel-x.svg';
|
||||
import { ReactComponent as CardViewIcon } from 'images/icons/card_view.svg';
|
||||
import { ReactComponent as CardsIcon } from 'images/icons/cards.svg';
|
||||
import { ReactComponent as CardsLockedIcon } from 'images/icons/cards_locked.svg';
|
||||
import { ReactComponent as CaretDownIcon } from 'images/icons/caret_down.svg';
|
||||
import { ReactComponent as CaretLeftIcon } from 'images/icons/caret_left.svg';
|
||||
import { ReactComponent as CaretRightIcon } from 'images/icons/caret_right.svg';
|
||||
import { ReactComponent as CaretUpIcon } from 'images/icons/caret_up.svg';
|
||||
import { ReactComponent as CertifiedIcon } from 'images/icons/certified.svg';
|
||||
import { ReactComponent as CheckIcon } from 'images/icons/check.svg';
|
||||
import { ReactComponent as CheckboxHalfIcon } from 'images/icons/checkbox-half.svg';
|
||||
import { ReactComponent as CheckboxOffIcon } from 'images/icons/checkbox-off.svg';
|
||||
import { ReactComponent as CheckboxOnIcon } from 'images/icons/checkbox-on.svg';
|
||||
import { ReactComponent as CircleCheckIcon } from 'images/icons/circle_check.svg';
|
||||
import { ReactComponent as CircleCheckSolidIcon } from 'images/icons/circle_check_solid.svg';
|
||||
import { ReactComponent as CircleIcon } from 'images/icons/circle.svg';
|
||||
import { ReactComponent as ClockIcon } from 'images/icons/clock.svg';
|
||||
import { ReactComponent as CloseIcon } from 'images/icons/close.svg';
|
||||
import { ReactComponent as CodeIcon } from 'images/icons/code.svg';
|
||||
import { ReactComponent as CogIcon } from 'images/icons/cog.svg';
|
||||
import { ReactComponent as CollapseIcon } from 'images/icons/collapse.svg';
|
||||
import { ReactComponent as ColorPaletteIcon } from 'images/icons/color_palette.svg';
|
||||
import { ReactComponent as ComponentsIcon } from 'images/icons/components.svg';
|
||||
import { ReactComponent as CopyIcon } from 'images/icons/copy.svg';
|
||||
import { ReactComponent as CursorTargeIcon } from 'images/icons/cursor_target.svg';
|
||||
import { ReactComponent as DatabaseIcon } from 'images/icons/database.svg';
|
||||
import { ReactComponent as DatasetPhysicalIcon } from 'images/icons/dataset_physical.svg';
|
||||
import { ReactComponent as DatasetVirtualGreyscaleIcon } from 'images/icons/dataset_virtual_greyscale.svg';
|
||||
import { ReactComponent as DatasetVirtualIcon } from 'images/icons/dataset_virtual.svg';
|
||||
import { ReactComponent as DownloadIcon } from 'images/icons/download.svg';
|
||||
import { ReactComponent as EditAltIcon } from 'images/icons/edit_alt.svg';
|
||||
import { ReactComponent as EditIcon } from 'images/icons/edit.svg';
|
||||
import { ReactComponent as EmailIcon } from 'images/icons/email.svg';
|
||||
import { ReactComponent as ErrorIcon } from 'images/icons/error.svg';
|
||||
import { ReactComponent as ErrorSolidIcon } from 'images/icons/error_solid.svg';
|
||||
import { ReactComponent as ErrorSolidSmallIcon } from 'images/icons/error_solid_small.svg';
|
||||
import { ReactComponent as ExclamationIcon } from 'images/icons/exclamation.svg';
|
||||
import { ReactComponent as ExpandIcon } from 'images/icons/expand.svg';
|
||||
import { ReactComponent as EyeIcon } from 'images/icons/eye.svg';
|
||||
import { ReactComponent as EyeSlashIcon } from 'images/icons/eye_slash.svg';
|
||||
import { ReactComponent as FavoriteSelectedIcon } from 'images/icons/favorite-selected.svg';
|
||||
import { ReactComponent as FavoriteSmallSelectedIcon } from 'images/icons/favorite_small_selected.svg';
|
||||
import { ReactComponent as FavoriteUnselectedIcon } from 'images/icons/favorite-unselected.svg';
|
||||
import { ReactComponent as FieldABCIcon } from 'images/icons/field_abc.svg';
|
||||
import { ReactComponent as FieldBooleanIcon } from 'images/icons/field_boolean.svg';
|
||||
import { ReactComponent as FieldDateIcon } from 'images/icons/field_date.svg';
|
||||
import { ReactComponent as FieldDerivedIcon } from 'images/icons/field_derived.svg';
|
||||
import { ReactComponent as FieldNumIcon } from 'images/icons/field_num.svg';
|
||||
import { ReactComponent as FieldStructIcon } from 'images/icons/field_struct.svg';
|
||||
import { ReactComponent as FileIcon } from 'images/icons/file.svg';
|
||||
import { ReactComponent as FilterIcon } from 'images/icons/filter.svg';
|
||||
import { ReactComponent as FilterSmallIcon } from 'images/icons/filter_small.svg';
|
||||
import { ReactComponent as FolderIcon } from 'images/icons/folder.svg';
|
||||
import { ReactComponent as FullIcon } from 'images/icons/full.svg';
|
||||
import { ReactComponent as FunctionIcon } from 'images/icons/function_x.svg';
|
||||
import { ReactComponent as GearIcon } from 'images/icons/gear.svg';
|
||||
import { ReactComponent as GridIcon } from 'images/icons/grid.svg';
|
||||
import { ReactComponent as ImageIcon } from 'images/icons/image.svg';
|
||||
import { ReactComponent as ImportIcon } from 'images/icons/import.svg';
|
||||
import { ReactComponent as InfoIcon } from 'images/icons/info.svg';
|
||||
import { ReactComponent as InfoSolidIcon } from 'images/icons/info-solid.svg';
|
||||
import { ReactComponent as InfoSolidSmallIcon } from 'images/icons/info_solid_small.svg';
|
||||
import { ReactComponent as JoinIcon } from 'images/icons/join.svg';
|
||||
import { ReactComponent as KeyboardIcon } from 'images/icons/keyboard.svg';
|
||||
import { ReactComponent as LayersIcon } from 'images/icons/layers.svg';
|
||||
import { ReactComponent as LightbulbIcon } from 'images/icons/lightbulb.svg';
|
||||
import { ReactComponent as LinkIcon } from 'images/icons/link.svg';
|
||||
import { ReactComponent as ListIcon } from 'images/icons/list.svg';
|
||||
import { ReactComponent as ListViewIcon } from 'images/icons/list_view.svg';
|
||||
import { ReactComponent as LocationIcon } from 'images/icons/location.svg';
|
||||
import { ReactComponent as LockLockedIcon } from 'images/icons/lock_locked.svg';
|
||||
import { ReactComponent as LockUnlockedIcon } from 'images/icons/lock_unlocked.svg';
|
||||
import { ReactComponent as MapIcon } from 'images/icons/map.svg';
|
||||
import { ReactComponent as MessageIcon } from 'images/icons/message.svg';
|
||||
import { ReactComponent as MinusIcon } from 'images/icons/minus.svg';
|
||||
import { ReactComponent as MinusSolidIcon } from 'images/icons/minus_solid.svg';
|
||||
import { ReactComponent as MoreHorizIcon } from 'images/icons/more_horiz.svg';
|
||||
import { ReactComponent as MoveIcon } from 'images/icons/move.svg';
|
||||
import { ReactComponent as NavChartsIcon } from 'images/icons/nav_charts.svg';
|
||||
import { ReactComponent as NavDashboardIcon } from 'images/icons/nav_dashboard.svg';
|
||||
import { ReactComponent as NavDataIcon } from 'images/icons/nav_data.svg';
|
||||
import { ReactComponent as NavExploreIcon } from 'images/icons/nav_explore.svg';
|
||||
import { ReactComponent as NavHomeIcon } from 'images/icons/nav_home.svg';
|
||||
import { ReactComponent as NavLabIcon } from 'images/icons/nav_lab.svg';
|
||||
import { ReactComponent as NoteIcon } from 'images/icons/note.svg';
|
||||
import { ReactComponent as OfflineIcon } from 'images/icons/offline.svg';
|
||||
import { ReactComponent as PaperclipIcon } from 'images/icons/paperclip.svg';
|
||||
import { ReactComponent as PlaceholderIcon } from 'images/icons/placeholder.svg';
|
||||
import { ReactComponent as PlusIcon } from 'images/icons/plus.svg';
|
||||
import { ReactComponent as PlusLargeIcon } from 'images/icons/plus_large.svg';
|
||||
import { ReactComponent as PlusSmallIcon } from 'images/icons/plus_small.svg';
|
||||
import { ReactComponent as PlusSolidIcon } from 'images/icons/plus_solid.svg';
|
||||
import { ReactComponent as QueuedIcon } from 'images/icons/queued.svg';
|
||||
import { ReactComponent as RefreshIcon } from 'images/icons/refresh.svg';
|
||||
import { ReactComponent as RunningIcon } from 'images/icons/running.svg';
|
||||
import { ReactComponent as SaveIcon } from 'images/icons/save.svg';
|
||||
import { ReactComponent as SQLIcon } from 'images/icons/sql.svg';
|
||||
import { ReactComponent as SearchIcon } from 'images/icons/search.svg';
|
||||
import { ReactComponent as ServerIcon } from 'images/icons/server.svg';
|
||||
import { ReactComponent as ShareIcon } from 'images/icons/share.svg';
|
||||
import { ReactComponent as SlackIcon } from 'images/icons/slack.svg';
|
||||
import { ReactComponent as SortAscIcon } from 'images/icons/sort_asc.svg';
|
||||
import { ReactComponent as SortDescIcon } from 'images/icons/sort_desc.svg';
|
||||
import { ReactComponent as SortIcon } from 'images/icons/sort.svg';
|
||||
import { ReactComponent as TableIcon } from 'images/icons/table.svg';
|
||||
import { ReactComponent as TagIcon } from 'images/icons/tag.svg';
|
||||
import { ReactComponent as TrashIcon } from 'images/icons/trash.svg';
|
||||
import { ReactComponent as TriangleChangeIcon } from 'images/icons/triangle_change.svg';
|
||||
import { ReactComponent as TriangleDownIcon } from 'images/icons/triangle_down.svg';
|
||||
import { ReactComponent as TriangleUpIcon } from 'images/icons/triangle_up.svg';
|
||||
import { ReactComponent as UpLevelIcon } from 'images/icons/up-level.svg';
|
||||
import { ReactComponent as UserIcon } from 'images/icons/user.svg';
|
||||
import { ReactComponent as WarningIcon } from 'images/icons/warning.svg';
|
||||
import { ReactComponent as WarningSolidIcon } from 'images/icons/warning_solid.svg';
|
||||
import { ReactComponent as XLargeIcon } from 'images/icons/x-large.svg';
|
||||
import { ReactComponent as XSmallIcon } from 'images/icons/x-small.svg';
|
||||
|
||||
import AntdEnhancedIcons from './AntdEnhanced';
|
||||
import { renderIcon } from './Icon';
|
||||
import IconType from './IconType';
|
||||
|
||||
export default {
|
||||
...AntdEnhancedIcons,
|
||||
Alert: (props: IconType) => renderIcon(AlertIcon, props),
|
||||
AlertSolid: (props: IconType) => renderIcon(AlertSolidIcon, props),
|
||||
AlertSolidSmall: (props: IconType) => renderIcon(AlertSolidSmallIcon, props),
|
||||
Binoculars: (props: IconType) => renderIcon(BinocularsIcon, props),
|
||||
Bolt: (props: IconType) => renderIcon(BoltIcon, props),
|
||||
BoltSmall: (props: IconType) => renderIcon(BoltSmallIcon, props),
|
||||
BoltSmallRun: (props: IconType) => renderIcon(BoltSmallRunIcon, props),
|
||||
Calendar: (props: IconType) => renderIcon(CalendarIcon, props),
|
||||
Cancel: (props: IconType) => renderIcon(CancelIcon, props),
|
||||
CancelSolid: (props: IconType) => renderIcon(CancelSolidIcon, props),
|
||||
CancelX: (props: IconType) => renderIcon(CancelXIcon, props),
|
||||
CardView: (props: IconType) => renderIcon(CardViewIcon, props),
|
||||
Cards: (props: IconType) => renderIcon(CardsIcon, props),
|
||||
CardsLocked: (props: IconType) => renderIcon(CardsLockedIcon, props),
|
||||
CaretDown: (props: IconType) => renderIcon(CaretDownIcon, props),
|
||||
CaretLeft: (props: IconType) => renderIcon(CaretLeftIcon, props),
|
||||
CaretRight: (props: IconType) => renderIcon(CaretRightIcon, props),
|
||||
CaretUp: (props: IconType) => renderIcon(CaretUpIcon, props),
|
||||
Certified: (props: IconType) => renderIcon(CertifiedIcon, props),
|
||||
Check: (props: IconType) => renderIcon(CheckIcon, props),
|
||||
CheckboxHalf: (props: IconType) => renderIcon(CheckboxHalfIcon, props),
|
||||
CheckboxOff: (props: IconType) => renderIcon(CheckboxOffIcon, props),
|
||||
CheckboxOn: (props: IconType) => renderIcon(CheckboxOnIcon, props),
|
||||
CircleCheck: (props: IconType) => renderIcon(CircleCheckIcon, props),
|
||||
CircleCheckSolid: (props: IconType) =>
|
||||
renderIcon(CircleCheckSolidIcon, props),
|
||||
Circle: (props: IconType) => renderIcon(CircleIcon, props),
|
||||
Clock: (props: IconType) => renderIcon(ClockIcon, props),
|
||||
Close: (props: IconType) => renderIcon(CloseIcon, props),
|
||||
Code: (props: IconType) => renderIcon(CodeIcon, props),
|
||||
Cog: (props: IconType) => renderIcon(CogIcon, props),
|
||||
Collapse: (props: IconType) => renderIcon(CollapseIcon, props),
|
||||
ColorPalette: (props: IconType) => renderIcon(ColorPaletteIcon, props),
|
||||
Components: (props: IconType) => renderIcon(ComponentsIcon, props),
|
||||
Copy: (props: IconType) => renderIcon(CopyIcon, props),
|
||||
CursorTarget: (props: IconType) => renderIcon(CursorTargeIcon, props),
|
||||
Database: (props: IconType) => renderIcon(DatabaseIcon, props),
|
||||
DatasetPhysical: (props: IconType) => renderIcon(DatasetPhysicalIcon, props),
|
||||
DatasetVirtualGreyscale: (props: IconType) =>
|
||||
renderIcon(DatasetVirtualGreyscaleIcon, props),
|
||||
DatasetVirtual: (props: IconType) => renderIcon(DatasetVirtualIcon, props),
|
||||
Download: (props: IconType) => renderIcon(DownloadIcon, props),
|
||||
EditAlt: (props: IconType) => renderIcon(EditAltIcon, props),
|
||||
Edit: (props: IconType) => renderIcon(EditIcon, props),
|
||||
Email: (props: IconType) => renderIcon(EmailIcon, props),
|
||||
Error: (props: IconType) => renderIcon(ErrorIcon, props),
|
||||
ErrorSolid: (props: IconType) => renderIcon(ErrorSolidIcon, props),
|
||||
ErrorSolidSmall: (props: IconType) => renderIcon(ErrorSolidSmallIcon, props),
|
||||
Exclamation: (props: IconType) => renderIcon(ExclamationIcon, props),
|
||||
Expand: (props: IconType) => renderIcon(ExpandIcon, props),
|
||||
Eye: (props: IconType) => renderIcon(EyeIcon, props),
|
||||
EyeSlash: (props: IconType) => renderIcon(EyeSlashIcon, props),
|
||||
FavoriteSelected: (props: IconType) =>
|
||||
renderIcon(FavoriteSelectedIcon, props),
|
||||
FavoriteSmallSelected: (props: IconType) =>
|
||||
renderIcon(FavoriteSmallSelectedIcon, props),
|
||||
FavoriteUnselected: (props: IconType) =>
|
||||
renderIcon(FavoriteUnselectedIcon, props),
|
||||
FieldABCIcon: (props: IconType) => renderIcon(FieldABCIcon, props),
|
||||
FieldBoolean: (props: IconType) => renderIcon(FieldBooleanIcon, props),
|
||||
FieldDate: (props: IconType) => renderIcon(FieldDateIcon, props),
|
||||
FieldDerived: (props: IconType) => renderIcon(FieldDerivedIcon, props),
|
||||
FieldNum: (props: IconType) => renderIcon(FieldNumIcon, props),
|
||||
FieldStruct: (props: IconType) => renderIcon(FieldStructIcon, props),
|
||||
File: (props: IconType) => renderIcon(FileIcon, props),
|
||||
Filter: (props: IconType) => renderIcon(FilterIcon, props),
|
||||
FilterSmall: (props: IconType) => renderIcon(FilterSmallIcon, props),
|
||||
Folder: (props: IconType) => renderIcon(FolderIcon, props),
|
||||
Full: (props: IconType) => renderIcon(FullIcon, props),
|
||||
Function: (props: IconType) => renderIcon(FunctionIcon, props),
|
||||
Gear: (props: IconType) => renderIcon(GearIcon, props),
|
||||
Grid: (props: IconType) => renderIcon(GridIcon, props),
|
||||
Image: (props: IconType) => renderIcon(ImageIcon, props),
|
||||
Import: (props: IconType) => renderIcon(ImportIcon, props),
|
||||
Info: (props: IconType) => renderIcon(InfoIcon, props),
|
||||
InfoSolid: (props: IconType) => renderIcon(InfoSolidIcon, props),
|
||||
InfoSolidSmall: (props: IconType) => renderIcon(InfoSolidSmallIcon, props),
|
||||
Join: (props: IconType) => renderIcon(JoinIcon, props),
|
||||
Keyboard: (props: IconType) => renderIcon(KeyboardIcon, props),
|
||||
Layers: (props: IconType) => renderIcon(LayersIcon, props),
|
||||
Lightbulb: (props: IconType) => renderIcon(LightbulbIcon, props),
|
||||
Link: (props: IconType) => renderIcon(LinkIcon, props),
|
||||
List: (props: IconType) => renderIcon(ListIcon, props),
|
||||
ListView: (props: IconType) => renderIcon(ListViewIcon, props),
|
||||
Location: (props: IconType) => renderIcon(LocationIcon, props),
|
||||
LockLocked: (props: IconType) => renderIcon(LockLockedIcon, props),
|
||||
LockUnlocked: (props: IconType) => renderIcon(LockUnlockedIcon, props),
|
||||
Map: (props: IconType) => renderIcon(MapIcon, props),
|
||||
Message: (props: IconType) => renderIcon(MessageIcon, props),
|
||||
Minus: (props: IconType) => renderIcon(MinusIcon, props),
|
||||
MinusSolid: (props: IconType) => renderIcon(MinusSolidIcon, props),
|
||||
MoreHoriz: (props: IconType) => renderIcon(MoreHorizIcon, props),
|
||||
Move: (props: IconType) => renderIcon(MoveIcon, props),
|
||||
NavCharts: (props: IconType) => renderIcon(NavChartsIcon, props),
|
||||
NavDashboard: (props: IconType) => renderIcon(NavDashboardIcon, props),
|
||||
NavData: (props: IconType) => renderIcon(NavDataIcon, props),
|
||||
NavExplore: (props: IconType) => renderIcon(NavExploreIcon, props),
|
||||
NavHome: (props: IconType) => renderIcon(NavHomeIcon, props),
|
||||
NavLab: (props: IconType) => renderIcon(NavLabIcon, props),
|
||||
Note: (props: IconType) => renderIcon(NoteIcon, props),
|
||||
Offline: (props: IconType) => renderIcon(OfflineIcon, props),
|
||||
Paperclip: (props: IconType) => renderIcon(PaperclipIcon, props),
|
||||
Placeholder: (props: IconType) => renderIcon(PlaceholderIcon, props),
|
||||
Plus: (props: IconType) => renderIcon(PlusIcon, props),
|
||||
PlusLarge: (props: IconType) => renderIcon(PlusLargeIcon, props),
|
||||
PlusSmall: (props: IconType) => renderIcon(PlusSmallIcon, props),
|
||||
PlusSolid: (props: IconType) => renderIcon(PlusSolidIcon, props),
|
||||
Queued: (props: IconType) => renderIcon(QueuedIcon, props),
|
||||
Refresh: (props: IconType) => renderIcon(RefreshIcon, props),
|
||||
Running: (props: IconType) => renderIcon(RunningIcon, props),
|
||||
Save: (props: IconType) => renderIcon(SaveIcon, props),
|
||||
SQL: (props: IconType) => renderIcon(SQLIcon, props),
|
||||
Search: (props: IconType) => renderIcon(SearchIcon, props),
|
||||
Server: (props: IconType) => renderIcon(ServerIcon, props),
|
||||
Share: (props: IconType) => renderIcon(ShareIcon, props),
|
||||
Slack: (props: IconType) => renderIcon(SlackIcon, props),
|
||||
SortAsc: (props: IconType) => renderIcon(SortAscIcon, props),
|
||||
SortDesc: (props: IconType) => renderIcon(SortDescIcon, props),
|
||||
Sort: (props: IconType) => renderIcon(SortIcon, props),
|
||||
Table: (props: IconType) => renderIcon(TableIcon, props),
|
||||
Tag: (props: IconType) => renderIcon(TagIcon, props),
|
||||
Trash: (props: IconType) => renderIcon(TrashIcon, props),
|
||||
TriangleChange: (props: IconType) => renderIcon(TriangleChangeIcon, props),
|
||||
TriangleDown: (props: IconType) => renderIcon(TriangleDownIcon, props),
|
||||
TriangleUp: (props: IconType) => renderIcon(TriangleUpIcon, props),
|
||||
UpLevel: (props: IconType) => renderIcon(UpLevelIcon, props),
|
||||
User: (props: IconType) => renderIcon(UserIcon, props),
|
||||
Warning: (props: IconType) => renderIcon(WarningIcon, props),
|
||||
WarningSolid: (props: IconType) => renderIcon(WarningSolidIcon, props),
|
||||
XLarge: (props: IconType) => renderIcon(XLargeIcon, props),
|
||||
XSmall: (props: IconType) => renderIcon(XSmallIcon, props),
|
||||
};
|
||||
166
superset-frontend/src/components/Icons/index.tsx
Normal file
166
superset-frontend/src/components/Icons/index.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import AntdEnhancedIcons from './AntdEnhanced';
|
||||
import Icon from './Icon';
|
||||
import IconType from './IconType';
|
||||
|
||||
const IconFileNames = [
|
||||
'alert',
|
||||
'alert_solid',
|
||||
'alert_solid_small',
|
||||
'binoculars',
|
||||
'bolt',
|
||||
'bolt_small',
|
||||
'bolt_small_run',
|
||||
'calendar',
|
||||
'cancel',
|
||||
'cancel_solid',
|
||||
'cancel-x',
|
||||
'card_view',
|
||||
'cards',
|
||||
'cards_locked',
|
||||
'caret_down',
|
||||
'caret_left',
|
||||
'caret_right',
|
||||
'caret_up',
|
||||
'certified',
|
||||
'check',
|
||||
'checkbox-half',
|
||||
'checkbox-off',
|
||||
'checkbox-on',
|
||||
'circle_check',
|
||||
'circle_check_solid',
|
||||
'circle',
|
||||
'clock',
|
||||
'close',
|
||||
'code',
|
||||
'cog',
|
||||
'collapse',
|
||||
'color_palette',
|
||||
'components',
|
||||
'copy',
|
||||
'cursor_target',
|
||||
'database',
|
||||
'dataset_physical',
|
||||
'dataset_virtual_greyscale',
|
||||
'dataset_virtual',
|
||||
'download',
|
||||
'edit_alt',
|
||||
'edit',
|
||||
'email',
|
||||
'error',
|
||||
'error_solid',
|
||||
'error_solid_small',
|
||||
'exclamation',
|
||||
'expand',
|
||||
'eye',
|
||||
'eye_slash',
|
||||
'favorite-selected',
|
||||
'favorite_small_selected',
|
||||
'favorite-unselected',
|
||||
'field_abc',
|
||||
'field_boolean',
|
||||
'field_date',
|
||||
'field_derived',
|
||||
'field_num',
|
||||
'field_struct',
|
||||
'file',
|
||||
'filter',
|
||||
'filter_small',
|
||||
'folder',
|
||||
'full',
|
||||
'function_x',
|
||||
'gear',
|
||||
'grid',
|
||||
'image',
|
||||
'import',
|
||||
'info',
|
||||
'info-solid',
|
||||
'info_solid_small',
|
||||
'join',
|
||||
'keyboard',
|
||||
'layers',
|
||||
'lightbulb',
|
||||
'link',
|
||||
'list',
|
||||
'list_view',
|
||||
'location',
|
||||
'lock_locked',
|
||||
'lock_unlocked',
|
||||
'map',
|
||||
'message',
|
||||
'minus',
|
||||
'minus_solid',
|
||||
'more_horiz',
|
||||
'move',
|
||||
'nav_charts',
|
||||
'nav_dashboard',
|
||||
'nav_data',
|
||||
'nav_explore',
|
||||
'nav_home',
|
||||
'nav_lab',
|
||||
'note',
|
||||
'offline',
|
||||
'paperclip',
|
||||
'placeholder',
|
||||
'plus',
|
||||
'plus_large',
|
||||
'plus_small',
|
||||
'plus_solid',
|
||||
'queued',
|
||||
'refresh',
|
||||
'running',
|
||||
'save',
|
||||
'sql',
|
||||
'search',
|
||||
'server',
|
||||
'share',
|
||||
'slack',
|
||||
'sort_asc',
|
||||
'sort_desc',
|
||||
'sort',
|
||||
'table',
|
||||
'tag',
|
||||
'trash',
|
||||
'triangle_change',
|
||||
'triangle_down',
|
||||
'triangle_up',
|
||||
'up-level',
|
||||
'user',
|
||||
'warning',
|
||||
'warning_solid',
|
||||
'x-large',
|
||||
'x-small',
|
||||
];
|
||||
|
||||
const iconOverrides: Record<string, React.FC> = {};
|
||||
IconFileNames.forEach(fileName => {
|
||||
const keyName = _.startCase(fileName).replace(/ /g, '');
|
||||
iconOverrides[keyName] = (props: IconType) => (
|
||||
<Icon fileName={fileName} {...props} />
|
||||
);
|
||||
});
|
||||
|
||||
export default {
|
||||
...AntdEnhancedIcons,
|
||||
...iconOverrides,
|
||||
};
|
||||
@@ -34,7 +34,7 @@ describe('LastUpdated', () => {
|
||||
it('renders a refresh action', () => {
|
||||
const mockAction = jest.fn();
|
||||
wrapper = mount(<LastUpdated updatedAt={updatedAt} update={mockAction} />);
|
||||
const props = wrapper.find('[data-test="refresh"]').props();
|
||||
const props = wrapper.find('[data-test="refresh"]').first().props();
|
||||
if (props.onClick) {
|
||||
props.onClick({} as React.MouseEvent);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import React from 'react';
|
||||
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { supersetTheme } from '@superset-ui/core';
|
||||
import Icon from 'src/components/Icon';
|
||||
import Icons from 'src/components/Icons';
|
||||
import Button from 'src/components/Button';
|
||||
import Popover from '.';
|
||||
|
||||
@@ -47,15 +47,13 @@ test('it should not render a title or content when not visible', () => {
|
||||
expect(title).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders with icon child', () => {
|
||||
test('renders with icon child', async () => {
|
||||
render(
|
||||
<Popover content="Content sample" title="Popover title">
|
||||
<Icon name="alert" role="img">
|
||||
Click me
|
||||
</Icon>
|
||||
<Icons.Alert>Click me</Icons.Alert>
|
||||
</Popover>,
|
||||
);
|
||||
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||
expect(await screen.findByRole('img')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('fires an event when visibility is changed', async () => {
|
||||
|
||||
@@ -21,7 +21,7 @@ import { render, screen } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { supersetTheme } from '@superset-ui/core';
|
||||
import Button from 'src/components/Button';
|
||||
import Icon from 'src/components/Icon';
|
||||
import Icons from 'src/components/Icons';
|
||||
import { Tooltip } from '.';
|
||||
|
||||
test('starts hidden with default props', () => {
|
||||
@@ -62,9 +62,7 @@ test('renders with theme', () => {
|
||||
test('renders with icon child', async () => {
|
||||
render(
|
||||
<Tooltip title="Simple tooltip">
|
||||
<Icon name="alert" role="img">
|
||||
Hover me
|
||||
</Icon>
|
||||
<Icons.Alert>Hover me</Icons.Alert>
|
||||
</Tooltip>,
|
||||
);
|
||||
userEvent.hover(screen.getByRole('img'));
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
import React from 'react';
|
||||
import { useTheme, SafeMarkdown } from '@superset-ui/core';
|
||||
import Icon from 'src/components/Icon';
|
||||
import Icons from 'src/components/Icons';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
|
||||
export interface WarningIconWithTooltipProps {
|
||||
@@ -28,7 +28,6 @@ export interface WarningIconWithTooltipProps {
|
||||
|
||||
function WarningIconWithTooltip({
|
||||
warningMarkdown,
|
||||
size = 24,
|
||||
}: WarningIconWithTooltipProps) {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
@@ -36,12 +35,7 @@ function WarningIconWithTooltip({
|
||||
id="warning-tooltip"
|
||||
title={<SafeMarkdown source={warningMarkdown} />}
|
||||
>
|
||||
<Icon
|
||||
color={theme.colors.alert.base}
|
||||
height={size}
|
||||
width={size}
|
||||
name="alert-solid"
|
||||
/>
|
||||
<Icons.AlertSolid iconColor={theme.colors.alert.base} />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import Icon from 'src/components/Icon';
|
||||
import Icons from 'src/components/Icons';
|
||||
import DetailsPanelPopover from './DetailsPanel';
|
||||
import { Pill } from './Styles';
|
||||
import { Indicator } from './selectors';
|
||||
@@ -77,7 +78,7 @@ const FiltersBadge = ({
|
||||
{incompatibleIndicators.length ? (
|
||||
<>
|
||||
{' '}
|
||||
<Icon name="alert-solid" />
|
||||
<Icons.AlertSolid />
|
||||
<span data-test="incompatible-filter-count">
|
||||
{incompatibleIndicators.length}
|
||||
</span>
|
||||
|
||||
@@ -143,7 +143,7 @@ test('should search and render matching metrics', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should render a warning', () => {
|
||||
test('should render a warning', async () => {
|
||||
const deprecatedDatasource = {
|
||||
...datasource,
|
||||
extra: JSON.stringify({ warning_markdown: 'This is a warning.' }),
|
||||
@@ -160,5 +160,7 @@ test('should render a warning', () => {
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(screen.getByTestId('alert-solid')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByRole('img', { name: 'alert-solid' }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ import { t, styled, supersetTheme } from '@superset-ui/core';
|
||||
import { Dropdown, Menu } from 'src/common/components';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
import Icon from 'src/components/Icon';
|
||||
import Icons from 'src/components/Icons';
|
||||
import ChangeDatasourceModal from 'src/datasource/ChangeDatasourceModal';
|
||||
import DatasourceModal from 'src/datasource/DatasourceModal';
|
||||
import { postForm } from 'src/explore/exploreUtils';
|
||||
@@ -202,10 +203,7 @@ class DatasourceControl extends React.PureComponent {
|
||||
)}
|
||||
{healthCheckMessage && (
|
||||
<Tooltip title={healthCheckMessage}>
|
||||
<Icon
|
||||
name="alert-solid"
|
||||
color={supersetTheme.colors.warning.base}
|
||||
/>
|
||||
<Icons.AlertSolid iconColor={supersetTheme.colors.warning.base} />
|
||||
</Tooltip>
|
||||
)}
|
||||
{extra?.warning_markdown && ( // eslint-disable-line camelcase
|
||||
|
||||
@@ -16,34 +16,34 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import { t, supersetTheme, withTheme } from '@superset-ui/core';
|
||||
import React from 'react';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
import Icon, { IconName } from 'src/components/Icon';
|
||||
import Icons from 'src/components/Icons';
|
||||
import { AlertState } from '../types';
|
||||
|
||||
const StatusIcon = styled(Icon, {
|
||||
shouldForwardProp: prop => prop !== 'status' && prop !== 'isReportEnabled',
|
||||
})<{ status: string; isReportEnabled: boolean }>`
|
||||
color: ${({ status, theme, isReportEnabled }) => {
|
||||
switch (status) {
|
||||
case AlertState.working:
|
||||
return theme.colors.primary.base;
|
||||
case AlertState.error:
|
||||
return theme.colors.error.base;
|
||||
case AlertState.success:
|
||||
return isReportEnabled
|
||||
? theme.colors.success.base
|
||||
: theme.colors.alert.base;
|
||||
case AlertState.noop:
|
||||
return theme.colors.success.base;
|
||||
case AlertState.grace:
|
||||
return theme.colors.alert.base;
|
||||
default:
|
||||
return theme.colors.grayscale.base;
|
||||
}
|
||||
}};
|
||||
`;
|
||||
function getStatusColor(
|
||||
status: string,
|
||||
isReportEnabled: boolean,
|
||||
theme: typeof supersetTheme,
|
||||
) {
|
||||
switch (status) {
|
||||
case AlertState.working:
|
||||
return theme.colors.primary.base;
|
||||
case AlertState.error:
|
||||
return theme.colors.error.base;
|
||||
case AlertState.success:
|
||||
return isReportEnabled
|
||||
? theme.colors.success.base
|
||||
: theme.colors.alert.base;
|
||||
case AlertState.noop:
|
||||
return theme.colors.success.base;
|
||||
case AlertState.grace:
|
||||
return theme.colors.alert.base;
|
||||
default:
|
||||
return theme.colors.grayscale.base;
|
||||
}
|
||||
}
|
||||
|
||||
export default function AlertStatusIcon({
|
||||
state,
|
||||
@@ -53,59 +53,58 @@ export default function AlertStatusIcon({
|
||||
isReportEnabled: boolean;
|
||||
}) {
|
||||
const lastStateConfig = {
|
||||
name: '',
|
||||
icon: Icons.Check,
|
||||
label: '',
|
||||
status: '',
|
||||
};
|
||||
switch (state) {
|
||||
case AlertState.success:
|
||||
lastStateConfig.name = isReportEnabled ? 'check' : 'alert-solid-small';
|
||||
lastStateConfig.icon = isReportEnabled
|
||||
? Icons.Check
|
||||
: Icons.AlertSolidSmall;
|
||||
lastStateConfig.label = isReportEnabled
|
||||
? t('Report sent')
|
||||
: t('Alert triggered, notification sent');
|
||||
lastStateConfig.status = AlertState.success;
|
||||
break;
|
||||
case AlertState.working:
|
||||
lastStateConfig.name = 'running';
|
||||
lastStateConfig.icon = Icons.Running;
|
||||
lastStateConfig.label = isReportEnabled
|
||||
? t('Report sending')
|
||||
: t('Alert running');
|
||||
lastStateConfig.status = AlertState.working;
|
||||
break;
|
||||
case AlertState.error:
|
||||
lastStateConfig.name = 'x-small';
|
||||
lastStateConfig.icon = Icons.XSmall;
|
||||
lastStateConfig.label = isReportEnabled
|
||||
? t('Report failed')
|
||||
: t('Alert failed');
|
||||
lastStateConfig.status = AlertState.error;
|
||||
break;
|
||||
case AlertState.noop:
|
||||
lastStateConfig.name = 'check';
|
||||
lastStateConfig.icon = Icons.Check;
|
||||
lastStateConfig.label = t('Nothing triggered');
|
||||
lastStateConfig.status = AlertState.noop;
|
||||
break;
|
||||
case AlertState.grace:
|
||||
lastStateConfig.name = 'alert-solid-small';
|
||||
lastStateConfig.icon = Icons.AlertSolidSmall;
|
||||
lastStateConfig.label = t('Alert Triggered, In Grace Period');
|
||||
lastStateConfig.status = AlertState.grace;
|
||||
break;
|
||||
default:
|
||||
lastStateConfig.name = 'check';
|
||||
lastStateConfig.icon = Icons.Check;
|
||||
lastStateConfig.label = t('Nothing triggered');
|
||||
lastStateConfig.status = AlertState.noop;
|
||||
}
|
||||
const Icon = lastStateConfig.icon;
|
||||
const AlertIcon = withTheme(({ theme }) => (
|
||||
<Icon
|
||||
iconColor={getStatusColor(lastStateConfig.status, isReportEnabled, theme)}
|
||||
/>
|
||||
));
|
||||
return (
|
||||
<Tooltip title={lastStateConfig.label} placement="bottomLeft">
|
||||
<StatusIcon
|
||||
name={lastStateConfig.name as IconName}
|
||||
status={lastStateConfig.status}
|
||||
isReportEnabled={isReportEnabled}
|
||||
viewBox={
|
||||
lastStateConfig.name === 'alert-solid-small'
|
||||
? '-6 -6 24 24'
|
||||
: '0 0 24 24'
|
||||
}
|
||||
/>
|
||||
<AlertIcon />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user