Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactor with authorization, change current account type #1046

Merged
merged 15 commits into from
Sep 20, 2024
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
69 changes: 69 additions & 0 deletions src/Authorization/PreventGuestWrapper.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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 = {
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: (
<div data-testid='content'>
<BuildIcon />
<BuildIcon />
<BuildIcon />
</div>
),
},
} satisfies Meta<typeof PreventGuestWrapper>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Guest = {
args: {
currentAccount: { type: AccountType.Guest } as CompleteGuest,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

// should see message
await expect(canvas.getByRole('alert')).toBeVisible();
},
} satisfies Story;

export const Individual = {
args: {
currentAccount: { id: 'member', name: 'member' } as CompleteMember,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

// should see content
await expect(canvas.getByTestId('content')).toBeVisible();
},
} satisfies Story;

export const ShowError = {
args: {
errorText: 'error text',
},
play: async ({ args, canvasElement }) => {
const canvas = within(canvasElement);

// should see error message
if (args.errorText) {
await expect(canvas.getByText(args.errorText)).toBeVisible();
}
},
} satisfies Story;
73 changes: 73 additions & 0 deletions src/Authorization/PreventGuestWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { ClipboardPen } from 'lucide-react';

import {
Alert,
Box,
Container,
Button as MuiButton,
Stack,
Typography,
} from '@mui/material';

import { ReactNode } from 'react';

import { AccountType, CurrentAccount } from '@graasp/sdk';

type Props = {
buttonText: string;
children?: ReactNode;
currentAccount?: CurrentAccount | null;
/**
* Component to display on error.
* Overrides errorText
*/
error?: JSX.Element;
errorText: string;
id?: string;
onButtonClick?: () => void;
startIcon?: JSX.Element;
text: string | JSX.Element;
};

const PreventGuestWrapper = ({
buttonText,
children,
currentAccount,
error,
id,
onButtonClick,
startIcon = <ClipboardPen />,
errorText,
text,
}: Props): ReactNode => {
if (currentAccount) {
// guest - should not have access to children
if (currentAccount.type === AccountType.Guest) {
return (
<Stack height='100%' justifyContent='center' alignItems='center'>
<Container maxWidth='md'>
<Alert severity='info' id={id}>
<Typography>{text}</Typography>
<Box mt={2} textAlign='center'>
<MuiButton
startIcon={startIcon}
variant='contained'
sx={{ textTransform: 'none' }}
onClick={onButtonClick}
>
{buttonText}
</MuiButton>
</Box>
</Alert>
</Container>
</Stack>
);
}

return children;
}

return error ?? <Alert severity='error'>{errorText}</Alert>;
};

export default PreventGuestWrapper;
19 changes: 9 additions & 10 deletions src/Authorization/Redirect.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof ComponentWithAuthorization> = {
const meta: Meta<typeof SignedInWrapper> = {
title: 'Actions/Authorization/Redirect',
component: ComponentWithAuthorization,
component: SignedInWrapper,
parameters: {
docs: {
source: {
Expand All @@ -24,6 +18,11 @@ const meta: Meta<typeof ComponentWithAuthorization> = {
},
},
},
render: () => (
<SignedInWrapper redirectionLink=''>
<div />
</SignedInWrapper>
),
decorators: [
(story) => {
return <BrowserRouter>{story()}</BrowserRouter>;
Expand All @@ -32,7 +31,7 @@ const meta: Meta<typeof ComponentWithAuthorization> = {
};
export default meta;

type Story = StoryObj<typeof ComponentWithAuthorization>;
type Story = StoryObj<typeof SignedInWrapper>;

export const Redirect = {
play: async ({ canvasElement }) => {
Expand Down
45 changes: 45 additions & 0 deletions src/Authorization/SignedInWrapper.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Meta, StoryObj } from '@storybook/react';
import { expect, within } from '@storybook/test';

import { CompleteMember } from '@graasp/sdk';

import BuildIcon from '@/icons/BuildIcon.js';

import SignedInWrapper from './SignedInWrapper.js';

const redirectionLink = 'https://redirect.org';

const meta: Meta<typeof SignedInWrapper> = {
title: 'Actions/SignedInWrapper',
component: SignedInWrapper,

argTypes: {
onRedirect: { action: 'onRedirect' },
},
args: {
redirectionLink,
children: (
<div data-testid='content'>
<BuildIcon />
<BuildIcon />
<BuildIcon />
</div>
),
},
};

export default meta;

type Story = StoryObj<typeof SignedInWrapper>;

export const Authorized: 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();
},
};
41 changes: 41 additions & 0 deletions src/Authorization/SignedInWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ReactNode } from 'react';

import { CurrentAccount, redirect } from '@graasp/sdk';

import RedirectionContent from './RedirectionContent.js';

export type SignedInWrapperProps = {
redirectionLink: string;
currentAccount?: CurrentAccount | null;
onRedirect?: () => void;
children?: ReactNode;
};

const SignedInWrapper = ({
currentAccount,
redirectionLink,
onRedirect,
children,
}: SignedInWrapperProps): SignedInWrapperProps['children'] => {
const redirectToSignIn = (): void => {
if (!redirectionLink) {
console.debug('No link has been set for redirection');
return;
}
redirect(window, redirectionLink);
};

// check authorization: user shouldn't be empty
if (currentAccount?.id) {
return children;
}

onRedirect?.();

redirectToSignIn();

// redirect page if redirection is not working
return <RedirectionContent link={redirectionLink} />;
};

export default SignedInWrapper;
36 changes: 0 additions & 36 deletions src/Authorization/withAuthorization.stories.tsx

This file was deleted.

39 changes: 0 additions & 39 deletions src/Authorization/withAuthorization.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions src/UserSwitch/UserSwitch.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Story = StoryObj<typeof meta>;
export const SignedIn = {
args: {
currentMember: MOCK_CURRENT_MEMBER,
renderAvatar: () => (
avatar: (
<Avatar
maxWidth={SMALL_AVATAR_SIZE}
maxHeight={SMALL_AVATAR_SIZE}
Expand Down Expand Up @@ -48,7 +48,7 @@ export const SignedIn = {

export const SignedOut = {
args: {
renderAvatar: () => (
avatar: (
<Avatar
maxWidth={SMALL_AVATAR_SIZE}
maxHeight={SMALL_AVATAR_SIZE}
Expand Down
Loading