Skip to content

Commit

Permalink
Start building out Storybook for the Penumbra UI library (#1452)
Browse files Browse the repository at this point in the history
* Git-ignore the built Storybook library

* Deprecate old component stories

* Set up theme and manager options

* Change how deprecation is done

* Tweak settings

* Set the brand title

* Create an Icon component and stories

* Set default color to rust

* More settings tweaks

* Change default color to make it more visible

* Rename and simplify

* Only add deprecated CSS to deprecated components

* Add a Normalize component

* ci: firebase config for storybook ui

Adds a deploy workflow for the new storybook ui static site, deploying
to:

  * preview.ui.penumbra.zone, on merge to main
  * ui.penumbra.zone, on tag push

Additionally, PRs will have an ephemeral URL for preview posted to them
via a bot as a comment, so unmerged work can be viewed in an online
context.

* Swap root order

* Change default icon color

* Start building out theme

* Remove Normalize component for now

* Fix formatting

* Ignore storybook-static from Prettier

* Move ignore to root

---------

Co-authored-by: Conor Schaefer <[email protected]>
  • Loading branch information
jessepinho and conorsch authored Jul 17, 2024
1 parent 1a57749 commit 877e0b8
Show file tree
Hide file tree
Showing 19 changed files with 370 additions and 18 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/deploy-ui-preview-main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Deploys the static website for the UI storybook to "preview" environment,
# on every merge into main branch.
name: Deploy UI to preview
on:
workflow_dispatch:
push:
branches:
- main
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- name: Install dependencies
run: pnpm install
working-directory: packages/ui

- name: Build static site
run: pnpm build-storybook
working-directory: packages/ui

- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_PENUMBRA_UI }}
channelId: live
target: preview
entryPoint: packages/ui
projectId: penumbra-ui
34 changes: 34 additions & 0 deletions .github/workflows/deploy-ui-preview-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Deploys the static website for the UI storybook to a temporary environment,
# with an ephemeral URL posted to the PR for sharing/review.
name: Deploy UI to temporary URL
on:
workflow_dispatch:
pull_request:
permissions:
checks: write
contents: read
pull-requests: write
jobs:
build_and_preview:
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- name: Install dependencies
run: pnpm install
working-directory: packages/ui

- name: Build static site
run: pnpm build-storybook
working-directory: packages/ui

- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_PENUMBRA_UI }}
target: preview
entryPoint: packages/ui
projectId: penumbra-ui
34 changes: 34 additions & 0 deletions .github/workflows/deploy-ui-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Deploys the static website for the UI storybook to final prod website,
# on every tag push into main branch.
name: Deploy UI to stable channel
on:
# Support ad-hoc runs
workflow_dispatch:
# Run automatically on tag push
push:
tags:
- '**[0-9]+.[0-9]+.[0-9]+*'
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- name: Install dependencies
run: pnpm install
working-directory: packages/ui

- name: Build static site
run: pnpm build-storybook
working-directory: packages/ui

- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_PENUMBRA_UI }}
channelId: live
target: stable
entryPoint: packages/ui
projectId: penumbra-ui
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,7 @@ packages/*/penumbra-zone-*.tgz
packages/*/repo-*-*.tgz
packages/*/package

tsconfig.tsbuildinfo
tsconfig.tsbuildinfo

# Storybook builds
storybook-static
19 changes: 19 additions & 0 deletions packages/ui/.firebaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"projects": {
"default": "penumbra-ui"
},
"targets": {
"penumbra-ui": {
"hosting": {
"preview": [
"penumbra-ui-preview"
],
"stable": [
"penumbra-ui"
]
}
}
},
"etags": {},
"dataconnectEmulatorConfig": {}
}
13 changes: 12 additions & 1 deletion packages/ui/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,18 @@ function getAbsolutePath(value) {

/** @type { import('@storybook/react-vite').StorybookConfig } */
const config = {
stories: ['../stories/**/*.mdx', '../components/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
stories: [
{
directory: '../src',
files: '**/@(*.stories.@(js|jsx|mjs|ts|tsx)|*.mdx)',
titlePrefix: 'UI library',
},
{
directory: '../components',
files: '**/@(*.stories.@(js|jsx|mjs|ts|tsx)|*.mdx)',
titlePrefix: 'Deprecated',
},
],
addons: [
getAbsolutePath('@storybook/addon-links'),
getAbsolutePath('@storybook/addon-essentials'),
Expand Down
10 changes: 10 additions & 0 deletions packages/ui/.storybook/manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { addons } from '@storybook/manager-api';
import penumbraTheme from './penumbraTheme';

addons.setConfig({
showToolbar: true,
theme: penumbraTheme,
sidebar: {
collapsedRoots: ['Deprecated'],
},
});
15 changes: 15 additions & 0 deletions packages/ui/.storybook/penumbraTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { create } from '@storybook/theming/create';
import logo from './public/logo.svg';

const penumbraTheme = create({
base: 'dark',
brandImage: logo,
brandTitle: 'Penumbra UI library',
colorPrimary: '#8d5728',
colorSecondary: '#629994',
fontBase: 'Poppins',
fontCode: '"Iosevka Term",monospace',
textMutedColor: '#e3e3e3',
});

export default penumbraTheme;
16 changes: 0 additions & 16 deletions packages/ui/.storybook/preview.js

This file was deleted.

43 changes: 43 additions & 0 deletions packages/ui/.storybook/preview.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import globalsCssUrl from '../styles/globals.css?url';
import penumbraTheme from './penumbraTheme';
import { ThemeProvider } from 'styled-components';
import { theme } from '../src/utils/theme';

/** @type { import('@storybook/react').Preview } */
const preview = {
decorators: [
(Story, { title }) => {
const isDeprecatedComponent = title.startsWith('Deprecated/');

if (isDeprecatedComponent) {
return (
<>
<link rel='stylesheet' type='text/css' href={globalsCssUrl} />
<Story />
</>
);
}

return (
<ThemeProvider theme={theme}>
<Story />
</ThemeProvider>
);
},
],
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
docs: {
theme: penumbraTheme,
},
},
};

export default preview;
3 changes: 3 additions & 0 deletions packages/ui/.storybook/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions packages/ui/components/readme.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Meta } from '@storybook/blocks';
import * as ToasterStories from './ui/toaster/toaster.stories';

<Meta title='README' />

# Deprecated Penumbra UI components

The `components/ui` directory contains deprecated Penumbra UI components. These will eventually all be replaced by components in the `src` directory; but until then, they're still here for reference and use when needed.

Note that there are not Storybook stories for all deprecated components. To find deprecated components not listed here, please see the `components/ui` directory of the `@penumbra-zone/ui` package.
14 changes: 14 additions & 0 deletions packages/ui/firebase.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"hosting": [
{
"target": "preview",
"public": "storybook-static",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
},
{
"target": "stable",
"public": "storybook-static",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
}
]
}
4 changes: 4 additions & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"license": "(MIT OR Apache-2.0)",
"type": "module",
"scripts": {
"build-storybook": "storybook build",
"lint": "eslint components lib",
"lint:fix": "eslint components lib --fix",
"lint:strict": "tsc --noEmit && eslint components lib --max-warnings 0",
Expand Down Expand Up @@ -67,6 +68,7 @@
"react-loader-spinner": "^6.1.6",
"react-router-dom": "^6.23.1",
"sonner": "1.4.3",
"styled-components": "^6.1.11",
"tailwind-merge": "^2.3.0",
"tinycolor2": "^1.6.0"
},
Expand All @@ -78,9 +80,11 @@
"@storybook/addon-links": "^8.1.1",
"@storybook/addon-postcss": "^2.0.0",
"@storybook/blocks": "^8.1.1",
"@storybook/manager-api": "^8.1.11",
"@storybook/preview-api": "^8.1.1",
"@storybook/react": "^8.1.1",
"@storybook/react-vite": "8.1.1",
"@storybook/theming": "^8.1.11",
"@types/humanize-duration": "^3.27.4",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
Expand Down
25 changes: 25 additions & 0 deletions packages/ui/src/Icon/index.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ArrowRightLeft, Send, Wallet } from 'lucide-react';
import { Meta, StoryObj } from '@storybook/react';

import { Icon } from '.';

const meta: Meta<typeof Icon> = {
component: Icon,
tags: ['autodocs'],
argTypes: {
IconComponent: {
options: ['ArrowRightLeft', 'Send', 'Wallet'],
mapping: { ArrowRightLeft, Send, Wallet },
},
},
};

export default meta;

export const Basic: StoryObj<typeof Icon> = {
args: {
IconComponent: ArrowRightLeft,
size: 'sm',
color: 'white',
},
};
52 changes: 52 additions & 0 deletions packages/ui/src/Icon/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { LucideIcon } from 'lucide-react';
import { ComponentProps } from 'react';

export type IconSize = 'sm' | 'md' | 'lg';

export interface IconProps {
/**
* The icon import from `lucide-react` to render.
*
* ```tsx
* import { ChevronRight } from 'lucide-react';
* <Icon IconComponent={ChevronRight} />
* ```
*/
IconComponent: LucideIcon;
/**
* - `sm`: 16px square
* - `md`: 24px square
* - `lg`: 48px square
*/
size: IconSize;
/**
* The CSS color to render the icon with. If left undefined, will default to
* the parent's text color (`currentColor` in SVG terms).
*/
color?: string;
}

const PROPS_BY_SIZE: Record<IconSize, ComponentProps<LucideIcon>> = {
sm: {
size: 16,
strokeWidth: 1,
},
md: {
size: 24,
strokeWidth: 1.5,
},
lg: {
size: 48,
strokeWidth: 2,
},
};

/**
* Renders the Lucide icon passed in via the `IconComponent` prop. Use this
* component rather than rendering Lucide icon components directly, since this
* component standardizes the stroke width and sizes throughout the Penumbra
* ecosystem.
*/
export const Icon = ({ IconComponent, size = 'sm', color }: IconProps) => (
<IconComponent absoluteStrokeWidth {...PROPS_BY_SIZE[size]} color={color} />
);
Loading

0 comments on commit 877e0b8

Please sign in to comment.