diff --git a/.prettierrc b/.prettierrc
index 0967ef4..ed2cb19 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1 +1,7 @@
-{}
+{
+ "printWidth": 80,
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": false
+}
diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html
index 05da1e9..2810f21 100644
--- a/.storybook/preview-head.html
+++ b/.storybook/preview-head.html
@@ -1,3 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx
index 0ba900c..06515bb 100644
--- a/.storybook/preview.tsx
+++ b/.storybook/preview.tsx
@@ -4,6 +4,11 @@ import React from "react";
const preview: Preview = {
parameters: {
+ options: {
+ storySort: {
+ order: ["Example", ["Introduction"]],
+ },
+ },
backgrounds: {
default: "light",
},
diff --git a/README.md b/README.md
index d4ac3cb..97c578d 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,9 @@ Storybook Addon Onboarding - Introduces a new onboarding experience
### Development scripts
-- `yarn start` runs babel in watch mode and starts Storybook
+- `yarn start` runs tsup in watch mode and starts Storybook
- `yarn build` build and package your addon code
+- `yarn storybook:watch` runs nodemon in watch mode so it reruns Storybook on changes. This is useful when testing the actual addon (as we cannot have HMR for addon changes) rather than just stories in Storybook
## Release Management
diff --git a/package.json b/package.json
index 255a8a2..831a5cf 100644
--- a/package.json
+++ b/package.json
@@ -50,26 +50,45 @@
"start": "run-p build:watch 'storybook --quiet'",
"release": "yarn build && auto shipit",
"storybook": "storybook dev -p 6006",
+ "storybook:watch": "nodemon",
"build-storybook": "storybook build",
"chromatic": "npx chromatic"
},
+ "nodemonConfig": {
+ "ignore": [
+ "src/stories",
+ "src/**/*.stories.*"
+ ],
+ "watch": [
+ "src",
+ ".storybook/main.ts",
+ "vite.config.ts"
+ ],
+ "ext": "js,jsx,ts,tsx",
+ "exec": "yarn storybook"
+ },
"devDependencies": {
"@storybook/addon-essentials": "^7.0.0",
"@storybook/addon-interactions": "^7.0.0",
"@storybook/addon-links": "^7.0.0",
+ "@storybook/blocks": "^7.0.0",
+ "@storybook/components": "^7.0.0",
+ "@storybook/core-events": "^7.0.0",
+ "@storybook/manager-api": "^7.0.0",
"@storybook/jest": "^0.1.0",
"@storybook/react": "^7.0.0",
"@storybook/react-vite": "^7.0.0",
"@storybook/testing-library": "^0.0.14-next.1",
"@storybook/theming": "^7.0.17",
"@types/node": "^18.15.0",
- "@types/react": "^18.0.34",
+ "@types/react": "^18.2.7",
"@types/react-dom": "^18.2.4",
"@vitejs/plugin-react": "^3.1.0",
"auto": "^10.3.0",
"boxen": "^5.0.1",
"chromatic": "^6.17.4",
"dedent": "^0.7.0",
+ "nodemon": "^2.0.22",
"npm-run-all": "^4.1.5",
"prettier": "^2.3.1",
"prompts": "^2.4.2",
@@ -87,7 +106,6 @@
"@storybook/components": "^7.0.0",
"@storybook/core-events": "^7.0.0",
"@storybook/manager-api": "^7.0.0",
- "@storybook/preview-api": "^7.0.0",
"@storybook/theming": "^7.0.0",
"@storybook/types": "^7.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
@@ -111,6 +129,7 @@
"homepage": "https://github.com/storybookjs/addon-onboarding#readme",
"dependencies": {
"@radix-ui/react-dialog": "^1.0.4",
- "react-confetti": "^6.1.0"
+ "react-confetti": "^6.1.0",
+ "react-joyride": "^2.5.4"
}
}
diff --git a/src/App.tsx b/src/App.tsx
index d55af1b..5d83f42 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,12 +1,124 @@
+import React, { useCallback, useEffect, useState } from "react";
import { ThemeProvider, ensure, themes } from "@storybook/theming";
-import React from "react";
+import { STORY_CHANGED, CURRENT_STORY_WAS_SET } from "@storybook/core-events";
+import { 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";
+
+type Step =
+ | "1:Welcome"
+ | "2:StorybookTour"
+ | "3:WriteYourStory"
+ | "4:VisitNewStory"
+ | "5:ConfigureYourProject";
const theme = ensure(themes.light);
-export function App() {
+export default function App({ api }: { api: API }) {
+ const [enabled, setEnabled] = useState(true);
+ const [showConfetti, setShowConfetti] = useState(false);
+ const [step, setStep] = useState("1:Welcome");
+
+ const skipTour = useCallback(() => {
+ // remove onboarding query parameter from current url
+ const url = new URL(window.location.href);
+ url.searchParams.delete("onboarding");
+ const path = decodeURIComponent(url.searchParams.get("path"));
+ url.search = `?path=${path}`;
+ history.replaceState({}, "", url.href);
+ setEnabled(false);
+ }, [setEnabled]);
+
+ useEffect(() => {
+ let stepTimeout: number;
+ if (step === "4:VisitNewStory") {
+ stepTimeout = window.setTimeout(() => {
+ setShowConfetti(true);
+ setStep("5:ConfigureYourProject");
+ }, 2000);
+ }
+
+ return () => {
+ clearTimeout(stepTimeout);
+ };
+ }, [step]);
+
+ useEffect(() => {
+ api.once(CURRENT_STORY_WAS_SET, ({ storyId }) => {
+ // 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") {
+ api.selectStory("example-button--primary", undefined, {
+ ref: undefined,
+ });
+ }
+ api.togglePanel(true);
+ api.togglePanelPosition("bottom");
+ api.setSelectedPanel("addon-controls");
+ });
+ }, []);
+
+ useEffect(() => {
+ const onStoryChanged = (storyId: string) => {
+ if (storyId === "configure-your-project--docs") {
+ skipTour();
+ }
+ };
+
+ api.on(STORY_CHANGED, onStoryChanged);
+
+ return () => {
+ api.off(STORY_CHANGED, onStoryChanged);
+ };
+ }, []);
+
+ if (!enabled) {
+ return null;
+ }
+
return (
- Hello World
+ {showConfetti && (
+ {
+ confetti.reset();
+ setShowConfetti(false);
+ }}
+ />
+ )}
+ {step === "1:Welcome" && (
+ {
+ setStep("2:StorybookTour");
+ }}
+ onSkip={skipTour}
+ />
+ )}
+ {(step === "2:StorybookTour" || step === "5:ConfigureYourProject") && (
+ {
+ setStep("3:WriteYourStory");
+ }}
+ />
+ )}
+ {step === "3:WriteYourStory" && (
+ {
+ // TODO: enable this
+ // api.selectStory("example-button--warning");
+ setStep("4:VisitNewStory");
+ }}
+ />
+ )}
);
}
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
new file mode 100644
index 0000000..263d01d
--- /dev/null
+++ b/src/components/Button/Button.tsx
@@ -0,0 +1,22 @@
+import React from "react";
+
+export const buttonStyles: React.ComponentProps<"button">["style"] = {
+ border: 0,
+ cursor: "pointer",
+ fontSize: 13,
+ lineHeight: 1,
+ padding: "9px 12px",
+ backgroundColor: "#029CFD",
+ borderRadius: 4,
+ color: "#fff",
+ fontWeight: 700,
+};
+
+export function Button(props: React.ComponentProps<"button">) {
+ const style = {
+ ...buttonStyles,
+ ...(props.style || {}),
+ };
+
+ return ;
+}
diff --git a/src/components/Confetti/Confetti.tsx b/src/components/Confetti/Confetti.tsx
index dc94f8e..19222fe 100644
--- a/src/components/Confetti/Confetti.tsx
+++ b/src/components/Confetti/Confetti.tsx
@@ -1,15 +1,15 @@
import ReactConfetti from "react-confetti";
-import React, { useEffect, useRef } from "react";
+import React, { useEffect } from "react";
import { styled } from "@storybook/theming";
import { createPortal } from "react-dom";
import { useState } from "react";
interface ConfettiProps
extends Omit, "drawShape"> {
- top: number;
- left: number;
- width: number;
- height: number;
+ top?: number;
+ left?: number;
+ width?: number;
+ height?: number;
}
const Wrapper = styled.div<{
@@ -32,7 +32,7 @@ export function Confetti({
width = window.innerWidth,
height = window.innerHeight,
...confettiProps
-}: ConfettiProps) {
+}: ConfettiProps): React.ReactPortal {
const [confettiContainer] = useState(() => {
const container = document.createElement("div");
container.setAttribute("id", "confetti-container");
diff --git a/src/components/Icons/StorybookLogo.tsx b/src/components/Icons/StorybookLogo.tsx
new file mode 100644
index 0000000..efecf33
--- /dev/null
+++ b/src/components/Icons/StorybookLogo.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+
+export function StorybookLogo() {
+ return (
+
+ );
+}
diff --git a/src/components/Modal/Modal.stories.tsx b/src/components/Modal/Modal.stories.tsx
index 4e46880..e031fe5 100644
--- a/src/components/Modal/Modal.stories.tsx
+++ b/src/components/Modal/Modal.stories.tsx
@@ -1,9 +1,10 @@
-import { Meta, StoryObj } from "@storybook/react";
-import { Modal } from "./Modal";
import React, { useState } from "react";
+import { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/testing-library";
import { expect } from "@storybook/jest";
+import { Modal } from "./Modal";
+
const meta: Meta = {
component: Modal,
decorators: [
diff --git a/src/components/Modal/Modal.styled.tsx b/src/components/Modal/Modal.styled.tsx
index e25e1ea..126a344 100644
--- a/src/components/Modal/Modal.styled.tsx
+++ b/src/components/Modal/Modal.styled.tsx
@@ -41,6 +41,17 @@ export const ContentWrapper = React.forwardRef<
));
-export const StyledTitle = styled(Title)``;
-export const StyledDescription = styled(Description)``;
+export const StyledTitle = styled(Title)`
+ color: #000;
+ font-weight: 700;
+ font-size: 20px;
+ line-height: 20px;
+`;
+export const StyledDescription = styled(Description)`
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 20px;
+ color: #454e54;
+`;
+
export const StyledClose = styled(Close)``;
diff --git a/src/components/PulsatingEffect/PulsatingEffect.stories.tsx b/src/components/PulsatingEffect/PulsatingEffect.stories.tsx
new file mode 100644
index 0000000..d3b08bf
--- /dev/null
+++ b/src/components/PulsatingEffect/PulsatingEffect.stories.tsx
@@ -0,0 +1,44 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { PulsatingEffect } from "./PulsatingEffect";
+import React from "react";
+import { within } from "@storybook/testing-library";
+import { expect } from "@storybook/jest";
+
+const meta: Meta = {
+ component: PulsatingEffect,
+ parameters: {
+ layout: "centered",
+ chromatic: {
+ disableSnapshot: true,
+ },
+ },
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: () => (
+ <>
+
+
+ >
+ ),
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement.parentElement);
+ const button = canvas.getByRole("button");
+ await expect(button).toHaveStyle(
+ "animation: 3s ease-in-out 0s infinite normal none running pulsate"
+ );
+ },
+};
diff --git a/src/components/PulsatingEffect/PulsatingEffect.tsx b/src/components/PulsatingEffect/PulsatingEffect.tsx
new file mode 100644
index 0000000..b3192d9
--- /dev/null
+++ b/src/components/PulsatingEffect/PulsatingEffect.tsx
@@ -0,0 +1,43 @@
+import { useEffect } from "react";
+
+export function PulsatingEffect({
+ targetSelector,
+}: {
+ targetSelector: string;
+}): JSX.Element {
+ useEffect(() => {
+ const element = document.querySelector(targetSelector);
+ if (element) {
+ element.style.animation = "pulsate 3s infinite";
+ element.style.transformOrigin = "center";
+ element.style.animationTimingFunction = "ease-in-out";
+
+ const keyframes = `
+ @keyframes pulsate {
+ 0% {
+ box-shadow: 0 0 0 0 rgba(2, 156, 253, 0.7), 0 0 0 0 rgba(2, 156, 253, 0.4);
+ }
+ 50% {
+ box-shadow: 0 0 0 20px rgba(2, 156, 253, 0), 0 0 0 40px rgba(2, 156, 253, 0);
+ }
+ 100% {
+ box-shadow: 0 0 0 0 rgba(2, 156, 253, 0), 0 0 0 0 rgba(2, 156, 253, 0);
+ }
+ }
+ `;
+ const style = document.createElement("style");
+ style.id = "sb-onboarding-pulsating-effect";
+ style.innerHTML = keyframes;
+ document.head.appendChild(style);
+ }
+
+ return () => {
+ const styleElement = document.querySelector(
+ "#sb-onboarding-pulsating-effect"
+ );
+ styleElement?.remove();
+ };
+ }, [targetSelector]);
+
+ return null;
+}
diff --git a/src/components/TitleBody/TitleBody.styled.tsx b/src/components/TitleBody/TitleBody.styled.tsx
new file mode 100644
index 0000000..1bb86b6
--- /dev/null
+++ b/src/components/TitleBody/TitleBody.styled.tsx
@@ -0,0 +1,19 @@
+import { styled } from "@storybook/theming";
+
+export const Wrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+`;
+
+export const Title = styled.strong`
+ font-size: 13px;
+`;
+
+export const Body = styled.p`
+ font-size: 13px;
+ text-align: start;
+ color: #798186;
+ margin: 0;
+ margin-top: 4px;
+`;
diff --git a/src/components/TitleBody/TitleBody.tsx b/src/components/TitleBody/TitleBody.tsx
new file mode 100644
index 0000000..1c096c2
--- /dev/null
+++ b/src/components/TitleBody/TitleBody.tsx
@@ -0,0 +1,20 @@
+import React from "react";
+import { Body, Title, Wrapper } from "./TitleBody.styled";
+
+export function TitleBody({
+ prefix,
+ title,
+ body,
+}: {
+ prefix?: React.ReactNode;
+ title: React.ReactNode;
+ body: React.ReactNode;
+}) {
+ return (
+
+ {prefix}
+ {title}
+ {body}
+
+ );
+}
diff --git a/src/features/GuidedTour/GuidedTour.tsx b/src/features/GuidedTour/GuidedTour.tsx
new file mode 100644
index 0000000..938d13b
--- /dev/null
+++ b/src/features/GuidedTour/GuidedTour.tsx
@@ -0,0 +1,188 @@
+import React, { useEffect, useState } from "react";
+import Joyride, { CallBackProps, STATUS, Step } from "react-joyride";
+
+import { PulsatingEffect } from "../../components/PulsatingEffect/PulsatingEffect";
+import { Confetti } from "../../components/Confetti/Confetti";
+import { TitleBody } from "../../components/TitleBody/TitleBody";
+import { API } from "@storybook/manager-api";
+import { STORY_ARGS_UPDATED } from "@storybook/core-events";
+import { buttonStyles } from "src/components/Button/Button";
+
+let INTERACTIONS_COUNT = 0;
+
+export function GuidedTour({
+ api,
+ isFinalStep,
+ onFirstTourDone,
+}: {
+ api: API;
+ isFinalStep?: boolean;
+ onFirstTourDone: () => void;
+}) {
+ const [stepIndex, setStepIndex] = useState();
+
+ useEffect(() => {
+ api.on(STORY_ARGS_UPDATED, () => {
+ INTERACTIONS_COUNT = INTERACTIONS_COUNT + 1;
+ if (INTERACTIONS_COUNT === 2) {
+ setStepIndex(3);
+ }
+ });
+ }, []);
+
+ const steps: Step[] = isFinalStep
+ ? [
+ {
+ target: "#configure-your-project--docs",
+ content: (
+
+ ),
+ placement: "right",
+ disableOverlay: true,
+ disableBeacon: true,
+ styles: {
+ buttonNext: {
+ display: "none",
+ },
+ },
+ floaterProps: {
+ disableAnimation: true,
+ },
+ },
+ ]
+ : [
+ {
+ target: "#storybook-explorer-tree > div",
+ content: (
+
+ ),
+ placement: "right",
+ disableBeacon: true,
+ styles: {
+ spotlight: {
+ transform: 'translateY(30px)',
+ },
+ },
+ floaterProps: {
+ disableAnimation: true,
+ },
+ },
+ {
+ target: "#storybook-preview-iframe",
+ content: (
+
+ ),
+ placement: "bottom",
+ },
+ {
+ target: "#root div[role=main]",
+ content: (
+ <>
+
+ See how a story renders with different data and state
+ without touching code.
+
+ Try it out by pressing this button.
+ >
+ }
+ />
+
+ >
+ ),
+ placement: "right",
+ spotlightClicks: true,
+ floaterProps: {
+ target: "#control-primary",
+ },
+ styles: {
+ buttonNext: {
+ display: "none",
+ },
+ },
+ },
+ {
+ target: "#control-primary",
+ content: (
+ <>
+
+
+ >
+ ),
+ placement: "right",
+ disableOverlay: true,
+ locale: {
+ last: "Next",
+ },
+ },
+ ];
+
+ return (
+ {
+ if (!isFinalStep && data.status === STATUS.FINISHED) {
+ onFirstTourDone();
+ }
+ }}
+ floaterProps={{
+ styles: {
+ floater: {
+ padding: 0,
+ paddingLeft: 8,
+ paddingTop: 8,
+ filter:
+ "drop-shadow(0px 5px 5px rgba(0,0,0,0.05)) drop-shadow(0 1px 3px rgba(0,0,0,0.1))",
+ },
+ },
+ }}
+ styles={{
+ spotlight: {
+ border: "solid 2px #004c7c",
+ },
+ tooltip: {
+ maxWidth: 260,
+ borderRadius: 4,
+ padding: 15,
+ },
+ overlay: {
+ backgroundColor: "rgba(0, 0, 0, 0.48)",
+ },
+ buttonNext: {
+ ...buttonStyles,
+ marginTop: 5,
+ },
+ tooltipContent: {
+ paddingTop: 4,
+ padding: 0,
+ },
+ options: {
+ zIndex: 10000,
+ primaryColor: "#029CFD",
+ },
+ }}
+ />
+ );
+}
diff --git a/src/features/WelcomeModal/WelcomeModal.stories.tsx b/src/features/WelcomeModal/WelcomeModal.stories.tsx
new file mode 100644
index 0000000..334eef5
--- /dev/null
+++ b/src/features/WelcomeModal/WelcomeModal.stories.tsx
@@ -0,0 +1,19 @@
+import React from "react";
+import { Meta, StoryObj } from "@storybook/react";
+
+import { WelcomeModal } from "./WelcomeModal";
+
+const meta: Meta = {
+ component: WelcomeModal,
+ decorators: [
+ (storyFn) => (
+ {storyFn()}
+ ),
+ ],
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {};
diff --git a/src/features/WelcomeModal/WelcomeModal.styled.tsx b/src/features/WelcomeModal/WelcomeModal.styled.tsx
new file mode 100644
index 0000000..e8344da
--- /dev/null
+++ b/src/features/WelcomeModal/WelcomeModal.styled.tsx
@@ -0,0 +1,80 @@
+import { Icons } from "@storybook/components";
+import { keyframes, styled } from "@storybook/theming";
+
+export const rainbowAnimation = keyframes`
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+`;
+
+export const ModalContentWrapper = styled.div`
+ background: radial-gradient(
+ circle at left,
+ #ffccd2,
+ #ffdbcb,
+ #ffe9c5,
+ #fff8c0,
+ #f2ffd8,
+ #d2f8e5,
+ #b3f0f1,
+ #a1e6f0,
+ #9fd8df
+ )
+ left,
+ radial-gradient(
+ circle at right,
+ #ffccd2,
+ #ffdbcb,
+ #ffe9c5,
+ #fff8c0,
+ #f2ffd8,
+ #d2f8e5,
+ #b3f0f1,
+ #a1e6f0,
+ #9fd8df
+ )
+ right,
+ linear-gradient(
+ 45deg,
+ #ffccd2,
+ #ffdbcb,
+ #ffe9c5,
+ #fff8c0,
+ #f2ffd8,
+ #d2f8e5,
+ #b3f0f1,
+ #a1e6f0,
+ #9fd8df
+ );
+ background-size: 300% 300%;
+ background-repeat: no-repeat;
+ animation: ${rainbowAnimation} 10s linear infinite;
+ border-radius: 5px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding-top: 100px;
+ padding-bottom: 20px;
+`;
+
+export const StyledIcon = styled(Icons)`
+ margin-left: 2px;
+ height: 10px;
+`;
+
+export const SkipButton = styled.button`
+ all: unset;
+ margin-top: 90px;
+ cursor: pointer;
+ font-size: 13px;
+ color: #798186;
+ :focus-visible {
+ outline: auto;
+ }
+`;
diff --git a/src/features/WelcomeModal/WelcomeModal.tsx b/src/features/WelcomeModal/WelcomeModal.tsx
new file mode 100644
index 0000000..44f4b30
--- /dev/null
+++ b/src/features/WelcomeModal/WelcomeModal.tsx
@@ -0,0 +1,43 @@
+import React from "react";
+
+import { Button } from "../../components/Button/Button";
+import { Modal } from "../../components/Modal/Modal";
+import { StorybookLogo } from "../../components/Icons/StorybookLogo";
+import {
+ ModalContentWrapper,
+ SkipButton,
+ StyledIcon,
+} from "./WelcomeModal.styled";
+
+export const WelcomeModal = ({
+ onSkip,
+ onProceed,
+}: {
+ onSkip: () => void;
+ onProceed: () => void;
+}) => {
+ return (
+
+ {({ Title, Description, Close }) => (
+
+
+ Welcome to Storybook
+
+ Storybook helps you develop UI components.
+
+ Learn the basics in a few simple steps.
+
+
+
+
+ Skip tour
+
+
+
+
+ )}
+
+ );
+};
diff --git a/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx b/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx
new file mode 100644
index 0000000..90af238
--- /dev/null
+++ b/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx
@@ -0,0 +1,19 @@
+import React from "react";
+import { Meta, StoryObj } from "@storybook/react";
+
+import { WriteStoriesModal } from "./WriteStoriesModal";
+
+const meta: Meta = {
+ component: WriteStoriesModal,
+ decorators: [
+ (storyFn) => (
+ {storyFn()}
+ ),
+ ],
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {};
diff --git a/src/features/WriteStoriesModal/WriteStoriesModal.tsx b/src/features/WriteStoriesModal/WriteStoriesModal.tsx
new file mode 100644
index 0000000..af9e891
--- /dev/null
+++ b/src/features/WriteStoriesModal/WriteStoriesModal.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+import { Button } from "../../components/Button/Button";
+
+import { Modal } from "../../components/Modal/Modal";
+import { StorybookLogo } from "../../components/Icons/StorybookLogo";
+import { ModalContentWrapper } from "../WelcomeModal/WelcomeModal.styled";
+
+export function WriteStoriesModal({ onFinish }: { onFinish: () => void }) {
+ return (
+
+ {({ Title, Description }) => (
+
+
+
+ Create your first story (WORK IN PROGRESS)
+
+
+ Now it's your turn.
+ See how easy it is to create your first story by following these
+ steps below.
+
+
+
+ )}
+
+ );
+}
diff --git a/src/manager.tsx b/src/manager.tsx
index a1f9074..b71d441 100644
--- a/src/manager.tsx
+++ b/src/manager.tsx
@@ -1,22 +1,40 @@
import ReactDOM from "react-dom";
-import React from "react";
-import { App } from "./App";
+import React, { lazy, Suspense } from "react";
+import { addons } from "@storybook/manager-api";
-// Add a new DOM element to document.body, where we will bootstrap our React app
-const domNode = document.createElement("div");
+const App = lazy(() => import("./App"));
-domNode.id = "addon-onboarding";
-domNode.style.position = "absolute";
-domNode.style.top = "0";
-domNode.style.left = "0";
-domNode.style.width = "0";
-domNode.style.height = "0";
-domNode.style.overflow = "hidden";
-domNode.style.opacity = "0";
-domNode.style.visibility = "hidden";
+// The addon is enabled only when:
+// 1. The onboarding query parameter is present
+// 2. The example button stories are present
+addons.register("@storybook/addon-onboarding", async (api) => {
+ const isOnboarding = api.getUrlState().queryParams.onboarding;
-// Append the new DOM element to document.body
-document.body.appendChild(domNode);
+ if (!isOnboarding) {
+ return;
+ }
-// Render the React app
-ReactDOM.render(, domNode);
+ let hasButtonStories = false;
+ try {
+ const response = await fetch("./index.json");
+ const index = await response.json();
+ hasButtonStories = !!index.entries["example-button--primary"];
+ } catch (e) {}
+
+ if (hasButtonStories) {
+ // Add a new DOM element to document.body, where we will bootstrap our React app
+ const domNode = document.createElement("div");
+
+ domNode.id = "addon-onboarding";
+ // Append the new DOM element to document.body
+ document.body.appendChild(domNode);
+
+ // Render the React app
+ ReactDOM.render(
+ Loading...}>
+
+ ,
+ domNode
+ );
+ }
+});
diff --git a/src/preview.ts b/src/preview.ts
index 5574b54..f4c7b0c 100644
--- a/src/preview.ts
+++ b/src/preview.ts
@@ -1,6 +1,4 @@
import type { Renderer, ProjectAnnotations } from "@storybook/types";
-import { ThemeProvider, ensure, themes } from "@storybook/theming";
-import React from "react";
/**
* Note: if you want to use JSX in this file, rename it to `preview.tsx`
diff --git a/src/stories/Button.stories.ts b/src/stories/Button.stories.ts
index 04d90eb..462d552 100644
--- a/src/stories/Button.stories.ts
+++ b/src/stories/Button.stories.ts
@@ -10,14 +10,10 @@ const meta: Meta = {
argTypes: {
backgroundColor: { control: "color" },
},
- tags: ["autodocs"],
parameters: {
- myAddonParameter: `
-
- a.id} />
-
-`,
+ layout: "centered",
},
+ tags: ["autodocs"],
};
export default meta;
@@ -51,3 +47,12 @@ export const Small: Story = {
label: "Button",
},
};
+
+// Comment this out to test the flow
+// export const Warning: Story = {
+// args: {
+// primary: true,
+// backgroundColor: "red",
+// label: "Delete now",
+// },
+// };
diff --git a/src/stories/Introduction.mdx b/src/stories/Introduction.mdx
index a314fa7..fdfcb71 100644
--- a/src/stories/Introduction.mdx
+++ b/src/stories/Introduction.mdx
@@ -8,7 +8,7 @@ import Plugin from './assets/plugin.svg';
import Repo from './assets/repo.svg';
import StackAlt from './assets/stackalt.svg';
-
+