Add placeholder to signinup modal's secondary logo (#12079)
closes https://github.com/twentyhq/core-team-issues/issues/972 checks - - [x] If we have an icon it should be displayed as `secondary` <img width="1064" alt="Screenshot 2025-05-16 at 00 33 42" src="https://github.com/user-attachments/assets/20716df3-28be-40c5-b0fc-2133fccdad83" /> - [x] If no icon is provided, display a placeholder <img width="1062" alt="Screenshot 2025-05-16 at 00 33 09" src="https://github.com/user-attachments/assets/0241dafe-3d31-4f8e-a9e4-321634f6d41c" /> - [x] Add story for auth logos <img width="550" alt="Screenshot 2025-05-16 at 16 16 24" src="https://github.com/user-attachments/assets/3859547d-32c5-469f-879b-a130a36a23fa" /> <img width="548" alt="Screenshot 2025-05-16 at 16 16 30" src="https://github.com/user-attachments/assets/4937fa6a-459a-4c75-86ae-2080b1c8a5ae" /> <img width="487" alt="Screenshot 2025-05-16 at 16 16 16" src="https://github.com/user-attachments/assets/2c634be1-4a51-4b06-a590-cdd7eff91b76" /> <img width="556" alt="Screenshot 2025-05-16 at 16 16 07" src="https://github.com/user-attachments/assets/e4a518b4-c338-45c1-a329-5c9f85319c52" />
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { getImageAbsoluteURI } from 'twenty-shared/utils';
|
||||
import { getImageAbsoluteURI, isDefined } from 'twenty-shared/utils';
|
||||
import { Avatar } from 'twenty-ui/display';
|
||||
import { UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { useRedirectToDefaultDomain } from '~/modules/domain-manager/hooks/useRedirectToDefaultDomain';
|
||||
@ -9,6 +10,7 @@ import { AppPath } from '~/modules/types/AppPath';
|
||||
type LogoProps = {
|
||||
primaryLogo?: string | null;
|
||||
secondaryLogo?: string | null;
|
||||
placeholder?: string | null;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@ -47,23 +49,27 @@ const StyledPrimaryLogo = styled.div<{ src: string }>`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const Logo = (props: LogoProps) => {
|
||||
export const Logo = ({
|
||||
primaryLogo,
|
||||
secondaryLogo,
|
||||
placeholder,
|
||||
}: LogoProps) => {
|
||||
const { redirectToDefaultDomain } = useRedirectToDefaultDomain();
|
||||
const defaultPrimaryLogoUrl = `${window.location.origin}/images/icons/android/android-launchericon-192-192.png`;
|
||||
|
||||
const primaryLogoUrl = getImageAbsoluteURI({
|
||||
imageUrl: props.primaryLogo ?? defaultPrimaryLogoUrl,
|
||||
imageUrl: primaryLogo ?? defaultPrimaryLogoUrl,
|
||||
baseUrl: REACT_APP_SERVER_BASE_URL,
|
||||
});
|
||||
|
||||
const secondaryLogoUrl = isNonEmptyString(props.secondaryLogo)
|
||||
const secondaryLogoUrl = isNonEmptyString(secondaryLogo)
|
||||
? getImageAbsoluteURI({
|
||||
imageUrl: props.secondaryLogo,
|
||||
imageUrl: secondaryLogo,
|
||||
baseUrl: REACT_APP_SERVER_BASE_URL,
|
||||
})
|
||||
: null;
|
||||
|
||||
const isUsingDefaultLogo = !props.primaryLogo;
|
||||
const isUsingDefaultLogo = !isDefined(primaryLogo);
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
@ -72,15 +78,26 @@ export const Logo = (props: LogoProps) => {
|
||||
to={AppPath.SignInUp}
|
||||
onClick={redirectToDefaultDomain}
|
||||
>
|
||||
<StyledPrimaryLogo src={primaryLogoUrl ?? ''} />
|
||||
<StyledPrimaryLogo src={primaryLogoUrl} />
|
||||
</UndecoratedLink>
|
||||
) : (
|
||||
<StyledPrimaryLogo src={primaryLogoUrl ?? ''} />
|
||||
<StyledPrimaryLogo src={primaryLogoUrl} />
|
||||
)}
|
||||
{secondaryLogoUrl && (
|
||||
{isDefined(secondaryLogoUrl) ? (
|
||||
<StyledSecondaryLogoContainer>
|
||||
<StyledSecondaryLogo src={secondaryLogoUrl} />
|
||||
</StyledSecondaryLogoContainer>
|
||||
) : (
|
||||
isDefined(placeholder) && (
|
||||
<StyledSecondaryLogoContainer>
|
||||
<Avatar
|
||||
size="lg"
|
||||
placeholder={placeholder}
|
||||
type="squared"
|
||||
placeholderColorSeed={placeholder}
|
||||
/>
|
||||
</StyledSecondaryLogoContainer>
|
||||
)
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import {
|
||||
ComponentDecorator,
|
||||
RecoilRootDecorator,
|
||||
RouterDecorator,
|
||||
} from 'twenty-ui/testing';
|
||||
import { Logo } from '../Logo';
|
||||
|
||||
const logoUrl = 'https://picsum.photos/192/192';
|
||||
|
||||
const meta: Meta<typeof Logo> = {
|
||||
title: 'Modules/Auth/Logo',
|
||||
component: Logo,
|
||||
decorators: [ComponentDecorator, RecoilRootDecorator, RouterDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Logo>;
|
||||
|
||||
export const WithSecondaryLogo: Story = {
|
||||
args: {
|
||||
primaryLogo: null,
|
||||
secondaryLogo: logoUrl,
|
||||
placeholder: 'A',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithPlaceholder: Story = {
|
||||
args: {
|
||||
primaryLogo: null,
|
||||
secondaryLogo: null,
|
||||
placeholder: 'B',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithPrimaryAndSecondaryLogo: Story = {
|
||||
args: {
|
||||
primaryLogo: logoUrl,
|
||||
secondaryLogo: logoUrl,
|
||||
placeholder: 'C',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithPrimaryLogoAndPlaceholder: Story = {
|
||||
args: {
|
||||
primaryLogo: logoUrl,
|
||||
secondaryLogo: null,
|
||||
placeholder: 'D',
|
||||
},
|
||||
};
|
||||
@ -18,7 +18,7 @@ const VerifyEmailEffectErrorState = ({ email = 'user@example.com' }) => {
|
||||
};
|
||||
|
||||
const meta: Meta<typeof VerifyEmailEffectErrorState> = {
|
||||
title: 'Pages/Auth/VerifyEmailEffect',
|
||||
title: 'Modules/Auth/VerifyEmailEffect',
|
||||
component: VerifyEmailEffectErrorState,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
|
||||
@ -19,7 +19,7 @@ const RenderWithModal = (
|
||||
};
|
||||
|
||||
const meta: Meta<typeof EmailVerificationSent> = {
|
||||
title: 'Pages/Auth/EmailVerificationSent',
|
||||
title: 'Modules/Auth/EmailVerificationSent',
|
||||
component: EmailVerificationSent,
|
||||
decorators: [ComponentDecorator, SnackBarDecorator],
|
||||
parameters: {
|
||||
|
||||
@ -173,7 +173,10 @@ export const PasswordReset = () => {
|
||||
<Modal.Content isVerticalCentered isHorizontalCentered>
|
||||
<StyledMainContainer>
|
||||
<AnimatedEaseIn>
|
||||
<Logo secondaryLogo={workspacePublicData?.logo} />
|
||||
<Logo
|
||||
secondaryLogo={workspacePublicData?.logo}
|
||||
placeholder={workspacePublicData?.displayName}
|
||||
/>
|
||||
</AnimatedEaseIn>
|
||||
<Title animate>
|
||||
<Trans>Reset Password</Trans>
|
||||
|
||||
@ -41,7 +41,10 @@ const StandardContent = ({
|
||||
return (
|
||||
<Modal.Content isVerticalCentered isHorizontalCentered>
|
||||
<AnimatedEaseIn>
|
||||
<Logo secondaryLogo={workspacePublicData?.logo} />
|
||||
<Logo
|
||||
secondaryLogo={workspacePublicData?.logo}
|
||||
placeholder={workspacePublicData?.displayName}
|
||||
/>
|
||||
</AnimatedEaseIn>
|
||||
<Title animate>{title}</Title>
|
||||
{signInUpForm}
|
||||
|
||||
Reference in New Issue
Block a user