Add first version of our Icon component
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -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';
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user