Add first version of our Icon component

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2024-11-18 18:14:56 +00:00
parent 21f0884a4d
commit 8e03e86b1f
11 changed files with 218 additions and 13 deletions
+2 -1
View File
@@ -40,7 +40,8 @@
"@vanilla-extract/css": "^1.16.0",
"@vanilla-extract/dynamic": "^2.1.2",
"@vanilla-extract/recipes": "^0.5.5",
"@vanilla-extract/sprinkles": "^1.6.3"
"@vanilla-extract/sprinkles": "^1.6.3",
"lucide-react": "^0.460.0"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
@@ -0,0 +1,40 @@
import React, { createContext, useContext, ReactNode } from 'react';
import { MoveUp, MoveDown } from 'lucide-react';
import { CustomIcon } from './custom-icon';
// List of icons available that can also be overridden.
export type IconNames = 'MoveDown' | 'MoveUp' | 'CustomIcon';
type IconMap = Partial<Record<IconNames, React.ComponentType>>;
interface IconContextProps {
icons: IconMap;
}
// Create a default icon map with only the necessary icons
const defaultIcons: IconMap = {
MoveUp,
MoveDown,
CustomIcon,
};
const IconContext = createContext<IconContextProps>({ icons: defaultIcons });
export const IconProvider = ({
children,
overrides,
}: {
children: ReactNode;
overrides: IconMap;
}) => {
// Merge provided overrides with default icons
const combinedIcons = { ...defaultIcons, ...overrides };
return (
<IconContext.Provider value={{ icons: combinedIcons }}>
{children}
</IconContext.Provider>
);
};
export const useIcons = () => useContext(IconContext);
@@ -0,0 +1,7 @@
import React from 'react';
export const CustomIcon = () => (
<svg viewBox="0 0 24 24" fill="red" width={24} height={24}>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg>
);
@@ -0,0 +1,66 @@
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { Icon } from './icon';
import { IconProvider } from './context';
import * as LucideIcons from 'lucide-react';
const meta = {
title: 'Components/Icon',
component: Icon,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
name: {
control: 'select',
options: Object.keys(LucideIcons),
},
},
args: {
name: 'MoveDown',
},
} satisfies Meta<typeof Icon>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary: Story = {
args: {
name: 'MoveDown',
},
};
export const CustomIcon: Story = {
args: {
name: 'CustomIcon',
},
};
export const WithCustomIcon: Story = {
args: {
name: 'MoveDown',
},
decorators: [
Story => (
<IconProvider overrides={{ MoveDown: () => <div>Custom Icon</div> }}>
<Story />
</IconProvider>
),
],
};
export const WithCustomIconOverride: Story = {
args: {
name: 'CustomIcon',
},
decorators: [
Story => (
<IconProvider
overrides={{ CustomIcon: () => <div>Custom Super Icon</div> }}
>
<Story />
</IconProvider>
),
],
};
@@ -0,0 +1,15 @@
import React from 'react';
import { useIcons, IconNames } from './context';
export const Icon = ({ name }: { name: IconNames }) => {
const { icons } = useIcons();
const LucideIcon = icons[name];
if (!LucideIcon) {
console.error(`Icon "${name}" not found.`);
return <svg />; // Return default icon perhaps?
}
return <LucideIcon />;
};
@@ -0,0 +1,2 @@
export * from './icon';
export * from './context';
+69 -6
View File
@@ -42,8 +42,7 @@ import * as Table from '../storybook/table';
--canon-text-primary: #f0f0f0;
--canon-text-secondary: #666;
--canon-font-sans: 'Geist', serif;
--canon-font-monospace: 'Monospace', monospace;
--canon-font-emoji: 'Apple Color Emoji', 'Segoe UI Emoji';
--canon-font-mono: 'Monospace', monospace;
/* ... other values */
}
@@ -58,8 +57,7 @@ import * as Table from '../storybook/table';
--canon-text-primary: #fff;
--canon-text-secondary: #666;
--canon-font-sans: 'Geist', serif;
--canon-font-monospace: 'Monospace', monospace;
--canon-font-emoji: 'Apple Color Emoji', 'Segoe UI Emoji';
--canon-font-mono: 'Monospace', monospace;
/* ... other values */
}
`}
@@ -68,6 +66,11 @@ import * as Table from '../storybook/table';
/>
<Title type="h2">Colors</Title>
<Text>
We provide a set of generic colours tokens that we use across Canon. By
changing these colours you can easily change the look and feel of your
application to match your brand.
</Text>
<Table.Root>
<Table.Header>
<Table.HeaderRow>
@@ -88,13 +91,73 @@ import * as Table from '../storybook/table';
</Table.Cell>
<Table.Cell>The background color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-surface-1</Chip>
</Table.Cell>
<Table.Cell>The first surface color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-surface-2</Chip>
</Table.Cell>
<Table.Cell>The second surface color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-outline</Chip>
</Table.Cell>
<Table.Cell>The outline color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-outline-focus</Chip>
</Table.Cell>
<Table.Cell>The outline focus color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-text-primary</Chip>
</Table.Cell>
<Table.Cell>The primary text color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-text-secondary</Chip>
</Table.Cell>
<Table.Cell>The secondary text color for the theme.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
<Title type="h2">Typography</Title>
<Text>
Our theming is system is still a work in progress. We will be adding more
features and documentation over time.
We have two fonts that we use across Canon. The first one is the sans-serif
font that we use for the body of the application. The second one is the
monospace font that we use for code blocks and tables.
</Text>
<Table.Root>
<Table.Header>
<Table.HeaderRow>
<Table.HeaderCell>Prop</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
<Chip>--canon-font-sans</Chip>
</Table.Cell>
<Table.Cell>The sans-serif font for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-font-mono</Chip>
</Table.Cell>
<Table.Cell>The monospace font for the theme.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
</Unstyled>
+3 -2
View File
@@ -15,5 +15,6 @@
*/
// Components
export { Button } from './components/button/button';
export { Box } from './components/box/box';
export * from './components/button/button';
export * from './components/box/box';
export * from './components/icon';
@@ -17,7 +17,7 @@ import { globalStyle, style } from '@vanilla-extract/css';
export const textStyles = style({
fontFamily: '"Geist", sans-serif',
fontSize: '18px',
fontSize: '16px',
lineHeight: '28px',
margin: '0',
fontWeight: '300',
@@ -26,7 +26,7 @@ export const textStyles = style({
globalStyle(`${textStyles} p`, {
fontFamily: '"Geist", sans-serif',
fontSize: '18px',
fontSize: '16px',
lineHeight: '28px',
margin: '0',
fontWeight: '300',
@@ -25,8 +25,8 @@ export const titleStyles = recipe({
variants: {
type: {
h1: {
fontSize: '40px',
lineHeight: '48px',
fontSize: '36px',
lineHeight: '44px',
marginBottom: '32px',
marginTop: '0px',
},
+10
View File
@@ -4131,6 +4131,7 @@ __metadata:
eslint-plugin-react-refresh: ^0.4.13
eslint-plugin-storybook: ^0.11.0
globals: ^15.11.0
lucide-react: ^0.460.0
mini-css-extract-plugin: ^2.9.2
storybook: ^8.4.4
typescript: ~5.6.2
@@ -35994,6 +35995,15 @@ __metadata:
languageName: node
linkType: hard
"lucide-react@npm:^0.460.0":
version: 0.460.0
resolution: "lucide-react@npm:0.460.0"
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
checksum: 6106dc16dd7ce7928d6136e81c9e84e87e1b6b0910a0c78777a387c795c0512755e8bf4c602ab8f09518919999a494ed86ba7190863e8e7aec6c82665147ead3
languageName: node
linkType: hard
"lunr@npm:^2.3.9":
version: 2.3.9
resolution: "lunr@npm:2.3.9"