feat: add active and disabled object tables to settings (#1885)

* feat: add active and disabled object tables to settings

Closes #1799, Closes #1800

* refactor: add align prop to TableCell
This commit is contained in:
Thaïs 2023-10-05 22:19:08 +02:00 committed by GitHub
parent 4ed77a9c51
commit 18e210b29b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 247 additions and 82 deletions

View File

@ -54,6 +54,7 @@ export {
IconLinkOff,
IconList,
IconLogout,
IconLuggage,
IconMail,
IconMap,
IconMinus,
@ -61,6 +62,7 @@ export {
IconNotes,
IconPencil,
IconPhone,
IconPlane,
IconPlus,
IconProgressCheck,
IconSearch,

View File

@ -1,8 +1,5 @@
import styled from '@emotion/styled';
const StyledTable = styled.table`
border-collapse: collapse;
border-spacing: 0;
`;
const StyledTable = styled.div``;
export { StyledTable as Table };

View File

@ -1,18 +1,18 @@
import { PropsWithChildren } from 'react';
import styled from '@emotion/styled';
const StyledTableCell = styled.td`
padding: 0 ${({ theme }) => theme.spacing(2)};
`;
const StyledTableCellContent = styled.div`
const StyledTableCell = styled.div<{ align?: 'left' | 'center' | 'right' }>`
align-items: center;
color: ${({ theme }) => theme.font.color.secondary};
display: flex;
height: ${({ theme }) => theme.spacing(8)};
justify-content: ${({ align }) =>
align === 'right'
? 'flex-end'
: align === 'center'
? 'center'
: 'flex-start'};
padding: 0 ${({ theme }) => theme.spacing(2)};
text-align: ${({ align }) => align ?? 'left'};
`;
export const TableCell = ({ children }: PropsWithChildren) => (
<StyledTableCell>
<StyledTableCellContent>{children}</StyledTableCellContent>
</StyledTableCell>
);
export { StyledTableCell as TableCell };

View File

@ -1,11 +1,20 @@
import styled from '@emotion/styled';
const StyledTableHeader = styled.th`
const StyledTableHeader = styled.div<{ align?: 'left' | 'center' | 'right' }>`
align-items: center;
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
color: ${({ theme }) => theme.font.color.tertiary};
display: flex;
font-weight: ${({ theme }) => theme.font.weight.medium};
height: ${({ theme }) => theme.spacing(8)};
justify-content: ${({ align }) =>
align === 'right'
? 'flex-end'
: align === 'center'
? 'center'
: 'flex-start'};
padding: 0 ${({ theme }) => theme.spacing(2)};
text-align: left;
text-align: ${({ align }) => align ?? 'left'};
`;
export { StyledTableHeader as TableHeader };

View File

@ -0,0 +1,19 @@
import styled from '@emotion/styled';
const StyledTableRow = styled.div<{ onClick?: () => void }>`
border-radius: ${({ theme }) => theme.border.radius.sm};
display: grid;
grid-auto-columns: 1fr;
grid-auto-flow: column;
transition: background-color
${({ theme }) => theme.animation.duration.normal}s;
width: 100%;
&:hover {
background-color: ${({ onClick, theme }) =>
onClick ? theme.background.transparent.light : 'transparent'};
cursor: ${({ onClick }) => (onClick ? 'pointer' : 'default')};
}
`;
export { StyledTableRow as TableRow };

View File

@ -9,26 +9,6 @@ type TableSectionProps = {
title: string;
};
const StyledTableBody = styled.tbody<{ isExpanded: boolean }>`
border-bottom: ${({ isExpanded, theme }) =>
isExpanded ? `1px solid ${theme.border.color.light}` : 0};
&:first-of-type {
border-top: ${({ theme }) => `1px solid ${theme.border.color.light}`};
}
td > div {
${({ isExpanded }) => (isExpanded ? '' : 'height: 0; opacity: 0;')};
overflow: hidden;
transition: height ${({ theme }) => theme.animation.duration.normal}s,
opacity ${({ theme }) => theme.animation.duration.normal}s;
}
`;
const StyledTh = styled.th`
padding: 0;
`;
const StyledSectionHeader = styled.div<{ isExpanded: boolean }>`
align-items: center;
background-color: ${({ theme }) => theme.background.transparent.lighter};
@ -45,6 +25,19 @@ const StyledSectionHeader = styled.div<{ isExpanded: boolean }>`
text-transform: uppercase;
`;
const StyledSection = styled.div<{ isExpanded: boolean }>`
max-height: ${({ isExpanded }) => (isExpanded ? '1000px' : 0)};
opacity: ${({ isExpanded }) => (isExpanded ? 1 : 0)};
overflow: hidden;
transition: max-height ${({ theme }) => theme.animation.duration.normal}s,
opacity ${({ theme }) => theme.animation.duration.normal}s;
`;
const StyledSectionContent = styled.div`
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
padding: ${({ theme }) => theme.spacing(2)} 0;
`;
export const TableSection = ({ children, title }: TableSectionProps) => {
const theme = useTheme();
const [isExpanded, setIsExpanded] = useState(true);
@ -53,23 +46,21 @@ export const TableSection = ({ children, title }: TableSectionProps) => {
setIsExpanded((previousIsExpanded) => !previousIsExpanded);
return (
<StyledTableBody isExpanded={isExpanded}>
<tr>
<StyledTh colSpan={500} scope="rowgroup">
<StyledSectionHeader
isExpanded={isExpanded}
onClick={handleToggleSection}
>
{title}
{isExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
) : (
<IconChevronDown size={theme.icon.size.sm} />
)}
</StyledSectionHeader>
</StyledTh>
</tr>
{children}
</StyledTableBody>
<>
<StyledSectionHeader
isExpanded={isExpanded}
onClick={handleToggleSection}
>
{title}
{isExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
) : (
<IconChevronDown size={theme.icon.size.sm} />
)}
</StyledSectionHeader>
<StyledSection isExpanded={isExpanded}>
<StyledSectionContent>{children}</StyledSectionContent>
</StyledSection>
</>
);
};

View File

@ -5,6 +5,7 @@ import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { Table } from '../Table';
import { TableCell } from '../TableCell';
import { TableHeader } from '../TableHeader';
import { TableRow } from '../TableRow';
import { TableSection } from '../TableSection';
const meta: Meta<typeof Table> = {
@ -23,31 +24,29 @@ type Story = StoryObj<typeof Table>;
export const Default: Story = {
render: () => (
<Table>
<thead>
<tr>
<TableHeader>Header 1</TableHeader>
<TableHeader>Header 2</TableHeader>
<TableHeader>Header 3</TableHeader>
</tr>
</thead>
<TableRow>
<TableHeader>Header 1</TableHeader>
<TableHeader>Header 2</TableHeader>
<TableHeader align="right">Numbers</TableHeader>
</TableRow>
<TableSection title="Section 1">
<tr>
<TableRow>
<TableCell>Cell 1</TableCell>
<TableCell>Cell 2</TableCell>
<TableCell>Cell 3</TableCell>
</tr>
<tr>
<TableCell align="right">3</TableCell>
</TableRow>
<TableRow>
<TableCell>Cell 4</TableCell>
<TableCell>Cell 5</TableCell>
<TableCell>Cell 6</TableCell>
</tr>
<TableCell align="right">6</TableCell>
</TableRow>
</TableSection>
<TableSection title="Section 2">
<tr>
<TableRow>
<TableCell>Lorem ipsum dolor sit amet</TableCell>
<TableCell>Lorem ipsum</TableCell>
<TableCell>Lorem ipsum</TableCell>
</tr>
<TableCell align="right">42</TableCell>
</TableRow>
</TableSection>
</Table>
),

View File

@ -41,13 +41,18 @@ const StyledTag = styled.h3<{
`;
export type TagProps = {
className?: string;
color: ThemeColor;
text: string;
onClick?: () => void;
};
export const Tag = ({ color, text, onClick }: TagProps) => (
<StyledTag color={castToTagColor(color)} onClick={onClick}>
export const Tag = ({ className, color, text, onClick }: TagProps) => (
<StyledTag
className={className}
color={castToTagColor(color)}
onClick={onClick}
>
{text}
</StyledTag>
);

View File

@ -1,18 +1,161 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconSettings } from '@/ui/icon';
import {
IconBuildingSkyscraper,
IconChevronRight,
IconDotsVertical,
IconLuggage,
IconPlane,
IconSettings,
IconUser,
} from '@/ui/icon';
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
import { Table } from '@/ui/table/components/Table';
import { TableCell } from '@/ui/table/components/TableCell';
import { TableHeader } from '@/ui/table/components/TableHeader';
import { TableRow } from '@/ui/table/components/TableRow';
import { TableSection } from '@/ui/table/components/TableSection';
import { Tag } from '@/ui/tag/components/Tag';
import { H1Title } from '@/ui/typography/components/H1Title';
import { H2Title } from '@/ui/typography/components/H2Title';
const StyledContainer = styled.div`
padding: ${({ theme }) => theme.spacing(8)};
width: 350px;
width: 512px;
`;
export const SettingsObjects = () => (
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<StyledContainer>
<H1Title title="Objects" />
</StyledContainer>
</SubMenuTopBarContainer>
);
const StyledTableRow = styled(TableRow)`
grid-template-columns: 180px 98.7px 98.7px 98.7px 36px;
`;
const StyledNameTableCell = styled(TableCell)`
color: ${({ theme }) => theme.font.color.primary};
gap: ${({ theme }) => theme.spacing(2)};
`;
const StyledTag = styled(Tag)`
box-sizing: border-box;
height: ${({ theme }) => theme.spacing(4)};
`;
const StyledIconTableCell = styled(TableCell)`
justify-content: center;
padding-right: ${({ theme }) => theme.spacing(1)};
`;
const StyledIconChevronRight = styled(IconChevronRight)`
color: ${({ theme }) => theme.font.color.tertiary};
`;
const StyledIconDotsVertical = styled(IconDotsVertical)`
color: ${({ theme }) => theme.font.color.tertiary};
`;
const activeObjectItems = [
{
name: 'Companies',
Icon: IconBuildingSkyscraper,
type: 'standard',
fields: 23,
instances: 165,
},
{
name: 'People',
Icon: IconUser,
type: 'standard',
fields: 16,
instances: 462,
},
];
const disabledObjectItems = [
{
name: 'Travels',
Icon: IconLuggage,
type: 'custom',
fields: 23,
instances: 165,
},
{
name: 'Flights',
Icon: IconPlane,
type: 'custom',
fields: 23,
instances: 165,
},
];
export const SettingsObjects = () => {
const theme = useTheme();
return (
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<StyledContainer>
<H1Title title="Objects" />
<H2Title title="Existing objects" />
<Table>
<StyledTableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Type</TableHeader>
<TableHeader align="right">Fields</TableHeader>
<TableHeader align="right">Instances</TableHeader>
<TableHeader></TableHeader>
</StyledTableRow>
<TableSection title="Active">
{activeObjectItems.map((objectItem) => (
<StyledTableRow key={objectItem.name} onClick={() => undefined}>
<StyledNameTableCell>
<objectItem.Icon size={theme.icon.size.md} />
{objectItem.name}
</StyledNameTableCell>
<TableCell>
{objectItem.type === 'standard' ? (
<StyledTag color="blue" text="Standard" />
) : (
<StyledTag color="orange" text="Custom" />
)}
</TableCell>
<TableCell align="right">{objectItem.fields}</TableCell>
<TableCell align="right">{objectItem.instances}</TableCell>
<StyledIconTableCell>
<StyledIconChevronRight
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
</StyledIconTableCell>
</StyledTableRow>
))}
</TableSection>
{!!disabledObjectItems.length && (
<TableSection title="Disabled">
{disabledObjectItems.map((objectItem) => (
<StyledTableRow key={objectItem.name}>
<StyledNameTableCell>
<objectItem.Icon size={theme.icon.size.md} />
{objectItem.name}
</StyledNameTableCell>
<TableCell>
{objectItem.type === 'standard' ? (
<StyledTag color="blue" text="Standard" />
) : (
<StyledTag color="orange" text="Custom" />
)}
</TableCell>
<TableCell align="right">{objectItem.fields}</TableCell>
<TableCell align="right">{objectItem.instances}</TableCell>
<StyledIconTableCell>
<StyledIconDotsVertical
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
</StyledIconTableCell>
</StyledTableRow>
))}
</TableSection>
)}
</Table>
</StyledContainer>
</SubMenuTopBarContainer>
);
};