Skip to content

Commit

Permalink
chore(astro): Allow child elements in unstyled Astro components (#4122)
Browse files Browse the repository at this point in the history
  • Loading branch information
wobsoriano authored Sep 9, 2024
1 parent 64f433b commit 17e6c2e
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 17 deletions.
17 changes: 17 additions & 0 deletions .changeset/rare-onions-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@clerk/astro": patch
---

Allow child elements in unstyled Astro components.

Usage:

```astro
---
import { SignInButton } from '@clerk/components/astro'
---
<SignInButton asChild>
<button>Sign in with Clerk</button>
</SignInButton>
```
5 changes: 4 additions & 1 deletion integration/templates/astro-node/src/pages/buttons.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import Layout from "../layouts/Layout.astro";

<Layout title="Welcome to Astro.">
<SignInButton
asChild
mode="modal"
fallbackRedirectUrl="/user"
>
Sign in
<button>
Sign in
</button>
</SignInButton>

<SignUpButton
Expand Down
31 changes: 26 additions & 5 deletions packages/astro/src/astro-components/unstyled/SignInButton.astro
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
---
import type { HTMLTag, Polymorphic } from 'astro/types'
import type { SignInProps } from "@clerk/types";
type Props<Tag extends HTMLTag = 'button'> = Polymorphic<SignInProps & { as: Tag; mode?: 'redirect' | 'modal' }>
import type { SignInProps } from '@clerk/types'
import type { ButtonProps } from '../../types';
import { addUnstyledAttributeToFirstTag, logAsPropUsageDeprecation } from './utils'
type Props<Tag extends HTMLTag = 'button'> = Polymorphic<SignInProps & ButtonProps<Tag>>
import { generateSafeId } from '@clerk/astro/internal';
const safeId = generateSafeId();
if ('as' in Astro.props) {
logAsPropUsageDeprecation()
}
const {
as: Tag = 'button',
asChild,
forceRedirectUrl,
fallbackRedirectUrl,
signUpFallbackRedirectUrl,
Expand All @@ -23,11 +31,24 @@ const signInOptions = {
signUpFallbackRedirectUrl,
signUpForceRedirectUrl,
};
let htmlElement = ''
if (asChild) {
htmlElement = await Astro.slots.render('default')
htmlElement = addUnstyledAttributeToFirstTag(htmlElement, safeId)
}
---

<Tag {...elementProps} data-clerk-unstyled-id={safeId}>
<slot>Sign in</slot>
</Tag >
{
asChild ? (
<Fragment set:html={htmlElement} />
) : (
<Tag {...elementProps} data-clerk-unstyled-id={safeId}>
<slot>Sign in</slot>
</Tag >
)
}

<script is:inline define:vars={{ signInOptions, mode, safeId }}>
const btn = document.querySelector(`[data-clerk-unstyled-id="${safeId}"]`);
Expand Down
33 changes: 27 additions & 6 deletions packages/astro/src/astro-components/unstyled/SignOutButton.astro
Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
---
import type { HTMLTag, Polymorphic } from 'astro/types'
import type { SignOutOptions } from '@clerk/types';
type Props<Tag extends HTMLTag = 'button'> = Polymorphic<{ as: Tag; } & SignOutOptions>
import type { SignOutOptions, Without } from '@clerk/types'
import type { ButtonProps } from '../../types';
import { addUnstyledAttributeToFirstTag, logAsPropUsageDeprecation } from './utils'
import { generateSafeId } from '@clerk/astro/internal';
type Props<Tag extends HTMLTag = 'button'> = Polymorphic<SignOutOptions & Without<ButtonProps<Tag>, 'mode'>>
import { generateSafeId } from '@clerk/astro/internal'
const safeId = generateSafeId();
if ('as' in Astro.props) {
logAsPropUsageDeprecation()
}
const {
as: Tag = 'button',
asChild,
redirectUrl = '/',
sessionId,
...elementProps
} = Astro.props
let htmlElement = ''
if (asChild) {
htmlElement = await Astro.slots.render('default')
htmlElement = addUnstyledAttributeToFirstTag(htmlElement, safeId)
}
---

<Tag {...elementProps} data-clerk-unstyled-id={safeId}>
<slot>Sign out</slot>
</Tag >
{
asChild ? (
<Fragment set:html={htmlElement} />
) : (
<Tag {...elementProps} data-clerk-unstyled-id={safeId}>
<slot>Sign out</slot>
</Tag >
)
}

<script is:inline define:vars={{ redirectUrl, sessionId, safeId }}>
const btn = document.querySelector(`[data-clerk-unstyled-id="${safeId}"]`);
Expand Down
31 changes: 26 additions & 5 deletions packages/astro/src/astro-components/unstyled/SignUpButton.astro
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
---
import type { HTMLTag, Polymorphic } from 'astro/types'
import type { SignUpProps } from "@clerk/types";
type Props<Tag extends HTMLTag = 'button'> = Polymorphic<SignUpProps & { as: Tag; mode?: 'redirect' | 'modal' }>
import type { SignUpProps } from '@clerk/types'
import type { ButtonProps } from '../../types'
import { addUnstyledAttributeToFirstTag, logAsPropUsageDeprecation } from './utils'
type Props<Tag extends HTMLTag = 'button'> = Polymorphic<SignUpProps & ButtonProps<Tag>>
import { generateSafeId } from '@clerk/astro/internal';
const safeId = generateSafeId();
if ('as' in Astro.props) {
logAsPropUsageDeprecation()
}
const {
as: Tag = 'button',
asChild,
fallbackRedirectUrl,
forceRedirectUrl,
signInFallbackRedirectUrl,
Expand All @@ -25,11 +33,24 @@ const signUpOptions = {
signInForceRedirectUrl,
unsafeMetadata,
}
let htmlElement = ''
if (asChild) {
htmlElement = await Astro.slots.render('default')
htmlElement = addUnstyledAttributeToFirstTag(htmlElement, safeId)
}
---

<Tag {...elementProps} data-clerk-unstyled-id={safeId}>
<slot>Sign up</slot>
</Tag >
{
asChild ? (
<Fragment set:html={htmlElement} />
) : (
<Tag {...elementProps} data-clerk-unstyled-id={safeId}>
<slot>Sign up</slot>
</Tag >
)
}

<script is:inline define:vars={{ signUpOptions, mode, safeId }}>
const btn = document.querySelector(`[data-clerk-unstyled-id="${safeId}"]`);
Expand Down
21 changes: 21 additions & 0 deletions packages/astro/src/astro-components/unstyled/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* This function is used when an element is passed as a default slot and we need
* to add an attribute to it so that we can reference it in a click listener.
*/
export function addUnstyledAttributeToFirstTag(html: string, attributeValue: string): string {
return html.replace(/(<[^>]+)>/, `$1 data-clerk-unstyled-id="${attributeValue}">`);
}

/**
* Logs a deprecation warning when the 'as' prop is used.
*/
export function logAsPropUsageDeprecation() {
if (import.meta.env.PROD) {
return;
}

console.warn(
`[@clerk/astro] The 'as' prop is deprecated and will be removed in a future version. ` +
`Use the default slot with the 'asChild' prop instead. `,
);
}
14 changes: 14 additions & 0 deletions packages/astro/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,17 @@ type ProtectProps =
};

export type { AstroClerkUpdateOptions, AstroClerkIntegrationParams, AstroClerkCreateInstanceParams, ProtectProps };

export type ButtonProps<Tag> = {
/**
* @deprecated The 'as' prop is deprecated and will be removed in a future version.
* Use the default slot with the 'asChild' prop instead.
* @example
* <SignInButton asChild>
* <button>Sign in</button>
* </SignInButton>
*/
as: Tag;
asChild?: boolean;
mode?: 'redirect' | 'modal';
};

0 comments on commit 17e6c2e

Please sign in to comment.