Fix margin on DeleteModal overlay (#998)

* Fix margin on DeleteModal overlay

* Update chromatic ci triggers

* Update chromatic ci triggers
This commit is contained in:
Charles Bochet
2023-07-30 13:17:33 -07:00
committed by GitHub
parent be835af48b
commit eafa30a9cf
24 changed files with 388 additions and 335 deletions

View File

@ -5,9 +5,10 @@ on:
branches: branches:
- main - main
pull_request_target: pull_request_target:
types: [labeled, opened, edited]
jobs: jobs:
chromatic-deployment: chromatic-deployment:
if: ${{ github.event.label.name == 'run-chromatic' || github.event_name == 'push' }} if: ${{ contains(github.event.*.labels.*.name, 'run-chromatic') }} || github.event_name == 'push' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
REACT_APP_API_URL: http://127.0.0.1:3000/graphql REACT_APP_API_URL: http://127.0.0.1:3000/graphql

View File

@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { useAuth } from '@/auth/hooks/useAuth'; import { useAuth } from '@/auth/hooks/useAuth';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { ButtonVariant } from '@/ui/button/components/Button'; import { ButtonVariant } from '@/ui/button/components/Button';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle'; import { H2Title } from '@/ui/title/components/H2Title';
import { useDeleteUserAccountMutation } from '~/generated/graphql'; import { useDeleteUserAccountMutation } from '~/generated/graphql';
import { DeleteModal, StyledDeleteButton } from './DeleteModal'; import { DeleteModal, StyledDeleteButton } from './DeleteModal';
@ -29,7 +29,7 @@ export function DeleteAccount() {
return ( return (
<> <>
<SubSectionTitle <H2Title
title="Danger zone" title="Danger zone"
description="Delete account and all the associated data" description="Delete account and all the associated data"
/> />

View File

@ -7,6 +7,8 @@ import { currentUserState } from '@/auth/states/currentUserState';
import { Button, ButtonVariant } from '@/ui/button/components/Button'; import { Button, ButtonVariant } from '@/ui/button/components/Button';
import { TextInput } from '@/ui/input/components/TextInput'; import { TextInput } from '@/ui/input/components/TextInput';
import { Modal } from '@/ui/modal/components/Modal'; import { Modal } from '@/ui/modal/components/Modal';
import { Section, SectionAlignment } from '@/ui/section/components/Section';
import { H1Title, H1TitleFontColor } from '@/ui/title/components/H1Title';
import { debounce } from '~/utils/debounce'; import { debounce } from '~/utils/debounce';
interface DeleteModalProps { interface DeleteModalProps {
@ -18,22 +20,6 @@ interface DeleteModalProps {
deleteButtonText?: string; deleteButtonText?: string;
} }
const StyledTitle = styled.div`
font-size: ${({ theme }) => theme.font.size.lg};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
`;
const StyledSubtitle = styled.div`
text-align: center;
`;
const StyledModal = styled(Modal)`
color: ${({ theme }) => theme.font.color.primary};
> * + * {
margin-top: ${({ theme }) => theme.spacing(8)};
}
`;
const StyledCenteredButton = styled(Button)` const StyledCenteredButton = styled(Button)`
justify-content: center; justify-content: center;
`; `;
@ -77,7 +63,7 @@ export function DeleteModal({
return ( return (
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
<LayoutGroup> <LayoutGroup>
<StyledModal <Modal
isOpen={isOpen} isOpen={isOpen}
onOutsideClick={() => { onOutsideClick={() => {
if (isOpen) { if (isOpen) {
@ -85,15 +71,17 @@ export function DeleteModal({
} }
}} }}
> >
<StyledTitle>{title}</StyledTitle> <H1Title title={title} fontColor={H1TitleFontColor.Primary} />
<StyledSubtitle>{subtitle}</StyledSubtitle> <Section alignment={SectionAlignment.Center}>{subtitle}</Section>
<TextInput <Section>
value={email} <TextInput
onChange={handleEmailChange} value={email}
placeholder={userEmail} onChange={handleEmailChange}
fullWidth placeholder={userEmail}
key={'email-' + userEmail} fullWidth
/> key={'email-' + userEmail}
/>
</Section>
<StyledDeleteButton <StyledDeleteButton
onClick={handleConfirmDelete} onClick={handleConfirmDelete}
variant={ButtonVariant.Secondary} variant={ButtonVariant.Secondary}
@ -110,7 +98,7 @@ export function DeleteModal({
marginTop: 10, marginTop: 10,
}} }}
/> />
</StyledModal> </Modal>
</LayoutGroup> </LayoutGroup>
</AnimatePresence> </AnimatePresence>
); );

View File

@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { useAuth } from '@/auth/hooks/useAuth'; import { useAuth } from '@/auth/hooks/useAuth';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { ButtonVariant } from '@/ui/button/components/Button'; import { ButtonVariant } from '@/ui/button/components/Button';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle'; import { H2Title } from '@/ui/title/components/H2Title';
import { useDeleteCurrentWorkspaceMutation } from '~/generated/graphql'; import { useDeleteCurrentWorkspaceMutation } from '~/generated/graphql';
import { DeleteModal, StyledDeleteButton } from './DeleteModal'; import { DeleteModal, StyledDeleteButton } from './DeleteModal';
@ -29,10 +29,7 @@ export function DeleteWorkspace() {
return ( return (
<> <>
<SubSectionTitle <H2Title title="Danger zone" description="Delete your whole workspace" />
title="Danger zone"
description="Delete your whole workspace"
/>
<StyledDeleteButton <StyledDeleteButton
onClick={() => setIsDeleteWorkSpaceModalOpen(true)} onClick={() => setIsDeleteWorkSpaceModalOpen(true)}
variant={ButtonVariant.Secondary} variant={ButtonVariant.Secondary}

View File

@ -50,13 +50,15 @@ export const Sizes: Story = {
size: { control: false }, size: { control: false },
}, },
parameters: { parameters: {
catalog: [ catalog: {
{ dimensions: [
name: 'sizes', {
values: Object.values(ButtonSize), name: 'sizes',
props: (size: ButtonSize) => ({ size }), values: Object.values(ButtonSize),
}, props: (size: ButtonSize) => ({ size }),
], },
],
},
}, },
decorators: [CatalogDecorator], decorators: [CatalogDecorator],
}; };
@ -68,22 +70,24 @@ export const Variants: Story = {
}, },
parameters: { parameters: {
pseudo: { hover: ['.hover'], active: ['.active'], focus: ['.focus'] }, pseudo: { hover: ['.hover'], active: ['.active'], focus: ['.focus'] },
catalog: [ catalog: {
{ dimensions: [
name: 'state', {
values: ['default', 'disabled', 'hover', 'active', 'focus'], name: 'state',
props: (state: string) => { values: ['default', 'disabled', 'hover', 'active', 'focus'],
if (state === 'disabled') return { disabled: true }; props: (state: string) => {
if (state === 'default') return {}; if (state === 'disabled') return { disabled: true };
return { className: state }; if (state === 'default') return {};
return { className: state };
},
}, },
}, {
{ name: 'variants',
name: 'variants', values: Object.values(ButtonVariant),
values: Object.values(ButtonVariant), props: (variant: ButtonVariant) => ({ variant }),
props: (variant: ButtonVariant) => ({ variant }), },
}, ],
], },
}, },
decorators: [CatalogDecorator], decorators: [CatalogDecorator],
}; };
@ -93,30 +97,34 @@ export const Positions: Story = {
position: { control: false }, position: { control: false },
}, },
parameters: { parameters: {
catalog: [ catalog: {
{ dimensions: [
name: 'positions', {
values: ['none', ...Object.values(ButtonPosition)], name: 'positions',
props: (position: ButtonPosition | 'none') => values: ['none', ...Object.values(ButtonPosition)],
position === 'none' ? {} : { position }, props: (position: ButtonPosition | 'none') =>
}, position === 'none' ? {} : { position },
], },
],
},
}, },
decorators: [CatalogDecorator], decorators: [CatalogDecorator],
}; };
export const WithAdornments: Story = { export const WithAdornments: Story = {
parameters: { parameters: {
catalog: [ catalog: {
{ dimensions: [
name: 'adornments', {
values: ['with icon', 'with soon pill'], name: 'adornments',
props: (value: string) => values: ['with icon', 'with soon pill'],
value === 'with icon' props: (value: string) =>
? { icon: <IconSearch size={14} /> } value === 'with icon'
: { soon: true }, ? { icon: <IconSearch size={14} /> }
}, : { soon: true },
], },
],
},
}, },
decorators: [CatalogDecorator], decorators: [CatalogDecorator],
}; };

View File

@ -38,29 +38,31 @@ export const Catalog: Story = {
}, },
parameters: { parameters: {
pseudo: { hover: ['.hover'], active: ['.active'] }, pseudo: { hover: ['.hover'], active: ['.active'] },
catalog: [ catalog: {
{ dimensions: [
name: 'states', {
values: ['default', 'hover', 'active', 'disabled'], name: 'states',
props: (state: string) => values: ['default', 'hover', 'active', 'disabled'],
state === 'default' ? {} : { className: state }, props: (state: string) =>
}, state === 'default' ? {} : { className: state },
{ },
name: 'variants', {
values: Object.values(ChipVariant), name: 'variants',
props: (variant: ChipVariant) => ({ variant }), values: Object.values(ChipVariant),
}, props: (variant: ChipVariant) => ({ variant }),
{ },
name: 'sizes', {
values: Object.values(ChipSize), name: 'sizes',
props: (size: ChipSize) => ({ size }), values: Object.values(ChipSize),
}, props: (size: ChipSize) => ({ size }),
{ },
name: 'accents', {
values: Object.values(ChipAccent), name: 'accents',
props: (accent: ChipAccent) => ({ accent }), values: Object.values(ChipAccent),
}, props: (accent: ChipAccent) => ({ accent }),
], },
],
},
}, },
decorators: [CatalogDecorator], decorators: [CatalogDecorator],
}; };

View File

@ -39,36 +39,38 @@ export const Catalog: Story = {
shape: { control: false }, shape: { control: false },
}, },
parameters: { parameters: {
catalog: [ catalog: {
{ dimensions: [
name: 'state', {
values: ['unchecked', 'checked', 'indeterminate'], name: 'state',
props: (state: string) => { values: ['unchecked', 'checked', 'indeterminate'],
if (state === 'checked') { props: (state: string) => {
return { checked: true }; if (state === 'checked') {
} return { checked: true };
}
if (state === 'indeterminate') { if (state === 'indeterminate') {
return { indeterminate: true }; return { indeterminate: true };
} }
},
}, },
}, {
{ name: 'shape',
name: 'shape', values: Object.values(CheckboxShape),
values: Object.values(CheckboxShape), props: (shape: CheckboxShape) => ({ shape }),
props: (shape: CheckboxShape) => ({ shape }), },
}, {
{ name: 'variant',
name: 'variant', values: Object.values(CheckboxVariant),
values: Object.values(CheckboxVariant), props: (variant: CheckboxVariant) => ({ variant }),
props: (variant: CheckboxVariant) => ({ variant }), },
}, {
{ name: 'size',
name: 'size', values: Object.values(CheckboxSize),
values: Object.values(CheckboxSize), props: (size: CheckboxSize) => ({ size }),
props: (size: CheckboxSize) => ({ size }), },
}, ],
], },
}, },
decorators: [CatalogDecorator], decorators: [CatalogDecorator],
}; };

View File

@ -35,26 +35,28 @@ export const Catalog = {
autoStart: defaultArgTypes, autoStart: defaultArgTypes,
}, },
parameters: { parameters: {
catalog: [ catalog: {
{ dimensions: [
name: 'animation', {
values: [true, false], name: 'animation',
props: (autoStart: string) => ({ autoStart }), values: [true, false],
labels: (autoStart: string) => `AutoStart: ${autoStart}`, props: (autoStart: string) => ({ autoStart }),
}, labels: (autoStart: string) => `AutoStart: ${autoStart}`,
{ },
name: 'colors', {
values: [undefined, 'blue'], name: 'colors',
props: (barColor: string) => ({ barColor }), values: [undefined, 'blue'],
labels: (color: string) => `Color: ${color ?? 'default'}`, props: (barColor: string) => ({ barColor }),
}, labels: (color: string) => `Color: ${color ?? 'default'}`,
{ },
name: 'sizes', {
values: [undefined, 10], name: 'sizes',
props: (barHeight: number) => ({ barHeight }), values: [undefined, 10],
labels: (size: number) => `Size: ${size ? size + ' px' : 'default'}`, props: (barHeight: number) => ({ barHeight }),
}, labels: (size: number) => `Size: ${size ? size + ' px' : 'default'}`,
], },
],
},
}, },
decorators: [CatalogDecorator], decorators: [CatalogDecorator],
}; };

View File

@ -0,0 +1,35 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
type OwnProps = {
children: ReactNode;
alignment?: SectionAlignment;
fullWidth?: boolean;
};
export enum SectionAlignment {
Left = 'left',
Center = 'center',
}
const StyledSection = styled.div<{
alignment: SectionAlignment;
fullWidth: boolean;
}>`
margin-bottom: ${({ theme }) => theme.spacing(4)};
margin-top: ${({ theme }) => theme.spacing(4)};
text-align: ${({ alignment }) => alignment};
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
`;
export function Section({
children,
alignment = SectionAlignment.Left,
fullWidth = true,
}: OwnProps) {
return (
<StyledSection alignment={alignment} fullWidth={fullWidth}>
{children}
</StyledSection>
);
}

View File

@ -29,19 +29,21 @@ export const Catalog: Story = {
}, },
parameters: { parameters: {
pseudo: { hover: ['.hover'], active: ['.active'] }, pseudo: { hover: ['.hover'], active: ['.active'] },
catalog: [ catalog: {
{ dimensions: [
name: 'states', {
values: ['default', 'hover', 'active'], name: 'states',
props: (state: string) => values: ['default', 'hover', 'active'],
state === 'default' ? {} : { className: state }, props: (state: string) =>
}, state === 'default' ? {} : { className: state },
{ },
name: 'Active', {
values: ['true', 'false'], name: 'Active',
props: (active: string) => ({ active: active === 'true' }), values: ['true', 'false'],
}, props: (active: string) => ({ active: active === 'true' }),
], },
],
},
}, },
decorators: [CatalogDecorator], decorators: [CatalogDecorator],
}; };

View File

@ -0,0 +1,30 @@
import styled from '@emotion/styled';
type OwnProps = {
title: string;
fontColor?: H1TitleFontColor;
};
export enum H1TitleFontColor {
Primary = 'primary',
Secondary = 'secondary',
Tertiary = 'tertiary',
}
const StyledTitle = styled.h2<{
fontColor: H1TitleFontColor;
}>`
color: ${({ theme, fontColor }) => theme.font.color[fontColor]};
font-size: ${({ theme }) => theme.font.size.lg};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
line-height: ${({ theme }) => theme.text.lineHeight.md};
margin: 0;
margin-bottom: ${({ theme }) => theme.spacing(4)};
`;
export function H1Title({
title,
fontColor = H1TitleFontColor.Tertiary,
}: OwnProps) {
return <StyledTitle fontColor={fontColor}>{title}</StyledTitle>;
}

View File

@ -23,10 +23,10 @@ const StyledDescription = styled.h3`
font-size: ${({ theme }) => theme.font.size.md}; font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.regular}; font-weight: ${({ theme }) => theme.font.weight.regular};
margin: 0; margin: 0;
margin-top: ${({ theme }) => theme.spacing(1)}; margin-top: ${({ theme }) => theme.spacing(3)};
`; `;
export function SubSectionTitle({ title, description }: Props) { export function H2Title({ title, description }: Props) {
return ( return (
<StyledContainer> <StyledContainer>
<StyledTitle>{title}</StyledTitle> <StyledTitle>{title}</StyledTitle>

View File

@ -1,18 +0,0 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
type OwnProps = {
children: ReactNode;
};
const StyledMainSectionTitle = styled.h2`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.xl};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
line-height: ${({ theme }) => theme.text.lineHeight.lg};
margin: 0;
`;
export function MainSectionTitle({ children }: OwnProps) {
return <StyledMainSectionTitle>{children}</StyledMainSectionTitle>;
}

View File

@ -0,0 +1,42 @@
import type { Meta, StoryObj } from '@storybook/react';
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { H1Title, H1TitleFontColor } from '../H1Title';
const meta: Meta<typeof H1Title> = {
title: 'UI/Title/H1Title',
component: H1Title,
decorators: [ComponentDecorator],
};
export default meta;
type Story = StoryObj<typeof H1Title>;
const args = {
title: 'Title',
fontColor: H1TitleFontColor.Primary,
};
export const Default: Story = {
args,
decorators: [ComponentDecorator],
};
export const Catalog: Story = {
args,
decorators: [CatalogDecorator],
parameters: {
catalog: {
dimensions: [
{
name: 'FontColor',
values: Object.values(H1TitleFontColor),
props: (fontColor: H1TitleFontColor) => ({ fontColor }),
},
],
},
},
};

View File

@ -2,16 +2,16 @@ import type { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { SubSectionTitle } from '../SubSectionTitle'; import { H2Title } from '../H2Title';
const args = { const args = {
title: 'Lorem ipsum', title: 'Sub title',
description: 'Lorem ipsum dolor sit amet', description: 'Lorem ipsum dolor sit amet',
}; };
const meta: Meta<typeof SubSectionTitle> = { const meta: Meta<typeof H2Title> = {
title: 'UI/Title/SubSectionTitle', title: 'UI/Title/H2Title',
component: SubSectionTitle, component: H2Title,
decorators: [ComponentDecorator], decorators: [ComponentDecorator],
args: { args: {
title: args.title, title: args.title,
@ -20,7 +20,7 @@ const meta: Meta<typeof SubSectionTitle> = {
export default meta; export default meta;
type Story = StoryObj<typeof SubSectionTitle>; type Story = StoryObj<typeof H2Title>;
export const Default: Story = { export const Default: Story = {
decorators: [ComponentDecorator], decorators: [ComponentDecorator],

View File

@ -1,24 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { MainSectionTitle } from '../MainSectionTitle';
const meta: Meta<typeof MainSectionTitle> = {
title: 'UI/Title/MainSectionTitle',
component: MainSectionTitle,
decorators: [ComponentDecorator],
};
export default meta;
type Story = StoryObj<typeof MainSectionTitle>;
const args = {
children: 'Lorem ipsum',
};
export const Default: Story = {
args,
decorators: [ComponentDecorator],
};

View File

@ -42,16 +42,18 @@ export const Catalog: Story = {
}); });
}, },
parameters: { parameters: {
catalog: [ catalog: {
{ dimensions: [
name: 'anchorSelect', {
values: Object.values(TooltipPosition), name: 'anchorSelect',
props: (anchorSelect: TooltipPosition) => ({ values: Object.values(TooltipPosition),
anchorSelect: `#${anchorSelect}`, props: (anchorSelect: TooltipPosition) => ({
place: anchorSelect, anchorSelect: `#${anchorSelect}`,
}), place: anchorSelect,
}, }),
], },
],
},
}, },
decorators: [CatalogDecorator], decorators: [CatalogDecorator],
}; };

View File

@ -17,7 +17,7 @@ import { MainButton } from '@/ui/button/components/MainButton';
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
import { TextInput } from '@/ui/input/components/TextInput'; import { TextInput } from '@/ui/input/components/TextInput';
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar'; import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle'; import { H2Title } from '@/ui/title/components/H2Title';
import { GET_CURRENT_USER } from '@/users/queries'; import { GET_CURRENT_USER } from '@/users/queries';
import { useUpdateUserMutation } from '~/generated/graphql'; import { useUpdateUserMutation } from '~/generated/graphql';
@ -128,11 +128,11 @@ export function CreateProfile() {
<SubTitle>How you'll be identified on the app.</SubTitle> <SubTitle>How you'll be identified on the app.</SubTitle>
<StyledContentContainer> <StyledContentContainer>
<StyledSectionContainer> <StyledSectionContainer>
<SubSectionTitle title="Picture" /> <H2Title title="Picture" />
<ProfilePictureUploader /> <ProfilePictureUploader />
</StyledSectionContainer> </StyledSectionContainer>
<StyledSectionContainer> <StyledSectionContainer>
<SubSectionTitle <H2Title
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"
/> />

View File

@ -14,7 +14,7 @@ import { MainButton } from '@/ui/button/components/MainButton';
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
import { TextInput } from '@/ui/input/components/TextInput'; import { TextInput } from '@/ui/input/components/TextInput';
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar'; import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle'; import { H2Title } from '@/ui/title/components/H2Title';
import { GET_CURRENT_USER } from '@/users/queries'; import { GET_CURRENT_USER } from '@/users/queries';
import { useUpdateWorkspaceMutation } from '~/generated/graphql'; import { useUpdateWorkspaceMutation } from '~/generated/graphql';
@ -107,11 +107,11 @@ export function CreateWorkspace() {
</SubTitle> </SubTitle>
<StyledContentContainer> <StyledContentContainer>
<StyledSectionContainer> <StyledSectionContainer>
<SubSectionTitle title="Workspace logo" /> <H2Title title="Workspace logo" />
<WorkspaceLogoUploader /> <WorkspaceLogoUploader />
</StyledSectionContainer> </StyledSectionContainer>
<StyledSectionContainer> <StyledSectionContainer>
<SubSectionTitle <H2Title
title="Workspace name" title="Workspace name"
description="The name of your organization" description="The name of your organization"
/> />

View File

@ -3,9 +3,10 @@ import styled from '@emotion/styled';
import { ColorSchemePicker } from '@/ui/color-scheme/components/ColorSchemePicker'; import { ColorSchemePicker } from '@/ui/color-scheme/components/ColorSchemePicker';
import { IconSettings } from '@/ui/icon'; import { IconSettings } from '@/ui/icon';
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer'; import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
import { Section } from '@/ui/section/components/Section';
import { useColorScheme } from '@/ui/themes/hooks/useColorScheme'; import { useColorScheme } from '@/ui/themes/hooks/useColorScheme';
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle'; import { H1Title } from '@/ui/title/components/H1Title';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle'; import { H2Title } from '@/ui/title/components/H2Title';
const StyledContainer = styled.div` const StyledContainer = styled.div`
display: flex; display: flex;
@ -13,15 +14,6 @@ const StyledContainer = styled.div`
padding: ${({ theme }) => theme.spacing(8)}; padding: ${({ theme }) => theme.spacing(8)};
padding-bottom: ${({ theme }) => theme.spacing(10)}; padding-bottom: ${({ theme }) => theme.spacing(10)};
width: 350px; width: 350px;
> * + * {
margin-top: ${({ theme }) => theme.spacing(8)};
}
`;
const StyledSectionContainer = styled.div`
> * + * {
margin-top: ${({ theme }) => theme.spacing(4)};
}
`; `;
export function SettingsExperience() { export function SettingsExperience() {
@ -29,15 +21,13 @@ export function SettingsExperience() {
return ( return (
<SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings"> <SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings">
<div> <StyledContainer>
<StyledContainer> <H1Title title="Experience" />
<MainSectionTitle>Experience</MainSectionTitle> <Section>
<StyledSectionContainer> <H2Title title="Appearance" />
<SubSectionTitle title="Appearance" /> <ColorSchemePicker value={colorScheme} onChange={setColorScheme} />
<ColorSchemePicker value={colorScheme} onChange={setColorScheme} /> </Section>
</StyledSectionContainer> </StyledContainer>
</StyledContainer>
</div>
</SubMenuTopBarContainer> </SubMenuTopBarContainer>
); );
} }

View File

@ -6,24 +6,18 @@ import { NameFields } from '@/settings/profile/components/NameFields';
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader'; import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
import { IconSettings } from '@/ui/icon'; import { IconSettings } from '@/ui/icon';
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer'; import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle'; import { Section } from '@/ui/section/components/Section';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle'; import { H1Title } from '@/ui/title/components/H1Title';
import { H2Title } from '@/ui/title/components/H2Title';
const StyledContainer = styled.div` const StyledContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: fit-content;
padding: ${({ theme }) => theme.spacing(8)}; padding: ${({ theme }) => theme.spacing(8)};
padding-bottom: ${({ theme }) => theme.spacing(10)}; padding-bottom: ${({ theme }) => theme.spacing(10)};
padding-bottom: 30px;
width: 350px; width: 350px;
> * + * {
margin-top: ${({ theme }) => theme.spacing(8)};
}
`;
const StyledSectionContainer = styled.div`
> * + * {
margin-top: ${({ theme }) => theme.spacing(4)};
}
`; `;
export function SettingsProfile() { export function SettingsProfile() {
@ -31,29 +25,28 @@ export function SettingsProfile() {
<SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings"> <SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings">
<> <>
<StyledContainer> <StyledContainer>
<MainSectionTitle>Profile</MainSectionTitle> <H1Title title="Profile" />
<StyledSectionContainer> <Section>
<SubSectionTitle title="Picture" /> <H2Title title="Picture" />
<ProfilePictureUploader /> <ProfilePictureUploader />
</StyledSectionContainer> </Section>
<StyledSectionContainer> <Section>
<SubSectionTitle <H2Title
title="Name" title="Name"
description="Your name as it will be displayed" description="Your name as it will be displayed"
/> />
<NameFields /> <NameFields />
</StyledSectionContainer> </Section>
<StyledSectionContainer> <Section>
<SubSectionTitle <H2Title
title="Email" title="Email"
description="The email associated to your account" description="The email associated to your account"
/> />
<EmailField /> <EmailField />
</StyledSectionContainer> </Section>
<Section>
<StyledSectionContainer>
<DeleteAccount /> <DeleteAccount />
</StyledSectionContainer> </Section>
</StyledContainer> </StyledContainer>
</> </>
</SubMenuTopBarContainer> </SubMenuTopBarContainer>

View File

@ -5,23 +5,15 @@ import { NameField } from '@/settings/workspace/components/NameField';
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader'; import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
import { IconSettings } from '@/ui/icon'; import { IconSettings } from '@/ui/icon';
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer'; import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle'; import { Section } from '@/ui/section/components/Section';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle'; import { H1Title } from '@/ui/title/components/H1Title';
import { H2Title } from '@/ui/title/components/H2Title';
const StyledContainer = styled.div` const StyledContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: ${({ theme }) => theme.spacing(8)}; padding: ${({ theme }) => theme.spacing(8)};
width: 350px; width: 350px;
> * + * {
margin-top: ${({ theme }) => theme.spacing(8)};
}
`;
const StyledSectionContainer = styled.div`
> * + * {
margin-top: ${({ theme }) => theme.spacing(4)};
}
`; `;
export function SettingsWorksapce() { export function SettingsWorksapce() {
@ -29,22 +21,19 @@ export function SettingsWorksapce() {
<SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings"> <SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings">
<div> <div>
<StyledContainer> <StyledContainer>
<MainSectionTitle>General</MainSectionTitle> <H1Title title="General" />
<StyledSectionContainer> <Section>
<SubSectionTitle title="Picture" /> <H2Title title="Picture" />
<WorkspaceLogoUploader /> <WorkspaceLogoUploader />
</StyledSectionContainer> </Section>
<StyledSectionContainer> <Section>
<SubSectionTitle <H2Title title="Name" description="Name of your workspace" />
title="Name"
description="Name of your workspace"
/>
<NameField /> <NameField />
</StyledSectionContainer> </Section>
<StyledSectionContainer> <Section>
<DeleteWorkspace /> <DeleteWorkspace />
</StyledSectionContainer> </Section>
</StyledContainer> </StyledContainer>
</div> </div>
</SubMenuTopBarContainer> </SubMenuTopBarContainer>

View File

@ -10,8 +10,9 @@ import {
} from '@/ui/button/components/Button'; } from '@/ui/button/components/Button';
import { IconSettings, IconTrash } from '@/ui/icon'; import { IconSettings, IconTrash } from '@/ui/icon';
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer'; import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle'; import { Section } from '@/ui/section/components/Section';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle'; import { H1Title } from '@/ui/title/components/H1Title';
import { H2Title } from '@/ui/title/components/H2Title';
import { WorkspaceInviteLink } from '@/workspace/components/WorkspaceInviteLink'; import { WorkspaceInviteLink } from '@/workspace/components/WorkspaceInviteLink';
import { WorkspaceMemberCard } from '@/workspace/components/WorkspaceMemberCard'; import { WorkspaceMemberCard } from '@/workspace/components/WorkspaceMemberCard';
import { import {
@ -22,11 +23,9 @@ import {
const StyledContainer = styled.div` const StyledContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-top: ${({ theme }) => theme.spacing(8)};
padding: ${({ theme }) => theme.spacing(8)}; padding: ${({ theme }) => theme.spacing(8)};
width: 350px; width: 350px;
> * + * {
margin-top: ${({ theme }) => theme.spacing(8)};
}
`; `;
const ButtonContainer = styled.div` const ButtonContainer = styled.div`
@ -81,40 +80,44 @@ export function SettingsWorkspaceMembers() {
return ( return (
<SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings"> <SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings">
<StyledContainer> <StyledContainer>
<MainSectionTitle>Members</MainSectionTitle> <H1Title title="Members" />
{workspace?.inviteHash && ( {workspace?.inviteHash && (
<> <Section>
<SubSectionTitle <H2Title
title="Invite" title="Invite"
description="Send an invitation to use Twenty" description="Send an invitation to use Twenty"
/> />
<WorkspaceInviteLink <WorkspaceInviteLink
inviteLink={`${window.location.origin}/invite/${workspace?.inviteHash}`} inviteLink={`${window.location.origin}/invite/${workspace?.inviteHash}`}
/> />
</> </Section>
)} )}
<SubSectionTitle <Section>
title="Members" <H2Title
description="Manage the members of your space here" title="Members"
/> description="Manage the members of your space here"
{data?.workspaceMembers?.map((member) => (
<WorkspaceMemberCard
key={member.user.id}
workspaceMember={{ user: member.user }}
accessory={
currentUser?.id !== member.user.id && (
<ButtonContainer>
<Button
onClick={() => handleRemoveWorkspaceMember(member.user.id)}
variant={ButtonVariant.Tertiary}
size={ButtonSize.Small}
icon={<IconTrash size={theme.icon.size.md} />}
/>
</ButtonContainer>
)
}
/> />
))} {data?.workspaceMembers?.map((member) => (
<WorkspaceMemberCard
key={member.user.id}
workspaceMember={{ user: member.user }}
accessory={
currentUser?.id !== member.user.id && (
<ButtonContainer>
<Button
onClick={() =>
handleRemoveWorkspaceMember(member.user.id)
}
variant={ButtonVariant.Tertiary}
size={ButtonSize.Small}
icon={<IconTrash size={theme.icon.size.md} />}
/>
</ButtonContainer>
)
}
/>
))}
</Section>
</StyledContainer> </StyledContainer>
</SubMenuTopBarContainer> </SubMenuTopBarContainer>
); );

View File

@ -57,6 +57,10 @@ const RowContainer = styled.div`
`; `;
export const ElementContainer = styled.div` export const ElementContainer = styled.div`
display: flex;
`;
export const CellContainer = styled.div`
align-items: center; align-items: center;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -70,13 +74,15 @@ const emptyVariable = {
}; };
export const CatalogDecorator: Decorator = (Story, context) => { export const CatalogDecorator: Decorator = (Story, context) => {
const { catalog } = context.parameters; const {
catalog: { dimensions, options },
} = context.parameters;
const [ const [
variable1, variable1,
variable2 = emptyVariable, variable2 = emptyVariable,
variable3 = emptyVariable, variable3 = emptyVariable,
variable4 = emptyVariable, variable4 = emptyVariable,
] = catalog; ] = dimensions;
return ( return (
<StyledContainer> <StyledContainer>
@ -96,22 +102,25 @@ export const CatalogDecorator: Decorator = (Story, context) => {
<RowTitle>{variable2.labels?.(value2) || value2}</RowTitle> <RowTitle>{variable2.labels?.(value2) || value2}</RowTitle>
)} )}
{variable1.values.map((value1: string) => ( {variable1.values.map((value1: string) => (
<ElementContainer key={value1} id={value1}> <CellContainer key={value1} id={value1}>
{(variable1.labels?.(value1) || value1) && ( {(variable1.labels?.(value1) || value1) && (
<ElementTitle> <ElementTitle>
{variable1.labels?.(value1) || value1} {variable1.labels?.(value1) || value1}
</ElementTitle> </ElementTitle>
)} )}
<Story
args={{ <ElementContainer {...options?.elementContainer}>
...context.args, <Story
...variable1.props(value1), args={{
...variable2.props(value2), ...context.args,
...variable3.props(value3), ...variable1.props(value1),
...variable4.props(value4), ...variable2.props(value2),
}} ...variable3.props(value3),
/> ...variable4.props(value4),
</ElementContainer> }}
/>
</ElementContainer>
</CellContainer>
))} ))}
</RowContainer> </RowContainer>
))} ))}