From 6ef2891a155c5ac791c4bf28202fdf3bd145fb2d Mon Sep 17 00:00:00 2001 From: kim Date: Tue, 17 Sep 2024 16:52:55 +0200 Subject: [PATCH 01/15] refactor: refactor with authorization, change current account type --- src/Authorization/Redirect.stories.tsx | 19 +++++---- src/Authorization/SignedInWrapper.stories.tsx | 33 ++++++++++++++++ src/Authorization/SignedInWrapper.tsx | 38 ++++++++++++++++++ .../withAuthorization.stories.tsx | 36 ----------------- src/Authorization/withAuthorization.tsx | 39 ------------------- src/index.ts | 2 +- 6 files changed, 81 insertions(+), 86 deletions(-) create mode 100644 src/Authorization/SignedInWrapper.stories.tsx create mode 100644 src/Authorization/SignedInWrapper.tsx delete mode 100644 src/Authorization/withAuthorization.stories.tsx delete mode 100644 src/Authorization/withAuthorization.tsx diff --git a/src/Authorization/Redirect.stories.tsx b/src/Authorization/Redirect.stories.tsx index 2be1dbe73..606beca21 100644 --- a/src/Authorization/Redirect.stories.tsx +++ b/src/Authorization/Redirect.stories.tsx @@ -3,19 +3,13 @@ import { expect, within } from '@storybook/test'; import { BrowserRouter } from 'react-router-dom'; -import BuildIcon from '../icons/BuildIcon.js'; -import withAuthorization from './withAuthorization.js'; - -const ComponentWithAuthorization = withAuthorization(BuildIcon, { - // use an empty string because we do not want to be redirected but the prop is mandatory - redirectionLink: '', -}); +import SignedInWrapper from './SignedInWrapper.js'; // this story is separated from the others // because the redirection breaks a bit the navigation in storybook -const meta: Meta = { +const meta: Meta = { title: 'Actions/Authorization/Redirect', - component: ComponentWithAuthorization, + component: SignedInWrapper, parameters: { docs: { source: { @@ -24,6 +18,11 @@ const meta: Meta = { }, }, }, + render: () => ( + +
+ + ), decorators: [ (story) => { return {story()}; @@ -32,7 +31,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Redirect = { play: async ({ canvasElement }) => { diff --git a/src/Authorization/SignedInWrapper.stories.tsx b/src/Authorization/SignedInWrapper.stories.tsx new file mode 100644 index 000000000..9799d32f4 --- /dev/null +++ b/src/Authorization/SignedInWrapper.stories.tsx @@ -0,0 +1,33 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { CompleteMember } from '@graasp/sdk'; + +import BuildIcon from '@/icons/BuildIcon.js'; + +import SignedInWrapper from './SignedInWrapper.js'; + +const redirectionLink = 'http://redirect.org'; + +const meta: Meta = { + title: 'Actions/SignedInWrapper', + component: SignedInWrapper, + + argTypes: { + onRedirect: { action: 'onRedirect' }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Authorized: Story = { + render: () => ( + + + + ), +}; diff --git a/src/Authorization/SignedInWrapper.tsx b/src/Authorization/SignedInWrapper.tsx new file mode 100644 index 000000000..bec07b2c2 --- /dev/null +++ b/src/Authorization/SignedInWrapper.tsx @@ -0,0 +1,38 @@ +import { CurrentAccount, redirect } from '@graasp/sdk'; + +import RedirectionContent from './RedirectionContent.js'; + +export type WithAuthorizationProps = { + redirectionLink: string; + currentAccount?: CurrentAccount | null; + onRedirect?: () => void; + children: JSX.Element; +}; + +const SignedInWrapper = ({ + currentAccount, + redirectionLink, + onRedirect, + children, +}: WithAuthorizationProps): JSX.Element => { + const redirectToSignIn = (): void => { + if (!redirectionLink) { + return console.debug('No link has been set for redirection'); + } + redirect(window, redirectionLink); + }; + + // check authorization: user shouldn't be empty + if (currentAccount && currentAccount.id) { + return children; + } + + onRedirect?.(); + + redirectToSignIn(); + + // redirect page if redirection is not working + return ; +}; + +export default SignedInWrapper; diff --git a/src/Authorization/withAuthorization.stories.tsx b/src/Authorization/withAuthorization.stories.tsx deleted file mode 100644 index 41dc1bdd7..000000000 --- a/src/Authorization/withAuthorization.stories.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { CompleteMember } from '@graasp/sdk'; - -import BuildIcon from '../icons/BuildIcon.js'; -import withAuthorization from './withAuthorization.js'; - -const redirectionLink = 'http://redirect.org'; - -const ComponentWithAuthorization = withAuthorization(BuildIcon, { - redirectionLink, - currentMember: { id: 'member', name: 'member' } as CompleteMember, -}); - -const meta: Meta = { - title: 'Actions/Authorization', - component: ComponentWithAuthorization, - - argTypes: { - onRedirect: { action: 'onRedirect' }, - }, -}; - -export default meta; - -type Story = StoryObj; - -export const Authorized: Story = { - render: () => { - const Component = withAuthorization(BuildIcon, { - redirectionLink, - currentMember: { id: 'member', name: 'member' } as CompleteMember, - }); - return ; - }, -}; diff --git a/src/Authorization/withAuthorization.tsx b/src/Authorization/withAuthorization.tsx deleted file mode 100644 index 77e87e654..000000000 --- a/src/Authorization/withAuthorization.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { ComponentType } from 'react'; - -import { CompleteMember, redirect } from '@graasp/sdk'; - -import RedirectionContent from './RedirectionContent.js'; - -export type WithAuthorizationProps = { - redirectionLink: string; - currentMember?: CompleteMember | null; - onRedirect?: () => void; -}; - -const withAuthorization = -

( - ChildComponent: ComponentType

, - { currentMember, redirectionLink, onRedirect }: WithAuthorizationProps, - ): ((props: P) => JSX.Element) => - (childProps: P) => { - const redirectToSignIn = (): void => { - if (!redirectionLink) { - return console.debug('No link has been set for redirection'); - } - redirect(window, redirectionLink); - }; - - // check authorization: user shouldn't be empty - if (currentMember && currentMember.id) { - return ; - } - - onRedirect?.(); - - redirectToSignIn(); - - // redirect page if redirection is not working - return ; - }; - -export default withAuthorization; diff --git a/src/index.ts b/src/index.ts index 9b0acb788..92397b947 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,7 +53,7 @@ export { default as CreativeCommons } from './CreativeCommons/CreativeCommons.js export { default as CookiesBanner } from './CookiesBanner/CookiesBanner.js'; -export { default as withAuthorization } from './Authorization/withAuthorization.js'; +export { default as SignedInWrapper } from './Authorization/SignedInWrapper.js'; export { default as RedirectionContent } from './Authorization/RedirectionContent.js'; export { UserSwitch } from './UserSwitch/UserSwitch.js'; From 318e5f1aba1ff872d0dfd42303853c78d599bb4b Mon Sep 17 00:00:00 2001 From: kim Date: Tue, 17 Sep 2024 17:01:34 +0200 Subject: [PATCH 02/15] refactor: allow array of components --- src/Authorization/SignedInWrapper.stories.tsx | 2 ++ src/Authorization/SignedInWrapper.tsx | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Authorization/SignedInWrapper.stories.tsx b/src/Authorization/SignedInWrapper.stories.tsx index 9799d32f4..d8ea1065b 100644 --- a/src/Authorization/SignedInWrapper.stories.tsx +++ b/src/Authorization/SignedInWrapper.stories.tsx @@ -28,6 +28,8 @@ export const Authorized: Story = { currentAccount={{ id: 'member', name: 'member' } as CompleteMember} > + + ), }; diff --git a/src/Authorization/SignedInWrapper.tsx b/src/Authorization/SignedInWrapper.tsx index bec07b2c2..791562d24 100644 --- a/src/Authorization/SignedInWrapper.tsx +++ b/src/Authorization/SignedInWrapper.tsx @@ -1,12 +1,14 @@ +import { ReactNode } from 'react'; + import { CurrentAccount, redirect } from '@graasp/sdk'; import RedirectionContent from './RedirectionContent.js'; -export type WithAuthorizationProps = { +export type SignedInWrapperProps = { redirectionLink: string; currentAccount?: CurrentAccount | null; onRedirect?: () => void; - children: JSX.Element; + children: ReactNode; }; const SignedInWrapper = ({ @@ -14,7 +16,7 @@ const SignedInWrapper = ({ redirectionLink, onRedirect, children, -}: WithAuthorizationProps): JSX.Element => { +}: SignedInWrapperProps): SignedInWrapperProps['children'] => { const redirectToSignIn = (): void => { if (!redirectionLink) { return console.debug('No link has been set for redirection'); From 39acbf4319345818457b7f0dd8915f422132d9b9 Mon Sep 17 00:00:00 2001 From: kim Date: Tue, 17 Sep 2024 17:45:17 +0200 Subject: [PATCH 03/15] refactor: item login authorization --- .../ItemLoginAuthorization.stories.tsx | 74 ++++++++++ src/itemLogin/ItemLoginAuthorization.tsx | 138 ++++++------------ 2 files changed, 121 insertions(+), 91 deletions(-) create mode 100644 src/itemLogin/ItemLoginAuthorization.stories.tsx diff --git a/src/itemLogin/ItemLoginAuthorization.stories.tsx b/src/itemLogin/ItemLoginAuthorization.stories.tsx new file mode 100644 index 000000000..0e1d47f90 --- /dev/null +++ b/src/itemLogin/ItemLoginAuthorization.stories.tsx @@ -0,0 +1,74 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { expect } from '@storybook/test'; +import { within } from '@storybook/testing-library'; + +import { + CompleteMember, + ItemLoginSchemaType, + PackedDocumentItemFactory, +} from '@graasp/sdk'; + +import Card from '@/Card/Card.js'; + +import ItemLoginAuthorization from './ItemLoginAuthorization.js'; +import { FORBIDDEN_TEXT } from './constants.js'; + +const meta: Meta = { + title: 'Actions/ItemLoginAuthorization', + component: ItemLoginAuthorization, + + argTypes: { + signIn: { action: 'onRedirect' }, + }, + args: { + children: , + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Authorized: Story = { + args: { + currentAccount: { id: 'member', name: 'member' } as CompleteMember, + item: PackedDocumentItemFactory(), + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + expect(canvas.getByText('card')).toBeVisible(); + }, +}; +export const LogInForm: Story = { + args: { + itemLoginSchemaType: ItemLoginSchemaType.Username, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + expect(canvas.getByText('Sign In')).toBeVisible(); + }, +}; + +export const Loading: Story = { + args: { + isLoading: true, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + expect(canvas.getByRole('progressbar')).toBeVisible(); + }, +}; + +export const Forbidden: Story = { + args: { + currentAccount: { id: 'member', name: 'member' } as CompleteMember, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + expect(canvas.getByText(FORBIDDEN_TEXT)).toBeVisible(); + }, +}; diff --git a/src/itemLogin/ItemLoginAuthorization.tsx b/src/itemLogin/ItemLoginAuthorization.tsx index 9f7747f10..cbc675524 100644 --- a/src/itemLogin/ItemLoginAuthorization.tsx +++ b/src/itemLogin/ItemLoginAuthorization.tsx @@ -1,12 +1,7 @@ -import type { UseQueryResult } from '@tanstack/react-query'; -import { StatusCodes, getReasonPhrase } from 'http-status-codes'; - -import { Alert } from '@mui/material'; - -import { ReactElement } from 'react'; +import { ReactElement, ReactNode } from 'react'; import { - CompleteMember, + CurrentAccount, DiscriminatedItem, ItemLoginSchemaType, UUID, @@ -19,99 +14,60 @@ import ItemLoginScreen, { SignInPropertiesType } from './ItemLoginScreen.js'; export type ItemLoginAuthorizationProps = { signIn: (args: { itemId: string } & SignInPropertiesType) => void; itemId: UUID; - useCurrentMember: () => UseQueryResult; - useItem: (itemId?: string) => UseQueryResult; - useItemLoginSchemaType: (args: { - itemId?: string; - }) => UseQueryResult; - Error?: ReactElement; + currentAccount?: CurrentAccount | null; + item?: DiscriminatedItem; + itemLoginSchemaType?: ItemLoginSchemaType; memberIdInputId?: string; usernameInputId?: string; signInButtonId?: string; passwordInputId?: string; + children: ReactNode; ForbiddenContent?: ReactElement; + isLoading?: boolean; }; -const ItemLoginAuthorization = - ({ - useCurrentMember, - useItem, - useItemLoginSchemaType, - itemId, - signIn, - Error: ErrorComponent, - usernameInputId, - signInButtonId, - passwordInputId, - ForbiddenContent = , - }: ItemLoginAuthorizationProps) => - (ChildComponent: () => JSX.Element) => { - const ComposedComponent = (): ReactElement => { - const { - data: user, - isLoading: isMemberLoading, - isError: isCurrentMemberError, - } = useCurrentMember(); - const { data: itemLoginSchemaType } = useItemLoginSchemaType({ itemId }); - const { - data: item, - isLoading: isItemLoading, - error: itemError, - isError: isItemError, - } = useItem(itemId); - - if (isMemberLoading || (isItemLoading && !item)) { - // get item login if the user is not authenticated and the item is empty - return ; - } - - // member should never trigger an error - // but can be empty - if (isCurrentMemberError) { - return ( - ErrorComponent ?? An error occurred. - ); - } +const ItemLoginAuthorization = ({ + currentAccount, + item, + itemLoginSchemaType, + itemId, + signIn, + isLoading, + usernameInputId, + signInButtonId, + passwordInputId, + ForbiddenContent = , + children, +}: ItemLoginAuthorizationProps): ReactNode => { + if (isLoading) { + // get item login if the user is not authenticated and the item is empty + return ; + } - if ( - isItemError && - [ - getReasonPhrase(StatusCodes.BAD_REQUEST), - getReasonPhrase(StatusCodes.NOT_FOUND), - ].includes((itemError as Error).message) - ) { - return ( - ErrorComponent ?? An error occurred. - ); - } + // the item could be fetched without errors + // because the user is signed in and has access + // or because the item is public + if (item && item.id) { + return children; + } - // the item could be fetched without errors - // because the user is signed in and has access - // or because the item is public - if (item && item.id) { - return ; - } + // signed out but can sign in with item login + if ((!currentAccount || !currentAccount.id) && itemLoginSchemaType) { + return ( + + ); + } - // signed out but can sign in with item login - if ((!user || !user.id) && itemLoginSchemaType) { - return ( - - ); - } - - // either the item does not allow item login - // or the user is already signed in as normal user and hasn't the access to this item - return ForbiddenContent; - }; - - return ComposedComponent; - }; + // either the item does not allow item login + // or the user is already signed in as normal user and hasn't the access to this item + return ForbiddenContent; +}; export default ItemLoginAuthorization; From d20f09f5d8b37000f5607447677ae7327f18471a Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 18 Sep 2024 08:55:44 +0200 Subject: [PATCH 04/15] refactor: change redirection link --- src/Authorization/SignedInWrapper.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Authorization/SignedInWrapper.stories.tsx b/src/Authorization/SignedInWrapper.stories.tsx index d8ea1065b..5855c2cef 100644 --- a/src/Authorization/SignedInWrapper.stories.tsx +++ b/src/Authorization/SignedInWrapper.stories.tsx @@ -6,7 +6,7 @@ import BuildIcon from '@/icons/BuildIcon.js'; import SignedInWrapper from './SignedInWrapper.js'; -const redirectionLink = 'http://redirect.org'; +const redirectionLink = 'https://redirect.org'; const meta: Meta = { title: 'Actions/SignedInWrapper', From 04f1fcbc1eefee85e6ea34782c92dc90f6e3951b Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 18 Sep 2024 09:05:08 +0200 Subject: [PATCH 05/15] refactor: fix children type --- src/Authorization/SignedInWrapper.tsx | 2 +- src/itemLogin/ItemLoginAuthorization.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Authorization/SignedInWrapper.tsx b/src/Authorization/SignedInWrapper.tsx index 791562d24..3f8f622e9 100644 --- a/src/Authorization/SignedInWrapper.tsx +++ b/src/Authorization/SignedInWrapper.tsx @@ -8,7 +8,7 @@ export type SignedInWrapperProps = { redirectionLink: string; currentAccount?: CurrentAccount | null; onRedirect?: () => void; - children: ReactNode; + children?: ReactNode; }; const SignedInWrapper = ({ diff --git a/src/itemLogin/ItemLoginAuthorization.tsx b/src/itemLogin/ItemLoginAuthorization.tsx index cbc675524..dbe204de5 100644 --- a/src/itemLogin/ItemLoginAuthorization.tsx +++ b/src/itemLogin/ItemLoginAuthorization.tsx @@ -21,7 +21,7 @@ export type ItemLoginAuthorizationProps = { usernameInputId?: string; signInButtonId?: string; passwordInputId?: string; - children: ReactNode; + children?: ReactNode; ForbiddenContent?: ReactElement; isLoading?: boolean; }; From 95d294caae9cac84bdaa322108ac7a9d74ed0b62 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 18 Sep 2024 12:06:04 +0200 Subject: [PATCH 06/15] refactor: change for current member --- src/UserSwitch/UserSwitch.stories.tsx | 4 ++-- src/UserSwitch/UserSwitch.tsx | 14 +++++++------- src/UserSwitch/UserSwitchWrapper.stories.tsx | 4 ++-- src/UserSwitch/UserSwitchWrapper.tsx | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/UserSwitch/UserSwitch.stories.tsx b/src/UserSwitch/UserSwitch.stories.tsx index 38f6697b9..a32e12794 100644 --- a/src/UserSwitch/UserSwitch.stories.tsx +++ b/src/UserSwitch/UserSwitch.stories.tsx @@ -20,7 +20,7 @@ type Story = StoryObj; export const SignedIn = { args: { currentMember: MOCK_CURRENT_MEMBER, - renderAvatar: () => ( + avatar: ( ( + avatar: ( MouseEventHandler; - renderAvatar?: (member?: CompleteMember | null) => JSX.Element; + avatar?: JSX.Element; signedOutTooltipText?: string; }; @@ -49,7 +49,7 @@ export const UserSwitch = ({ isMemberLoading = false, currentMember, menuId, - renderAvatar = () => <>, + avatar, signedOutTooltipText = 'You are not signed in.', }: Props): JSX.Element => { const [anchorEl, setAnchorEl] = useState<(EventTarget & Element) | null>( @@ -121,7 +121,7 @@ export const UserSwitch = ({ return ( - {renderAvatar(currentMember)} + {avatar}

@@ -130,7 +130,7 @@ export const UserSwitch = ({ {/* show info only for normal member */} {/* todo: show which item a pseudonymized member as access to */} - {!isPseudoMember(currentMember) && ( + {currentMember.type === AccountType.Individual && ( <> {currentMember.email} @@ -165,7 +165,7 @@ export const UserSwitch = ({ return ( <> - {renderAvatar(currentMember)} + {avatar} {memberName && !isMobile && ( ( + avatar: ( { export const SignedOut: Story = { args: { switchMemberText: 'Sign In', - renderAvatar: () => ( + avatar: ( string; ButtonContent?: JSX.Element; buttonId?: string; - currentMember?: CompleteMember | null; + currentMember?: CurrentAccount | null; // domain: string; isCurrentMemberLoading: boolean; // isCurrentMemberSuccess: boolean; profilePath: string; redirectPath: string; - renderAvatar: (member?: CompleteMember | null) => JSX.Element; + avatar: JSX.Element; seeProfileButtonId?: string; seeProfileText?: string; signedOutTooltipText?: string; @@ -56,7 +56,7 @@ export const UserSwitchWrapper = ({ // isCurrentMemberSuccess, profilePath, redirectPath, - renderAvatar, + avatar, seeProfileButtonId, seeProfileText = 'See Profile', signedOutTooltipText = 'You are not signed in.', @@ -167,7 +167,7 @@ export const UserSwitchWrapper = ({ signedOutTooltipText={signedOutTooltipText} buttonId={buttonId} buildMemberMenuItemId={buildMemberMenuItemId} - renderAvatar={renderAvatar} + avatar={avatar} /> ); }; From 94d0b9ee4c80dc00026d9d857ffca7d67a4e72d5 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 18 Sep 2024 12:34:14 +0200 Subject: [PATCH 07/15] refactor: fix type --- src/UserSwitch/UserSwitchWrapper.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UserSwitch/UserSwitchWrapper.tsx b/src/UserSwitch/UserSwitchWrapper.tsx index 7df905204..b13488481 100644 --- a/src/UserSwitch/UserSwitchWrapper.tsx +++ b/src/UserSwitch/UserSwitchWrapper.tsx @@ -35,7 +35,7 @@ interface Props { * @param memberId Id of the user to sign out (current user) * @returns Promise of void */ - signOut: (memberId: string) => Promise; + signOut: () => Promise; signOutMenuItemId?: string; signOutText?: string; // switchMember: (args: { memberId: string; domain: string }) => Promise; @@ -95,7 +95,7 @@ export const UserSwitchWrapper = ({ const handleSignOut = async (): Promise => { if (currentMember) { - await signOut(currentMember.id); + await signOut(); } // on sign out success should redirect to sign in redirect(window, redirectPath); From d7442ae3f51015c029a70696b2a8f6840f9c8773 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 18 Sep 2024 12:47:48 +0200 Subject: [PATCH 08/15] feat: add prevent guest wrapper --- src/Authorization/PreventGuestWrapper.tsx | 63 +++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/Authorization/PreventGuestWrapper.tsx diff --git a/src/Authorization/PreventGuestWrapper.tsx b/src/Authorization/PreventGuestWrapper.tsx new file mode 100644 index 000000000..45a3edb6d --- /dev/null +++ b/src/Authorization/PreventGuestWrapper.tsx @@ -0,0 +1,63 @@ +import { ClipboardPen } from 'lucide-react'; + +import { + Alert, + Box, + Container, + Button as MuiButton, + Stack, + Typography, +} from '@mui/material'; + +import { AccountType, CurrentAccount } from '@graasp/sdk'; + +type Props = { + buttonText: string; + children: JSX.Element; + currentAccount?: CurrentAccount | null; + error?: JSX.Element; + onButtonClick: () => void; + startIcon?: JSX.Element; + text: string | JSX.Element; +}; + +const PreventGuestWrapper = ({ + buttonText = 'Log out and Create a Graasp account', + children, + currentAccount, + error, + onButtonClick, + startIcon = , + text = 'You are currently using Graasp with a guest account. In order to use all features of Graasp, you have to log out and create a Graasp account.', +}: Props): JSX.Element => { + if (currentAccount) { + // guest - should not have access to home + if (currentAccount.type === AccountType.Guest) { + return ( + + + + {text} + + + {buttonText} + + + + + + ); + } + + return children; + } + + return error ?? An error occured.; +}; + +export default PreventGuestWrapper; From 1a17167f69de1f27d6b19b92ea27e7696de751c7 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 18 Sep 2024 12:50:29 +0200 Subject: [PATCH 09/15] refactor: export prevent guest --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 92397b947..bc80d5fc3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,6 +54,7 @@ export { default as CreativeCommons } from './CreativeCommons/CreativeCommons.js export { default as CookiesBanner } from './CookiesBanner/CookiesBanner.js'; export { default as SignedInWrapper } from './Authorization/SignedInWrapper.js'; +export { default as PreventGuestWrapper } from './Authorization/PreventGuestWrapper.js'; export { default as RedirectionContent } from './Authorization/RedirectionContent.js'; export { UserSwitch } from './UserSwitch/UserSwitch.js'; From f831e9b1bec97cefd96312a13fa25f513a653354 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 18 Sep 2024 14:10:42 +0200 Subject: [PATCH 10/15] refactor: add id to preventguestwrapper --- .../PreventGuestWrapper.stories.tsx | 64 +++++++++++++++++++ src/Authorization/PreventGuestWrapper.tsx | 24 +++++-- src/Authorization/SignedInWrapper.stories.tsx | 36 ++++++++--- 3 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 src/Authorization/PreventGuestWrapper.stories.tsx diff --git a/src/Authorization/PreventGuestWrapper.stories.tsx b/src/Authorization/PreventGuestWrapper.stories.tsx new file mode 100644 index 000000000..623172191 --- /dev/null +++ b/src/Authorization/PreventGuestWrapper.stories.tsx @@ -0,0 +1,64 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { expect, within } from '@storybook/test'; + +import { AccountType, CompleteGuest, CompleteMember } from '@graasp/sdk'; + +import BuildIcon from '@/icons/BuildIcon.js'; + +import PreventGuestWrapper from './PreventGuestWrapper.js'; + +const meta: Meta = { + title: 'Actions/PreventGuestWrapper', + component: PreventGuestWrapper, + + argTypes: {}, + args: { + children: ( +
+ + + +
+ ), + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Guest: Story = { + args: { + currentAccount: { type: AccountType.Guest } as CompleteGuest, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + // should see message + await expect(canvas.getByRole('alert')).toBeVisible(); + }, +}; + +export const Individual: Story = { + args: { + currentAccount: { id: 'member', name: 'member' } as CompleteMember, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + // should see content + await expect(canvas.getByTestId('content')).toBeVisible(); + }, +}; + +export const Error: Story = { + args: { + errorText: 'error text', + }, + play: async ({ args, canvasElement }) => { + const canvas = within(canvasElement); + + // should see error message + await expect(canvas.getByText(args.errorText!)).toBeVisible(); + }, +}; diff --git a/src/Authorization/PreventGuestWrapper.tsx b/src/Authorization/PreventGuestWrapper.tsx index 45a3edb6d..ac17da8c9 100644 --- a/src/Authorization/PreventGuestWrapper.tsx +++ b/src/Authorization/PreventGuestWrapper.tsx @@ -9,16 +9,24 @@ import { Typography, } from '@mui/material'; +import { ReactNode } from 'react'; + import { AccountType, CurrentAccount } from '@graasp/sdk'; type Props = { - buttonText: string; - children: JSX.Element; + buttonText?: string; + children?: ReactNode; currentAccount?: CurrentAccount | null; + /** + * Component to display on error. + * Overrides errorText + */ error?: JSX.Element; - onButtonClick: () => void; + errorText?: string; + id?: string; + onButtonClick?: () => void; startIcon?: JSX.Element; - text: string | JSX.Element; + text?: string | JSX.Element; }; const PreventGuestWrapper = ({ @@ -26,17 +34,19 @@ const PreventGuestWrapper = ({ children, currentAccount, error, + id, onButtonClick, startIcon = , + errorText = 'An error occured.', text = 'You are currently using Graasp with a guest account. In order to use all features of Graasp, you have to log out and create a Graasp account.', -}: Props): JSX.Element => { +}: Props): ReactNode => { if (currentAccount) { // guest - should not have access to home if (currentAccount.type === AccountType.Guest) { return ( - + {text} An error occured.; + return error ?? {errorText}; }; export default PreventGuestWrapper; diff --git a/src/Authorization/SignedInWrapper.stories.tsx b/src/Authorization/SignedInWrapper.stories.tsx index 5855c2cef..61263bee0 100644 --- a/src/Authorization/SignedInWrapper.stories.tsx +++ b/src/Authorization/SignedInWrapper.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { expect, within } from '@storybook/test'; import { CompleteMember } from '@graasp/sdk'; @@ -15,6 +16,16 @@ const meta: Meta = { argTypes: { onRedirect: { action: 'onRedirect' }, }, + args: { + redirectionLink, + children: ( +
+ + + +
+ ), + }, }; export default meta; @@ -22,14 +33,19 @@ export default meta; type Story = StoryObj; export const Authorized: Story = { - render: () => ( - - - - - - ), + args: { + currentAccount: { id: 'member', name: 'member' } as CompleteMember, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + // should see content + await expect(canvas.getByTestId('content')).toBeVisible(); + }, +}; + +export const SignedOut: Story = { + args: { + currentAccount: undefined, + }, }; From b1643566fac0361d8816e1bb043172b032a6d91f Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 18 Sep 2024 14:53:14 +0200 Subject: [PATCH 11/15] refactor: remove signed out test --- src/Authorization/SignedInWrapper.stories.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Authorization/SignedInWrapper.stories.tsx b/src/Authorization/SignedInWrapper.stories.tsx index 61263bee0..a83435bef 100644 --- a/src/Authorization/SignedInWrapper.stories.tsx +++ b/src/Authorization/SignedInWrapper.stories.tsx @@ -43,9 +43,3 @@ export const Authorized: Story = { await expect(canvas.getByTestId('content')).toBeVisible(); }, }; - -export const SignedOut: Story = { - args: { - currentAccount: undefined, - }, -}; From dc6366c118d1b4cab83aeecf6f289f1167d20bc2 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 18 Sep 2024 14:58:28 +0200 Subject: [PATCH 12/15] refactor: fix issues --- src/Authorization/PreventGuestWrapper.stories.tsx | 6 ++++-- src/Authorization/SignedInWrapper.tsx | 2 +- src/itemLogin/ItemLoginAuthorization.tsx | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Authorization/PreventGuestWrapper.stories.tsx b/src/Authorization/PreventGuestWrapper.stories.tsx index 623172191..281a519ce 100644 --- a/src/Authorization/PreventGuestWrapper.stories.tsx +++ b/src/Authorization/PreventGuestWrapper.stories.tsx @@ -51,7 +51,7 @@ export const Individual: Story = { }, }; -export const Error: Story = { +export const ShowError: Story = { args: { errorText: 'error text', }, @@ -59,6 +59,8 @@ export const Error: Story = { const canvas = within(canvasElement); // should see error message - await expect(canvas.getByText(args.errorText!)).toBeVisible(); + if (args.errorText) { + await expect(canvas.getByText(args.errorText)).toBeVisible(); + } }, }; diff --git a/src/Authorization/SignedInWrapper.tsx b/src/Authorization/SignedInWrapper.tsx index 3f8f622e9..bed233532 100644 --- a/src/Authorization/SignedInWrapper.tsx +++ b/src/Authorization/SignedInWrapper.tsx @@ -25,7 +25,7 @@ const SignedInWrapper = ({ }; // check authorization: user shouldn't be empty - if (currentAccount && currentAccount.id) { + if (currentAccount?.id) { return children; } diff --git a/src/itemLogin/ItemLoginAuthorization.tsx b/src/itemLogin/ItemLoginAuthorization.tsx index dbe204de5..5bd48af57 100644 --- a/src/itemLogin/ItemLoginAuthorization.tsx +++ b/src/itemLogin/ItemLoginAuthorization.tsx @@ -17,7 +17,6 @@ export type ItemLoginAuthorizationProps = { currentAccount?: CurrentAccount | null; item?: DiscriminatedItem; itemLoginSchemaType?: ItemLoginSchemaType; - memberIdInputId?: string; usernameInputId?: string; signInButtonId?: string; passwordInputId?: string; From 68f3e7d1e2a1715f9b7a2974f994c8db8f6d06bb Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 19 Sep 2024 14:11:37 +0200 Subject: [PATCH 13/15] refactor: apply PR requested changes --- .../ItemLoginAuthorization.stories.tsx | 29 +++++++++++-------- src/itemLogin/ItemLoginAuthorization.tsx | 1 - src/itemLogin/ItemLoginScreen.stories.tsx | 22 ++++++++------ src/itemLogin/ItemLoginScreen.tsx | 2 +- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/itemLogin/ItemLoginAuthorization.stories.tsx b/src/itemLogin/ItemLoginAuthorization.stories.tsx index 0e1d47f90..f44937251 100644 --- a/src/itemLogin/ItemLoginAuthorization.stories.tsx +++ b/src/itemLogin/ItemLoginAuthorization.stories.tsx @@ -13,7 +13,8 @@ import Card from '@/Card/Card.js'; import ItemLoginAuthorization from './ItemLoginAuthorization.js'; import { FORBIDDEN_TEXT } from './constants.js'; -const meta: Meta = { +const item = PackedDocumentItemFactory(); +const meta = { title: 'Actions/ItemLoginAuthorization', component: ItemLoginAuthorization, @@ -21,26 +22,30 @@ const meta: Meta = { signIn: { action: 'onRedirect' }, }, args: { + signIn: () => {}, + itemId: item.id, + children: , }, -}; +} satisfies Meta; export default meta; -type Story = StoryObj; +type Story = StoryObj; -export const Authorized: Story = { +export const Authorized = { args: { currentAccount: { id: 'member', name: 'member' } as CompleteMember, - item: PackedDocumentItemFactory(), + item, }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); expect(canvas.getByText('card')).toBeVisible(); }, -}; -export const LogInForm: Story = { +} satisfies Story; + +export const LogInForm = { args: { itemLoginSchemaType: ItemLoginSchemaType.Username, }, @@ -49,9 +54,9 @@ export const LogInForm: Story = { expect(canvas.getByText('Sign In')).toBeVisible(); }, -}; +} satisfies Story; -export const Loading: Story = { +export const Loading = { args: { isLoading: true, }, @@ -60,9 +65,9 @@ export const Loading: Story = { expect(canvas.getByRole('progressbar')).toBeVisible(); }, -}; +} satisfies Story; -export const Forbidden: Story = { +export const Forbidden = { args: { currentAccount: { id: 'member', name: 'member' } as CompleteMember, }, @@ -71,4 +76,4 @@ export const Forbidden: Story = { expect(canvas.getByText(FORBIDDEN_TEXT)).toBeVisible(); }, -}; +} satisfies Story; diff --git a/src/itemLogin/ItemLoginAuthorization.tsx b/src/itemLogin/ItemLoginAuthorization.tsx index 5bd48af57..1cd20dc26 100644 --- a/src/itemLogin/ItemLoginAuthorization.tsx +++ b/src/itemLogin/ItemLoginAuthorization.tsx @@ -39,7 +39,6 @@ const ItemLoginAuthorization = ({ children, }: ItemLoginAuthorizationProps): ReactNode => { if (isLoading) { - // get item login if the user is not authenticated and the item is empty return ; } diff --git a/src/itemLogin/ItemLoginScreen.stories.tsx b/src/itemLogin/ItemLoginScreen.stories.tsx index 85a1ead8a..4f95e17c8 100644 --- a/src/itemLogin/ItemLoginScreen.stories.tsx +++ b/src/itemLogin/ItemLoginScreen.stories.tsx @@ -2,18 +2,20 @@ import type { Meta, StoryObj } from '@storybook/react'; import { expect, fn } from '@storybook/test'; import { userEvent, within } from '@storybook/testing-library'; -import { ItemLoginSchemaType } from '@graasp/sdk'; +import { ItemLoginSchemaType, PackedDocumentItemFactory } from '@graasp/sdk'; import { TABLE_CATEGORIES } from '../utils/storybook.js'; import ItemLoginScreen from './ItemLoginScreen.js'; import { FORBIDDEN_TEXT } from './constants.js'; -const meta: Meta = { +const item = PackedDocumentItemFactory(); +const meta = { title: 'Actions/ItemLogin/ItemLoginScreen', component: ItemLoginScreen, args: { signIn: fn(), + itemId: item.id, }, argTypes: { passwordInputId: { @@ -33,11 +35,11 @@ const meta: Meta = { }, signIn: { action: 'signin' }, }, -}; +} satisfies Meta; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const ItemLoginUsernameAndPassword: Story = { args: { @@ -55,7 +57,7 @@ export const ItemLoginUsernameAndPassword: Story = { expect(args.signIn).toHaveBeenCalled(); }, -}; +} satisfies Story; export const ItemLoginUsername: Story = { args: { @@ -72,13 +74,15 @@ export const ItemLoginUsername: Story = { expect(args.signIn).toHaveBeenCalled(); }, -}; +} satisfies Story; -export const Forbidden: Story = { - args: {}, +export const Forbidden = { + args: { + itemId: item.id, + }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await expect(canvas.getByText(FORBIDDEN_TEXT)).toBeInTheDocument(); }, -}; +} satisfies Story; diff --git a/src/itemLogin/ItemLoginScreen.tsx b/src/itemLogin/ItemLoginScreen.tsx index e2f21f003..03e8c2e2c 100644 --- a/src/itemLogin/ItemLoginScreen.tsx +++ b/src/itemLogin/ItemLoginScreen.tsx @@ -37,7 +37,7 @@ export type ItemLoginScreenProps = { /** * item login schema object */ - itemLoginSchemaType: ItemLoginSchemaType; + itemLoginSchemaType?: ItemLoginSchemaType; signIn: (args: { itemId: string } & SignInPropertiesType) => void; /** * content to display when the user doesn't have access From fd51a3888ae36f701a7c3753514be88a4b892d4c Mon Sep 17 00:00:00 2001 From: kim Date: Fri, 20 Sep 2024 10:36:20 +0200 Subject: [PATCH 14/15] refactor: apply PR requested changes --- .../PreventGuestWrapper.stories.tsx | 21 +++++++++++-------- src/Authorization/PreventGuestWrapper.tsx | 14 ++++++------- src/Authorization/SignedInWrapper.tsx | 3 ++- src/itemLogin/ItemLoginAuthorization.tsx | 2 +- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/Authorization/PreventGuestWrapper.stories.tsx b/src/Authorization/PreventGuestWrapper.stories.tsx index 281a519ce..8eb64889a 100644 --- a/src/Authorization/PreventGuestWrapper.stories.tsx +++ b/src/Authorization/PreventGuestWrapper.stories.tsx @@ -7,12 +7,15 @@ import BuildIcon from '@/icons/BuildIcon.js'; import PreventGuestWrapper from './PreventGuestWrapper.js'; -const meta: Meta = { +const meta = { title: 'Actions/PreventGuestWrapper', component: PreventGuestWrapper, argTypes: {}, args: { + buttonText: 'Log out and Create a Graasp account', + errorText: 'An error occured.', + text: 'You are currently using Graasp with a guest account. In order to use all features of Graasp, you have to log out and create a Graasp account.', children: (
@@ -21,13 +24,13 @@ const meta: Meta = {
), }, -}; +} satisfies Meta; export default meta; -type Story = StoryObj; +type Story = StoryObj; -export const Guest: Story = { +export const Guest = { args: { currentAccount: { type: AccountType.Guest } as CompleteGuest, }, @@ -37,9 +40,9 @@ export const Guest: Story = { // should see message await expect(canvas.getByRole('alert')).toBeVisible(); }, -}; +} satisfies Story; -export const Individual: Story = { +export const Individual = { args: { currentAccount: { id: 'member', name: 'member' } as CompleteMember, }, @@ -49,9 +52,9 @@ export const Individual: Story = { // should see content await expect(canvas.getByTestId('content')).toBeVisible(); }, -}; +} satisfies Story; -export const ShowError: Story = { +export const ShowError = { args: { errorText: 'error text', }, @@ -63,4 +66,4 @@ export const ShowError: Story = { await expect(canvas.getByText(args.errorText)).toBeVisible(); } }, -}; +} satisfies Story; diff --git a/src/Authorization/PreventGuestWrapper.tsx b/src/Authorization/PreventGuestWrapper.tsx index ac17da8c9..4a785285a 100644 --- a/src/Authorization/PreventGuestWrapper.tsx +++ b/src/Authorization/PreventGuestWrapper.tsx @@ -14,7 +14,7 @@ import { ReactNode } from 'react'; import { AccountType, CurrentAccount } from '@graasp/sdk'; type Props = { - buttonText?: string; + buttonText: string; children?: ReactNode; currentAccount?: CurrentAccount | null; /** @@ -22,26 +22,26 @@ type Props = { * Overrides errorText */ error?: JSX.Element; - errorText?: string; + errorText: string; id?: string; onButtonClick?: () => void; startIcon?: JSX.Element; - text?: string | JSX.Element; + text: string | JSX.Element; }; const PreventGuestWrapper = ({ - buttonText = 'Log out and Create a Graasp account', + buttonText, children, currentAccount, error, id, onButtonClick, startIcon = , - errorText = 'An error occured.', - text = 'You are currently using Graasp with a guest account. In order to use all features of Graasp, you have to log out and create a Graasp account.', + errorText, + text, }: Props): ReactNode => { if (currentAccount) { - // guest - should not have access to home + // guest - should not have access to children if (currentAccount.type === AccountType.Guest) { return ( diff --git a/src/Authorization/SignedInWrapper.tsx b/src/Authorization/SignedInWrapper.tsx index bed233532..a33f79e50 100644 --- a/src/Authorization/SignedInWrapper.tsx +++ b/src/Authorization/SignedInWrapper.tsx @@ -19,7 +19,8 @@ const SignedInWrapper = ({ }: SignedInWrapperProps): SignedInWrapperProps['children'] => { const redirectToSignIn = (): void => { if (!redirectionLink) { - return console.debug('No link has been set for redirection'); + console.debug('No link has been set for redirection'); + return; } redirect(window, redirectionLink); }; diff --git a/src/itemLogin/ItemLoginAuthorization.tsx b/src/itemLogin/ItemLoginAuthorization.tsx index 1cd20dc26..e3631b99b 100644 --- a/src/itemLogin/ItemLoginAuthorization.tsx +++ b/src/itemLogin/ItemLoginAuthorization.tsx @@ -50,7 +50,7 @@ const ItemLoginAuthorization = ({ } // signed out but can sign in with item login - if ((!currentAccount || !currentAccount.id) && itemLoginSchemaType) { + if (!currentAccount?.id && itemLoginSchemaType) { return ( Date: Fri, 20 Sep 2024 11:01:28 +0200 Subject: [PATCH 15/15] feat: do not show profile for user switch --- src/UserSwitch/UserSwitchWrapper.stories.tsx | 125 +++++++++++++------ src/UserSwitch/UserSwitchWrapper.tsx | 43 ++++--- 2 files changed, 115 insertions(+), 53 deletions(-) diff --git a/src/UserSwitch/UserSwitchWrapper.stories.tsx b/src/UserSwitch/UserSwitchWrapper.stories.tsx index 5ca1cc12b..a998b76d4 100644 --- a/src/UserSwitch/UserSwitchWrapper.stories.tsx +++ b/src/UserSwitch/UserSwitchWrapper.stories.tsx @@ -2,26 +2,41 @@ import type { Meta, StoryObj } from '@storybook/react'; import { expect } from '@storybook/test'; import { screen, userEvent, within } from '@storybook/testing-library'; +import { + GuestFactory, + ItemLoginSchemaFactory, + ItemLoginSchemaType, + PackedFolderItemFactory, +} from '@graasp/sdk'; + import { SMALL_AVATAR_SIZE } from '@/constants.js'; import Avatar from '../Avatar/Avatar.js'; import { MOCK_CURRENT_MEMBER } from '../utils/fixtures.js'; import UserSwitchWrapper from './UserSwitchWrapper.js'; -const meta: Meta = { +const meta = { title: 'Common/UserSwitch/UserSwitchWrapper', component: UserSwitchWrapper, -}; + argTypes: { + signOut: { action: 'signOut' }, + }, + args: { + signOut: async () => {}, + profilePath: 'profilePath', + redirectPath: 'redirectPath', + }, +} satisfies Meta; export default meta; -type Story = StoryObj; +type Story = StoryObj; -export const SignedIn: Story = { +export const SignedIn = { args: { - currentMember: MOCK_CURRENT_MEMBER, seeProfileText: 'See Profile', signOutText: 'Sign Out', + currentMember: MOCK_CURRENT_MEMBER, avatar: ( ), }, -}; -SignedIn.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); - // open dialog - const nameText = canvas.getByLabelText(MOCK_CURRENT_MEMBER.name); - await userEvent.click(nameText); + // open dialog + const nameText = canvas.getByLabelText(MOCK_CURRENT_MEMBER.name); + await userEvent.click(nameText); - const menuCanvas = within(await screen.getByRole('menu')); + const menuCanvas = within(await screen.getByRole('menu')); - // profile button - const profileButton = menuCanvas.getByText(SignedIn.args!.seeProfileText!); - expect(profileButton).toBeInTheDocument(); + // profile button + const profileButton = menuCanvas.getByText(SignedIn.args!.seeProfileText!); + expect(profileButton).toBeInTheDocument(); - // email - const emailText = menuCanvas.getByText(MOCK_CURRENT_MEMBER.email); - expect(emailText).toBeInTheDocument(); + // email + const emailText = menuCanvas.getByText(MOCK_CURRENT_MEMBER.email); + expect(emailText).toBeInTheDocument(); - // sign out button - const signOutButton = menuCanvas.getByText(SignedIn.args!.signOutText!); - expect(signOutButton).toBeInTheDocument(); -}; + // sign out button + const signOutButton = menuCanvas.getByText(SignedIn.args!.signOutText!); + expect(signOutButton).toBeInTheDocument(); + }, +} satisfies Story; -export const SignedOut: Story = { +export const Guest = { + args: { + seeProfileText: 'See Profile', + signOutText: 'Sign Out', + currentMember: GuestFactory({ + itemLoginSchema: ItemLoginSchemaFactory({ + item: PackedFolderItemFactory(), + type: ItemLoginSchemaType.Username, + }), + }), + avatar: ( + + ), + }, + play: async ({ args, canvasElement }) => { + const canvas = within(canvasElement); + + // open dialog + const nameText = canvas.getByLabelText(args.currentMember!.name); + await userEvent.click(nameText); + + const menuCanvas = within(await screen.getByRole('menu')); + + // have 2 menu items - do not show profile button + expect(menuCanvas.getAllByRole('menuitem')).toHaveLength(2); + + // sign out button + const signOutButton = menuCanvas.getByText(SignedIn.args!.signOutText!); + expect(signOutButton).toBeInTheDocument(); + }, +} satisfies Story; + +export const SignedOut = { args: { switchMemberText: 'Sign In', avatar: ( @@ -70,17 +124,18 @@ export const SignedOut: Story = { /> ), }, -}; - -SignedOut.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); - // open dialog - const nameText = canvas.getByRole('button'); - await userEvent.click(nameText); + // open dialog + const nameText = canvas.getByRole('button'); + await userEvent.click(nameText); - // custom content - const menuCanvas = within(await screen.getByRole('menu')); - const signInButton = menuCanvas.getByText(SignedOut.args!.switchMemberText!); - expect(signInButton).toBeInTheDocument(); -}; + // custom content + const menuCanvas = within(await screen.getByRole('menu')); + const signInButton = menuCanvas.getByText( + SignedOut.args!.switchMemberText!, + ); + expect(signInButton).toBeInTheDocument(); + }, +} satisfies Story; diff --git a/src/UserSwitch/UserSwitchWrapper.tsx b/src/UserSwitch/UserSwitchWrapper.tsx index b13488481..ce91b9bbd 100644 --- a/src/UserSwitch/UserSwitchWrapper.tsx +++ b/src/UserSwitch/UserSwitchWrapper.tsx @@ -4,7 +4,7 @@ import { } from '@mui/icons-material'; import { ListItemIcon, MenuItem, Typography } from '@mui/material'; -import { CurrentAccount, redirect } from '@graasp/sdk'; +import { AccountType, CurrentAccount, redirect } from '@graasp/sdk'; import Loader from '../Loader/Loader.js'; import { UserSwitch } from './UserSwitch.js'; @@ -21,7 +21,7 @@ interface Props { buttonId?: string; currentMember?: CurrentAccount | null; // domain: string; - isCurrentMemberLoading: boolean; + isCurrentMemberLoading?: boolean; // isCurrentMemberSuccess: boolean; profilePath: string; redirectPath: string; @@ -41,7 +41,7 @@ interface Props { // switchMember: (args: { memberId: string; domain: string }) => Promise; switchMemberText?: string; - userMenuItems: UserMenuItem[]; + userMenuItems?: UserMenuItem[]; // useMembers: (ids: string[]) => UseQueryResult>; } @@ -52,7 +52,7 @@ export const UserSwitchWrapper = ({ buttonId, currentMember, // domain, - isCurrentMemberLoading, + isCurrentMemberLoading = false, // isCurrentMemberSuccess, profilePath, redirectPath, @@ -107,7 +107,7 @@ export const UserSwitchWrapper = ({ return redirect(window, redirectPath); }; - const goToSettings = (): void => { + const goToProfile = (): void => { redirect(window, profilePath); }; @@ -127,25 +127,32 @@ export const UserSwitchWrapper = ({ )); if (currentMember && currentMember.id) { - Actions = [ - - - - - {seeProfileText} - , - ...MenuItems, + Actions = + currentMember.type === AccountType.Individual + ? [ + + + + + {seeProfileText} + , + ] + : []; + + Actions.push(...MenuItems); + + Actions.push( {signOutText} , - ]; + ); } else { Actions = [