Skip to content

Commit

Permalink
feat(clerk-js): Use TypeScript for sandbox (#4665)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstaley authored Nov 27, 2024
1 parent a18d45e commit b0d165a
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 192 deletions.
2 changes: 2 additions & 0 deletions .changeset/old-peaches-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
4 changes: 2 additions & 2 deletions packages/clerk-js/rspack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ const prodConfig = ({ mode, env, analysis }) => {
entryForVariant(variants.clerkBrowser),
isSandbox
? {
entry: { sandbox: './sandbox/app.js' },
entry: { sandbox: './sandbox/app.ts' },
plugins: [
new rspack.HtmlRspackPlugin({
minify: false,
Expand Down Expand Up @@ -530,7 +530,7 @@ const devConfig = ({ mode, env }) => {
// prettier-ignore
[variants.clerkBrowser]: merge(
entryForVariant(variants.clerkBrowser),
isSandbox ? { entry: { sandbox: './sandbox/app.js' } } : {},
isSandbox ? { entry: { sandbox: './sandbox/app.ts' } } : {},
common({ mode }),
commonForDev(),
),
Expand Down
189 changes: 0 additions & 189 deletions packages/clerk-js/sandbox/app.js

This file was deleted.

162 changes: 162 additions & 0 deletions packages/clerk-js/sandbox/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import type { Clerk as ClerkType } from '../';

interface ComponentPropsControl {
setProps: (props: unknown) => void;
getProps: () => any | null;
}

const AVAILABLE_COMPONENTS = [
'clerk', // While not a component, we want to support passing options to the Clerk class.
'signIn',
'signUp',
'userButton',
'userProfile',
'createOrganization',
'organizationList',
'organizationProfile',
'organizationSwitcher',
'waitlist',
] as const;

const COMPONENT_PROPS_NAMESPACE = 'clerk-js-sandbox';

const urlParams = new URL(window.location.href).searchParams;
for (const [component, encodedProps] of urlParams.entries()) {
if (AVAILABLE_COMPONENTS.includes(component as (typeof AVAILABLE_COMPONENTS)[number])) {
localStorage.setItem(`${COMPONENT_PROPS_NAMESPACE}-${component}`, encodedProps);
}
}

function setComponentProps(component: (typeof AVAILABLE_COMPONENTS)[number], props: unknown) {
const encodedProps = JSON.stringify(props);

const url = new URL(window.location.href);
url.searchParams.set(component, encodedProps);

window.location.href = url.toString();
}

function getComponentProps(component: (typeof AVAILABLE_COMPONENTS)[number]): unknown | null {
const url = new URL(window.location.href);
const encodedProps = url.searchParams.get(component);
if (encodedProps) {
return JSON.parse(encodedProps);
}

const localEncodedProps = localStorage.getItem(`${COMPONENT_PROPS_NAMESPACE}-${component}`);
if (localEncodedProps) {
return JSON.parse(localEncodedProps);
}

return null;
}

function buildComponentControls(component: (typeof AVAILABLE_COMPONENTS)[number]): ComponentPropsControl {
return {
setProps(props) {
setComponentProps(component, props);
},
getProps() {
return getComponentProps(component);
},
};
}

const componentControls: Record<(typeof AVAILABLE_COMPONENTS)[number], ComponentPropsControl> = {
clerk: buildComponentControls('clerk'),
signIn: buildComponentControls('signIn'),
signUp: buildComponentControls('signUp'),
userButton: buildComponentControls('userButton'),
userProfile: buildComponentControls('userProfile'),
createOrganization: buildComponentControls('createOrganization'),
organizationList: buildComponentControls('organizationList'),
organizationProfile: buildComponentControls('organizationProfile'),
organizationSwitcher: buildComponentControls('organizationSwitcher'),
waitlist: buildComponentControls('waitlist'),
};

declare global {
interface Window {
components: Record<(typeof AVAILABLE_COMPONENTS)[number], ComponentPropsControl>;
}
}

window.components = componentControls;

const Clerk = window.Clerk;
function assertClerkIsLoaded(c: ClerkType | undefined): asserts c is ClerkType {
if (!c) {
throw new Error('Clerk is not loaded');
}
}

const app = document.getElementById('app') as HTMLDivElement;

function mountIndex(element: HTMLDivElement) {
assertClerkIsLoaded(Clerk);
const user = Clerk.user;
element.innerHTML = `<pre><code>${JSON.stringify({ user }, null, 2)}</code></pre>`;
}

function addCurrentRouteIndicator(currentRoute: string) {
const link = document.querySelector(`a[href="${currentRoute}"]`);
if (!link) {
return;
}
link.removeAttribute('aria-current');
link.setAttribute('aria-current', 'page');
}

(async () => {
assertClerkIsLoaded(Clerk);

const routes = {
'/': () => {
mountIndex(app);
},
'/sign-in': () => {
Clerk.mountSignIn(app, componentControls.signIn.getProps() ?? {});
},
'/sign-up': () => {
Clerk.mountSignUp(app, componentControls.signUp.getProps() ?? {});
},
'/user-button': () => {
Clerk.mountUserButton(app, componentControls.userButton.getProps() ?? {});
},
'/user-profile': () => {
Clerk.mountUserProfile(app, componentControls.userProfile.getProps() ?? {});
},
'/create-organization': () => {
Clerk.mountCreateOrganization(app, componentControls.createOrganization.getProps() ?? {});
},
'/organization-list': () => {
Clerk.mountOrganizationList(app, componentControls.organizationList.getProps() ?? {});
},
'/organization-profile': () => {
Clerk.mountOrganizationProfile(app, componentControls.organizationProfile.getProps() ?? {});
},
'/organization-switcher': () => {
Clerk.mountOrganizationSwitcher(app, componentControls.organizationSwitcher.getProps() ?? {});
},
'/waitlist': () => {
Clerk.mountWaitlist(app, componentControls.waitlist.getProps() ?? {});
},
'/accountless': () => {
Clerk.__unstable__updateProps({ options: { __internal_claimAccountlessKeysUrl: '/test-url' } });
},
};

const route = window.location.pathname;
if (route in routes) {
const renderCurrentRoute = routes[route];
addCurrentRouteIndicator(route);
await Clerk.load({
...(componentControls.clerk.getProps() ?? {}),
signInUrl: '/sign-in',
signUpUrl: '/sign-up',
});
renderCurrentRoute();
} else {
console.error(`Unknown route: "${route}".`);
}
})();
2 changes: 1 addition & 1 deletion packages/clerk-js/sandbox/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
viewBox="0 0 62 18"
fill="none"
aria-hidden="true"
class="h-[1.125rem] text-gray-950 dark:text-white"
class="h-[1.125rem] text-gray-950"
>
<ellipse
cx="8.99999"
Expand Down

0 comments on commit b0d165a

Please sign in to comment.