1+ import { PageMocking , type MockScenario } from '@clerk/msw' ;
12import * as l from '../../localizations' ;
23import type { Clerk as ClerkType } from '../' ;
3-
4- const AVAILABLE_LOCALES = Object . keys ( l ) as ( keyof typeof l ) [ ] ;
5-
6- function fillLocalizationSelect ( ) {
7- const select = document . getElementById ( 'localizationSelect' ) as HTMLSelectElement ;
8-
9- for ( const locale of AVAILABLE_LOCALES ) {
10- if ( locale === 'enUS' ) {
11- select . add ( new Option ( locale , locale , true , true ) ) ;
12- continue ;
13- }
14-
15- select . add ( new Option ( locale , locale ) ) ;
16- }
17- }
4+ import * as scenarios from './scenarios' ;
185
196interface ComponentPropsControl {
207 setProps : ( props : unknown ) => void ;
218 getProps : ( ) => any | null ;
229}
2310
11+ interface ScenarioControls {
12+ setScenario : ( scenario : AvailableScenario | null ) => void ;
13+ availableScenarios : typeof AVAILABLE_SCENARIOS ;
14+ }
15+
16+ const COMPONENT_PROPS_NAMESPACE = 'clerk-js-sandbox' ;
17+
18+ const AVAILABLE_LOCALES = Object . keys ( l ) as ( keyof typeof l ) [ ] ;
19+
2420const AVAILABLE_COMPONENTS = [
2521 'clerk' , // While not a component, we want to support passing options to the Clerk class.
2622 'signIn' ,
@@ -39,17 +35,57 @@ const AVAILABLE_COMPONENTS = [
3935 'taskChooseOrganization' ,
4036 'taskResetPassword' ,
4137] as const ;
38+ type AvailableComponent = ( typeof AVAILABLE_COMPONENTS ) [ number ] ;
4239
43- const COMPONENT_PROPS_NAMESPACE = 'clerk-js-sandbox' ;
40+ const AVAILABLE_SCENARIOS = Object . keys ( scenarios ) as ( keyof typeof scenarios ) [ ] ;
41+ type AvailableScenario = ( typeof AVAILABLE_SCENARIOS ) [ number ] ;
4442
45- const urlParams = new URL ( window . location . href ) . searchParams ;
46- for ( const [ component , encodedProps ] of urlParams . entries ( ) ) {
47- if ( AVAILABLE_COMPONENTS . includes ( component as ( typeof AVAILABLE_COMPONENTS ) [ number ] ) ) {
48- localStorage . setItem ( `${ COMPONENT_PROPS_NAMESPACE } -${ component } ` , encodedProps ) ;
43+ function fillLocalizationSelect ( ) {
44+ const select = document . getElementById ( 'localizationSelect' ) as HTMLSelectElement ;
45+
46+ for ( const locale of AVAILABLE_LOCALES ) {
47+ if ( locale === 'enUS' ) {
48+ select . add ( new Option ( locale , locale , true , true ) ) ;
49+ continue ;
50+ }
51+
52+ select . add ( new Option ( locale , locale ) ) ;
53+ }
54+ }
55+
56+ function getScenario ( ) : ( ( ) => MockScenario ) | null {
57+ const scenarioName = localStorage . getItem ( `${ COMPONENT_PROPS_NAMESPACE } -scenario` ) ;
58+ if ( scenarioName && AVAILABLE_SCENARIOS . includes ( scenarioName as AvailableScenario ) ) {
59+ return scenarios [ scenarioName as AvailableScenario ] ;
60+ }
61+ return null ;
62+ }
63+
64+ function setScenario ( scenario : AvailableScenario | null ) {
65+ if ( ! scenario ) {
66+ localStorage . removeItem ( `${ COMPONENT_PROPS_NAMESPACE } -scenario` ) ;
67+ const url = new URL ( window . location . href ) ;
68+ url . searchParams . delete ( 'scenario' ) ;
69+ window . location . href = url . toString ( ) ;
70+ return ;
71+ }
72+
73+ if ( ! AVAILABLE_SCENARIOS . includes ( scenario ) ) {
74+ throw new Error ( `Invalid scenario: "${ scenario } ". Available scenarios: ${ AVAILABLE_SCENARIOS . join ( ', ' ) } ` ) ;
4975 }
76+ localStorage . setItem ( `${ COMPONENT_PROPS_NAMESPACE } -scenario` , scenario ) ;
77+
78+ const url = new URL ( window . location . href ) ;
79+ url . searchParams . set ( 'scenario' , scenario ) ;
80+ window . location . href = url . toString ( ) ;
5081}
5182
52- function setComponentProps ( component : ( typeof AVAILABLE_COMPONENTS ) [ number ] , props : unknown ) {
83+ const scenarioControls : ScenarioControls = {
84+ setScenario,
85+ availableScenarios : AVAILABLE_SCENARIOS ,
86+ } ;
87+
88+ function setComponentProps ( component : AvailableComponent , props : unknown ) {
5389 const encodedProps = JSON . stringify ( props ) ;
5490
5591 const url = new URL ( window . location . href ) ;
@@ -58,7 +94,7 @@ function setComponentProps(component: (typeof AVAILABLE_COMPONENTS)[number], pro
5894 window . location . href = url . toString ( ) ;
5995}
6096
61- function getComponentProps ( component : ( typeof AVAILABLE_COMPONENTS ) [ number ] ) : unknown | null {
97+ function getComponentProps ( component : AvailableComponent ) : unknown | null {
6298 const url = new URL ( window . location . href ) ;
6399 const encodedProps = url . searchParams . get ( component ) ;
64100 if ( encodedProps ) {
@@ -73,7 +109,7 @@ function getComponentProps(component: (typeof AVAILABLE_COMPONENTS)[number]): un
73109 return null ;
74110}
75111
76- function buildComponentControls ( component : ( typeof AVAILABLE_COMPONENTS ) [ number ] ) : ComponentPropsControl {
112+ function buildComponentControls ( component : AvailableComponent ) : ComponentPropsControl {
77113 return {
78114 setProps ( props ) {
79115 setComponentProps ( component , props ) ;
@@ -84,7 +120,7 @@ function buildComponentControls(component: (typeof AVAILABLE_COMPONENTS)[number]
84120 } ;
85121}
86122
87- const componentControls : Record < ( typeof AVAILABLE_COMPONENTS ) [ number ] , ComponentPropsControl > = {
123+ const componentControls : Record < AvailableComponent , ComponentPropsControl > = {
88124 clerk : buildComponentControls ( 'clerk' ) ,
89125 signIn : buildComponentControls ( 'signIn' ) ,
90126 signUp : buildComponentControls ( 'signUp' ) ,
@@ -105,11 +141,21 @@ const componentControls: Record<(typeof AVAILABLE_COMPONENTS)[number], Component
105141
106142declare global {
107143 interface Window {
108- components : Record < ( typeof AVAILABLE_COMPONENTS ) [ number ] , ComponentPropsControl > ;
144+ components : Record < AvailableComponent , ComponentPropsControl > ;
145+ scenario : typeof scenarioControls ;
146+ AVAILABLE_SCENARIOS : Record < AvailableScenario , AvailableScenario > ;
109147 }
110148}
111149
112150window . components = componentControls ;
151+ window . scenario = scenarioControls ;
152+ window . AVAILABLE_SCENARIOS = AVAILABLE_SCENARIOS . reduce (
153+ ( acc , scenario ) => {
154+ acc [ scenario ] = scenario ;
155+ return acc ;
156+ } ,
157+ { } as Record < AvailableScenario , AvailableScenario > ,
158+ ) ;
113159
114160const Clerk = window . Clerk ;
115161function assertClerkIsLoaded ( c : ClerkType | undefined ) : asserts c is ClerkType {
@@ -118,8 +164,6 @@ function assertClerkIsLoaded(c: ClerkType | undefined): asserts c is ClerkType {
118164 }
119165}
120166
121- const app = document . getElementById ( 'app' ) as HTMLDivElement ;
122-
123167function mountIndex ( element : HTMLDivElement ) {
124168 assertClerkIsLoaded ( Clerk ) ;
125169 const user = Clerk . user ;
@@ -267,6 +311,17 @@ function otherOptions() {
267311 return { updateOtherOptions } ;
268312}
269313
314+ const urlParams = new URL ( window . location . href ) . searchParams ;
315+ for ( const [ component , encodedProps ] of urlParams . entries ( ) ) {
316+ if ( AVAILABLE_COMPONENTS . includes ( component as AvailableComponent ) ) {
317+ localStorage . setItem ( `${ COMPONENT_PROPS_NAMESPACE } -${ component } ` , encodedProps ) ;
318+ }
319+
320+ if ( component === 'scenario' && AVAILABLE_SCENARIOS . includes ( encodedProps as AvailableScenario ) ) {
321+ localStorage . setItem ( `${ COMPONENT_PROPS_NAMESPACE } -scenario` , encodedProps ) ;
322+ }
323+ }
324+
270325void ( async ( ) => {
271326 assertClerkIsLoaded ( Clerk ) ;
272327 fillLocalizationSelect ( ) ;
@@ -280,6 +335,8 @@ void (async () => {
280335 }
281336 } ) ;
282337
338+ const app = document . getElementById ( 'app' ) as HTMLDivElement ;
339+
283340 const routes = {
284341 '/' : ( ) => {
285342 mountIndex ( app ) ;
@@ -373,6 +430,17 @@ void (async () => {
373430 if ( route in routes ) {
374431 const renderCurrentRoute = routes [ route ] ;
375432 addCurrentRouteIndicator ( route ) ;
433+
434+ const scenario = getScenario ( ) ;
435+ if ( scenario ) {
436+ const mocking = new PageMocking ( {
437+ onStateChange : state => {
438+ console . log ( 'Mocking state changed:' , state ) ;
439+ } ,
440+ } ) ;
441+ await mocking . initialize ( route , { scenario } ) ;
442+ }
443+
376444 await Clerk . load ( {
377445 ...( componentControls . clerk . getProps ( ) ?? { } ) ,
378446 signInUrl : '/sign-in' ,
0 commit comments