feat: add Table and TableSection components (#1849)

* refactor: rename ui/table to ui/data-table

* feat: add Table and TableSection components

Closes #1806
This commit is contained in:
Thaïs
2023-10-04 17:46:14 +02:00
committed by GitHub
parent d217142e7e
commit 7af306792b
118 changed files with 236 additions and 67 deletions

View File

@ -4,7 +4,7 @@ import { useSetRecoilState } from 'recoil';
import { useCompanyTableActionBarEntries } from '@/companies/hooks/useCompanyTableActionBarEntries';
import { CompanyTableMockMode } from '@/companies/table/components/CompanyTableMockMode';
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';

View File

@ -4,7 +4,7 @@ import { useSetRecoilState } from 'recoil';
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
import { CompanyTableMockMode } from '@/companies/table/components/CompanyTableMockMode';
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';

View File

@ -6,13 +6,13 @@ import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/Style
import { FieldMetadata } from '@/ui/field/types/FieldMetadata';
import { IconPlus } from '@/ui/icon';
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { ColumnDefinition } from '@/ui/table/types/ColumnDefinition';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { useTableColumns } from '../hooks/useTableColumns';
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
import { hiddenTableColumnsScopedSelector } from '../states/selectors/hiddenTableColumnsScopedSelector';
import { ColumnDefinition } from '../types/ColumnDefinition';
const StyledColumnMenu = styled(StyledDropdownMenu)`
font-weight: ${({ theme }) => theme.font.weight.regular};

View File

@ -4,7 +4,6 @@ import { useRecoilCallback } from 'recoil';
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
import { OptimisticEffectDefinition } from '@/apollo/optimistic-effect/types/OptimisticEffectDefinition';
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
@ -15,6 +14,7 @@ import { FilterDefinition } from '@/ui/view-bar/types/FilterDefinition';
import { SortDefinition } from '@/ui/view-bar/types/SortDefinition';
import { SortOrder } from '~/generated/graphql';
import { useSetEntityTableData } from '../hooks/useSetEntityTableData';
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
export const EntityTableEffect = ({

View File

@ -1,10 +1,11 @@
import styled from '@emotion/styled';
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
import {
EntityTableHeaderOptionsProps,
TableColumnDropdownMenu,
} from '@/ui/table/components/TableColumnDropdownMenu';
} from './TableColumnDropdownMenu';
const StyledDropdownContainer = styled.div`
left: 0px;

View File

@ -1,9 +1,6 @@
import { useRecoilCallback } from 'recoil';
import { entityFieldsFamilyState } from '@/ui/field/states/entityFieldsFamilyState';
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
import { availableFiltersScopedState } from '@/ui/view-bar/states/availableFiltersScopedState';
import { availableSortsScopedState } from '@/ui/view-bar/states/availableSortsScopedState';
@ -13,6 +10,10 @@ import { SortDefinition } from '@/ui/view-bar/types/SortDefinition';
import { isFetchingEntityTableDataState } from '../states/isFetchingEntityTableDataState';
import { numberOfTableRowsState } from '../states/numberOfTableRowsState';
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
import { tableRowIdsState } from '../states/tableRowIdsState';
import { useResetTableRowSelection } from './useResetTableRowSelection';
export const useSetEntityTableData = () => {
const resetTableRowSelection = useResetTableRowSelection();

View File

@ -1,6 +1,6 @@
import { TableOptionsDropdownId } from '@/ui/data-table/constants/TableOptionsDropdownId';
import { StyledHeaderDropdownButton } from '@/ui/dropdown/components/StyledHeaderDropdownButton';
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
import { TableOptionsDropdownId } from '@/ui/table/constants/TableOptionsDropdownId';
export const TableOptionsDropdownButton = () => {
const { isDropdownButtonOpen, toggleDropdownButton } = useDropdownButton({

View File

@ -26,6 +26,7 @@ export {
IconChevronLeft,
IconChevronRight,
IconChevronsRight,
IconChevronUp,
IconCircleDot,
IconCirclePlus,
IconColorSwatch,

View File

@ -1,4 +1,4 @@
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
import { TableHotkeyScope } from '@/ui/data-table/types/TableHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
type OwnProps = {

View File

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

View File

@ -0,0 +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`
align-items: center;
display: flex;
height: ${({ theme }) => theme.spacing(8)};
`;
export const TableCell = ({ children }: PropsWithChildren) => (
<StyledTableCell>
<StyledTableCellContent>{children}</StyledTableCellContent>
</StyledTableCell>
);

View File

@ -0,0 +1,11 @@
import styled from '@emotion/styled';
const StyledTableHeader = styled.th`
color: ${({ theme }) => theme.font.color.tertiary};
font-weight: ${({ theme }) => theme.font.weight.medium};
height: ${({ theme }) => theme.spacing(8)};
padding: 0 ${({ theme }) => theme.spacing(2)};
text-align: left;
`;
export { StyledTableHeader as TableHeader };

View File

@ -0,0 +1,75 @@
import { ReactNode, useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconChevronDown, IconChevronUp } from '@/ui/icon';
type TableSectionProps = {
children: ReactNode;
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};
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
color: ${({ theme }) => theme.font.color.light};
cursor: pointer;
display: flex;
font-size: ${({ theme }) => theme.font.size.xs};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
height: ${({ theme }) => theme.spacing(6)};
justify-content: space-between;
padding: 0 ${({ theme }) => theme.spacing(2)};
text-align: left;
text-transform: uppercase;
`;
export const TableSection = ({ children, title }: TableSectionProps) => {
const theme = useTheme();
const [isExpanded, setIsExpanded] = useState(true);
const handleToggleSection = () =>
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>
);
};

View File

@ -0,0 +1,54 @@
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { Table } from '../Table';
import { TableCell } from '../TableCell';
import { TableHeader } from '../TableHeader';
import { TableSection } from '../TableSection';
const meta: Meta<typeof Table> = {
title: 'UI/Table/Table',
component: Table,
decorators: [ComponentDecorator],
argTypes: {
as: { table: { disable: true } },
theme: { table: { disable: true } },
},
};
export default meta;
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>
<TableSection title="Section 1">
<tr>
<TableCell>Cell 1</TableCell>
<TableCell>Cell 2</TableCell>
<TableCell>Cell 3</TableCell>
</tr>
<tr>
<TableCell>Cell 4</TableCell>
<TableCell>Cell 5</TableCell>
<TableCell>Cell 6</TableCell>
</tr>
</TableSection>
<TableSection title="Section 2">
<tr>
<TableCell>Lorem ipsum dolor sit amet</TableCell>
<TableCell>Lorem ipsum</TableCell>
<TableCell>Lorem ipsum</TableCell>
</tr>
</TableSection>
</Table>
),
};