-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #26176 from storybookjs/monorepo-migration
Addon-onboarding: Move onboarding to monorepo
- Loading branch information
Showing
65 changed files
with
4,511 additions
and
484 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Storybook Addon Onboarding | ||
|
||
This addon provides a guided tour in some of Storybook's features, helping you get to know about the basics of Storybook and learn how to write stories! | ||
|
||
![](./.github/assets/onboarding-intro.png) | ||
|
||
## Triggering the onboarding | ||
|
||
This addon comes installed by default in Storybook projects and should trigger automatically. | ||
If you want to retrigger the addon, you should make sure that your Storybook still contains the example stories that come when initializing Storybook, and you can then navigate to http://localhost:6006/?path=/onboarding after running Storybook. | ||
|
||
## Uninstalling | ||
|
||
This addon serves to provide you a guided experience on the basics of Storybook. Once you are done, the addon is therefore not needed anymore and will not get activated (unless triggered manually), so you can freely remove it. Here's how to do so: | ||
|
||
### 1. Remove the dependency | ||
|
||
yarn: | ||
|
||
```zsh | ||
yarn remove @storybook/addon-onboarding | ||
``` | ||
|
||
npm: | ||
|
||
```zsh | ||
npm uninstall -D @storybook/addon-onboarding | ||
``` | ||
|
||
pnpm: | ||
|
||
```zsh | ||
pnpm remove -D @storybook/addon-onboarding | ||
``` | ||
|
||
### 2. Remove the addon in your `.storybook/main.js` file | ||
|
||
```diff | ||
const config = { | ||
stories: [ | ||
"../stories/**/*.stories.mdx", | ||
"../stories/**/*.stories.@(js|jsx|ts|tsx)", | ||
], | ||
addons: [ | ||
"@storybook/addon-essentials", | ||
- "@storybook/addon-onboarding" | ||
], | ||
}; | ||
export default config; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
{ | ||
"name": "@storybook/addon-onboarding", | ||
"version": "8.0.0-beta.5", | ||
"description": "Storybook Addon Onboarding - Introduces a new onboarding experience", | ||
"keywords": [ | ||
"storybook-addons", | ||
"addon-onboarding" | ||
], | ||
"homepage": "https://github.com/storybookjs/storybook/tree/next/code/addons/onboarding", | ||
"bugs": { | ||
"url": "https://github.com/storybookjs/storybook/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/storybookjs/storybook.git", | ||
"directory": "code/addons/onboarding" | ||
}, | ||
"funding": { | ||
"type": "opencollective", | ||
"url": "https://opencollective.com/storybook" | ||
}, | ||
"license": "MIT", | ||
"exports": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"node": "./dist/index.js", | ||
"require": "./dist/index.js", | ||
"import": "./dist/index.mjs" | ||
}, | ||
"./manager": "./dist/manager.js", | ||
"./preset": "./dist/preset.js", | ||
"./package.json": "./package.json" | ||
}, | ||
"main": "dist/index.js", | ||
"module": "dist/index.mjs", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
"dist/**/*", | ||
"README.md", | ||
"*.js", | ||
"*.d.ts", | ||
"!src/**/*" | ||
], | ||
"scripts": { | ||
"check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts", | ||
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" | ||
}, | ||
"devDependencies": { | ||
"@radix-ui/react-dialog": "^1.0.5", | ||
"@storybook/channels": "workspace:*", | ||
"@storybook/components": "workspace:*", | ||
"@storybook/core-events": "workspace:*", | ||
"@storybook/icons": "^1.2.5", | ||
"@storybook/manager-api": "workspace:*", | ||
"@storybook/react": "workspace:*", | ||
"@storybook/telemetry": "workspace:*", | ||
"@storybook/test": "workspace:*", | ||
"@storybook/testing-library": "next", | ||
"@storybook/theming": "workspace:*", | ||
"@storybook/types": "workspace:*", | ||
"framer-motion": "^11.0.3", | ||
"react": "^18.2.0", | ||
"react-confetti": "^6.1.0", | ||
"react-dom": "^18.2.0", | ||
"react-joyride": "^2.7.2", | ||
"react-use-measure": "^2.1.1", | ||
"typescript": "^5.3.2" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"bundler": { | ||
"exportEntries": [ | ||
"./src/index.ts" | ||
], | ||
"managerEntries": [ | ||
"./src/manager.tsx" | ||
], | ||
"nodeEntries": [ | ||
"./src/preset.ts" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
function managerEntries(entry = []) { | ||
return [...entry, require.resolve('./dist/manager.mjs')]; | ||
} | ||
|
||
module.exports = { | ||
managerEntries, | ||
...require('./dist/preset'), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"name": "@storybook/addon-onboarding", | ||
"$schema": "../../node_modules/nx/schemas/project-schema.json", | ||
"implicitDependencies": [], | ||
"type": "library" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import React, { useCallback, useEffect, useState } from 'react'; | ||
import { ThemeProvider, convert } from '@storybook/theming'; | ||
import { addons, type API } from '@storybook/manager-api'; | ||
|
||
import { GuidedTour } from './features/GuidedTour/GuidedTour'; | ||
import { WelcomeModal } from './features/WelcomeModal/WelcomeModal'; | ||
import { WriteStoriesModal } from './features/WriteStoriesModal/WriteStoriesModal'; | ||
import { Confetti } from './components/Confetti/Confetti'; | ||
import { STORYBOOK_ADDON_ONBOARDING_CHANNEL } from './constants'; | ||
import { useGetProject } from './features/WriteStoriesModal/hooks/useGetProject'; | ||
|
||
type Step = | ||
| '1:Welcome' | ||
| '2:StorybookTour' | ||
| '3:WriteYourStory' | ||
| '4:VisitNewStory' | ||
| '5:ConfigureYourProject'; | ||
|
||
const theme = convert(); | ||
|
||
export default function App({ api }: { api: API }) { | ||
const [enabled, setEnabled] = useState(true); | ||
const [showConfetti, setShowConfetti] = useState(false); | ||
const [step, setStep] = useState<Step>('1:Welcome'); | ||
const { data: codeSnippets } = useGetProject(); | ||
|
||
const skipOnboarding = useCallback(() => { | ||
// remove onboarding query parameter from current url | ||
const url = new URL(window.location.href); | ||
// @ts-expect-error (not strict) | ||
const path = decodeURIComponent(url.searchParams.get('path')); | ||
url.search = `?path=${path}&onboarding=false`; | ||
history.replaceState({}, '', url.href); | ||
api.setQueryParams({ onboarding: 'false' }); | ||
setEnabled(false); | ||
}, [setEnabled, api]); | ||
|
||
useEffect(() => { | ||
api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, { | ||
step: '1:Welcome', | ||
type: 'telemetry', | ||
}); | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (step !== '1:Welcome') { | ||
api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, { | ||
step, | ||
type: 'telemetry', | ||
}); | ||
} | ||
}, [api, step]); | ||
|
||
useEffect(() => { | ||
let stepTimeout: number; | ||
if (step === '4:VisitNewStory') { | ||
setShowConfetti(true); | ||
stepTimeout = window.setTimeout(() => { | ||
setStep('5:ConfigureYourProject'); | ||
}, 2000); | ||
} | ||
|
||
return () => { | ||
clearTimeout(stepTimeout); | ||
}; | ||
}, [step]); | ||
|
||
useEffect(() => { | ||
const storyId = api.getCurrentStoryData()?.id; | ||
api.setQueryParams({ onboarding: 'true' }); | ||
// make sure the initial state is set correctly: | ||
// 1. Selected story is primary button | ||
// 2. The addon panel is opened, in the bottom and the controls tab is selected | ||
if (storyId !== 'example-button--primary') { | ||
try { | ||
api.selectStory('example-button--primary', undefined, { | ||
ref: undefined, | ||
}); | ||
} catch (e) { | ||
// | ||
} | ||
} | ||
}, []); | ||
|
||
if (!enabled) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<ThemeProvider theme={theme}> | ||
{enabled && showConfetti && ( | ||
<Confetti | ||
numberOfPieces={800} | ||
recycle={false} | ||
tweenDuration={20000} | ||
onConfettiComplete={(confetti) => { | ||
confetti?.reset(); | ||
setShowConfetti(false); | ||
}} | ||
/> | ||
)} | ||
{enabled && step === '1:Welcome' && ( | ||
<WelcomeModal | ||
onProceed={() => { | ||
setStep('2:StorybookTour'); | ||
}} | ||
skipOnboarding={() => { | ||
skipOnboarding(); | ||
|
||
api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, { | ||
step: 'X:SkippedOnboarding', | ||
where: 'WelcomeModal', | ||
type: 'telemetry', | ||
}); | ||
}} | ||
/> | ||
)} | ||
{enabled && (step === '2:StorybookTour' || step === '5:ConfigureYourProject') && ( | ||
<GuidedTour | ||
api={api} | ||
isFinalStep={step === '5:ConfigureYourProject'} | ||
onFirstTourDone={() => { | ||
setStep('3:WriteYourStory'); | ||
}} | ||
codeSnippets={codeSnippets || undefined} | ||
onLastTourDone={() => { | ||
try { | ||
api.selectStory('configure-your-project--docs'); | ||
} catch (e) { | ||
// | ||
} | ||
api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, { | ||
step: '6:FinishedOnboarding', | ||
type: 'telemetry', | ||
}); | ||
skipOnboarding(); | ||
}} | ||
/> | ||
)} | ||
{enabled && step === '3:WriteYourStory' && codeSnippets && ( | ||
<WriteStoriesModal | ||
api={api} | ||
codeSnippets={codeSnippets} | ||
addonsStore={addons} | ||
onFinish={() => { | ||
api.selectStory('example-button--warning'); | ||
|
||
setStep('4:VisitNewStory'); | ||
}} | ||
skipOnboarding={skipOnboarding} | ||
/> | ||
)} | ||
</ThemeProvider> | ||
); | ||
} |
15 changes: 15 additions & 0 deletions
15
code/addons/onboarding/src/components/Button/Button.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import { Button } from './Button'; | ||
|
||
const meta: Meta<typeof Button> = { | ||
title: 'Components/Button', | ||
component: Button, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof Button>; | ||
|
||
export const Default: Story = { | ||
args: { children: 'Button' }, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import type { ComponentProps } from 'react'; | ||
import React, { forwardRef } from 'react'; | ||
import { styled } from '@storybook/theming'; | ||
|
||
export interface ButtonProps extends ComponentProps<'button'> { | ||
children: string; | ||
onClick?: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void; | ||
variant?: 'primary' | 'secondary' | 'outline'; | ||
} | ||
|
||
const StyledButton = styled.button<{ variant: ButtonProps['variant'] }>` | ||
all: unset; | ||
box-sizing: border-box; | ||
border: 0; | ||
border-radius: 0.25rem; | ||
cursor: pointer; | ||
display: inline-flex; | ||
align-items: center; | ||
justify-content: center; | ||
padding: 0 0.75rem; | ||
background: ${({ theme, variant }) => { | ||
if (variant === 'primary') return theme.color.secondary; | ||
if (variant === 'secondary') return theme.color.lighter; | ||
if (variant === 'outline') return 'transparent'; | ||
return theme.color.secondary; | ||
}}; | ||
color: ${({ theme, variant }) => { | ||
if (variant === 'primary') return theme.color.lightest; | ||
if (variant === 'secondary') return theme.darkest; | ||
if (variant === 'outline') return theme.darkest; | ||
return theme.color.lightest; | ||
}}; | ||
box-shadow: ${({ variant }) => { | ||
if (variant === 'primary') return 'none'; | ||
if (variant === 'secondary') return '#D9E8F2 0 0 0 1px inset'; | ||
if (variant === 'outline') return '#D9E8F2 0 0 0 1px inset'; | ||
return 'none'; | ||
}}; | ||
height: 32px; | ||
font-size: 0.8125rem; | ||
font-weight: 700; | ||
font-family: ${({ theme }) => theme.typography.fonts.base}; | ||
transition: background-color, box-shadow, opacity; | ||
transition-duration: 0.16s; | ||
transition-timing-function: ease-in-out; | ||
text-decoration: none; | ||
&:hover { | ||
background-color: ${({ variant }) => { | ||
if (variant === 'primary') return '#0b94eb'; | ||
if (variant === 'secondary') return '#eef4f9'; | ||
if (variant === 'outline') return 'transparent'; | ||
return '#0b94eb'; | ||
}}; | ||
} | ||
&:focus { | ||
box-shadow: ${({ variant }) => { | ||
if (variant === 'primary') return 'inset 0 0 0 1px rgba(0, 0, 0, 0.2)'; | ||
if (variant === 'secondary') return 'inset 0 0 0 1px #0b94eb'; | ||
if (variant === 'outline') return 'inset 0 0 0 1px #0b94eb'; | ||
return 'inset 0 0 0 2px rgba(0, 0, 0, 0.1)'; | ||
}}; | ||
} | ||
`; | ||
|
||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button( | ||
{ children, onClick, variant = 'primary', ...rest }, | ||
ref | ||
) { | ||
return ( | ||
<StyledButton ref={ref} onClick={onClick} variant={variant} {...rest}> | ||
{children} | ||
</StyledButton> | ||
); | ||
}); |
Oops, something went wrong.