diff --git a/apps/nextjs/.storybook/decorators/AnalyticsDecorator.tsx b/apps/nextjs/.storybook/decorators/AnalyticsDecorator.tsx new file mode 100644 index 000000000..7ea156986 --- /dev/null +++ b/apps/nextjs/.storybook/decorators/AnalyticsDecorator.tsx @@ -0,0 +1,35 @@ +import React from "react"; + +import type { Decorator } from "@storybook/react"; +import { fn } from "@storybook/test"; + +import { + analyticsContext, + type AnalyticsContext, +} from "../../src/components/ContextProviders/AnalyticsProvider"; + +declare module "@storybook/csf" { + interface Parameters { + analyticsContext?: Partial; + } +} + +export const AnalyticsDecorator: Decorator = (Story, { parameters }) => { + return ( + + + + ); +}; diff --git a/apps/nextjs/.storybook/decorators/ChatDecorator.tsx b/apps/nextjs/.storybook/decorators/ChatDecorator.tsx new file mode 100644 index 000000000..38caa8d10 --- /dev/null +++ b/apps/nextjs/.storybook/decorators/ChatDecorator.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +import type { Decorator } from "@storybook/react"; + +import { + ChatContext, + type ChatContextProps, +} from "../../src/components/ContextProviders/ChatProvider"; + +declare module "@storybook/csf" { + interface Parameters { + chatContext?: Partial; + } +} + +export const ChatDecorator: Decorator = (Story, { parameters }) => ( + + + +); diff --git a/apps/nextjs/.storybook/decorators/DemoDecorator.tsx b/apps/nextjs/.storybook/decorators/DemoDecorator.tsx new file mode 100644 index 000000000..111525528 --- /dev/null +++ b/apps/nextjs/.storybook/decorators/DemoDecorator.tsx @@ -0,0 +1,62 @@ +import React from "react"; + +import type { Decorator } from "@storybook/react"; +import invariant from "tiny-invariant"; + +import { + DemoContext, + type DemoContextProps, +} from "@/components/ContextProviders/Demo"; + +declare module "@storybook/csf" { + interface Parameters { + demoContext?: DemoContextProps; + } +} + +export const DemoDecorator: Decorator = (Story, { parameters }) => { + const value = parameters.demoContext; + invariant( + value, + "DemoDecorator requires a DemoContext. Please call ...demoParams() in the parameters", + ); + + return ( + + + + ); +}; + +const demoBase: DemoContextProps["demo"] = { + appSessionsRemaining: 2, + appSessionsPerMonth: 3, + contactHref: "https://share.hsforms.com/1R9ulYSNPQgqElEHde3KdhAbvumd", +}; + +type DemoParams = { + isDemoUser: boolean; + demo?: Partial; + isSharingEnabled?: boolean; +}; +export const demoParams = ( + args: DemoParams, +): { demoContext: DemoContextProps } => { + const isSharingEnabled = args.isSharingEnabled ?? true; + + const context: DemoContextProps = args.isDemoUser + ? { + isDemoUser: true, + demo: { ...demoBase, ...args.demo }, + isSharingEnabled, + } + : { + isDemoUser: false, + demo: undefined, + isSharingEnabled, + }; + + return { + demoContext: context, + }; +}; diff --git a/apps/nextjs/.storybook/decorators/DialogContentDecorator.tsx b/apps/nextjs/.storybook/decorators/DialogContentDecorator.tsx new file mode 100644 index 000000000..052d7598b --- /dev/null +++ b/apps/nextjs/.storybook/decorators/DialogContentDecorator.tsx @@ -0,0 +1,30 @@ +import React from "react"; + +import type { Decorator } from "@storybook/react"; +import { fn } from "@storybook/test"; + +import type { DialogTypes } from "../../src/components/AppComponents/Chat/Chat/types"; +import { DialogContext } from "../../src/components/AppComponents/DialogContext"; + +declare module "@storybook/csf" { + interface Parameters { + dialogWindow?: DialogTypes; + } +} + +export const DialogContentDecorator: Decorator = (Story, { parameters }) => { + return ( + + + + ); +}; diff --git a/apps/nextjs/.storybook/decorators/LessonPlanTrackingDecorator.tsx b/apps/nextjs/.storybook/decorators/LessonPlanTrackingDecorator.tsx new file mode 100644 index 000000000..96b6b46dc --- /dev/null +++ b/apps/nextjs/.storybook/decorators/LessonPlanTrackingDecorator.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +import type { Decorator } from "@storybook/react"; +import { fn } from "@storybook/test"; + +import { LessonPlanTrackingContext } from "../../src/lib/analytics/lessonPlanTrackingContext"; + +export const LessonPlanTrackingDecorator: Decorator = (Story) => ( + + + +); diff --git a/apps/nextjs/.storybook/decorators/RadixThemeDecorator.tsx b/apps/nextjs/.storybook/decorators/RadixThemeDecorator.tsx index 590c5bf62..457a5a501 100644 --- a/apps/nextjs/.storybook/decorators/RadixThemeDecorator.tsx +++ b/apps/nextjs/.storybook/decorators/RadixThemeDecorator.tsx @@ -1,8 +1,9 @@ import React from "react"; import { Theme } from "@radix-ui/themes"; +import { Decorator } from "@storybook/react"; -export const RadixThemeDecorator = (Story: React.ComponentType) => ( +export const RadixThemeDecorator: Decorator = (Story) => ( diff --git a/apps/nextjs/.storybook/decorators/SidebarDecorator.tsx b/apps/nextjs/.storybook/decorators/SidebarDecorator.tsx new file mode 100644 index 000000000..6d3f44644 --- /dev/null +++ b/apps/nextjs/.storybook/decorators/SidebarDecorator.tsx @@ -0,0 +1,25 @@ +import React from "react"; + +import type { Decorator } from "@storybook/react"; +import { fn } from "@storybook/test"; + +import { SidebarContext } from "../../src/lib/hooks/use-sidebar"; + +declare module "@storybook/csf" { + interface Parameters { + // Please fill out as we add configuration + sidebarContext?: {}; + } +} + +export const SidebarDecorator: Decorator = (Story) => ( + + + +); diff --git a/apps/nextjs/.storybook/preview.tsx b/apps/nextjs/.storybook/preview.tsx index 55f606d81..59f9ee53d 100644 --- a/apps/nextjs/.storybook/preview.tsx +++ b/apps/nextjs/.storybook/preview.tsx @@ -54,13 +54,7 @@ const preview: Preview = { loaders: [mswLoader], }; -// Providers not currently used -// - CookieConsentProvider -// - DemoProvider -// - LessonPlanTrackingProvider -// - DialogProvider -// - SidebarProvider -// - ChatModerationProvider +// NOTE: See ./decorators for more decorators available to use in stories export const decorators: Decorator[] = [ RadixThemeDecorator, diff --git a/apps/nextjs/src/app/aila/[id]/download/DownloadView.stories.tsx b/apps/nextjs/src/app/aila/[id]/download/DownloadView.stories.tsx index 4cacabe31..164aaa6e6 100644 --- a/apps/nextjs/src/app/aila/[id]/download/DownloadView.stories.tsx +++ b/apps/nextjs/src/app/aila/[id]/download/DownloadView.stories.tsx @@ -1,7 +1,8 @@ import type { AilaPersistedChat } from "@oakai/aila/src/protocol/schema"; import type { Meta, StoryObj } from "@storybook/react"; -import { chromaticParams } from "../../../../../.storybook/chromatic"; +import { chromaticParams } from "@/storybook/chromatic"; + import { DemoProvider } from "../../../../../src/components/ContextProviders/Demo"; import { DownloadContent } from "./DownloadView"; diff --git a/apps/nextjs/src/app/aila/[id]/share/index.stories.tsx b/apps/nextjs/src/app/aila/[id]/share/index.stories.tsx index b15660f44..2136dbd62 100644 --- a/apps/nextjs/src/app/aila/[id]/share/index.stories.tsx +++ b/apps/nextjs/src/app/aila/[id]/share/index.stories.tsx @@ -1,7 +1,8 @@ import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import type { Meta, StoryObj } from "@storybook/react"; -import { chromaticParams } from "../../../../../.storybook/chromatic"; +import { chromaticParams } from "@/storybook/chromatic"; + import ShareChat from "./"; const meta: Meta = { diff --git a/apps/nextjs/src/app/aila/help/index.stories.tsx b/apps/nextjs/src/app/aila/help/index.stories.tsx index 6f1a40e2f..2fabd3d90 100644 --- a/apps/nextjs/src/app/aila/help/index.stories.tsx +++ b/apps/nextjs/src/app/aila/help/index.stories.tsx @@ -1,9 +1,9 @@ import type { Meta, StoryObj } from "@storybook/react"; import { DemoProvider } from "@/components/ContextProviders/Demo"; +import { chromaticParams } from "@/storybook/chromatic"; import { HelpContent } from "."; -import { chromaticParams } from "../../../../.storybook/chromatic"; const meta: Meta = { title: "Pages/Chat/Help", diff --git a/apps/nextjs/src/app/faqs/index.stories.tsx b/apps/nextjs/src/app/faqs/index.stories.tsx index 0fa4b4da3..35b3504b9 100644 --- a/apps/nextjs/src/app/faqs/index.stories.tsx +++ b/apps/nextjs/src/app/faqs/index.stories.tsx @@ -1,7 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react"; +import { chromaticParams } from "@/storybook/chromatic"; + import { FAQPageContent } from "."; -import { chromaticParams } from "../../../.storybook/chromatic"; const meta: Meta = { title: "Pages/FAQs", diff --git a/apps/nextjs/src/app/home-page.stories.tsx b/apps/nextjs/src/app/home-page.stories.tsx index 15fb1e328..3e370c474 100644 --- a/apps/nextjs/src/app/home-page.stories.tsx +++ b/apps/nextjs/src/app/home-page.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { chromaticParams } from "../../.storybook/chromatic"; +import { chromaticParams } from "@/storybook/chromatic"; + import { HomePageContent } from "./home-page"; const meta: Meta = { diff --git a/apps/nextjs/src/app/legal/[slug]/legal.stories.tsx b/apps/nextjs/src/app/legal/[slug]/legal.stories.tsx index 85a0ab9d2..928cfee17 100644 --- a/apps/nextjs/src/app/legal/[slug]/legal.stories.tsx +++ b/apps/nextjs/src/app/legal/[slug]/legal.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { chromaticParams } from "../../../../.storybook/chromatic"; +import { chromaticParams } from "@/storybook/chromatic"; + import { LegalContent } from "./legal"; const meta: Meta = { diff --git a/apps/nextjs/src/app/legal/account-locked/account-locked.stories.tsx b/apps/nextjs/src/app/legal/account-locked/account-locked.stories.tsx index 846a1a046..3376e8996 100644 --- a/apps/nextjs/src/app/legal/account-locked/account-locked.stories.tsx +++ b/apps/nextjs/src/app/legal/account-locked/account-locked.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { chromaticParams } from "../../../../.storybook/chromatic"; +import { chromaticParams } from "@/storybook/chromatic"; + import { AccountLocked } from "./account-locked"; const meta: Meta = { diff --git a/apps/nextjs/src/app/prompts/prompts.stories.tsx b/apps/nextjs/src/app/prompts/prompts.stories.tsx index e3bbea25c..97b18aea5 100644 --- a/apps/nextjs/src/app/prompts/prompts.stories.tsx +++ b/apps/nextjs/src/app/prompts/prompts.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { chromaticParams } from "../../../.storybook/chromatic"; +import { chromaticParams } from "@/storybook/chromatic"; + import { PromptsContent } from "./prompts"; const meta: Meta = { diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.stories.tsx index 6400caf3a..bb02e9628 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.stories.tsx @@ -1,34 +1,25 @@ import type { Meta, StoryObj } from "@storybook/react"; import type { ChatContextProps } from "@/components/ContextProviders/ChatProvider"; -import { ChatContext } from "@/components/ContextProviders/ChatProvider"; +import { ChatDecorator } from "@/storybook/decorators/ChatDecorator"; import LessonPlanDisplay from "./chat-lessonPlanDisplay"; -const ChatDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); +const chatContext: Partial = { + id: "123", + lastModeration: null, + messages: [], + lessonPlan: { + title: "About Frogs", + keyStage: "Key Stage 2", + subject: "Science", + topic: "Amphibians", + basedOn: { title: "Frogs in Modern Britain" }, + learningOutcome: + "To understand the importance of frogs in British society and culture", + }, + ailaStreamingStatus: "Idle", +}; const meta: Meta = { title: "Components/LessonPlan/LessonPlanDisplay", @@ -47,7 +38,7 @@ type Story = StoryObj; export const Default: Story = { args: {}, parameters: { - chatContext: {}, + chatContext, }, }; @@ -55,6 +46,7 @@ export const Loading: Story = { args: {}, parameters: { chatContext: { + ...chatContext, lessonPlan: {}, }, }, @@ -64,7 +56,9 @@ export const WithModeration: Story = { args: {}, parameters: { chatContext: { + ...chatContext, lastModeration: { + id: "123", categories: ["l/strong-language"], }, }, diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.stories.tsx index 8f2e48dbd..e4e63c3f9 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.stories.tsx @@ -1,25 +1,9 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { - ChatContext, - type ChatContextProps, -} from "@/components/ContextProviders/ChatProvider"; +import { ChatDecorator } from "@/storybook/decorators/ChatDecorator"; import ChatLhsHeader from "./chat-lhs-header"; -const ChatDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - const meta: Meta = { title: "Components/Chat/ChatLhsHeader", component: ChatLhsHeader, @@ -28,6 +12,11 @@ const meta: Meta = { args: { showStreamingStatus: false, }, + parameters: { + chatContext: { + ailaStreamingStatus: "Idle", + }, + }, }; export default meta; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-list/in-chat-download-buttons.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-list/in-chat-download-buttons.stories.tsx index 69a678a93..179e99445 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-list/in-chat-download-buttons.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-list/in-chat-download-buttons.stories.tsx @@ -1,21 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { DemoContext } from "@/components/ContextProviders/Demo"; +import { + DemoDecorator, + demoParams, +} from "@/storybook/decorators/DemoDecorator"; import { InChatDownloadButtons } from "./in-chat-download-buttons"; -const DemoDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - const meta: Meta = { title: "Components/Chat/InChatDownloadButtons", component: InChatDownloadButtons, @@ -24,6 +15,9 @@ const meta: Meta = { id: "test-chat-id", }, decorators: [DemoDecorator], + parameters: { + ...demoParams({ isDemoUser: true }), + }, }; export default meta; @@ -33,8 +27,6 @@ export const Default: Story = {}; export const SharingDisabled: Story = { parameters: { - demoContext: { - isSharingEnabled: false, - }, + ...demoParams({ isDemoUser: true, isSharingEnabled: false }), }, }; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-panel.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-panel.stories.tsx index c2cff638c..989c13892 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-panel.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-panel.stories.tsx @@ -1,68 +1,31 @@ +import type { Message } from "@oakai/aila/src/core/chat"; import type { Meta, StoryObj } from "@storybook/react"; -import { - ChatContext, - type ChatContextProps, -} from "@/components/ContextProviders/ChatProvider"; -import { lessonPlanTrackingContext } from "@/lib/analytics/lessonPlanTrackingContext"; -import { SidebarContext } from "@/lib/hooks/use-sidebar"; +import { ChatDecorator } from "@/storybook/decorators/ChatDecorator"; +import { LessonPlanTrackingDecorator } from "@/storybook/decorators/LessonPlanTrackingDecorator"; +import { SidebarDecorator } from "@/storybook/decorators/SidebarDecorator"; import { ChatPanel } from "./chat-panel"; -const DummyMessage = {}; - -const ChatDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - -const LessonPlanTrackingContextDecorator: Story["decorators"] = (Story) => ( - {}, - onClickRetry: () => {}, - onClickStartFromExample: () => {}, - onClickStartFromFreeText: () => {}, - onStreamFinished: () => {}, - onSubmitText: () => {}, - }} - > - - -); - -const SidebarContextDecorator: Story["decorators"] = (Story) => ( - {}, - isLoading: false, - isSidebarOpen: false, - }} - > - - -); +const DummyMessage: Message = { + content: "Dummy message", + id: "123", + role: "user", +}; const meta: Meta = { title: "Components/Chat/ChatPanel", component: ChatPanel, tags: ["autodocs"], - decorators: [ - ChatDecorator, - LessonPlanTrackingContextDecorator, - SidebarContextDecorator, - ], + decorators: [ChatDecorator, LessonPlanTrackingDecorator, SidebarDecorator], args: { isDemoLocked: false, }, + parameters: { + chatContext: { + messages: [DummyMessage], + }, + }, }; export default meta; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.stories.tsx index a3baefe15..9ec91fd30 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.stories.tsx @@ -1,48 +1,27 @@ +import type { Message } from "@oakai/aila/src/core/chat"; import type { Meta, StoryObj } from "@storybook/react"; -import { - ChatContext, - type ChatContextProps, -} from "@/components/ContextProviders/ChatProvider"; -import { lessonPlanTrackingContext } from "@/lib/analytics/lessonPlanTrackingContext"; +import { ChatDecorator } from "@/storybook/decorators/ChatDecorator"; +import { LessonPlanTrackingDecorator } from "@/storybook/decorators/LessonPlanTrackingDecorator"; import ChatQuickButtons from "./chat-quick-buttons"; -const DummyMessage = {}; - -const ChatDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - -const LessonPlanTrackingContextDecorator: Story["decorators"] = (Story) => ( - {}, - onClickRetry: () => {}, - onClickStartFromExample: () => {}, - onClickStartFromFreeText: () => {}, - onStreamFinished: () => {}, - onSubmitText: () => {}, - }} - > - - -); +const DummyMessage: Message = { + content: "Dummy message", + id: "123", + role: "user", +}; const meta: Meta = { title: "Components/Chat/ChatQuickButtons", component: ChatQuickButtons, tags: ["autodocs"], - decorators: [ChatDecorator, LessonPlanTrackingContextDecorator], + decorators: [ChatDecorator, LessonPlanTrackingDecorator], + parameters: { + chatContext: { + messages: [DummyMessage], + }, + }, }; export default meta; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-start.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-start.stories.tsx index 1907a21c2..cb44eea25 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-start.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-start.stories.tsx @@ -1,8 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react"; import { DemoProvider } from "@/components/ContextProviders/Demo"; +import { chromaticParams } from "@/storybook/chromatic"; -import { chromaticParams } from "../../../../.storybook/chromatic"; import { DialogProvider } from "../DialogContext"; import { ChatStart } from "./chat-start"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/index.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/index.stories.tsx index 8c116fdae..d425661e9 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/index.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/index.stories.tsx @@ -1,8 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { userEvent, within } from "@storybook/test"; -import type { ChatContextProps } from "@/components/ContextProviders/ChatProvider"; -import { ChatContext } from "@/components/ContextProviders/ChatProvider"; +import { ChatDecorator } from "@/storybook/decorators/ChatDecorator"; import DropDownSection from "./"; @@ -10,31 +9,6 @@ const MAX_INT32 = 2 ** 31 - 1; const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); -const ChatDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - const meta: Meta = { title: "Components/LessonPlan/DropDownSection", component: DropDownSection, @@ -47,6 +21,23 @@ const meta: Meta = { streamingTimeout: 0, }, decorators: [ChatDecorator], + parameters: { + chatContext: { + id: "123", + lastModeration: null, + messages: [], + lessonPlan: { + title: "About Frogs", + keyStage: "Key Stage 2", + subject: "Science", + topic: "Amphibians", + basedOn: { id: "testId", title: "Frogs in Modern Britain" }, + learningOutcome: + "To understand the importance of frogs in British society and culture", + }, + ailaStreamingStatus: "Idle", + }, + }, }; export default meta; diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/MobileExportButtons.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/MobileExportButtons.stories.tsx index 9d220d9c0..542377fe5 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/MobileExportButtons.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/MobileExportButtons.stories.tsx @@ -1,37 +1,14 @@ import type { Meta, StoryObj } from "@storybook/react"; -import type { ChatContextProps } from "@/components/ContextProviders/ChatProvider"; -import { ChatContext } from "@/components/ContextProviders/ChatProvider"; -import { DemoContext } from "@/components/ContextProviders/Demo"; +import { chromaticParams } from "@/storybook/chromatic"; +import { ChatDecorator } from "@/storybook/decorators/ChatDecorator"; +import { + DemoDecorator, + demoParams, +} from "@/storybook/decorators/DemoDecorator"; -import { chromaticParams } from "../../../../../.storybook/chromatic"; import { MobileExportButtons } from "./MobileExportButtons"; -const ChatDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - -const DemoDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - const meta: Meta = { title: "Components/LessonPlan/MobileExportButtons", component: MobileExportButtons, @@ -42,6 +19,10 @@ const meta: Meta = { defaultViewport: "mobile1", }, ...chromaticParams(["mobile"]), + ...demoParams({ isDemoUser: false }), + chatContext: { + id: "123", + }, }, args: { closeMobileLessonPullOut: () => {}, @@ -55,9 +36,6 @@ export const Default: Story = {}; export const SharingDisabled: Story = { parameters: { - demoContext: { - isDemoUser: true, - isSharingEnabled: false, - }, + ...demoParams({ isDemoUser: true, isSharingEnabled: false }), }, }; diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/index.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/index.stories.tsx index fa246ab2b..60000c652 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/index.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/index.stories.tsx @@ -1,39 +1,13 @@ import type { Meta, StoryObj } from "@storybook/react"; -import type { ChatContextProps } from "@/components/ContextProviders/ChatProvider"; -import { ChatContext } from "@/components/ContextProviders/ChatProvider"; -import { DemoContext } from "@/components/ContextProviders/Demo"; +import { ChatDecorator } from "@/storybook/decorators/ChatDecorator"; +import { + DemoDecorator, + demoParams, +} from "@/storybook/decorators/DemoDecorator"; -import { chromaticParams } from "../../../../../.storybook/chromatic"; import ExportButtons from "./"; -const ChatDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - -const DemoDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - const meta: Meta = { title: "Components/LessonPlan/ExportButtons", component: ExportButtons, @@ -43,6 +17,14 @@ const meta: Meta = { sectionRefs: {}, documentContainerRef: { current: null }, }, + parameters: { + chatContext: { + id: "123", + isStreaming: false, + lessonPlan: {}, + }, + ...demoParams({ isDemoUser: false }), + }, }; export default meta; @@ -60,9 +42,6 @@ export const IsStreaming: Story = { export const SharingDisabled: Story = { parameters: { - demoContext: { - isDemoUser: true, - isSharingEnabled: false, - }, + ...demoParams({ isDemoUser: true, isSharingEnabled: false }), }, }; diff --git a/apps/nextjs/src/components/AppComponents/Chat/header.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/header.stories.tsx index d563f73cc..c92263948 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/header.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/header.stories.tsx @@ -1,22 +1,13 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { DemoContext } from "@/components/ContextProviders/Demo"; +import { chromaticParams } from "@/storybook/chromatic"; +import { + DemoDecorator, + demoParams, +} from "@/storybook/decorators/DemoDecorator"; -import { chromaticParams } from "../../../../.storybook/chromatic"; import { Header } from "./header"; -const DemoDecorator: Story["decorators"] = (Story, { parameters }) => ( - - - -); - const meta: Meta = { title: "Components/Layout/ChatHeader", component: Header, @@ -29,6 +20,7 @@ const meta: Meta = { height: "150px", }, }, + ...demoParams({ isDemoUser: false }), }, }; @@ -42,11 +34,13 @@ export const Default: Story = { export const DemoUser: Story = { args: {}, parameters: { - demoContext: { + ...demoParams({ isDemoUser: true, - appSessionsPerMonth: 3, - appSessionsRemaining: 2, - }, + demo: { + appSessionsPerMonth: 3, + appSessionsRemaining: 2, + }, + }), ...chromaticParams(["desktop", "desktop-wide"]), }, }; @@ -54,10 +48,12 @@ export const DemoUser: Story = { export const DemoLoading: Story = { args: {}, parameters: { - demoContext: { + ...demoParams({ isDemoUser: true, - appSessionsPerMonth: 3, - appSessionsRemaining: undefined, - }, + demo: { + appSessionsPerMonth: 3, + appSessionsRemaining: undefined, + }, + }), }, }; diff --git a/apps/nextjs/src/components/AppComponents/Chat/header.tsx b/apps/nextjs/src/components/AppComponents/Chat/header.tsx index 24ee90044..3fdf0b708 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/header.tsx @@ -22,7 +22,7 @@ import { OpenSideBarButton } from "./open-side-bar-button"; import { UserOrLogin } from "./user-or-login"; export function Header() { - const demo = useDemoUser(); + const { isDemoUser, demo } = useDemoUser(); // Check whether clerk metadata has loaded to prevent the banner from flashing const clerkMetadata = useClerkDemoMetadata(); @@ -36,7 +36,7 @@ export function Header() { $zIndex={"banner"} $width={"100%"} > - {clerkMetadata.isSet && demo.isDemoUser && ( + {clerkMetadata.isSet && isDemoUser && ( (null); @@ -52,14 +50,17 @@ export function DemoProvider({ children }: Readonly) { isDemoUser ? { isDemoUser, - appSessionsRemaining, - appSessionsPerMonth: DEMO_APP_SESSIONS_PER_30D, - contactHref: - "https://share.hsforms.com/1R9ulYSNPQgqElEHde3KdhAbvumd", + demo: { + appSessionsRemaining, + appSessionsPerMonth: DEMO_APP_SESSIONS_PER_30D, + contactHref: + "https://share.hsforms.com/1R9ulYSNPQgqElEHde3KdhAbvumd", + }, isSharingEnabled, } : { isDemoUser, + demo: undefined, isSharingEnabled, }, [isDemoUser, appSessionsRemaining, isSharingEnabled], diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx index 2f033ae50..3f7192aaf 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx @@ -46,18 +46,18 @@ const CreatingChatDialog = ({ submit, closeDialog, }: CreatingChatDialogProps) => { - const demo = useDemoUser(); + const { isDemoUser, demo } = useDemoUser(); const [isSubmitting, setIsSubmitting] = useState(false); const [appSessionsRemaining, setAppSessionsRemaining] = useState< number | undefined - >(demo.isDemoUser ? demo.appSessionsRemaining : undefined); + >(isDemoUser ? demo.appSessionsRemaining : undefined); // Don't update the remaining count while submitting as the mutation will change it useEffect(() => { - if (demo.isDemoUser && !isSubmitting) { + if (isDemoUser && !isSubmitting) { setAppSessionsRemaining(demo.appSessionsRemaining); } - }, [isSubmitting, demo]); + }, [isSubmitting, isDemoUser, demo]); const createAppSession = useCallback(() => { if (!submit) { @@ -74,7 +74,7 @@ const CreatingChatDialog = ({ } }, [submit]); - if (!demo.isDemoUser) { + if (!isDemoUser) { return null; } diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoShareLockedDialog.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoShareLockedDialog.tsx index d409cc28b..0ce7ad715 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoShareLockedDialog.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoShareLockedDialog.tsx @@ -29,9 +29,9 @@ const DemoShareLockedDialog = ({ }: { readonly closeDialog: () => void; }) => { - const demo = useDemoUser(); + const { isDemoUser, demo } = useDemoUser(); - if (!demo.isDemoUser) { + if (!isDemoUser) { return null; } diff --git a/apps/nextjs/src/components/DialogControl/DialogContents.stories.tsx b/apps/nextjs/src/components/DialogControl/DialogContents.stories.tsx index 77e245f78..a9b212545 100644 --- a/apps/nextjs/src/components/DialogControl/DialogContents.stories.tsx +++ b/apps/nextjs/src/components/DialogControl/DialogContents.stories.tsx @@ -1,32 +1,19 @@ import type { Meta, StoryObj } from "@storybook/react"; +import { fn } from "@storybook/test"; import { http, HttpResponse } from "msw"; +import { SurveyQuestionType, SurveyType } from "posthog-js"; +import type { PostHog } from "posthog-js"; -import type { AnalyticsContext } from "@/components/ContextProviders/AnalyticsProvider"; -import { analyticsContext } from "@/components/ContextProviders/AnalyticsProvider"; +import { AnalyticsDecorator } from "@/storybook/decorators/AnalyticsDecorator"; +import { DialogContentDecorator } from "@/storybook/decorators/DialogContentDecorator"; -import { DialogContext } from "../AppComponents/DialogContext"; import { DemoProvider } from "../ContextProviders/Demo"; import DialogContents from "./DialogContents"; const meta: Meta = { title: "Components/Dialogs/DialogContents", component: DialogContents, - decorators: (Story, { parameters }) => { - return ( - {}, - dialogProps: {}, - setDialogProps: () => {}, - openSidebar: false, - setOpenSidebar: () => {}, - }} - > - - - ); - }, + decorators: [DialogContentDecorator], }; export default meta; @@ -115,53 +102,39 @@ export const DemoInterstitialLimited: Story = { export const Feedback: Story = { args: {}, + decorators: [AnalyticsDecorator], parameters: { dialogWindow: "feedback", - }, - decorators: (Story) => { - return ( - {}, - trackEvent: () => {}, - identify: () => {}, - reset: () => {}, - page: () => {}, - posthogAiBetaClient: { - capture: () => {}, - getSurveys: (fn) => { - fn([ - { - id: "01917ac7-e417-0000-0c86-99ef890e6807", - name: "End of Aila generation survey launch aug24", - type: "api", - questions: [ - { - type: "rating", - question: - "How would you rate the structure and content of this lesson plan?", - }, - { - type: "rating", - question: - "How would you rate the ease of creating this lesson with Aila?", - }, - { - type: "open", - question: - "What suggestions do you have to improve the lesson planning experience with Aila?", - }, - ], - }, - ]); - }, + analyticsContext: { + posthogAiBetaClient: { + capture: fn(), + getSurveys: (fn) => { + fn([ + { + id: "01917ac7-e417-0000-0c86-99ef890e6807", + name: "End of Aila generation survey launch aug24", + type: SurveyType.API, + questions: [ + { + type: SurveyQuestionType.Rating, + question: + "How would you rate the structure and content of this lesson plan?", + }, + { + type: SurveyQuestionType.Rating, + question: + "How would you rate the ease of creating this lesson with Aila?", + }, + { + type: SurveyQuestionType.Open, + question: + "What suggestions do you have to improve the lesson planning experience with Aila?", + }, + ], }, - } as unknown as AnalyticsContext - } - > - - - ); + ]); + }, + } as unknown as PostHog, + }, }, }; diff --git a/apps/nextjs/src/components/Onboarding/AcceptTermsForm.stories.tsx b/apps/nextjs/src/components/Onboarding/AcceptTermsForm.stories.tsx index ca173a4d6..efc0e2527 100644 --- a/apps/nextjs/src/components/Onboarding/AcceptTermsForm.stories.tsx +++ b/apps/nextjs/src/components/Onboarding/AcceptTermsForm.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { chromaticParams } from "../../../.storybook/chromatic"; +import { chromaticParams } from "@/storybook/chromatic"; + import { AcceptTermsForm } from "./AcceptTermsForm"; const meta: Meta = { diff --git a/apps/nextjs/src/components/Onboarding/LegacyUpgradeNotice.stories.tsx b/apps/nextjs/src/components/Onboarding/LegacyUpgradeNotice.stories.tsx index 29b5d1ee6..b2e2fccf9 100644 --- a/apps/nextjs/src/components/Onboarding/LegacyUpgradeNotice.stories.tsx +++ b/apps/nextjs/src/components/Onboarding/LegacyUpgradeNotice.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { chromaticParams } from "../../../.storybook/chromatic"; +import { chromaticParams } from "@/storybook/chromatic"; + import { LegacyUpgradeNotice } from "./LegacyUpgradeNotice"; const meta: Meta = { diff --git a/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx b/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx index d664640d4..e18c92d52 100644 --- a/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx +++ b/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx @@ -21,7 +21,7 @@ type OnStreamFinishedProps = { nextLesson: LooseLessonPlan; messages: Message[]; }; -type LessonPlanTrackingContext = { +type LessonPlanTrackingContextProps = { onStreamFinished: (props: OnStreamFinishedProps) => void; onSubmitText: (text: string) => void; onClickContinue: () => void; @@ -30,8 +30,8 @@ type LessonPlanTrackingContext = { onClickStartFromFreeText: (text: string) => void; }; -export const lessonPlanTrackingContext = - createContext(null); +export const LessonPlanTrackingContext = + createContext(null); export type LessonPlanTrackingProviderProps = Readonly<{ readonly children?: React.ReactNode; @@ -91,7 +91,7 @@ const LessonPlanTrackingProvider: FC = ({ setUserMessageContent(text); }, []); - const value: LessonPlanTrackingContext = useMemo( + const value: LessonPlanTrackingContextProps = useMemo( () => ({ onStreamFinished, onSubmitText, @@ -111,14 +111,14 @@ const LessonPlanTrackingProvider: FC = ({ ); return ( - + {children} - + ); }; export const useLessonPlanTracking = () => { - const context = useContext(lessonPlanTrackingContext); + const context = useContext(LessonPlanTrackingContext); if (!context) { throw new Error( "useLessonPlanTracking must be used within a LessonPlanTrackingProvider", diff --git a/apps/nextjs/src/mocks/clerk/ClerkDecorator.tsx b/apps/nextjs/src/mocks/clerk/ClerkDecorator.tsx index 328cec32f..4bb3a7b85 100644 --- a/apps/nextjs/src/mocks/clerk/ClerkDecorator.tsx +++ b/apps/nextjs/src/mocks/clerk/ClerkDecorator.tsx @@ -2,6 +2,12 @@ import type { Decorator } from "@storybook/react"; import { ClerkProvider } from "./nextjsComponents"; +declare module "@storybook/csf" { + interface Parameters { + auth?: "loading" | "signedIn" | "signedInDemo" | "signedOut"; + } +} + export const ClerkDecorator: Decorator = (Story, { parameters }) => { return ( diff --git a/apps/nextjs/tsconfig.json b/apps/nextjs/tsconfig.json index 8b235fd0e..c06a83690 100644 --- a/apps/nextjs/tsconfig.json +++ b/apps/nextjs/tsconfig.json @@ -23,7 +23,8 @@ "@/app/*": ["app/*"], "@/lib/*": ["lib/*"], "@/utils/*": ["utils/*"], - "@/assets/*": ["assets/*"] + "@/assets/*": ["assets/*"], + "@/storybook/*": ["../.storybook/*"] }, "plugins": [ {