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:
@ -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';
|
||||
|
||||
|
||||
@ -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';
|
||||
|
||||
|
||||
@ -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};
|
||||
@ -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 = ({
|
||||
@ -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;
|
||||
@ -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();
|
||||
@ -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({
|
||||
@ -26,6 +26,7 @@ export {
|
||||
IconChevronLeft,
|
||||
IconChevronRight,
|
||||
IconChevronsRight,
|
||||
IconChevronUp,
|
||||
IconCircleDot,
|
||||
IconCirclePlus,
|
||||
IconColorSwatch,
|
||||
|
||||
@ -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 = {
|
||||
|
||||
8
front/src/modules/ui/table/components/Table.tsx
Normal file
8
front/src/modules/ui/table/components/Table.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledTable = styled.table`
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
`;
|
||||
|
||||
export { StyledTable as Table };
|
||||
18
front/src/modules/ui/table/components/TableCell.tsx
Normal file
18
front/src/modules/ui/table/components/TableCell.tsx
Normal 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>
|
||||
);
|
||||
11
front/src/modules/ui/table/components/TableHeader.tsx
Normal file
11
front/src/modules/ui/table/components/TableHeader.tsx
Normal 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 };
|
||||
75
front/src/modules/ui/table/components/TableSection.tsx
Normal file
75
front/src/modules/ui/table/components/TableSection.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@ -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>
|
||||
),
|
||||
};
|
||||
Reference in New Issue
Block a user