Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
41 changes: 22 additions & 19 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,42 +57,45 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: pnpm install
- run: pnpm build
- run: pnpm test

cypress:
storybook-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5
with:
node-version: lts/*
- uses: cypress-io/github-action@v6
- run: pnpm install
- name: Store Playwright's version
id: playwright-version
run: |
PLAYWRIGHT_VERSION=$(pnpm --filter sanity-ui-storybook exec playwright --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
echo "Playwright's version: $PLAYWRIGHT_VERSION"
echo "version=${PLAYWRIGHT_VERSION}" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers for Playwright's version
id: cache-playwright-browsers
uses: actions/cache@v4
with:
build: pnpm workshop:build
start: pnpm workshop:start
wait-on: http://localhost:1337
env:
NODE_OPTIONS: "--no-experimental-require-module --no-experimental-detect-module"
path: ~/.cache/ms-playwright
key: ${{ steps.playwright-version.outputs.version }}-playwright-browsers-chromium
- name: Install Playwright browsers
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
run: pnpm --filter sanity-ui-storybook exec playwright install --with-deps chromium
- run: pnpm test:browser
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
if-no-files-found: ignore
- uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-videos
path: cypress/videos
name: vitest-failure-screenshots
path: apps/storybook/**/__screenshots__/
if-no-files-found: ignore

release:
permissions:
id-token: write # to enable use of OIDC for npm provenance
name: "Semantic release"
needs: [lint, test, cypress]
needs: [lint, test, storybook-test]
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v2
Expand All @@ -114,8 +117,8 @@ jobs:
with:
node-version: lts/*
- run: pnpm install
# Branches that will release new versions are defined in .releaserc.json
- run: pnpm exec semantic-release
# Branches that will release new versions are defined in packages/ui/package.json
- run: pnpm --filter @sanity/ui exec semantic-release
env:
NPM_CONFIG_PROVENANCE: true
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pkg-pr-new.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:

- id: pre-flight
run: |
VERSION="$(jq -r .version package.json)-pkg.pr.new@$(git rev-parse --short HEAD)"
VERSION="$(jq -r .version packages/ui/package.json)-pkg.pr.new@$(git rev-parse --short HEAD)"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"

- run: pnpm build
Expand All @@ -42,4 +42,4 @@ jobs:

- run: |
echo "Publishing to pkg.pr.new"
pnpm pnpx pkg-pr-new publish
pnpm pnpx pkg-pr-new publish ./packages/ui
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

.DS_Store
.env.local
.workshop
dist
etc
node_modules
storybook-static
.eslintcache

# Vitest browser mode failure screenshots and attachments
__screenshots__
.vitest-attachments
2 changes: 0 additions & 2 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
strict-peer-dependencies = false
link-workspace-packages = deep
# https://github.com/cypress-io/github-action/blob/43c624e40c65c7c3a255a901d0a0a0a2040b7140/examples/basic-pnpm/.npmrc#L2C1-L2C25
side-effects-cache=false
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
.workshop
dist
pnpm-lock.yaml
CHANGELOG.md
31 changes: 31 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# AGENTS.md

## Cursor Cloud specific instructions

This is the `v2` maintenance branch of the `@sanity/ui` React component
library, structured as a pnpm monorepo: the published package lives in
`packages/ui`, the Figma plugin in `packages/figma`, and the Storybook app in
`apps/storybook` (`pnpm-workspace.yaml`). The root `package.json` is a private
workspace root whose scripts orchestrate via pnpm filters. Package manager is
pnpm (`packageManager` pin in `package.json`).

Standard scripts live in the root `package.json` (`lint`, `ts:check`, `test`,
`build`, `dev`) and forward to the workspace packages. Notes that are not
obvious from the scripts:

- `pnpm test` runs the unit tests with vitest (`packages/ui/vitest.config.ts`).
`@sanity/ui` is aliased to the `packages/ui/exports/` source, so unit tests
run directly against source and do not require a `pnpm build` first.
- `pnpm dev` starts Storybook (`apps/storybook`) on http://localhost:6006. It
resolves `@sanity/ui` to the `packages/ui` source, so it hot-reloads source
edits directly (no rebuild needed).
- `pnpm test:browser` runs the Storybook tests (`apps/storybook`): vitest
renders every story in headless Chromium via `@storybook/addon-vitest` and
executes story `play` interactions, plus the browser tests in
`apps/storybook/tests/` (see `apps/storybook/vitest.config.ts`). The
Playwright-provided browser must be installed once via
`pnpm --filter sanity-ui-storybook exec playwright install chromium`.
Stories opt out of being tested with the `!test` tag.
- Releases are automated with semantic-release: commit messages must follow
Conventional Commits (enforced via commitlint), and pushes to the `v2`
branch publish `@sanity/ui` to npm from `packages/ui`.
45 changes: 45 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,50 @@
# Contributing guidelines

This repository is a pnpm monorepo. The published `@sanity/ui` package lives in
[`packages/ui`](packages/ui), the Figma plugin lives in
[`packages/figma`](packages/figma), and the Storybook app lives in
[`apps/storybook`](apps/storybook).

## Getting started

```sh
pnpm install
pnpm build
pnpm test
```

Run `pnpm dev` to start Storybook (http://localhost:6006). Storybook resolves
`@sanity/ui` from the package source, so edits to `packages/ui/src` hot-reload
without a rebuild.

## Testing

Unit tests are written with [vitest](https://vitest.dev) and live next to the
source in `packages/ui/src`. Run them with `pnpm test` (or
`pnpm test:watch` in `packages/ui` for watch mode). They run against the
package source, so no build is required.

Browser tests live in the Storybook app (`apps/storybook`) and use
[Storybook's Vitest addon](https://storybook.js.org/docs/writing-tests/integrations/vitest-addon):
every story is rendered as a smoke test in headless Chromium, and interaction
tests are written as story [`play` functions](https://storybook.js.org/docs/writing-stories/play-function).
Tests that need direct control over the browser (e.g. resizing the viewport)
live in `apps/storybook/tests/`.

Install the Playwright-provided browser once with
`pnpm --filter sanity-ui-storybook exec playwright install chromium`, then run
`pnpm test:browser`. While developing, `pnpm dev` exposes the same tests
interactively through the testing panel in the Storybook UI.

## Releasing

Releases are automated with
[semantic-release](https://github.com/semantic-release/semantic-release):
commits that land on the `v2` branch are analyzed (commit messages must follow
[Conventional Commits](https://www.conventionalcommits.org)), and new versions
of `@sanity/ui` are published to npm from `packages/ui` by the
[CI & Release workflow](https://github.com/sanity-io/ui/actions/workflows/main.yml).

## Working with prereleases

### Going from `alpha => beta`
Expand Down
33 changes: 0 additions & 33 deletions README.md

This file was deleted.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import {Card, studioTheme, ThemeProvider} from '@sanity/ui'
import {DecoratorHelpers} from '@storybook/addon-themes'
import {StoryFn} from '@storybook/react'
import {StoryFn} from '@storybook/react-vite'
import {createGlobalStyle} from 'styled-components'

const {initializeThemeState, pluckThemeFromContext, useThemeParameters} = DecoratorHelpers
const {initializeThemeState, pluckThemeFromContext} = DecoratorHelpers

export const GlobalStyle = createGlobalStyle`
body,
Expand All @@ -18,22 +18,25 @@ export const GlobalStyle = createGlobalStyle`
* Story decorator which wraps all stories in a Sanity <ThemeProvider> and passes the current theme
* value defined in Story.
*
* Stories are also wrapped in a <Card> for layout.
* Stories are also wrapped in a <Card> for layout. Set the `padding` parameter to change (or
* remove, with `padding: 0`) the default padding – e.g. for stories that depend on exact
* viewport dimensions.
*/

export const withSanityTheme = ({themes, defaultTheme}) => {
initializeThemeState(Object.keys(themes), defaultTheme)

return (Story: StoryFn, context) => {
const selectedTheme = pluckThemeFromContext(context)
const {themeOverride} = useThemeParameters()
const {themeOverride} = context.parameters.themes ?? {}
const {padding = 4} = context.parameters

const selected = themeOverride || selectedTheme || defaultTheme

return (
<ThemeProvider scheme={selected} theme={studioTheme}>
<GlobalStyle />
<Card padding={4}>
<Card padding={padding}>
<Story />
</Card>
</ThemeProvider>
Expand Down
24 changes: 14 additions & 10 deletions .storybook/main.ts → apps/storybook/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
import path from 'node:path'

import type {StorybookConfig} from '@storybook/react-vite'
import viteReact from '@vitejs/plugin-react'
import {mergeConfig} from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'

// Resolve `@sanity/ui` to the package source so that edits to `packages/ui/src`
// hot-reload without a rebuild.
const UI_EXPORTS_PATH = path.resolve(import.meta.dirname, '../../../packages/ui/exports')

const config: StorybookConfig = {
stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
stories: ['../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-a11y',
'@storybook/addon-docs',
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-storysource',
'@storybook/addon-themes',
'@storybook/addon-mdx-gfm',
'@storybook/addon-vitest',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
viteFinal(config) {
return mergeConfig(config, {
plugins: [
viteReact({
babel: {plugins: [['babel-plugin-react-compiler', {target: '19'}]]},
}),
tsconfigPaths(),
],
resolve: {
alias: {
'@sanity/ui': UI_EXPORTS_PATH,
},
},
})
},
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {addons} from '@storybook/manager-api'
import {themes} from '@storybook/theming'
import {addons} from 'storybook/manager-api'
import {themes} from 'storybook/theming'

addons.setConfig({
theme: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
-webkit-tap-highlight-color: transparent;
-webkit-font-smoothing: antialiased;
}
/* Make the preview body fill the viewport (like the app frame the components are designed
for), so that stories – and tests that click outside of them – behave predictably */
html,
body {
height: 100%;
}
/* Temporarily hide docs + story preloader skeletons, which currently show an unsightly flash when loading */
.sb-preparing-docs,
.sb-preparing-story {
Expand Down
18 changes: 14 additions & 4 deletions .storybook/preview.tsx → apps/storybook/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import type {Preview} from '@storybook/react'
import {themes} from '@storybook/theming'
import type {Preview} from '@storybook/react-vite'
import Refractor from 'react-refractor'
import javascript from 'refractor/lang/javascript'
import json from 'refractor/lang/json'
import jsx from 'refractor/lang/jsx'
import typescript from 'refractor/lang/typescript'
import {themes} from 'storybook/theming'

import {withSanityTheme} from './decorators/withSanityTheme.decorator'

Refractor.registerLanguage(javascript)
Refractor.registerLanguage(json)
Refractor.registerLanguage(jsx)
Refractor.registerLanguage(typescript)

const preview: Preview = {
decorators: [
withSanityTheme({
Expand All @@ -12,7 +22,7 @@ const preview: Preview = {
],
parameters: {
actions: {argTypesRegex: '^on[A-Z].*'},
backgrounds: {disable: true},
backgrounds: {disabled: true},
controls: {
matchers: {
color: /(background|color)$/i,
Expand All @@ -28,7 +38,7 @@ const preview: Preview = {
layout: 'fullscreen',
options: {
storySort: {
order: ['primitives', 'components', '*'],
order: ['primitives', 'components', 'hooks', 'utils', 'theme', '*'],
},
},
},
Expand Down
11 changes: 11 additions & 0 deletions apps/storybook/.storybook/vitest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {setProjectAnnotations} from '@storybook/react-vite'
import {beforeAll} from 'vitest'

import * as projectAnnotations from './preview'

// Apply the project-level annotations (decorators, parameters, etc.) from
// .storybook/preview.tsx to stories rendered with `composeStories` in the
// "tests" vitest project. The "storybook" project applies these automatically.
const annotations = setProjectAnnotations([projectAnnotations])

beforeAll(annotations.beforeAll)
Loading
Loading