Remove Icon component in BUI

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-10-13 22:56:22 +01:00
parent de53c65aeb
commit 7b319c5304
16 changed files with 11 additions and 573 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/ui': minor
---
Remove Icon component in Backstage UI. This component was creating issue for tree-shaking. It is recommended to use icons from @remixicon/react until we found a better alternative in Backstage UI.
@@ -9,8 +9,8 @@ import {
SelectValue,
} from 'react-aria-components';
import styles from './theme-name.module.css';
import { Icon } from '@backstage/ui';
import { usePlayground } from '@/utils/playground-context';
import { RiArrowDownSLine } from '@remixicon/react';
const themes = [
{ name: 'Backstage', value: 'backstage' },
@@ -28,7 +28,7 @@ export const ThemeNameSelector = () => {
>
<Button className={styles.Select}>
<SelectValue />
<Icon name="chevron-down" />
<RiArrowDownSLine aria-hidden="true" />
</Button>
<Popover className={styles.Popup}>
<ListBox className={styles.ListBox}>
+3 -3
View File
@@ -1,9 +1,9 @@
'use client';
import { Tabs } from '@base-ui-components/react/tabs';
import { Icon } from '@backstage/ui';
import { usePlayground } from '@/utils/playground-context';
import styles from './theme.module.css';
import { RiMoonLine, RiSunLine } from '@remixicon/react';
export const ThemeSelector = () => {
const { selectedTheme, setSelectedTheme } = usePlayground();
@@ -16,10 +16,10 @@ export const ThemeSelector = () => {
>
<Tabs.List className={styles.list}>
<Tabs.Tab className={styles.tab} value="light">
<Icon name="sun" />
<RiSunLine aria-hidden="true" size={16} />
</Tabs.Tab>
<Tabs.Tab className={styles.tab} value="dark">
<Icon name="moon" />
<RiMoonLine aria-hidden="true" size={16} />
</Tabs.Tab>
<Tabs.Indicator className={styles.indicator} />
</Tabs.List>
@@ -8,7 +8,6 @@ import * as ButtonLinkStories from '../../../packages/ui/src/components/ButtonLi
import * as CheckboxStories from '../../../packages/ui/src/components/Checkbox/Checkbox.stories';
import * as ContainerStories from '../../../packages/ui/src/components/Container/Container.stories';
import * as GridStories from '../../../packages/ui/src/components/Grid/Grid.stories';
import * as IconStories from '../../../packages/ui/src/components/Icon/Icon.stories';
import * as TextFieldStories from '../../../packages/ui/src/components/TextField/TextField.stories';
import * as TextStories from '../../../packages/ui/src/components/Text/Text.stories';
import * as FlexStories from '../../../packages/ui/src/components/Flex/Flex.stories';
@@ -53,7 +52,6 @@ export const ButtonLinkSnippet = createSnippetComponent(ButtonLinkStories);
export const CheckboxSnippet = createSnippetComponent(CheckboxStories);
export const ContainerSnippet = createSnippetComponent(ContainerStories);
export const GridSnippet = createSnippetComponent(GridStories);
export const IconSnippet = createSnippetComponent(IconStories);
export const TextFieldSnippet = createSnippetComponent(TextFieldStories);
export const PasswordFieldSnippet =
createSnippetComponent(PasswordFieldStories);
-5
View File
@@ -116,11 +116,6 @@ export const components: Page[] = [
slug: 'header-page',
status: 'alpha',
},
{
title: 'Icon',
slug: 'icon',
status: 'alpha',
},
{
title: 'Link',
slug: 'link',
-101
View File
@@ -10,7 +10,6 @@ import { Collapsible as Collapsible_2 } from '@base-ui-components/react/collapsi
import { ColumnProps as ColumnProps_2 } from 'react-aria-components';
import { ComponentProps } from 'react';
import type { ComponentPropsWithRef } from 'react';
import { Context } from 'react';
import { DetailedHTMLProps } from 'react';
import type { DialogTriggerProps as DialogTriggerProps_2 } from 'react-aria-components';
import type { ElementType } from 'react';
@@ -32,7 +31,6 @@ import type { RadioProps as RadioProps_2 } from 'react-aria-components';
import { ReactElement } from 'react';
import { ReactNode } from 'react';
import { RefAttributes } from 'react';
import type { RemixiconComponentType } from '@remixicon/react';
import { RowProps } from 'react-aria-components';
import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area';
import type { SearchFieldProps as SearchFieldProps_2 } from 'react-aria-components';
@@ -986,102 +984,6 @@ export interface HeaderTab {
matchStrategy?: TabMatchStrategy;
}
// @public (undocumented)
export const Icon: (props: IconProps) => JSX_2.Element | null;
// @public (undocumented)
export const IconContext: Context<IconContextProps>;
// @public (undocumented)
export interface IconContextProps {
// (undocumented)
icons: IconMap;
}
// @public (undocumented)
export type IconMap = Partial<Record<IconNames, RemixiconComponentType>>;
// @public (undocumented)
export type IconNames =
| 'account-circle'
| 'alert'
| 'arrow-down'
| 'arrow-down-circle'
| 'caret-down'
| 'caret-left'
| 'caret-right'
| 'caret-up'
| 'arrow-left'
| 'arrow-left-circle'
| 'arrow-left-down'
| 'arrow-left-up'
| 'arrow-right'
| 'arrow-right-circle'
| 'arrow-right-down'
| 'arrow-right-up'
| 'arrow-up'
| 'arrow-up-circle'
| 'braces'
| 'brackets'
| 'bug'
| 'check'
| 'check-double'
| 'chevron-down'
| 'chevron-left'
| 'chevron-right'
| 'chevron-up'
| 'close'
| 'cloud'
| 'code'
| 'discord'
| 'download'
| 'external-link'
| 'eye'
| 'eye-off'
| 'filter'
| 'flower'
| 'github'
| 'git-repository'
| 'group'
| 'heart'
| 'moon'
| 'plus'
| 'search'
| 'sidebar-fold'
| 'sidebar-unfold'
| 'sparkling'
| 'star'
| 'sun'
| 'terminal'
| 'trash'
| 'upload'
| 'user'
| 'youtube'
| 'zoom-in'
| 'zoom-out';
// @public (undocumented)
export type IconProps = {
name: IconNames;
size?: number;
className?: string;
style?: React.CSSProperties;
};
// @public (undocumented)
export const IconProvider: (props: IconProviderProps) => JSX_2.Element;
// @public (undocumented)
export interface IconProviderProps {
// (undocumented)
children?: ReactNode;
// (undocumented)
overrides?: Partial<Record<IconNames, RemixiconComponentType>>;
}
// @public (undocumented)
export const icons: IconMap;
// @public (undocumented)
export type JustifyContent =
| 'stretch'
@@ -1588,9 +1490,6 @@ export const useBreakpoint: () => {
down: (key: Breakpoint) => boolean;
};
// @public (undocumented)
export const useIcons: () => IconContextProps;
// @public
export function useTable<T = any>(
config?: UseTableConfig<T>,
@@ -16,13 +16,7 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Card, CardHeader, CardBody, CardFooter } from './Card';
import { IconNames, Text } from '../..';
export interface ListItem {
id: string;
name: string;
icon?: IconNames;
}
import { Text } from '../..';
const meta = {
title: 'Backstage UI/Card',
@@ -1,22 +0,0 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed 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.
*/
@layer components {
.bui-Icon {
width: 1rem;
height: 1rem;
}
}
@@ -1,56 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed 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 { Meta, StoryObj } from '@storybook/react-vite';
import { Icon } from './Icon';
import { IconProvider } from './provider';
import { icons } from './icons';
const meta = {
title: 'Backstage UI/Icon',
component: Icon,
argTypes: {
name: {
control: 'select',
options: Object.keys(icons),
},
},
args: {
name: 'heart',
},
} satisfies Meta<typeof Icon>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
name: 'heart',
},
};
export const WithCustomIcon: Story = {
args: {
name: 'arrow-down',
},
decorators: [
Story => (
<IconProvider overrides={{ 'arrow-down': () => <div>Custom Icon</div> }}>
<Story />
</IconProvider>
),
],
};
-47
View File
@@ -1,47 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed 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 { ComponentType } from 'react';
import { useIcons } from './context';
import type { IconProps } from './types';
import clsx from 'clsx';
import { useStyles } from '../../hooks/useStyles';
import styles from './Icon.module.css';
/** @public */
export const Icon = (props: IconProps) => {
const { classNames, cleanedProps } = useStyles('Icon', props);
const { name, size, className, style, ...restProps } = cleanedProps;
const { icons } = useIcons();
const BckstageIcon = icons[name] as ComponentType<Omit<IconProps, 'name'>>;
if (!BckstageIcon) {
console.error(`Icon "${name}" not found or is not a valid component.`);
return null;
}
return (
<BckstageIcon
className={clsx(classNames.root, styles[classNames.root], className)}
style={{
...(size ? { width: size, height: size } : {}),
...style,
}}
{...restProps}
/>
);
};
@@ -1,27 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed 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 { createContext, useContext } from 'react';
import { icons } from './icons';
import type { IconContextProps } from './types';
/** @public */
export const IconContext = createContext<IconContextProps>({
icons,
});
/** @public */
export const useIcons = () => useContext(IconContext);
-137
View File
@@ -1,137 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed 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.
*/
// We can add custom icons to the list outside of Remix
import type { IconMap } from './types';
import {
RiHeartLine,
RiArrowDownLine,
RiCloudLine,
RiArrowLeftLine,
RiArrowRightLine,
RiArrowUpLine,
RiDeleteBin6Line,
RiAddLine,
RiArrowDownSLine,
RiArrowUpSLine,
RiArrowLeftSLine,
RiArrowRightSLine,
RiArrowDownCircleLine,
RiArrowLeftCircleLine,
RiArrowRightCircleLine,
RiArrowUpCircleLine,
RiCheckLine,
RiMoonLine,
RiSunLine,
RiExternalLinkLine,
RiArrowLeftDownLine,
RiArrowRightDownLine,
RiArrowLeftUpLine,
RiArrowRightUpLine,
RiArrowDropDownLine,
RiArrowDropUpLine,
RiArrowDropLeftLine,
RiArrowDropRightLine,
RiSparklingLine,
RiFlowerLine,
RiCodeLine,
RiTerminalLine,
RiBracketsLine,
RiBracesLine,
RiBugLine,
RiGitRepositoryLine,
RiCheckDoubleLine,
RiFilterLine,
RiEyeLine,
RiEyeOffLine,
RiDownloadLine,
RiUploadLine,
RiStarLine,
RiSidebarFoldLine,
RiSidebarUnfoldLine,
RiZoomInLine,
RiZoomOutLine,
RiAlertLine,
RiAccountCircleLine,
RiGroupLine,
RiUserLine,
RiGithubLine,
RiDiscordLine,
RiYoutubeLine,
RiCloseLine,
RiSearchLine,
} from '@remixicon/react';
/** @public */
export const icons: IconMap = {
'account-circle': RiAccountCircleLine,
alert: RiAlertLine,
'arrow-down': RiArrowDownLine,
'arrow-down-circle': RiArrowDownCircleLine,
'caret-down': RiArrowDropDownLine,
'caret-left': RiArrowDropLeftLine,
'caret-right': RiArrowDropRightLine,
'caret-up': RiArrowDropUpLine,
'arrow-left': RiArrowLeftLine,
'arrow-left-circle': RiArrowLeftCircleLine,
'arrow-left-down': RiArrowLeftDownLine,
'arrow-left-up': RiArrowLeftUpLine,
'arrow-right': RiArrowRightLine,
'arrow-right-circle': RiArrowRightCircleLine,
'arrow-right-down': RiArrowRightDownLine,
'arrow-right-up': RiArrowRightUpLine,
'arrow-up': RiArrowUpLine,
'arrow-up-circle': RiArrowUpCircleLine,
braces: RiBracesLine,
brackets: RiBracketsLine,
bug: RiBugLine,
check: RiCheckLine,
'check-double': RiCheckDoubleLine,
'chevron-down': RiArrowDownSLine,
'chevron-left': RiArrowLeftSLine,
'chevron-right': RiArrowRightSLine,
'chevron-up': RiArrowUpSLine,
close: RiCloseLine,
cloud: RiCloudLine,
code: RiCodeLine,
discord: RiDiscordLine,
download: RiDownloadLine,
'external-link': RiExternalLinkLine,
eye: RiEyeLine,
'eye-off': RiEyeOffLine,
filter: RiFilterLine,
flower: RiFlowerLine,
github: RiGithubLine,
'git-repository': RiGitRepositoryLine,
group: RiGroupLine,
heart: RiHeartLine,
moon: RiMoonLine,
plus: RiAddLine,
search: RiSearchLine,
'sidebar-fold': RiSidebarFoldLine,
'sidebar-unfold': RiSidebarUnfoldLine,
sparkling: RiSparklingLine,
star: RiStarLine,
sun: RiSunLine,
terminal: RiTerminalLine,
trash: RiDeleteBin6Line,
upload: RiUploadLine,
user: RiUserLine,
youtube: RiYoutubeLine,
'zoom-in': RiZoomInLine,
'zoom-out': RiZoomOutLine,
} as const;
-28
View File
@@ -1,28 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed 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.
*/
// Icons
export * from './icons';
// Icon component
export * from './Icon';
// Context and Provider
export * from './context';
export * from './provider';
// Types
export type * from './types';
@@ -1,33 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed 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 { icons } from './icons';
import { IconContext } from './context';
import type { IconProviderProps } from './types';
/** @public */
export const IconProvider = (props: IconProviderProps) => {
const { children, overrides } = props;
// Merge provided overrides with default icons
const combinedIcons = { ...icons, ...overrides };
return (
<IconContext.Provider value={{ icons: combinedIcons }}>
{children}
</IconContext.Provider>
);
};
-99
View File
@@ -1,99 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed 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 { ReactNode } from 'react';
import type { RemixiconComponentType } from '@remixicon/react';
/** @public */
export type IconNames =
| 'account-circle'
| 'alert'
| 'arrow-down'
| 'arrow-down-circle'
| 'caret-down'
| 'caret-left'
| 'caret-right'
| 'caret-up'
| 'arrow-left'
| 'arrow-left-circle'
| 'arrow-left-down'
| 'arrow-left-up'
| 'arrow-right'
| 'arrow-right-circle'
| 'arrow-right-down'
| 'arrow-right-up'
| 'arrow-up'
| 'arrow-up-circle'
| 'braces'
| 'brackets'
| 'bug'
| 'check'
| 'check-double'
| 'chevron-down'
| 'chevron-left'
| 'chevron-right'
| 'chevron-up'
| 'close'
| 'cloud'
| 'code'
| 'discord'
| 'download'
| 'external-link'
| 'eye'
| 'eye-off'
| 'filter'
| 'flower'
| 'github'
| 'git-repository'
| 'group'
| 'heart'
| 'moon'
| 'plus'
| 'search'
| 'sidebar-fold'
| 'sidebar-unfold'
| 'sparkling'
| 'star'
| 'sun'
| 'terminal'
| 'trash'
| 'upload'
| 'user'
| 'youtube'
| 'zoom-in'
| 'zoom-out';
/** @public */
export type IconMap = Partial<Record<IconNames, RemixiconComponentType>>;
/** @public */
export type IconProps = {
name: IconNames;
size?: number;
className?: string;
style?: React.CSSProperties;
};
/** @public */
export interface IconContextProps {
icons: IconMap;
}
/** @public */
export interface IconProviderProps {
children?: ReactNode;
overrides?: Partial<Record<IconNames, RemixiconComponentType>>;
}
-4
View File
@@ -20,9 +20,6 @@
* @packageDocumentation
*/
// Providers
export * from './components/Icon/context';
// Layout components
export * from './components/Box';
export * from './components/Grid';
@@ -38,7 +35,6 @@ export * from './components/Dialog';
export * from './components/FieldLabel';
export * from './components/Header';
export * from './components/HeaderPage';
export * from './components/Icon';
export * from './components/ButtonIcon';
export * from './components/ButtonLink';
export * from './components/Checkbox';