Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/components/Icon/Icon.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Canvas, ArgTypes, Meta, Subtitle, Title } from '@storybook/blocks';
import { linkTo } from '@storybook/addon-links';
import * as IconStories from './Icon.stories';

<Meta of={IconStories} />

<Title>Icon</Title>
<Subtitle>Collection of SVG icons exported as React components</Subtitle>

## Usage

```jsx
import { Search, Settings, Check } from "@openai/apps-sdk-ui/components/Icon";
```

<Canvas of={IconStories.Base} additionalActions={[
{ title: 'View story', onClick: linkTo(IconStories.title, 'Base')}
]} />

## Reference

<ArgTypes of={IconStories.Base} exclude={['ref']} />

## Examples

### Sizing

Icons accept `width` and `height` props to control their size. By default, icons use `1em` which scales with the font size.

<Canvas of={IconStories.Sizing} />

### Colors

Icons use `currentColor` for their fill, so they inherit the text color from their parent. You can style them using Tailwind color classes.

<Canvas of={IconStories.Colors} />

### Common icons

A selection of commonly used icons from the collection.

<Canvas of={IconStories.CommonIcons} />

### With text

Icons work well alongside text content, inheriting the text color and scaling appropriately.

<Canvas of={IconStories.WithText} />

## Icon gallery

For a complete list of all available icons, see the [Icon Gallery](/foundations-icons).

80 changes: 80 additions & 0 deletions src/components/Icon/Icon.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { type Meta } from "@storybook/react"
import { type ComponentProps } from "react"
import { ArrowRight, Check, Copy, Robot, Search, Settings } from "./"

type IconProps = ComponentProps<typeof Search>

const meta = {
title: "Components/Icon",
component: Search,
args: {
width: 24,
height: 24,
},
argTypes: {
className: { control: false },
},
} satisfies Meta<typeof Search>

export default meta

export const Base = (args: IconProps) => <Search {...args} />

export const Sizing = () => (
<div className="flex items-center gap-6">
<div className="flex flex-col items-center gap-2">
<Search width={16} height={16} />
<span className="text-sm text-tertiary">16px</span>
</div>
<div className="flex flex-col items-center gap-2">
<Search width={24} height={24} />
<span className="text-sm text-tertiary">24px</span>
</div>
<div className="flex flex-col items-center gap-2">
<Search width={32} height={32} />
<span className="text-sm text-tertiary">32px</span>
</div>
<div className="flex flex-col items-center gap-2">
<Search width={48} height={48} />
<span className="text-sm text-tertiary">48px</span>
</div>
</div>
)

export const Colors = () => (
<div className="flex items-center gap-6">
<Search className="text-primary" width={24} height={24} />
<Search className="text-success" width={24} height={24} />
<Search className="text-warning" width={24} height={24} />
<Search className="text-danger" width={24} height={24} />
<Search className="text-tertiary" width={24} height={24} />
</div>
)

export const CommonIcons = () => (
<div className="flex items-center gap-4">
<Search width={20} height={20} />
<Settings width={20} height={20} />
<Check width={20} height={20} />
<Copy width={20} height={20} />
<ArrowRight width={20} height={20} />
<Robot width={20} height={20} />
</div>
)

export const WithText = () => (
<div className="flex flex-col gap-4">
<div className="flex items-center gap-2">
<Search width={18} height={18} />
<span>Search</span>
</div>
<div className="flex items-center gap-2">
<Settings width={18} height={18} />
<span>Settings</span>
</div>
<div className="flex items-center gap-2">
<Check width={18} height={18} />
<span>Complete</span>
</div>
</div>
)
48 changes: 48 additions & 0 deletions src/components/Image/Image.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Canvas, ArgTypes, Meta, Subtitle, Title } from '@storybook/blocks';
import { linkTo } from '@storybook/addon-links';
import * as ImageStories from './Image.stories';

<Meta of={ImageStories} />

<Title>Image</Title>
<Subtitle>Load remote images with fade-in animation and error handling</Subtitle>

## Usage
```jsx
import { Image } from "@openai/apps-sdk-ui/components/Image";
```

<Canvas of={ImageStories.Base} additionalActions={[
{ title: 'View story', onClick: linkTo(ImageStories.title, 'Base')}
]} />

## Reference

<ArgTypes of={ImageStories.Base} exclude={['ref']} />

## Examples

### Loading

Images fade in smoothly once loaded. The component tracks loaded images to provide instant display on subsequent renders.

<Canvas of={ImageStories.Loading} />

### Error

By default, if an image fails to load, the component returns `null` and nothing is rendered.

<Canvas of={ImageStories.Error} />

### Force render on error

Use `forceRenderAfterLoadFail` to display the broken image icon when an image fails to load, rather than hiding it completely.

<Canvas of={ImageStories.ForceRenderOnError} />

### Draggable

Control whether the image can be dragged by setting the `draggable` prop.

<Canvas of={ImageStories.Draggable} />

105 changes: 105 additions & 0 deletions src/components/Image/Image.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { type Meta } from "@storybook/react"
import { type ComponentProps } from "react"
import { Image } from "./"

type ImageProps = ComponentProps<"img"> & {
forceRenderAfterLoadFail?: boolean
}

const meta = {
title: "Components/Image",
component: Image,
args: {
src: "https://picsum.photos/400/300",
draggable: false,
forceRenderAfterLoadFail: false,
},
argTypes: {
src: { control: "text" },
className: { control: false },
onLoad: { control: false },
onError: { control: false },
},
} satisfies Meta<typeof Image>

export default meta

export const Base = (args: ImageProps) => <Image {...args} />

export const Loading = () => (
<div className="w-[400px]">
<Image src="https://picsum.photos/400/300?random=1" alt="Example image" />
</div>
)

Loading.parameters = {
docs: {
source: {
code: `<Image src="https://picsum.photos/400/300" alt="Example image" />`,
},
},
}

export const Error = () => (
<div className="w-[400px]">
<Image
src="https://invalid-url-that-does-not-exist.com/image.jpg"
alt="This will fail to load"
/>
</div>
)

Error.parameters = {
docs: {
source: {
code: `<Image src="https://invalid-url-that-does-not-exist.com/image.jpg" alt="This will fail to load" />`,
},
description: {
story: "When an image fails to load, the component returns `null` and nothing is rendered.",
},
},
}

export const ForceRenderOnError = () => (
<div className="w-[400px]">
<Image
src="https://invalid-url-that-does-not-exist.com/image.jpg"
alt="This will fail to load"
forceRenderAfterLoadFail
/>
</div>
)

ForceRenderOnError.parameters = {
docs: {
source: {
code: `<Image
src="https://invalid-url-that-does-not-exist.com/image.jpg"
alt="This will fail to load"
forceRenderAfterLoadFail
/>`,
},
description: {
story:
"Use `forceRenderAfterLoadFail` to display the broken image icon when an image fails to load.",
},
},
}

export const Draggable = (args: ImageProps) => (
<div className="w-[400px]">
<Image {...args} draggable alt="Draggable image" />
</div>
)

Draggable.args = {
draggable: true,
}

Draggable.parameters = {
docs: {
source: {
code: `<Image src="https://picsum.photos/400/300" draggable alt="Draggable image" />`,
},
},
}