Introduce accent for chips (#911)
* Introduce accent for chips * Add top bar on Mobile on Settings pages * Various fixes * Fix according to peer review
This commit is contained in:
@ -1,60 +1,27 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import {
|
||||
DropdownButton,
|
||||
DropdownOptionType,
|
||||
} from '@/ui/button/components/DropdownButton';
|
||||
import { IconCheck, IconNotes } from '@/ui/icon';
|
||||
import {
|
||||
ActivityType,
|
||||
CommentThread,
|
||||
useUpdateCommentThreadMutation,
|
||||
} from '~/generated/graphql';
|
||||
Chip,
|
||||
ChipAccent,
|
||||
ChipSize,
|
||||
ChipVariant,
|
||||
} from '@/ui/chip/components/Chip';
|
||||
import { IconPhone } from '@/ui/icon';
|
||||
import { CommentThread } from '~/generated/graphql';
|
||||
|
||||
type OwnProps = {
|
||||
commentThread: Pick<CommentThread, 'id' | 'type'>;
|
||||
commentThread: Pick<CommentThread, 'type'>;
|
||||
};
|
||||
|
||||
export function CommentThreadTypeDropdown({ commentThread }: OwnProps) {
|
||||
const [updateCommentThreadMutation] = useUpdateCommentThreadMutation();
|
||||
const options: DropdownOptionType[] = [
|
||||
{ label: 'Note', key: 'note', icon: <IconNotes /> },
|
||||
{ label: 'Task', key: 'task', icon: <IconCheck /> },
|
||||
];
|
||||
|
||||
function getSelectedOptionKey() {
|
||||
if (commentThread.type === ActivityType.Note) {
|
||||
return 'note';
|
||||
} else if (commentThread.type === ActivityType.Task) {
|
||||
return 'task';
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const convertSelectionOptionKeyToActivityType = (key: string) => {
|
||||
switch (key) {
|
||||
case 'note':
|
||||
return ActivityType.Note;
|
||||
case 'task':
|
||||
return ActivityType.Task;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelect = (selectedOption: DropdownOptionType) => {
|
||||
updateCommentThreadMutation({
|
||||
variables: {
|
||||
id: commentThread.id,
|
||||
type: convertSelectionOptionKeyToActivityType(selectedOption.key),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<DropdownButton
|
||||
options={options}
|
||||
onSelection={handleSelect}
|
||||
selectedOptionKey={getSelectedOptionKey()}
|
||||
<Chip
|
||||
label={commentThread.type}
|
||||
leftComponent={<IconPhone size={theme.icon.size.md} />}
|
||||
size={ChipSize.Large}
|
||||
accent={ChipAccent.TextSecondary}
|
||||
variant={ChipVariant.Highlighted}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -47,10 +47,6 @@ export const SmallName: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Clickable: Story = {
|
||||
args: { ...SmallName.args, clickable: true },
|
||||
};
|
||||
|
||||
export const BigName: Story = {
|
||||
args: {
|
||||
id: 'google',
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { PersonChip } from '@/people/components/PersonChip';
|
||||
import { RelationPickerHotkeyScope } from '@/ui/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
|
||||
import { Company, User } from '~/generated/graphql';
|
||||
|
||||
import { UserChip } from '../../users/components/UserChip';
|
||||
|
||||
import { CompanyAccountOwnerPicker } from './CompanyAccountOwnerPicker';
|
||||
|
||||
export type CompanyAccountOnwer = Pick<Company, 'id'> & {
|
||||
@ -38,7 +39,7 @@ export function CompanyAccountOwnerCell({ company }: OwnProps) {
|
||||
}
|
||||
nonEditModeContent={
|
||||
company.accountOwner?.displayName ? (
|
||||
<PersonChip
|
||||
<UserChip
|
||||
id={company.accountOwner.id}
|
||||
name={company.accountOwner?.displayName ?? ''}
|
||||
pictureUrl={company.accountOwner?.avatarUrl ?? ''}
|
||||
|
||||
@ -19,6 +19,7 @@ import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedSta
|
||||
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
import { EntityChipVariant } from '../../ui/chip/components/EntityChip';
|
||||
import { PipelineProgressForBoard } from '../types/CompanyProgress';
|
||||
|
||||
import { CompanyChip } from './CompanyChip';
|
||||
@ -177,7 +178,7 @@ export function CompanyBoardCard() {
|
||||
id={company.id}
|
||||
name={company.name}
|
||||
pictureUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||
clickable={false}
|
||||
variant={EntityChipVariant.Transparent}
|
||||
/>
|
||||
<StyledCheckboxContainer className="checkbox-container">
|
||||
<Checkbox
|
||||
|
||||
@ -1,25 +1,26 @@
|
||||
import { EntityChip } from '@/ui/chip/components/EntityChip';
|
||||
import { EntityChip, EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
||||
|
||||
type OwnProps = {
|
||||
id: string;
|
||||
name: string;
|
||||
pictureUrl?: string;
|
||||
clickable?: boolean;
|
||||
variant?: EntityChipVariant;
|
||||
};
|
||||
|
||||
export function CompanyChip({
|
||||
id,
|
||||
name,
|
||||
pictureUrl,
|
||||
clickable = true,
|
||||
variant = EntityChipVariant.Regular,
|
||||
}: OwnProps) {
|
||||
return (
|
||||
<EntityChip
|
||||
entityId={id}
|
||||
linkToEntity={clickable ? `/companies/${id}` : undefined}
|
||||
linkToEntity={`/companies/${id}`}
|
||||
name={name}
|
||||
avatarType="squared"
|
||||
pictureUrl={pictureUrl}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -37,7 +37,6 @@ export function CompanyEditableNameChipCell({ company }: OwnProps) {
|
||||
<CompanyChip
|
||||
id={company.id}
|
||||
name={company.name}
|
||||
clickable
|
||||
pictureUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||
/>
|
||||
}
|
||||
|
||||
@ -1,25 +1,26 @@
|
||||
import { EntityChip } from '@/ui/chip/components/EntityChip';
|
||||
import { EntityChip, EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
||||
|
||||
export type PersonChipPropsType = {
|
||||
id: string;
|
||||
name: string;
|
||||
pictureUrl?: string;
|
||||
clickable?: boolean;
|
||||
variant?: EntityChipVariant;
|
||||
};
|
||||
|
||||
export function PersonChip({
|
||||
id,
|
||||
name,
|
||||
pictureUrl,
|
||||
clickable = true,
|
||||
variant,
|
||||
}: PersonChipPropsType) {
|
||||
return (
|
||||
<EntityChip
|
||||
entityId={id}
|
||||
linkToEntity={clickable ? `/person/${id}` : undefined}
|
||||
linkToEntity={`/person/${id}`}
|
||||
name={name}
|
||||
avatarType="rounded"
|
||||
pictureUrl={pictureUrl}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,144 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconChevronDown } from '@/ui/icon/index';
|
||||
|
||||
type ButtonProps = React.ComponentProps<'button'>;
|
||||
|
||||
export type DropdownOptionType = {
|
||||
key: string;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
};
|
||||
|
||||
type OwnProps = {
|
||||
options: DropdownOptionType[];
|
||||
selectedOptionKey?: string;
|
||||
onSelection: (value: DropdownOptionType) => void;
|
||||
} & ButtonProps;
|
||||
|
||||
const StyledButton = styled.button<ButtonProps & { isOpen: boolean }>`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.tertiary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-bottom-left-radius: ${({ isOpen, theme }) =>
|
||||
isOpen ? 0 : theme.border.radius.sm};
|
||||
border-bottom-right-radius: ${({ isOpen, theme }) =>
|
||||
isOpen ? 0 : theme.border.radius.sm};
|
||||
border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-top-right-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)};
|
||||
|
||||
svg {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 14px;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDropdownItem = styled.button<ButtonProps>`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.tertiary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-top: none;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)};
|
||||
|
||||
svg {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 14px;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const DropdownContainer = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const DropdownMenu = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export function DropdownButton({
|
||||
options,
|
||||
selectedOptionKey,
|
||||
onSelection,
|
||||
...buttonProps
|
||||
}: OwnProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedOption, setSelectedOption] = useState<
|
||||
DropdownOptionType | undefined
|
||||
>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedOptionKey) {
|
||||
const option = options.find((option) => option.key === selectedOptionKey);
|
||||
setSelectedOption(option);
|
||||
} else {
|
||||
setSelectedOption(options[0]);
|
||||
}
|
||||
}, [selectedOptionKey, options]);
|
||||
|
||||
if (!options.length) {
|
||||
throw new Error('You must provide at least one option.');
|
||||
}
|
||||
|
||||
const handleSelect =
|
||||
(option: DropdownOptionType) =>
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
onSelection(option);
|
||||
setSelectedOption(option);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedOption && (
|
||||
<DropdownContainer>
|
||||
<StyledButton
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
{...buttonProps}
|
||||
isOpen={isOpen}
|
||||
>
|
||||
{selectedOption.icon}
|
||||
{selectedOption.label}
|
||||
{options.length > 1 && <IconChevronDown />}
|
||||
</StyledButton>
|
||||
{isOpen && (
|
||||
<DropdownMenu>
|
||||
{options
|
||||
.filter((option) => option.label !== selectedOption.label)
|
||||
.map((option, index) => (
|
||||
<StyledDropdownItem
|
||||
key={index}
|
||||
onClick={handleSelect(option)}
|
||||
>
|
||||
{option.icon}
|
||||
{option.label}
|
||||
</StyledDropdownItem>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</DropdownContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -8,6 +8,11 @@ export enum ChipSize {
|
||||
Small = 'small',
|
||||
}
|
||||
|
||||
export enum ChipAccent {
|
||||
TextPrimary = 'text-primary',
|
||||
TextSecondary = 'text-secondary',
|
||||
}
|
||||
|
||||
export enum ChipVariant {
|
||||
Highlighted = 'highlighted',
|
||||
Regular = 'regular',
|
||||
@ -21,6 +26,7 @@ type OwnProps = {
|
||||
label: string;
|
||||
maxWidth?: string;
|
||||
variant?: ChipVariant;
|
||||
accent?: ChipAccent;
|
||||
leftComponent?: React.ReactNode;
|
||||
rightComponent?: React.ReactNode;
|
||||
className?: string;
|
||||
@ -34,14 +40,18 @@ const StyledContainer = styled.div<Partial<OwnProps>>`
|
||||
? theme.background.transparent.light
|
||||
: 'transparent'};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${({ theme, disabled }) =>
|
||||
disabled ? theme.font.color.light : theme.font.color.primary};
|
||||
color: ${({ theme, disabled, accent }) =>
|
||||
disabled
|
||||
? theme.font.color.light
|
||||
: accent === ChipAccent.TextPrimary
|
||||
? theme.font.color.primary
|
||||
: theme.font.color.secondary};
|
||||
cursor: ${({ clickable, disabled, variant }) =>
|
||||
disabled || variant === ChipVariant.Transparent
|
||||
? 'auto'
|
||||
? 'inherit'
|
||||
: clickable
|
||||
? 'pointer'
|
||||
: 'auto'};
|
||||
: 'inherit'};
|
||||
display: inline-flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
|
||||
@ -98,6 +108,7 @@ export function Chip({
|
||||
variant = ChipVariant.Regular,
|
||||
leftComponent,
|
||||
rightComponent,
|
||||
accent = ChipAccent.TextPrimary,
|
||||
maxWidth,
|
||||
className,
|
||||
}: OwnProps) {
|
||||
@ -106,9 +117,11 @@ export function Chip({
|
||||
data-testid="chip"
|
||||
clickable={clickable}
|
||||
variant={variant}
|
||||
accent={accent}
|
||||
size={size}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
maxWidth={maxWidth}
|
||||
>
|
||||
{leftComponent}
|
||||
<StyledLabel>
|
||||
|
||||
@ -12,14 +12,21 @@ type OwnProps = {
|
||||
name: string;
|
||||
pictureUrl?: string;
|
||||
avatarType?: AvatarType;
|
||||
variant?: EntityChipVariant;
|
||||
};
|
||||
|
||||
export enum EntityChipVariant {
|
||||
Regular = 'regular',
|
||||
Transparent = 'transparent',
|
||||
}
|
||||
|
||||
export function EntityChip({
|
||||
linkToEntity,
|
||||
entityId,
|
||||
name,
|
||||
pictureUrl,
|
||||
avatarType = 'rounded',
|
||||
variant = EntityChipVariant.Regular,
|
||||
}: OwnProps) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -35,7 +42,13 @@ export function EntityChip({
|
||||
<div onClick={handleLinkClick}>
|
||||
<Chip
|
||||
label={name}
|
||||
variant={linkToEntity ? ChipVariant.Highlighted : ChipVariant.Regular}
|
||||
variant={
|
||||
linkToEntity
|
||||
? variant === EntityChipVariant.Regular
|
||||
? ChipVariant.Highlighted
|
||||
: ChipVariant.Regular
|
||||
: ChipVariant.Transparent
|
||||
}
|
||||
leftComponent={
|
||||
<Avatar
|
||||
avatarUrl={pictureUrl}
|
||||
|
||||
@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
import { ExhaustiveComponentDecorator } from '~/testing/decorators/ExhaustiveComponentDecorator';
|
||||
|
||||
import { Chip, ChipSize, ChipVariant } from '../Chip';
|
||||
import { Chip, ChipAccent, ChipSize, ChipVariant } from '../Chip';
|
||||
|
||||
const meta: Meta<typeof Chip> = {
|
||||
title: 'UI/Chip/Chip',
|
||||
@ -18,13 +18,15 @@ export const Default: Story = {
|
||||
label: 'Chip test',
|
||||
size: ChipSize.Small,
|
||||
variant: ChipVariant.Highlighted,
|
||||
accent: ChipAccent.TextPrimary,
|
||||
disabled: false,
|
||||
clickable: true,
|
||||
maxWidth: '200px',
|
||||
},
|
||||
decorators: [ComponentDecorator],
|
||||
};
|
||||
|
||||
export const All: Story = {
|
||||
export const Catalog: Story = {
|
||||
args: { size: ChipSize.Large, clickable: true, label: 'Hello' },
|
||||
argTypes: {
|
||||
size: { control: false },
|
||||
@ -42,6 +44,7 @@ export const All: Story = {
|
||||
ChipVariant.Transparent,
|
||||
],
|
||||
sizes: [ChipSize.Small, ChipSize.Large],
|
||||
accents: [ChipAccent.TextPrimary, ChipAccent.TextSecondary],
|
||||
states: ['default', 'hover', 'active', 'disabled'],
|
||||
},
|
||||
decorators: [ExhaustiveComponentDecorator],
|
||||
|
||||
@ -1,20 +1,29 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useIsMobile } from '../../hooks/useIsMobile';
|
||||
import { TopBar } from '../top-bar/components/TopBar';
|
||||
|
||||
import { RightDrawerContainer } from './RightDrawerContainer';
|
||||
|
||||
type OwnProps = {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
title: string;
|
||||
icon: React.ReactNode;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
const StyledContainer = styled.div<{ isMobile: boolean }>`
|
||||
display: flex;
|
||||
padding-top: ${({ theme }) => theme.spacing(4)};
|
||||
flex-direction: column;
|
||||
padding-top: ${({ theme, isMobile }) => (!isMobile ? theme.spacing(4) : 0)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export function SubMenuTopBarContainer({ children }: OwnProps) {
|
||||
export function SubMenuTopBarContainer({ children, title, icon }: OwnProps) {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledContainer isMobile={isMobile}>
|
||||
{isMobile && <TopBar title={title} icon={icon} />}
|
||||
<RightDrawerContainer topMargin={16}>{children}</RightDrawerContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
@ -74,11 +74,8 @@ export function TopBar({
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = useCallback(() => navigate(-1), [navigate]);
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
const isNavbarOpened = useRecoilValue(isNavbarOpenedState);
|
||||
|
||||
const showNavCollapseButton = isMobile || !isNavbarOpened;
|
||||
|
||||
const iconSize = useIsMobile()
|
||||
? navbarIconSize.mobile
|
||||
: navbarIconSize.desktop;
|
||||
@ -87,7 +84,7 @@ export function TopBar({
|
||||
<>
|
||||
<TopBarContainer>
|
||||
<StyledLeftContainer>
|
||||
{showNavCollapseButton && (
|
||||
{!isNavbarOpened && (
|
||||
<TopBarButtonContainer>
|
||||
<NavCollapseButton direction="right" />
|
||||
</TopBarButtonContainer>
|
||||
|
||||
@ -11,6 +11,8 @@ type OwnProps = {
|
||||
|
||||
const StyledClickable = styled.div`
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
|
||||
@ -15,8 +15,8 @@ type OwnProps = {
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
width: ${({ theme }) => (useIsMobile() ? '100%' : leftNavbarWidth.desktop)};
|
||||
padding-top: ${({ theme }) => theme.spacing(9)};
|
||||
width: ${() => (useIsMobile() ? '100%' : leftNavbarWidth.desktop)};
|
||||
`;
|
||||
|
||||
export default function SubMenuNavbar({ children, backButtonTitle }: OwnProps) {
|
||||
|
||||
@ -82,8 +82,6 @@ export function RightDrawer() {
|
||||
: theme.rightDrawerWidth
|
||||
: '0';
|
||||
|
||||
console.log(rightDrawerWidth);
|
||||
|
||||
if (!isDefined(rightDrawerPage)) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
19
front/src/modules/users/components/UserChip.tsx
Normal file
19
front/src/modules/users/components/UserChip.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { EntityChip, EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
||||
|
||||
export type UserChipPropsType = {
|
||||
id: string;
|
||||
name: string;
|
||||
pictureUrl?: string;
|
||||
variant?: EntityChipVariant;
|
||||
};
|
||||
|
||||
export function UserChip({ id, name, pictureUrl, variant }: UserChipPropsType) {
|
||||
return (
|
||||
<EntityChip
|
||||
entityId={id}
|
||||
name={name}
|
||||
avatarType="rounded"
|
||||
pictureUrl={pictureUrl}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -24,7 +24,10 @@ export function People() {
|
||||
async function handleAddButtonClick() {
|
||||
await insertOnePerson({
|
||||
variables: {
|
||||
data: {},
|
||||
data: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
},
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { ColorSchemePicker } from '@/ui/color-scheme/components/ColorSchemePicker';
|
||||
import { IconSettings } from '@/ui/icon';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
|
||||
import { useColorScheme } from '@/ui/themes/hooks/useColorScheme';
|
||||
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle';
|
||||
@ -27,7 +28,7 @@ export function SettingsExperience() {
|
||||
const { colorScheme, setColorScheme } = useColorScheme();
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer>
|
||||
<SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings">
|
||||
<div>
|
||||
<StyledContainer>
|
||||
<MainSectionTitle>Experience</MainSectionTitle>
|
||||
|
||||
@ -3,6 +3,7 @@ import styled from '@emotion/styled';
|
||||
import { EmailField } from '@/settings/profile/components/EmailField';
|
||||
import { NameFields } from '@/settings/profile/components/NameFields';
|
||||
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
|
||||
import { IconSettings } from '@/ui/icon';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
|
||||
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle';
|
||||
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle';
|
||||
@ -26,8 +27,8 @@ const StyledSectionContainer = styled.div`
|
||||
|
||||
export function SettingsProfile() {
|
||||
return (
|
||||
<SubMenuTopBarContainer>
|
||||
<div>
|
||||
<SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings">
|
||||
<>
|
||||
<StyledContainer>
|
||||
<MainSectionTitle>Profile</MainSectionTitle>
|
||||
<StyledSectionContainer>
|
||||
@ -49,7 +50,7 @@ export function SettingsProfile() {
|
||||
<EmailField />
|
||||
</StyledSectionContainer>
|
||||
</StyledContainer>
|
||||
</div>
|
||||
</>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { NameField } from '@/settings/workspace/components/NameField';
|
||||
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
|
||||
import { IconSettings } from '@/ui/icon';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
|
||||
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle';
|
||||
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle';
|
||||
@ -24,7 +25,7 @@ const StyledSectionContainer = styled.div`
|
||||
|
||||
export function SettingsWorksapce() {
|
||||
return (
|
||||
<SubMenuTopBarContainer>
|
||||
<SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings">
|
||||
<div>
|
||||
<StyledContainer>
|
||||
<MainSectionTitle>General</MainSectionTitle>
|
||||
|
||||
@ -4,7 +4,7 @@ import { useRecoilState } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { Button } from '@/ui/button/components/Button';
|
||||
import { IconTrash } from '@/ui/icon';
|
||||
import { IconSettings, IconTrash } from '@/ui/icon';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
|
||||
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle';
|
||||
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle';
|
||||
@ -75,7 +75,7 @@ export function SettingsWorkspaceMembers() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer>
|
||||
<SubMenuTopBarContainer icon={<IconSettings size={16} />} title="Settings">
|
||||
<StyledContainer>
|
||||
<MainSectionTitle>Members</MainSectionTitle>
|
||||
{workspace?.inviteHash && (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Decorator, StrictArgs } from '@storybook/react';
|
||||
import { Decorator } from '@storybook/react';
|
||||
|
||||
function stateProps(state: string) {
|
||||
switch (state) {
|
||||
@ -22,11 +22,20 @@ const StyledSizeTitle = styled.h1`
|
||||
margin: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledVariantTitle = styled.h1`
|
||||
const StyledVariantTitle = styled.h2`
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
margin: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100px;
|
||||
`;
|
||||
|
||||
const StyledAccentTitle = styled.h3`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
margin: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100px;
|
||||
`;
|
||||
|
||||
const StyledStateTitle = styled.span`
|
||||
@ -49,62 +58,58 @@ const StyledSizeContainer = styled.div`
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledLineContainer = styled.div`
|
||||
const StyledVariantContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledAccentContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledComponentContainer = styled.div`
|
||||
const StyledStateContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
function renderSize(
|
||||
size: string,
|
||||
variants: string[],
|
||||
states: string[],
|
||||
args: StrictArgs,
|
||||
Story: React.FC<StrictArgs>,
|
||||
) {
|
||||
return (
|
||||
<StyledSizeContainer key={size}>
|
||||
<StyledSizeTitle>{size}</StyledSizeTitle>
|
||||
{variants.map((variant) => (
|
||||
<div key={variant}>
|
||||
<StyledVariantTitle>{variant}</StyledVariantTitle>
|
||||
<StyledLineContainer>
|
||||
{states.map((state) => (
|
||||
<StyledComponentContainer key={`${variant}-container-${state}`}>
|
||||
<StyledStateTitle>{state}</StyledStateTitle>
|
||||
<Story
|
||||
args={{ ...args, variant: variant, ...stateProps(state) }}
|
||||
/>
|
||||
</StyledComponentContainer>
|
||||
))}
|
||||
</StyledLineContainer>
|
||||
</div>
|
||||
))}
|
||||
</StyledSizeContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export const ExhaustiveComponentDecorator: Decorator = (Story, context) => {
|
||||
const parameters = context.parameters;
|
||||
return (
|
||||
<StyledContainer>
|
||||
{parameters.sizes.map((size: string) =>
|
||||
renderSize(
|
||||
size,
|
||||
parameters.variants,
|
||||
parameters.states,
|
||||
context.args,
|
||||
Story,
|
||||
),
|
||||
)}
|
||||
{parameters.sizes.map((size: string) => (
|
||||
<StyledSizeContainer key={size}>
|
||||
<StyledSizeTitle>{size}</StyledSizeTitle>
|
||||
{parameters.variants.map((variant: string) => (
|
||||
<StyledVariantContainer key={variant}>
|
||||
<StyledVariantTitle>{variant}</StyledVariantTitle>
|
||||
{parameters.accents.map((accent: string) => (
|
||||
<StyledAccentContainer key={accent}>
|
||||
<StyledAccentTitle>{accent}</StyledAccentTitle>
|
||||
{parameters.states.map((state: string) => (
|
||||
<StyledStateContainer key={state}>
|
||||
<StyledStateTitle>{state}</StyledStateTitle>
|
||||
<Story
|
||||
args={{
|
||||
...context.args,
|
||||
accent: accent,
|
||||
variant: variant,
|
||||
...stateProps(state),
|
||||
}}
|
||||
/>
|
||||
</StyledStateContainer>
|
||||
))}
|
||||
</StyledAccentContainer>
|
||||
))}
|
||||
</StyledVariantContainer>
|
||||
))}
|
||||
</StyledSizeContainer>
|
||||
))}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user