Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/every-adults-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@asgardeo/javascript': patch
'@asgardeo/react': patch
'@asgardeo/i18n': patch
---

Fix social button rendering issue in v2
3 changes: 3 additions & 0 deletions packages/i18n/src/models/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export interface I18nTranslations {
'elements.buttons.multi.option.text': string;
'elements.buttons.social.text': string;

/* Display */
'elements.display.divider.or_separator': string;

/* Fields */
'elements.fields.generic.placeholder': string;
'elements.fields.username.label': string;
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/translations/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const translations: I18nTranslations = {
'elements.buttons.multi.option.text': 'Continue with {connection}',
'elements.buttons.social.text': 'Continue with {connection}',

/* Display */
'elements.display.divider.or_separator': 'OR',

/* Fields */
'elements.fields.generic.placeholder': 'Enter your {field}',
'elements.fields.username.label': 'Username',
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/translations/fr-FR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const translations: I18nTranslations = {
'elements.buttons.multi.option.text': 'Continuer avec {connection}',
'elements.buttons.social.text': 'Continuer avec {connection}',

/* Display */
'elements.display.divider.or_separator': 'OU',

/* Fields */
'elements.fields.generic.placeholder': 'Entrez votre {field}',
'elements.fields.username.label': "Nom d'utilisateur",
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/translations/hi-IN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ const translations: I18nTranslations = {
'elements.buttons.multi.option.text': '{connection} के साथ जारी रखें',
'elements.buttons.social.text': '{connection} के साथ जारी रखें',

/* Display */
'elements.display.divider.or_separator': 'या',

/* Fields */
'elements.fields.generic.placeholder': '{field} दर्ज करें',
'elements.fields.username.label': 'उपयोगकर्ता नाम',
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/translations/ja-JP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const translations: I18nTranslations = {
'elements.buttons.multi.option.text': '{connection}で続行',
'elements.buttons.social.text': '{connection}で続行',

/* Display */
'elements.display.divider.or_separator': 'または',

/* Fields */
'elements.fields.generic.placeholder': '{field}を入力してください',
'elements.fields.username.label': 'ユーザー名',
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/translations/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const translations: I18nTranslations = {
'elements.buttons.multi.option.text': 'Entrar com {connection}',
'elements.buttons.social.text': 'Entrar com {connection}',

/* Display */
'elements.display.divider.or_separator': 'OU',

/* Fields */
'elements.fields.generic.placeholder': 'Digite seu {field}',
'elements.fields.username.label': 'Nome de usuário',
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/translations/pt-PT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const translations: I18nTranslations = {
'elements.buttons.multi.option.text': 'Iniciar Sessão com {connection}',
'elements.buttons.social.text': 'Iniciar Sessão com {connection}',

/* Display */
'elements.display.divider.or_separator': 'OU',

/* Fields */
'elements.fields.generic.placeholder': 'Introduza o seu {field}',
'elements.fields.username.label': 'Nome de utilizador',
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/translations/si-LK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const translations: I18nTranslations = {
'elements.buttons.multi.option.text': '{connection} සමග ඉදිරියට යන්න',
'elements.buttons.social.text': '{connection} සමග ඉදිරියට යන්න',

/* Display */
'elements.display.divider.or_separator': 'හෝ',

/* Fields */
'elements.fields.generic.placeholder': 'ඔබේ {field} ඇතුලත් කරන්න',
'elements.fields.username.label': 'පරිශීලක නාමය',
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/translations/ta-IN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const translations: I18nTranslations = {
'elements.buttons.multi.option.text': '{connection} மூலம் தொடரவும்',
'elements.buttons.social.text': '{connection} மூலம் தொடரவும்',

/* Display */
'elements.display.divider.or_separator': 'அல்லது',

/* Fields */
'elements.fields.generic.placeholder': '{field} உள்ளிடவும்',
'elements.fields.username.label': 'பயனர்பெயர்',
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/translations/te-IN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const translations: I18nTranslations = {
'elements.buttons.multi.option.text': '{connection} తో కొనసాగించండి',
'elements.buttons.social.text': '{connection} తో కొనసాగించండి',

/* Display */
'elements.display.divider.or_separator': 'లేదా',

/* Fields */
'elements.fields.generic.placeholder': 'మీ {field} ను నమోదు చేయండి',
'elements.fields.username.label': 'వినియోగదారు పేరు',
Expand Down
61 changes: 58 additions & 3 deletions packages/javascript/src/models/v2/embedded-flow-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ export enum EmbeddedFlowComponentType {
/** Password input field with masking for sensitive data */
PasswordInput = 'PASSWORD_INPUT',

/**
* Email input field with validation for email addresses.
*/
/** Email input field with validation for email addresses. */
EmailInput = 'EMAIL_INPUT',

/** Text display component for labels, headings, and messages */
Expand All @@ -57,6 +55,9 @@ export enum EmbeddedFlowComponentType {

/** Container block component that groups other components */
Block = 'BLOCK',

/** Divider component for visual separation of content */
Divider = 'DIVIDER',
}

/**
Expand All @@ -65,14 +66,32 @@ export enum EmbeddedFlowComponentType {
* @experimental This API may change in future versions
*/
export enum EmbeddedFlowActionVariant {
/** Primary action button with highest visual emphasis */
Primary = 'PRIMARY',

/** Secondary action button with moderate visual emphasis */
Secondary = 'SECONDARY',

/** Tertiary action button with minimal visual emphasis */
Tertiary = 'TERTIARY',

/** Danger action button for destructive operations */
Danger = 'DANGER',

/** Success action button for positive confirmations */
Success = 'SUCCESS',

/** Info action button for informational purposes */
Info = 'INFO',

/** Warning action button for cautionary actions */
Warning = 'WARNING',

/** Link-styled action button */
Link = 'LINK',

/** Social media action button (e.g., Google, Facebook) */
Social = 'SOCIAL',
}

/**
Expand All @@ -81,18 +100,43 @@ export enum EmbeddedFlowActionVariant {
* @experimental This API may change in future versions
*/
export enum EmbeddedFlowTextVariant {
/** Largest heading level for main titles */
Heading1 = 'HEADING_1',

/** Second level heading for major sections */
Heading2 = 'HEADING_2',

/** Third level heading for subsections */
Heading3 = 'HEADING_3',

/** Fourth level heading for minor sections */
Heading4 = 'HEADING_4',

/** Fifth level heading for detailed sections */
Heading5 = 'HEADING_5',

/** Smallest heading level for fine-grained sections */
Heading6 = 'HEADING_6',

/** Primary subtitle text with larger emphasis */
Subtitle1 = 'SUBTITLE_1',

/** Secondary subtitle text with moderate emphasis */
Subtitle2 = 'SUBTITLE_2',

/** Primary body text for main content */
Body1 = 'BODY_1',

/** Secondary body text for supplementary content */
Body2 = 'BODY_2',

/** Small caption text for annotations and descriptions */
Caption = 'CAPTION',

/** Overline text for labels and categories */
Overline = 'OVERLINE',

/** Text styled for button labels */
ButtonText = 'BUTTON_TEXT',
}

Expand All @@ -102,11 +146,22 @@ export enum EmbeddedFlowTextVariant {
* @experimental This API may change in future versions
*/
export enum EmbeddedFlowEventType {
/** Trigger an action or event */
Trigger = 'TRIGGER',

/** Submit form data to the server */
Submit = 'SUBMIT',

/** Navigate to a different flow step or page */
Navigate = 'NAVIGATE',

/** Cancel the current operation */
Cancel = 'CANCEL',

/** Reset form fields to initial state */
Reset = 'RESET',

/** Navigate back to the previous step */
Back = 'BACK',
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
EmbeddedFlowComponentV2 as EmbeddedFlowComponent,
EmbeddedFlowComponentTypeV2 as EmbeddedFlowComponentType,
EmbeddedFlowTextVariantV2 as EmbeddedFlowTextVariant,
EmbeddedFlowActionVariantV2 as EmbeddedFlowActionVariant,
EmbeddedFlowEventTypeV2 as EmbeddedFlowEventType,
} from '@asgardeo/browser';
import {createField} from '../../factories/FieldFactory';
import Button from '../../primitives/Button/Button';
Expand Down Expand Up @@ -86,10 +88,16 @@ const matchesSocialProvider = (
buttonText: string,
provider: string,
authType: AuthType,
componentVariant?: string,
): boolean => {
const providerId = `${provider}_auth`;
const providerMatches = actionId === providerId || eventType === providerId;

// For social variant, also check button text for provider name
if (componentVariant?.toUpperCase() === EmbeddedFlowActionVariant.Social) {
return buttonText.toLowerCase().includes(provider);
}

// For signup, also check button text
if (authType === 'signup') {
return providerMatches || buttonText.toLowerCase().includes(provider);
Expand All @@ -115,7 +123,7 @@ const createAuthComponentFromFlow = (
inputClassName?: string;
key?: string | number;
onInputBlur?: (name: string) => void;
onSubmit?: (component: EmbeddedFlowComponent, data?: Record<string, any>) => void;
onSubmit?: (component: EmbeddedFlowComponent, data?: Record<string, any>, skipValidation?: boolean) => void;
size?: 'small' | 'medium' | 'large';
variant?: any;
} = {},
Expand Down Expand Up @@ -153,6 +161,14 @@ const createAuthComponentFromFlow = (
}

case EmbeddedFlowComponentType.Action: {
const actionId: string = component.id;
const eventType: string = (component.eventType as string) || '';
const buttonText: string = component.label || '';
const componentVariant: string = (component.variant as string) || '';

// Only validate on submit type events.
const shouldSkipValidation: boolean = eventType.toUpperCase() === EmbeddedFlowEventType.Trigger;

const handleClick = () => {
if (options.onSubmit) {
const formData: Record<string, any> = {};
Expand All @@ -161,31 +177,28 @@ const createAuthComponentFromFlow = (
formData[field] = formValues[field];
}
});
options.onSubmit(component, formData);
options.onSubmit(component, formData, shouldSkipValidation);
}
};

// Render branded social login buttons for known action IDs
const actionId: string = component.id;
const eventType: string = (component.eventType as string) || '';
const buttonText: string = component.label || '';

if (matchesSocialProvider(actionId, eventType, buttonText, 'google', authType)) {
if (matchesSocialProvider(actionId, eventType, buttonText, 'google', authType, componentVariant)) {
return <GoogleButton key={key} onClick={handleClick} className={options.buttonClassName} />;
}
if (matchesSocialProvider(actionId, eventType, buttonText, 'github', authType)) {
if (matchesSocialProvider(actionId, eventType, buttonText, 'github', authType, componentVariant)) {
return <GitHubButton key={key} onClick={handleClick} className={options.buttonClassName} />;
}
if (matchesSocialProvider(actionId, eventType, buttonText, 'facebook', authType)) {
if (matchesSocialProvider(actionId, eventType, buttonText, 'facebook', authType, componentVariant)) {
return <FacebookButton key={key} onClick={handleClick} className={options.buttonClassName} />;
}
if (matchesSocialProvider(actionId, eventType, buttonText, 'microsoft', authType)) {
if (matchesSocialProvider(actionId, eventType, buttonText, 'microsoft', authType, componentVariant)) {
return <MicrosoftButton key={key} onClick={handleClick} className={options.buttonClassName} />;
}
if (matchesSocialProvider(actionId, eventType, buttonText, 'linkedin', authType)) {
if (matchesSocialProvider(actionId, eventType, buttonText, 'linkedin', authType, componentVariant)) {
return <LinkedInButton key={key} onClick={handleClick} className={options.buttonClassName} />;
}
if (matchesSocialProvider(actionId, eventType, buttonText, 'ethereum', authType)) {
if (matchesSocialProvider(actionId, eventType, buttonText, 'ethereum', authType, componentVariant)) {
return <SignInWithEthereumButton key={key} onClick={handleClick} className={options.buttonClassName} />;
}
if (actionId === 'prompt_mobile' || eventType === 'prompt_mobile') {
Expand Down Expand Up @@ -217,6 +230,10 @@ const createAuthComponentFromFlow = (
);
}

case EmbeddedFlowComponentType.Divider: {
return <Divider key={key}>{component.label || ''}</Divider>;
}

case EmbeddedFlowComponentType.Block: {
if (component.components && component.components.length > 0) {
const blockComponents = component.components
Expand All @@ -238,7 +255,11 @@ const createAuthComponentFromFlow = (
)
.filter(Boolean);

return <div key={key}>{blockComponents}</div>;
return (
<form id={component.id} key={key}>
{blockComponents}
</form>
);
}
return null;
}
Expand Down Expand Up @@ -268,7 +289,7 @@ export const renderSignInComponents = (
buttonClassName?: string;
inputClassName?: string;
onInputBlur?: (name: string) => void;
onSubmit?: (component: EmbeddedFlowComponent, data?: Record<string, any>) => void;
onSubmit?: (component: EmbeddedFlowComponent, data?: Record<string, any>, skipValidation?: boolean) => void;
size?: 'small' | 'medium' | 'large';
variant?: any;
},
Expand Down Expand Up @@ -307,7 +328,7 @@ export const renderSignUpComponents = (
buttonClassName?: string;
inputClassName?: string;
onInputBlur?: (name: string) => void;
onSubmit?: (component: EmbeddedFlowComponent, data?: Record<string, any>) => void;
onSubmit?: (component: EmbeddedFlowComponent, data?: Record<string, any>, skipValidation?: boolean) => void;
size?: 'small' | 'medium' | 'large';
variant?: any;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,13 +379,21 @@ const BaseSignInContent: FC<BaseSignInProps> = ({
/**
* Handle component submission (for buttons and actions).
*/
const handleSubmit = async (component: EmbeddedFlowComponent, data?: Record<string, any>): Promise<void> => {
// Mark all fields as touched before validation
touchAllFields();

const validation = validateForm();
if (!validation.isValid) {
return;
const handleSubmit = async (
component: EmbeddedFlowComponent,
data?: Record<string, any>,
skipValidation?: boolean,
): Promise<void> => {
// Only validate for form submit actions, skip for social/trigger actions
if (!skipValidation) {
// Mark all fields as touched before validation
touchAllFields();

const validation: ReturnType<typeof validateForm> = validateForm();

if (!validation.isValid) {
return;
}
}

setIsSubmitting(true);
Expand Down
Loading
Loading