@storybook/addon-onboarding 10.1.0-alpha.9 → 10.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,35 @@
1
+ import {
2
+ IntentSurvey
3
+ } from "./chunk-BTSM33XX.js";
4
+ import {
5
+ ADDON_ONBOARDING_CHANNEL
6
+ } from "./chunk-VFOIHBP2.js";
7
+ import "./chunk-ZYVL3X5E.js";
8
+
9
+ // src/Survey.tsx
10
+ import React, { useCallback } from "react";
11
+ import { ThemeProvider, convert } from "storybook/theming";
12
+ var theme = convert();
13
+ function Survey({ api }) {
14
+ let userAgent = globalThis?.navigator?.userAgent, disableOnboarding = useCallback(() => {
15
+ let url = new URL(window.location.href);
16
+ url.searchParams.set("onboarding", "false"), history.replaceState({}, "", url.href), api.setQueryParams({ onboarding: "false" });
17
+ }, [api]), complete = useCallback(
18
+ (answers) => {
19
+ api.emit(ADDON_ONBOARDING_CHANNEL, {
20
+ answers,
21
+ type: "survey",
22
+ userAgent
23
+ }), disableOnboarding();
24
+ },
25
+ [api, disableOnboarding, userAgent]
26
+ ), dismiss = useCallback(() => {
27
+ api.emit(ADDON_ONBOARDING_CHANNEL, {
28
+ type: "dismissSurvey"
29
+ }), disableOnboarding();
30
+ }, [api, disableOnboarding]);
31
+ return React.createElement(ThemeProvider, { theme }, React.createElement(IntentSurvey, { onComplete: complete, onDismiss: dismiss }));
32
+ }
33
+ export {
34
+ Survey as default
35
+ };
@@ -0,0 +1,181 @@
1
+ // src/features/IntentSurvey/IntentSurvey.tsx
2
+ import React, { Fragment, useState } from "react";
3
+ import { Button, Form, Modal } from "storybook/internal/components";
4
+ import { styled } from "storybook/theming";
5
+
6
+ // ../../.storybook/isChromatic.ts
7
+ function isChromatic(windowArg) {
8
+ let windowToCheck = windowArg || typeof window < "u" && window;
9
+ return !!(windowToCheck && (windowToCheck.navigator.userAgent.match(/Chromatic/) || windowToCheck.location.href.match(/chromatic=true/)));
10
+ }
11
+
12
+ // src/features/IntentSurvey/IntentSurvey.tsx
13
+ var Content = styled(Modal.Content)(({ theme }) => ({
14
+ fontSize: theme.typography.size.s2,
15
+ color: theme.color.defaultText,
16
+ gap: 8
17
+ })), Row = styled.div({
18
+ display: "grid",
19
+ gridTemplateColumns: "1fr 1fr",
20
+ gap: 14,
21
+ marginBottom: 8
22
+ }), Question = styled.div(({ theme }) => ({
23
+ marginTop: 8,
24
+ marginBottom: 2,
25
+ fontWeight: theme.typography.weight.bold
26
+ })), Label = styled.label({
27
+ display: "flex",
28
+ gap: 8,
29
+ '&:has(input[type="checkbox"]:not(:disabled), input[type="radio"]:not(:disabled))': {
30
+ cursor: "pointer"
31
+ }
32
+ }), Actions = styled(Modal.Actions)({
33
+ marginTop: 8
34
+ }), Checkbox = styled(Form.Checkbox)({
35
+ margin: 2
36
+ }), IntentSurvey = ({
37
+ onComplete,
38
+ onDismiss
39
+ }) => {
40
+ let [isSubmitting, setIsSubmitting] = useState(!1), [formFields, setFormFields] = useState({
41
+ building: {
42
+ label: "What are you building?",
43
+ type: "checkbox",
44
+ required: !0,
45
+ options: shuffleObject({
46
+ "design-system": { label: "Design system" },
47
+ "application-ui": { label: "Application UI" }
48
+ }),
49
+ values: {
50
+ "design-system": !1,
51
+ "application-ui": !1
52
+ }
53
+ },
54
+ interest: {
55
+ label: "Which of these are you interested in?",
56
+ type: "checkbox",
57
+ required: !0,
58
+ options: shuffleObject({
59
+ "ui-documentation": { label: "Generating UI docs" },
60
+ "functional-testing": { label: "Functional testing" },
61
+ "accessibility-testing": { label: "Accessibility testing" },
62
+ "visual-testing": { label: "Visual testing" },
63
+ "ai-augmented-development": { label: "Building UI with AI" },
64
+ "team-collaboration": { label: "Team collaboration" },
65
+ "design-handoff": { label: "Design handoff" }
66
+ }),
67
+ values: {
68
+ "ui-documentation": !1,
69
+ "functional-testing": !1,
70
+ "accessibility-testing": !1,
71
+ "visual-testing": !1,
72
+ "ai-augmented-development": !1,
73
+ "team-collaboration": !1,
74
+ "design-handoff": !1
75
+ }
76
+ },
77
+ referrer: {
78
+ label: "How did you discover Storybook?",
79
+ type: "select",
80
+ required: !0,
81
+ options: shuffleObject({
82
+ "we-use-it-at-work": { label: "We use it at work" },
83
+ "via-friend-or-colleague": { label: "Via friend or colleague" },
84
+ "via-social-media": { label: "Via social media" },
85
+ youtube: { label: "YouTube" },
86
+ "web-search": { label: "Web Search" },
87
+ "ai-agent": { label: "AI Agent (e.g. ChatGPT)" }
88
+ }),
89
+ values: {
90
+ "we-use-it-at-work": !1,
91
+ "via-friend-or-colleague": !1,
92
+ "via-social-media": !1,
93
+ youtube: !1,
94
+ "web-search": !1,
95
+ "ai-agent": !1
96
+ }
97
+ }
98
+ }), updateFormData = (key, optionOrValue, value) => {
99
+ let field = formFields[key];
100
+ setFormFields((fields) => {
101
+ if (field.type === "checkbox") {
102
+ let values = { ...field.values, [optionOrValue]: !!value };
103
+ return { ...fields, [key]: { ...field, values } };
104
+ }
105
+ if (field.type === "select") {
106
+ let values = Object.fromEntries(
107
+ Object.entries(field.values).map(([opt]) => [opt, opt === optionOrValue])
108
+ );
109
+ return { ...fields, [key]: { ...field, values } };
110
+ }
111
+ return fields;
112
+ });
113
+ }, isValid = Object.values(formFields).every((field) => field.required ? Object.values(field.values).some((value) => value === !0) : !0);
114
+ return React.createElement(
115
+ Modal,
116
+ {
117
+ ariaLabel: "Storybook user survey",
118
+ defaultOpen: !0,
119
+ width: 420,
120
+ onOpenChange: (isOpen) => {
121
+ isOpen || onDismiss();
122
+ }
123
+ },
124
+ React.createElement(Form, { onSubmit: (e) => {
125
+ isValid && (e.preventDefault(), setIsSubmitting(!0), onComplete(
126
+ Object.fromEntries(Object.entries(formFields).map(([key, field]) => [key, field.values]))
127
+ ));
128
+ }, id: "intent-survey-form" }, React.createElement(Content, null, React.createElement(Modal.Header, { onClose: onDismiss }, React.createElement(Modal.Title, null, "Help improve Storybook")), Object.keys(formFields).map((key) => {
129
+ let field = formFields[key];
130
+ return React.createElement(Fragment, { key }, React.createElement(Question, null, field.label), field.type === "checkbox" && React.createElement(Row, null, Object.entries(field.options).map(([opt, option]) => {
131
+ let id = `${key}:${opt}`;
132
+ return React.createElement("div", { key: id }, React.createElement(Label, { htmlFor: id }, React.createElement(
133
+ Checkbox,
134
+ {
135
+ name: id,
136
+ id,
137
+ checked: field.values[opt],
138
+ disabled: isSubmitting,
139
+ onChange: (e) => updateFormData(key, opt, e.target.checked)
140
+ }
141
+ ), option.label));
142
+ })), field.type === "select" && React.createElement(
143
+ Form.Select,
144
+ {
145
+ name: key,
146
+ id: key,
147
+ value: Object.entries(field.values).find(([, isSelected]) => isSelected)?.[0] || "",
148
+ required: field.required,
149
+ disabled: isSubmitting,
150
+ onChange: (e) => updateFormData(key, e.target.value)
151
+ },
152
+ React.createElement("option", { disabled: !0, hidden: !0, value: "" }, "Select an option..."),
153
+ Object.entries(field.options).map(([opt, option]) => React.createElement("option", { key: opt, value: opt }, option.label))
154
+ ));
155
+ }), React.createElement(Actions, null, React.createElement(
156
+ Button,
157
+ {
158
+ ariaLabel: !1,
159
+ disabled: isSubmitting || !isValid,
160
+ size: "medium",
161
+ type: "submit",
162
+ variant: "solid"
163
+ },
164
+ "Submit"
165
+ ))))
166
+ );
167
+ };
168
+ function shuffle(array) {
169
+ for (let i = array.length - 1; i > 0; i--) {
170
+ let j = Math.floor(Math.random() * (i + 1));
171
+ [array[i], array[j]] = [array[j], array[i]];
172
+ }
173
+ return array;
174
+ }
175
+ function shuffleObject(object) {
176
+ return isChromatic() ? object : Object.fromEntries(shuffle(Object.entries(object)));
177
+ }
178
+
179
+ export {
180
+ IntentSurvey
181
+ };
@@ -0,0 +1,8 @@
1
+ // src/constants.ts
2
+ var ADDON_ID = "storybook/onboarding", ADDON_ONBOARDING_CHANNEL = `${ADDON_ID}/channel`, ADDON_CONTROLS_ID = "addon-controls";
3
+
4
+ export {
5
+ ADDON_ID,
6
+ ADDON_ONBOARDING_CHANNEL,
7
+ ADDON_CONTROLS_ID
8
+ };
@@ -2,18 +2,14 @@ var __create = Object.create;
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
- var __commonJS = (cb, mod) => function __require() {
5
+ var __getProtoOf = Object.getPrototypeOf, __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __commonJS = (cb, mod) => function() {
9
7
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
8
  };
11
9
  var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
10
+ if (from && typeof from == "object" || typeof from == "function")
13
11
  for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
12
+ !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
13
  return to;
18
14
  };
19
15
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
@@ -21,12 +17,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
17
  // file that has been converted to a CommonJS file using a Babel-
22
18
  // compatible transform (i.e. "__esModule" has not been set), then set
23
19
  // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
20
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: !0 }) : target,
25
21
  mod
26
22
  ));
27
23
 
28
24
  export {
29
- __name,
30
25
  __commonJS,
31
26
  __toESM
32
27
  };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import "./_browser-chunks/chunk-CBI7MY27.js";
1
+ import "./_browser-chunks/chunk-ZYVL3X5E.js";
2
2
 
3
3
  // src/index.ts
4
4
  var index_default = {};
package/dist/manager.js CHANGED
@@ -1,37 +1,32 @@
1
1
  import {
2
- ADDON_CONTROLS_ID
3
- } from "./_browser-chunks/chunk-B266DSPW.js";
4
- import "./_browser-chunks/chunk-CBI7MY27.js";
2
+ ADDON_CONTROLS_ID,
3
+ ADDON_ID
4
+ } from "./_browser-chunks/chunk-VFOIHBP2.js";
5
+ import "./_browser-chunks/chunk-ZYVL3X5E.js";
5
6
 
6
7
  // src/manager.tsx
7
8
  import React, { Suspense, lazy } from "react";
8
- import ReactDOM from "react-dom";
9
+ import { createRoot } from "react-dom/client";
9
10
  import { STORY_SPECIFIED } from "storybook/internal/core-events";
10
- import { addons } from "storybook/manager-api";
11
- var Onboarding = lazy(() => import("./_browser-chunks/Onboarding-XBLXWDOE.js"));
12
- addons.register("@storybook/addon-onboarding", async (api) => {
13
- const urlState = api.getUrlState();
14
- const isOnboarding = urlState.path === "/onboarding" || urlState.queryParams.onboarding === "true";
15
- api.once(STORY_SPECIFIED, () => {
16
- const hasButtonStories = !!api.getData("example-button--primary") || !!document.getElementById("example-button--primary");
17
- if (!hasButtonStories) {
18
- console.warn(
19
- `[@storybook/addon-onboarding] It seems like you have finished the onboarding experience in Storybook! Therefore this addon is not necessary anymore and will not be loaded. You are free to remove it from your project. More info: https://github.com/storybookjs/storybook/tree/next/code/addons/onboarding#uninstalling`
20
- );
21
- return;
22
- }
23
- if (!isOnboarding || window.innerWidth < 730) {
24
- return;
25
- }
26
- api.togglePanel(true);
27
- api.togglePanelPosition("bottom");
28
- api.setSelectedPanel(ADDON_CONTROLS_ID);
29
- const domNode = document.createElement("div");
30
- domNode.id = "storybook-addon-onboarding";
31
- document.body.appendChild(domNode);
32
- ReactDOM.render(
33
- React.createElement(Suspense, { fallback: React.createElement("div", null) }, React.createElement(Onboarding, { api })),
34
- domNode
35
- );
11
+ import { addons, internal_universalChecklistStore as checklistStore } from "storybook/manager-api";
12
+ var Onboarding = lazy(() => import("./_browser-chunks/Onboarding-4UKVZPT6.js")), Survey = lazy(() => import("./_browser-chunks/Survey-RTIK67MQ.js")), root = null, render = (node) => {
13
+ let container = document.getElementById("storybook-addon-onboarding");
14
+ container || (container = document.createElement("div"), container.id = "storybook-addon-onboarding", document.body.appendChild(container)), root = root ?? createRoot(container), root.render(React.createElement(Suspense, { fallback: React.createElement("div", null) }, node));
15
+ };
16
+ addons.register(ADDON_ID, async (api) => {
17
+ let { path, queryParams } = api.getUrlState(), isOnboarding = path === "/onboarding" || queryParams.onboarding === "true", isSurvey = queryParams.onboarding === "survey", hasCompletedSurvey = await new Promise((resolve) => {
18
+ let unsubscribe = checklistStore.onStateChange(({ loaded, items }) => {
19
+ loaded && (unsubscribe(), resolve(items.onboardingSurvey.status === "accepted"));
20
+ });
36
21
  });
22
+ if (isSurvey)
23
+ return hasCompletedSurvey ? null : render(React.createElement(Survey, { api }));
24
+ if (await new Promise((resolve) => api.once(STORY_SPECIFIED, resolve)), !(!!api.getData("example-button--primary") || !!document.getElementById("example-button--primary"))) {
25
+ console.warn(
26
+ "[@storybook/addon-onboarding] It seems like you have finished the onboarding experience in Storybook! Therefore this addon is not necessary anymore and will not be loaded. You are free to remove it from your project. More info: https://github.com/storybookjs/storybook/tree/next/code/addons/onboarding#uninstalling"
27
+ );
28
+ return;
29
+ }
30
+ if (!(!isOnboarding || window.innerWidth < 730))
31
+ return api.togglePanel(!0), api.togglePanelPosition("bottom"), api.setSelectedPanel(ADDON_CONTROLS_ID), render(React.createElement(Onboarding, { api, hasCompletedSurvey }));
37
32
  });
package/dist/preset.js CHANGED
@@ -1,41 +1,31 @@
1
- import CJS_COMPAT_NODE_URL_rljys7y6wo from 'node:url';
2
- import CJS_COMPAT_NODE_PATH_rljys7y6wo from 'node:path';
3
- import CJS_COMPAT_NODE_MODULE_rljys7y6wo from "node:module";
1
+ import CJS_COMPAT_NODE_URL_x8wp8fakaa from 'node:url';
2
+ import CJS_COMPAT_NODE_PATH_x8wp8fakaa from 'node:path';
3
+ import CJS_COMPAT_NODE_MODULE_x8wp8fakaa from "node:module";
4
4
 
5
- var __filename = CJS_COMPAT_NODE_URL_rljys7y6wo.fileURLToPath(import.meta.url);
6
- var __dirname = CJS_COMPAT_NODE_PATH_rljys7y6wo.dirname(__filename);
7
- var require = CJS_COMPAT_NODE_MODULE_rljys7y6wo.createRequire(import.meta.url);
5
+ var __filename = CJS_COMPAT_NODE_URL_x8wp8fakaa.fileURLToPath(import.meta.url);
6
+ var __dirname = CJS_COMPAT_NODE_PATH_x8wp8fakaa.dirname(__filename);
7
+ var require = CJS_COMPAT_NODE_MODULE_x8wp8fakaa.createRequire(import.meta.url);
8
8
 
9
9
  // ------------------------------------------------------------
10
10
  // end of CJS compatibility banner, injected by Storybook's esbuild configuration
11
11
  // ------------------------------------------------------------
12
- var __defProp = Object.defineProperty;
13
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
14
12
 
15
13
  // src/preset.ts
16
14
  import { telemetry } from "storybook/internal/telemetry";
17
15
 
18
16
  // package.json
19
- var version = "10.1.0-alpha.9";
17
+ var version = "10.1.0-beta.0";
20
18
 
21
19
  // src/constants.ts
22
- var STORYBOOK_ADDON_ONBOARDING_CHANNEL = "STORYBOOK_ADDON_ONBOARDING_CHANNEL";
20
+ var ADDON_ID = "storybook/onboarding", ADDON_ONBOARDING_CHANNEL = `${ADDON_ID}/channel`;
23
21
 
24
22
  // src/preset.ts
25
- var experimental_serverChannel = /* @__PURE__ */ __name(async (channel, options) => {
26
- const { disableTelemetry } = await options.presets.apply("core", {});
27
- if (disableTelemetry) {
28
- return channel;
29
- }
30
- channel.on(STORYBOOK_ADDON_ONBOARDING_CHANNEL, ({ type, ...event }) => {
31
- if (type === "telemetry") {
32
- telemetry("addon-onboarding", { ...event, addonVersion: version });
33
- } else if (type === "survey") {
34
- telemetry("onboarding-survey", { ...event, addonVersion: version });
35
- }
36
- });
37
- return channel;
38
- }, "experimental_serverChannel");
23
+ var experimental_serverChannel = async (channel, options) => {
24
+ let { disableTelemetry } = await options.presets.apply("core", {});
25
+ return disableTelemetry || channel.on(ADDON_ONBOARDING_CHANNEL, ({ type, ...event }) => {
26
+ type === "telemetry" ? telemetry("addon-onboarding", { ...event, addonVersion: version }) : type === "survey" && telemetry("onboarding-survey", { ...event, addonVersion: version });
27
+ }), channel;
28
+ };
39
29
  export {
40
30
  experimental_serverChannel
41
31
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/addon-onboarding",
3
- "version": "10.1.0-alpha.9",
3
+ "version": "10.1.0-beta.0",
4
4
  "description": "Storybook Onboarding: Help new users learn how to write stories",
5
5
  "keywords": [
6
6
  "storybook",
@@ -52,14 +52,14 @@
52
52
  },
53
53
  "devDependencies": {
54
54
  "@neoconfetti/react": "^1.0.0",
55
- "@storybook/icons": "^1.6.0",
55
+ "@storybook/icons": "^2.0.0",
56
56
  "react": "^18.2.0",
57
57
  "react-dom": "^18.2.0",
58
58
  "react-joyride": "^2.8.2",
59
59
  "typescript": "^5.8.3"
60
60
  },
61
61
  "peerDependencies": {
62
- "storybook": "^10.1.0-alpha.9"
62
+ "storybook": "^10.1.0-beta.0"
63
63
  },
64
64
  "publishConfig": {
65
65
  "access": "public"