Remove MockMode mocking apollo queries + Add profile picture image upload during onboarding (#539)

* Remove MockMode mocking apollo queries + Add profile picture image upload

* lower line code coverage until we have tests on hotkyes
This commit is contained in:
Charles Bochet
2023-07-08 15:13:14 -07:00
committed by GitHub
parent 9cd5f7c057
commit b3d0061e0d
16 changed files with 204 additions and 93 deletions

View File

@ -159,8 +159,8 @@
"workerDirectory": "public" "workerDirectory": "public"
}, },
"nyc": { "nyc": {
"lines": 70, "lines": 65,
"statements": 70, "statements": 65,
"exclude": [ "exclude": [
"src/generated/**/*" "src/generated/**/*"
] ]

View File

@ -1,17 +1,10 @@
import { useMemo, useRef } from 'react'; import { useMemo, useRef } from 'react';
import { import { InMemoryCache, NormalizedCacheObject } from '@apollo/client';
ApolloLink,
InMemoryCache,
NormalizedCacheObject,
} from '@apollo/client';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { isMockModeState } from '@/auth/states/isMockModeState';
import { tokenPairState } from '@/auth/states/tokenPairState'; import { tokenPairState } from '@/auth/states/tokenPairState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState'; import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { CommentThreadTarget } from '~/generated/graphql'; import { CommentThreadTarget } from '~/generated/graphql';
import { mockedCompaniesData } from '~/testing/mock-data/companies';
import { mockedUsersData } from '~/testing/mock-data/users';
import { ApolloFactory } from '../services/apollo.factory'; import { ApolloFactory } from '../services/apollo.factory';
@ -20,22 +13,8 @@ export function useApolloFactory() {
const [isDebugMode] = useRecoilState(isDebugModeState); const [isDebugMode] = useRecoilState(isDebugModeState);
const [tokenPair, setTokenPair] = useRecoilState(tokenPairState); const [tokenPair, setTokenPair] = useRecoilState(tokenPairState);
const [isMockMode] = useRecoilState(isMockModeState);
const apolloClient = useMemo(() => { const apolloClient = useMemo(() => {
const mockLink = new ApolloLink((operation, forward) => {
return forward(operation).map((response) => {
if (operation.operationName === 'GetCompanies') {
return { data: { companies: mockedCompaniesData } };
}
if (operation.operationName === 'GetCurrentUser') {
return { data: { currentUser: mockedUsersData[0] } };
}
return response;
});
});
apolloRef.current = new ApolloFactory({ apolloRef.current = new ApolloFactory({
uri: `${process.env.REACT_APP_API_URL}`, uri: `${process.env.REACT_APP_API_URL}`,
cache: new InMemoryCache({ cache: new InMemoryCache({
@ -65,13 +44,13 @@ export function useApolloFactory() {
onUnauthenticatedError() { onUnauthenticatedError() {
setTokenPair(null); setTokenPair(null);
}, },
extraLinks: isMockMode ? [mockLink] : [], extraLinks: [],
isDebugMode, isDebugMode,
tokenPair, tokenPair,
}); });
return apolloRef.current.getClient(); return apolloRef.current.getClient();
}, [isMockMode, setTokenPair, isDebugMode, tokenPair]); }, [setTokenPair, isDebugMode, tokenPair]);
return apolloClient; return apolloClient;
} }

View File

@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { Modal as UIModal } from '@/ui/components/modal/Modal'; import { Modal as UIModal } from '@/ui/components/modal/Modal';
type Props = React.ComponentProps<'div'>; type Props = React.ComponentProps<'div'>;
@ -19,11 +17,6 @@ const StyledContainer = styled.div`
`; `;
export function AuthModal({ children, ...restProps }: Props) { export function AuthModal({ children, ...restProps }: Props) {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.Modal,
customScopes: { 'command-menu': false, goto: false },
});
return ( return (
<UIModal isOpen={true}> <UIModal isOpen={true}>
<StyledContainer {...restProps}>{children}</StyledContainer> <StyledContainer {...restProps}>{children}</StyledContainer>

View File

@ -7,8 +7,11 @@ export enum InternalHotkeysScope {
CellEditMode = 'cell-edit-mode', CellEditMode = 'cell-edit-mode',
RightDrawer = 'right-drawer', RightDrawer = 'right-drawer',
TableHeaderDropdownButton = 'table-header-dropdown-button', TableHeaderDropdownButton = 'table-header-dropdown-button',
CreateProfile = 'create-profile',
RelationPicker = 'relation-picker', RelationPicker = 'relation-picker',
CellDoubleTextInput = 'cell-double-text-input', CellDoubleTextInput = 'cell-double-text-input',
Modal = 'modal', Settings = 'settings',
CreateWokspace = 'create-workspace',
PasswordLogin = 'password-login',
AuthIndex = 'auth-index',
CreateProfile = 'create-profile',
} }

View File

@ -17,7 +17,17 @@ const StyledComboInputContainer = styled.div`
} }
`; `;
export function NameFields() { type OwnProps = {
autoSave?: boolean;
onFirstNameUpdate?: (firstName: string) => void;
onLastNameUpdate?: (lastName: string) => void;
};
export function NameFields({
autoSave = true,
onFirstNameUpdate,
onLastNameUpdate,
}: OwnProps) {
const currentUser = useRecoilValue(currentUserState); const currentUser = useRecoilValue(currentUserState);
const [firstName, setFirstName] = useState(currentUser?.firstName ?? ''); const [firstName, setFirstName] = useState(currentUser?.firstName ?? '');
@ -27,26 +37,34 @@ export function NameFields() {
// TODO: Enhance this with react-hook-form (https://www.react-hook-form.com) // TODO: Enhance this with react-hook-form (https://www.react-hook-form.com)
const debouncedUpdate = debounce(async () => { const debouncedUpdate = debounce(async () => {
if (onFirstNameUpdate) {
onFirstNameUpdate(firstName);
}
if (onLastNameUpdate) {
onLastNameUpdate(lastName);
}
try { try {
const { data, errors } = await updateUser({ if (autoSave) {
variables: { const { data, errors } = await updateUser({
where: { variables: {
id: currentUser?.id, where: {
}, id: currentUser?.id,
data: {
firstName: {
set: firstName,
}, },
lastName: { data: {
set: lastName, firstName: {
set: firstName,
},
lastName: {
set: lastName,
},
}, },
}, },
}, refetchQueries: [getOperationName(GET_CURRENT_USER) ?? ''],
refetchQueries: [getOperationName(GET_CURRENT_USER) ?? ''], });
});
if (errors || !data?.updateUser) { if (errors || !data?.updateUser) {
throw errors; throw errors;
}
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -64,7 +82,7 @@ export function NameFields() {
return () => { return () => {
debouncedUpdate.cancel(); debouncedUpdate.cancel();
}; };
}, [firstName, lastName, currentUser, debouncedUpdate]); }, [firstName, lastName, currentUser, debouncedUpdate, autoSave]);
return ( return (
<StyledComboInputContainer> <StyledComboInputContainer>

View File

@ -1,10 +1,14 @@
import { useRecoilValue } from 'recoil';
import { isMockModeState } from '@/auth/states/isMockModeState';
import { Companies } from '~/pages/companies/Companies'; import { Companies } from '~/pages/companies/Companies';
import { CompaniesMockMode } from '~/pages/companies/CompaniesMockMode';
export function AuthLayout({ children }: React.PropsWithChildren) { export function AuthLayout({ children }: React.PropsWithChildren) {
const isMockMode = useRecoilValue(isMockModeState);
return ( return (
<> <>
{/** Mocked data */} {isMockMode ? <CompaniesMockMode /> : <Companies />}
<Companies />
{children} {children}
</> </>
); );

View File

@ -2,6 +2,7 @@ import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState'; import { currentUserState } from '@/auth/states/currentUserState';
import { mockedUsersData } from '~/testing/mock-data/users';
import NavCollapseButton from './NavCollapseButton'; import NavCollapseButton from './NavCollapseButton';
@ -50,15 +51,16 @@ function NavWorkspaceButton() {
const currentWorkspace = currentUser?.workspaceMember?.workspace; const currentWorkspace = currentUser?.workspaceMember?.workspace;
if (!currentWorkspace) {
return null;
}
return ( return (
<StyledContainer> <StyledContainer>
<LogoAndNameContainer> <LogoAndNameContainer>
<StyledLogo logo={currentWorkspace?.logo}></StyledLogo> <StyledLogo
<StyledName>{currentWorkspace?.displayName}</StyledName> logo={
currentWorkspace?.logo ??
mockedUsersData[0].workspaceMember.workspace.logo
}
></StyledLogo>
<StyledName>{currentWorkspace?.displayName ?? 'Twenty'}</StyledName>
</LogoAndNameContainer> </LogoAndNameContainer>
<NavCollapseButton /> <NavCollapseButton />
</StyledContainer> </StyledContainer>

View File

@ -9,12 +9,14 @@ import { SubTitle } from '@/auth/components/ui/SubTitle';
import { Title } from '@/auth/components/ui/Title'; import { Title } from '@/auth/components/ui/Title';
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
import { currentUserState } from '@/auth/states/currentUserState'; import { currentUserState } from '@/auth/states/currentUserState';
import { isMockModeState } from '@/auth/states/isMockModeState';
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { NameFields } from '@/settings/profile/components/NameFields';
import { PictureUploader } from '@/settings/profile/components/PictureUploader';
import { MainButton } from '@/ui/components/buttons/MainButton'; import { MainButton } from '@/ui/components/buttons/MainButton';
import { ImageInput } from '@/ui/components/inputs/ImageInput';
import { TextInput } from '@/ui/components/inputs/TextInput';
import { SubSectionTitle } from '@/ui/components/section-titles/SubSectionTitle'; import { SubSectionTitle } from '@/ui/components/section-titles/SubSectionTitle';
import { GET_CURRENT_USER } from '@/users/queries'; import { GET_CURRENT_USER } from '@/users/queries';
import { useUpdateUserMutation } from '~/generated/graphql'; import { useUpdateUserMutation } from '~/generated/graphql';
@ -36,16 +38,13 @@ const StyledButtonContainer = styled.div`
width: 200px; width: 200px;
`; `;
const StyledComboInputContainer = styled.div`
display: flex;
flex-direction: row;
> * + * {
margin-left: ${({ theme }) => theme.spacing(4)};
}
`;
export function CreateProfile() { export function CreateProfile() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.CreateProfile,
customScopes: { 'command-menu': false, goto: false },
});
const navigate = useNavigate(); const navigate = useNavigate();
const [, setMockMode] = useRecoilState(isMockModeState);
const onboardingStatus = useOnboardingStatus(); const onboardingStatus = useOnboardingStatus();
const [currentUser] = useRecoilState(currentUserState); const [currentUser] = useRecoilState(currentUserState);
@ -94,15 +93,16 @@ export function CreateProfile() {
() => { () => {
handleCreate(); handleCreate();
}, },
InternalHotkeysScope.Modal, InternalHotkeysScope.CreateProfile,
[handleCreate], [handleCreate],
); );
useEffect(() => { useEffect(() => {
setMockMode(true);
if (onboardingStatus !== OnboardingStatus.OngoingProfileCreation) { if (onboardingStatus !== OnboardingStatus.OngoingProfileCreation) {
navigate('/'); navigate('/');
} }
}, [onboardingStatus, navigate]); }, [onboardingStatus, navigate, setMockMode]);
return ( return (
<> <>
@ -111,29 +111,18 @@ export function CreateProfile() {
<StyledContentContainer> <StyledContentContainer>
<StyledSectionContainer> <StyledSectionContainer>
<SubSectionTitle title="Picture" /> <SubSectionTitle title="Picture" />
<ImageInput picture={null} disabled /> <PictureUploader />
</StyledSectionContainer> </StyledSectionContainer>
<StyledSectionContainer> <StyledSectionContainer>
<SubSectionTitle <SubSectionTitle
title="Name" title="Name"
description="Your name as it will be displayed on the app" description="Your name as it will be displayed on the app"
/> />
<StyledComboInputContainer> <NameFields
<TextInput autoSave={false}
label="First Name" onFirstNameUpdate={setFirstName}
value={firstName} onLastNameUpdate={setLastName}
placeholder="Tim" />
onChange={setFirstName}
fullWidth
/>
<TextInput
label="Last Name"
value={lastName}
placeholder="Cook"
onChange={setLastName}
fullWidth
/>
</StyledComboInputContainer>
</StyledSectionContainer> </StyledSectionContainer>
</StyledContentContainer> </StyledContentContainer>
<StyledButtonContainer> <StyledButtonContainer>

View File

@ -2,11 +2,14 @@ import { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { getOperationName } from '@apollo/client/utilities'; import { getOperationName } from '@apollo/client/utilities';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { SubTitle } from '@/auth/components/ui/SubTitle'; import { SubTitle } from '@/auth/components/ui/SubTitle';
import { Title } from '@/auth/components/ui/Title'; import { Title } from '@/auth/components/ui/Title';
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
import { isMockModeState } from '@/auth/states/isMockModeState';
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { MainButton } from '@/ui/components/buttons/MainButton'; import { MainButton } from '@/ui/components/buttons/MainButton';
@ -34,6 +37,11 @@ const StyledButtonContainer = styled.div`
`; `;
export function CreateWorkspace() { export function CreateWorkspace() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.CreateWokspace,
customScopes: { 'command-menu': false, goto: false },
});
const [, setMockMode] = useRecoilState(isMockModeState);
const navigate = useNavigate(); const navigate = useNavigate();
const onboardingStatus = useOnboardingStatus(); const onboardingStatus = useOnboardingStatus();
@ -74,15 +82,16 @@ export function CreateWorkspace() {
() => { () => {
handleCreate(); handleCreate();
}, },
InternalHotkeysScope.Modal, InternalHotkeysScope.CreateWokspace,
[handleCreate], [handleCreate],
); );
useEffect(() => { useEffect(() => {
setMockMode(true);
if (onboardingStatus !== OnboardingStatus.OngoingWorkspaceCreation) { if (onboardingStatus !== OnboardingStatus.OngoingWorkspaceCreation) {
navigate('/auth/create/profile'); navigate('/auth/create/profile');
} }
}, [onboardingStatus, navigate]); }, [onboardingStatus, navigate, setMockMode]);
return ( return (
<> <>

View File

@ -13,6 +13,7 @@ import { authFlowUserEmailState } from '@/auth/states/authFlowUserEmailState';
import { isMockModeState } from '@/auth/states/isMockModeState'; import { isMockModeState } from '@/auth/states/isMockModeState';
import { authProvidersState } from '@/client-config/states/authProvidersState'; import { authProvidersState } from '@/client-config/states/authProvidersState';
import { isDemoModeState } from '@/client-config/states/isDemoModeState'; import { isDemoModeState } from '@/client-config/states/isDemoModeState';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { MainButton } from '@/ui/components/buttons/MainButton'; import { MainButton } from '@/ui/components/buttons/MainButton';
@ -32,6 +33,10 @@ const StyledFooterNote = styled(FooterNote)`
`; `;
export function Index() { export function Index() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.AuthIndex,
customScopes: { 'command-menu': false, goto: false },
});
const navigate = useNavigate(); const navigate = useNavigate();
const theme = useTheme(); const theme = useTheme();
const [, setMockMode] = useRecoilState(isMockModeState); const [, setMockMode] = useRecoilState(isMockModeState);
@ -62,7 +67,7 @@ export function Index() {
() => { () => {
onPasswordLoginClick(); onPasswordLoginClick();
}, },
InternalHotkeysScope.Modal, InternalHotkeysScope.AuthIndex,
[onPasswordLoginClick], [onPasswordLoginClick],
); );

View File

@ -11,6 +11,7 @@ import { useAuth } from '@/auth/hooks/useAuth';
import { authFlowUserEmailState } from '@/auth/states/authFlowUserEmailState'; import { authFlowUserEmailState } from '@/auth/states/authFlowUserEmailState';
import { isMockModeState } from '@/auth/states/isMockModeState'; import { isMockModeState } from '@/auth/states/isMockModeState';
import { isDemoModeState } from '@/client-config/states/isDemoModeState'; import { isDemoModeState } from '@/client-config/states/isDemoModeState';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { MainButton } from '@/ui/components/buttons/MainButton'; import { MainButton } from '@/ui/components/buttons/MainButton';
@ -50,6 +51,10 @@ const StyledErrorContainer = styled.div`
`; `;
export function PasswordLogin() { export function PasswordLogin() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.PasswordLogin,
customScopes: { 'command-menu': false, goto: false },
});
const navigate = useNavigate(); const navigate = useNavigate();
const [isDemoMode] = useRecoilState(isDemoModeState); const [isDemoMode] = useRecoilState(isDemoModeState);
@ -81,7 +86,7 @@ export function PasswordLogin() {
() => { () => {
handleLogin(); handleLogin();
}, },
InternalHotkeysScope.Modal, InternalHotkeysScope.PasswordLogin,
[handleLogin], [handleLogin],
); );

View File

@ -0,0 +1,40 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { EntityTableActionBar } from '@/ui/components/table/action-bar/EntityTableActionBar';
import { IconBuildingSkyscraper } from '@/ui/icons/index';
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
import { TableContext } from '@/ui/tables/states/TableContext';
import { TableActionBarButtonCreateCommentThreadCompany } from './table/TableActionBarButtonCreateCommentThreadCompany';
import { TableActionBarButtonDeleteCompanies } from './table/TableActionBarButtonDeleteCompanies';
import { CompanyTableMockMode } from './CompanyTableMockMode';
const StyledTableContainer = styled.div`
display: flex;
width: 100%;
`;
export function CompaniesMockMode() {
const theme = useTheme();
return (
<>
<WithTopBarContainer
title="Companies"
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
>
<RecoilScope SpecificContext={TableContext}>
<StyledTableContainer>
<CompanyTableMockMode />
</StyledTableContainer>
<EntityTableActionBar>
<TableActionBarButtonCreateCommentThreadCompany />
<TableActionBarButtonDeleteCompanies />
</EntityTableActionBar>
</RecoilScope>
</WithTopBarContainer>
</>
);
}

View File

@ -0,0 +1,32 @@
import { IconList } from '@tabler/icons-react';
import { EntityTable } from '@/ui/components/table/EntityTable';
import { HooksEntityTable } from '@/ui/components/table/HooksEntityTable';
import { mockedCompaniesData } from '~/testing/mock-data/companies';
import { useCompaniesColumns } from './companies-columns';
import { companiesFilters } from './companies-filters';
import { availableSorts } from './companies-sorts';
export function CompanyTableMockMode() {
const companiesColumns = useCompaniesColumns();
const companies = mockedCompaniesData;
return (
<>
<HooksEntityTable
numberOfColumns={companiesColumns.length}
numberOfRows={companies.length}
availableTableFilters={companiesFilters}
/>
<EntityTable
data={companies}
columns={companiesColumns}
viewName="All Companies"
viewIcon={<IconList size={16} />}
availableSorts={availableSorts}
/>
</>
);
}

View File

@ -1,5 +1,7 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { EmailField } from '@/settings/profile/components/EmailField'; import { EmailField } from '@/settings/profile/components/EmailField';
import { NameFields } from '@/settings/profile/components/NameFields'; import { NameFields } from '@/settings/profile/components/NameFields';
import { PictureUploader } from '@/settings/profile/components/PictureUploader'; import { PictureUploader } from '@/settings/profile/components/PictureUploader';
@ -24,6 +26,11 @@ const StyledSectionContainer = styled.div`
`; `;
export function SettingsProfile() { export function SettingsProfile() {
useHotkeysScopeOnMountOnly({
scope: InternalHotkeysScope.Settings,
customScopes: { 'command-menu': true, goto: false },
});
return ( return (
<NoTopBarContainer> <NoTopBarContainer>
<StyledContainer> <StyledContainer>

View File

@ -0,0 +1,25 @@
import type { Meta, StoryObj } from '@storybook/react';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
import { SettingsWorkspaceMembers } from '../SettingsWorkspaceMembers';
const meta: Meta<typeof SettingsWorkspaceMembers> = {
title: 'Pages/Settings/SettingsWorkspaceMembers',
component: SettingsWorkspaceMembers,
};
export default meta;
export type Story = StoryObj<typeof SettingsWorkspaceMembers>;
export const Default: Story = {
render: getRenderWrapperForPage(
<SettingsWorkspaceMembers />,
'/settings/workspace-members',
),
parameters: {
msw: graphqlMocks,
},
};