diff --git a/.changeset/metal-lemons-pick.md b/.changeset/metal-lemons-pick.md new file mode 100644 index 00000000..c715d1b3 --- /dev/null +++ b/.changeset/metal-lemons-pick.md @@ -0,0 +1,6 @@ +--- +'@asgardeo/javascript': patch +'@asgardeo/react': patch +--- + +Improve `` & other components diff --git a/packages/javascript/src/models/v2/embedded-flow-v2.ts b/packages/javascript/src/models/v2/embedded-flow-v2.ts index 1f3d7068..dca4b72c 100644 --- a/packages/javascript/src/models/v2/embedded-flow-v2.ts +++ b/packages/javascript/src/models/v2/embedded-flow-v2.ts @@ -96,15 +96,15 @@ export enum EmbeddedFlowActionVariant { /** Link-styled action button */ Link = 'LINK', + /** Outlined action button for secondary emphasis */ + Outlined = 'OUTLINED', + /** Primary action button with highest visual emphasis */ Primary = 'PRIMARY', /** Secondary action button with moderate visual emphasis */ Secondary = 'SECONDARY', - /** Social media action button (e.g., Google, Facebook) */ - Social = 'SOCIAL', - /** Success action button for positive confirmations */ Success = 'SUCCESS', diff --git a/packages/react/src/components/adapters/SubmitButton.tsx b/packages/react/src/components/adapters/SubmitButton.tsx index 16a7a345..9b7cf606 100644 --- a/packages/react/src/components/adapters/SubmitButton.tsx +++ b/packages/react/src/components/adapters/SubmitButton.tsx @@ -47,6 +47,7 @@ const ButtonComponent: FC = ({ case 'TEXT': return {color: 'primary' as const, variant: 'text' as const}; case 'SOCIAL': + case 'OUTLINED': return {color: 'primary' as const, variant: 'outline' as const}; default: return {color: 'primary' as const, variant: 'solid' as const}; diff --git a/packages/react/src/components/presentation/LanguageSwitcher/BaseLanguageSwitcher.tsx b/packages/react/src/components/presentation/LanguageSwitcher/BaseLanguageSwitcher.tsx index 7c0da8f7..a38f1966 100644 --- a/packages/react/src/components/presentation/LanguageSwitcher/BaseLanguageSwitcher.tsx +++ b/packages/react/src/components/presentation/LanguageSwitcher/BaseLanguageSwitcher.tsx @@ -30,7 +30,7 @@ import { useInteractions, useRole, } from '@floating-ui/react'; -import {FC, ReactElement, ReactNode, useState} from 'react'; +import {FC, ReactElement, ReactNode, useEffect, useState} from 'react'; import useStyles from './BaseLanguageSwitcher.styles'; import useTheme from '../../../contexts/Theme/useTheme'; import Check from '../../primitives/Icons/Check'; @@ -109,6 +109,13 @@ const BaseLanguageSwitcher: FC = ({ const {theme, colorScheme} = useTheme(); const styles: Record = useStyles(theme, colorScheme); const [isOpen, setIsOpen] = useState(false); + const hasMultipleLanguages: boolean = languages.length > 1; + + useEffect(() => { + if (!hasMultipleLanguages && isOpen) { + setIsOpen(false); + } + }, [hasMultipleLanguages, isOpen]); const {refs, floatingStyles, context} = useFloating({ middleware: [offset(4), flip(), shift()], @@ -117,9 +124,9 @@ const BaseLanguageSwitcher: FC = ({ whileElementsMounted: autoUpdate, }); - const click: ReturnType = useClick(context); - const dismiss: ReturnType = useDismiss(context); - const role: ReturnType = useRole(context, {role: 'listbox'}); + const click: ReturnType = useClick(context, {enabled: hasMultipleLanguages}); + const dismiss: ReturnType = useDismiss(context, {enabled: hasMultipleLanguages}); + const role: ReturnType = useRole(context, {enabled: hasMultipleLanguages, role: 'listbox'}); const {getReferenceProps, getFloatingProps} = useInteractions([click, dismiss, role]); const currentOption: LanguageOption | undefined = languages.find((l: LanguageOption) => l.code === currentLanguage); @@ -149,10 +156,10 @@ const BaseLanguageSwitcher: FC = ({ > {currentOption && {currentOption.emoji}} {currentOption?.displayName ?? currentLanguage} - + {hasMultipleLanguages && } - {isOpen && ( + {isOpen && hasMultipleLanguages && (
= ({children, className}: Lang const {currentLanguage} = useTranslation(); const availableLanguageCodes: string[] = meta?.i18n?.languages ?? []; + const effectiveLanguageCodes: string[] = useMemo(() => { + const fallbackCodes: string[] = availableLanguageCodes.length > 0 ? availableLanguageCodes : [currentLanguage]; + + // Ensure the current language is always resolvable for display label and emoji. + return Array.from(new Set([currentLanguage, ...fallbackCodes])); + }, [availableLanguageCodes, currentLanguage]); const languages: LanguageOption[] = useMemo( () => - availableLanguageCodes.map((code: string) => ({ + effectiveLanguageCodes.map((code: string) => ({ code, - displayName: resolveLocaleDisplayName(code, currentLanguage), + // Resolve each label in its own locale so option names stay stable across UI language switches. + displayName: resolveLocaleDisplayName(code, code) || code, emoji: resolveLocaleEmoji(code), })), - [availableLanguageCodes, currentLanguage], + [effectiveLanguageCodes], ); const handleLanguageChange = (language: string): void => { diff --git a/packages/react/src/components/presentation/auth/AuthOptionFactory.tsx b/packages/react/src/components/presentation/auth/AuthOptionFactory.tsx index d3f47d09..a1c711f2 100644 --- a/packages/react/src/components/presentation/auth/AuthOptionFactory.tsx +++ b/packages/react/src/components/presentation/auth/AuthOptionFactory.tsx @@ -22,7 +22,6 @@ import { EmbeddedFlowComponentV2 as EmbeddedFlowComponent, EmbeddedFlowComponentTypeV2 as EmbeddedFlowComponentType, EmbeddedFlowTextVariantV2 as EmbeddedFlowTextVariant, - EmbeddedFlowActionVariantV2 as EmbeddedFlowActionVariant, EmbeddedFlowEventTypeV2 as EmbeddedFlowEventType, createPackageComponentLogger, resolveVars, @@ -110,14 +109,16 @@ const matchesSocialProvider = ( buttonText: string, provider: string, authType: AuthType, - componentVariant?: string, + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + _componentVariant?: string, ): boolean => { const providerId: any = `${provider}_auth`; const providerMatches: any = actionId === providerId || eventType === providerId; - // For social variant, also check button text for provider name - if (componentVariant?.toUpperCase() === EmbeddedFlowActionVariant.Social) { - return buttonText.toLowerCase().includes(provider); + // Social buttons usually have "Sign in with X" or "Continue with X" text, + // so also check button text for the provider name to increase chances of correct detection (especially for signup flows where action IDs are less standardized) + if (buttonText.toLowerCase().includes(provider)) { + return true; } // For signup, also check button text @@ -142,6 +143,7 @@ const createAuthComponentFromFlow = ( authType: AuthType, options: { buttonClassName?: string; + inStack?: boolean; inputClassName?: string; key?: string | number; /** Flow metadata for resolving {{meta(...)}} expressions at render time */ @@ -355,6 +357,8 @@ const createAuthComponentFromFlow = ( } case EmbeddedFlowComponentType.Image: { + const explicitHeight: string = resolve(component.height?.toString()); + const explicitWidth: string = resolve(component.width?.toString()); return ( = ({color = 'currentColor', size = 24}: ArrowRightLeftProps) => ( + + + + + + +); + +ArrowRightLeft.displayName = 'ArrowRightLeft'; + +export default ArrowRightLeft; diff --git a/packages/react/src/components/primitives/Icons/flowIconRegistry.tsx b/packages/react/src/components/primitives/Icons/flowIconRegistry.tsx index 9dd72eee..8d85dfd0 100644 --- a/packages/react/src/components/primitives/Icons/flowIconRegistry.tsx +++ b/packages/react/src/components/primitives/Icons/flowIconRegistry.tsx @@ -18,6 +18,7 @@ import {FC} from 'react'; import ArrowLeftRight from './ArrowLeftRight'; +import ArrowRightLeft from './ArrowRightLeft'; export interface FlowIconProps { color?: string; @@ -30,6 +31,7 @@ export interface FlowIconProps { */ const flowIconRegistry: Record> = { ArrowLeftRight, + ArrowRightLeft, }; export default flowIconRegistry;