diff --git a/.changeset/cool-chairs-swim.md b/.changeset/cool-chairs-swim.md
new file mode 100644
index 00000000000..73d3feb4c42
--- /dev/null
+++ b/.changeset/cool-chairs-swim.md
@@ -0,0 +1,5 @@
+---
+"@clerk/astro": patch
+---
+
+Add an Astro component and a React UI Component for Google One Tap.
diff --git a/packages/astro/src/astro-components/interactive/GoogleOneTap.astro b/packages/astro/src/astro-components/interactive/GoogleOneTap.astro
new file mode 100644
index 00000000000..e161a232ca5
--- /dev/null
+++ b/packages/astro/src/astro-components/interactive/GoogleOneTap.astro
@@ -0,0 +1,8 @@
+---
+import type { GoogleOneTapProps } from "@clerk/types";
+type Props = GoogleOneTapProps
+
+import InternalUIComponentRenderer from './InternalUIComponentRenderer.astro'
+---
+
+
diff --git a/packages/astro/src/astro-components/interactive/InternalUIComponentRenderer.astro b/packages/astro/src/astro-components/interactive/InternalUIComponentRenderer.astro
index d0e89ccc541..0f217aa156e 100644
--- a/packages/astro/src/astro-components/interactive/InternalUIComponentRenderer.astro
+++ b/packages/astro/src/astro-components/interactive/InternalUIComponentRenderer.astro
@@ -1,7 +1,7 @@
---
interface Props {
[key: string]: unknown
- component: 'sign-in' | 'sign-up' | 'organization-list' | 'organization-profile' | 'organization-switcher' | 'user-button' | 'user-profile'
+ component: 'sign-in' | 'sign-up' | 'organization-list' | 'organization-profile' | 'organization-switcher' | 'user-button' | 'user-profile' | 'google-one-tap'
}
import { customAlphabet, urlAlphabet } from "nanoid";
diff --git a/packages/astro/src/astro-components/interactive/index.ts b/packages/astro/src/astro-components/interactive/index.ts
index 09f98a26afc..d73a17d01a7 100644
--- a/packages/astro/src/astro-components/interactive/index.ts
+++ b/packages/astro/src/astro-components/interactive/index.ts
@@ -14,3 +14,5 @@ export { default as OrganizationProfile } from './OrganizationProfile.astro';
export { default as OrganizationSwitcher } from './OrganizationSwitcher.astro';
// @ts-ignore
export { default as OrganizationList } from './OrganizationList.astro';
+// @ts-ignore
+export { default as GoogleOneTap } from './GoogleOneTap.astro';
diff --git a/packages/astro/src/client/mount-clerk-astro-js-components.ts b/packages/astro/src/client/mount-clerk-astro-js-components.ts
index ba050907734..af8fb87ea78 100644
--- a/packages/astro/src/client/mount-clerk-astro-js-components.ts
+++ b/packages/astro/src/client/mount-clerk-astro-js-components.ts
@@ -12,6 +12,7 @@ const mountAllClerkAstroJSComponents = () => {
'user-profile': 'mountUserProfile',
'sign-in': 'mountSignIn',
'sign-up': 'mountSignUp',
+ 'google-one-tap': 'openGoogleOneTap',
} as const;
Object.entries(mountFns).forEach(([category, mountFn]) => {
diff --git a/packages/astro/src/client/react/uiComponents.tsx b/packages/astro/src/client/react/uiComponents.tsx
index 9f58a8dcfb1..3bedd626e42 100644
--- a/packages/astro/src/client/react/uiComponents.tsx
+++ b/packages/astro/src/client/react/uiComponents.tsx
@@ -1,4 +1,5 @@
import type {
+ GoogleOneTapProps,
OrganizationListProps,
OrganizationProfileProps,
OrganizationSwitcherProps,
@@ -7,22 +8,40 @@ import type {
UserButtonProps,
UserProfileProps,
} from '@clerk/types';
-import React, { createElement } from 'react';
+import React from 'react';
import { withClerk, type WithClerkProp } from './utils';
+export interface OpenProps {
+ open: ((props: any) => void) | undefined;
+ close: (() => void) | undefined;
+ props?: any;
+}
+
export interface MountProps {
- mount?: (node: HTMLDivElement, props: any) => void;
- unmount?: (node: HTMLDivElement) => void;
+ mount: ((node: HTMLDivElement, props: any) => void) | undefined;
+ unmount: ((node: HTMLDivElement) => void) | undefined;
updateProps?: (props: any) => void;
props?: any;
- customPagesPortals?: any[];
+ // TODO: Support custom pages
+ // customPagesPortals?: any[];
}
-class Portal extends React.PureComponent {
+const isMountProps = (props: any): props is MountProps => {
+ return 'mount' in props;
+};
+
+const isOpenProps = (props: any): props is OpenProps => {
+ return 'open' in props;
+};
+
+class Portal extends React.PureComponent {
private portalRef = React.createRef();
componentDidUpdate(prevProps: Readonly) {
+ if (!isMountProps(prevProps) || !isMountProps(this.props)) {
+ return;
+ }
if (
prevProps.props.appearance !== this.props.props.appearance ||
prevProps.props?.customPages?.length !== this.props.props?.customPages?.length
@@ -36,13 +55,24 @@ class Portal extends React.PureComponent {
componentDidMount() {
if (this.portalRef.current) {
- this.props.mount?.(this.portalRef.current, this.props.props);
+ if (isMountProps(this.props)) {
+ this.props.mount?.(this.portalRef.current, this.props.props);
+ }
+
+ if (isOpenProps(this.props)) {
+ this.props.open?.(this.props.props);
+ }
}
}
componentWillUnmount() {
if (this.portalRef.current) {
- this.props.unmount?.(this.portalRef.current);
+ if (isMountProps(this.props)) {
+ this.props.unmount?.(this.portalRef.current);
+ }
+ if (isOpenProps(this.props)) {
+ this.props.close?.();
+ }
}
}
@@ -50,7 +80,9 @@ class Portal extends React.PureComponent {
return (
<>
- {this.props?.customPagesPortals?.map((portal, index) => createElement(portal, { key: index }))}
+ {/*TODO: Support custom pages*/}
+ {/*{isMountProps(this.props) &&*/}
+ {/* this.props?.customPagesPortals?.map((portal, index) => createElement(portal, { key: index }))}*/}
>
);
}
@@ -132,3 +164,13 @@ export const OrganizationList = withClerk(({ clerk, ...props }: WithClerkProp
);
}, 'OrganizationList');
+
+export const GoogleOneTap = withClerk(({ clerk, ...props }: WithClerkProp) => {
+ return (
+
+ );
+}, 'GoogleOneTap');