Skip to content

Commit

Permalink
feat: implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
kostasdano committed May 14, 2024
1 parent 3ebd563 commit b3b6d0a
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 39 deletions.
44 changes: 28 additions & 16 deletions src/components/Table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { flexRender } from '@tanstack/react-table';
import React, { useRef } from 'react';
import isEqual from 'react-fast-compare';

import type { TableProps } from '.';
import { TBody, TH, TD, THead, TR, TTitle } from './components';
import { TBody, TD, TH, THead, TR, TTitle, type TableProps } from '.';
import useTable from './hooks/useTable';
import { tableContainer, tableStyles } from './Table.style';

const Table = <TData,>({
type = 'read-only',
rowsConfig,
data,
columns,
rowSize = 'sm',
Expand All @@ -16,14 +17,11 @@ const Table = <TData,>({
hasStickyHeader = false,
sx,
}: TableProps<TData>) => {
const { columnVisibility, setColumnVisibility } = columnsConfig ?? {};

const hasColumnVisibilityConfig = Boolean(columnVisibility && setColumnVisibility);

/** If true, the scrollbar of tbody is visible */
const [hasScrollbar, setHasScrollbar] = React.useState(false);

const tBodyRef = useRef<HTMLTableSectionElement>();
const containerRef = useRef(null);

React.useEffect(() => {
if (tBodyRef?.current) {
Expand All @@ -32,27 +30,35 @@ const Table = <TData,>({
}, [tBodyRef.current]);

const table = useTable<TData>({
type,
data,
columns,
/** Column Visibility */
...(hasColumnVisibilityConfig && {
state: {
columnVisibility,
},
onColumnVisibilityChange: setColumnVisibility,
}),
sorting,
rowsConfig,
columnsConfig,
});

const hasTitle = Boolean(columnsConfig || rowsConfig);

return (
<div css={tableContainer()}>
{hasColumnVisibilityConfig && <TTitle columnsConfig={columnsConfig} columns={columns} />}
<div css={tableContainer()} ref={containerRef}>
{hasTitle && (
<TTitle
type={type}
columnsConfig={columnsConfig}
columns={columns}
rowsConfig={rowsConfig}
containerRef={containerRef}
rowsCount={table.getRowModel().rows.length}
/>
)}
<table css={tableStyles({ sx: sx?.table })}>
<THead hasStickyHeader={hasStickyHeader} hasScrollbar={hasScrollbar} sx={sx?.thead}>
{table.getHeaderGroups().map((headerGroup) => (
<TR key={headerGroup.id} sx={sx?.tr}>
{headerGroup.headers.map((header) => (
<TH
id={header.id}
key={header.id}
colSpan={header.colSpan}
rowSize={rowSize}
Expand All @@ -79,7 +85,13 @@ const Table = <TData,>({
<TR key={row.id} sx={sx?.tr}>
{row.getVisibleCells().map((cell) => {
return (
<TD key={cell.id} rowSize={rowSize} width={cell.column.getSize()} sx={sx?.td}>
<TD
columnId={cell.column.id}
key={cell.id}
rowSize={rowSize}
width={cell.column.getSize()}
sx={sx?.td}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TD>
);
Expand Down
12 changes: 11 additions & 1 deletion src/components/Table/components/TD/TD.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@ export const tdContainer =
({
rowSize,
width,
isCheckbox,
sx,
}: Pick<TableProps<any>, 'rowSize'> & { width?: number; isLastCell?: boolean; sx?: CSSObject }) =>
}: Pick<TableProps<any>, 'rowSize'> & {
isCheckbox?: boolean;
width?: number;
isLastCell?: boolean;
sx?: CSSObject;
}) =>
(theme: Theme): SerializedStyles => {
const getWidth = () => {
if (isCheckbox) {
return '52px';
}

if (width) {
return `${width}%`;
}
Expand Down
16 changes: 14 additions & 2 deletions src/components/Table/components/TD/TD.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,23 @@ type Props = {
width?: number;
/** Style overrides */
sx?: CSSObject;
/** Column Id */
columnId?: string;
};

const TD: React.FCC<Props> = ({ colSpan, rowSize = 'sm', width, sx, children, ...rest }) => {
const TD: React.FCC<Props> = ({
colSpan,
rowSize = 'sm',
width,
sx,
children,
columnId,
...rest
}) => {
const isCheckbox = columnId === 'checkbox_select';

return (
<td css={tdContainer({ rowSize, width, sx })} colSpan={colSpan} {...rest}>
<td css={tdContainer({ rowSize, width, isCheckbox, sx })} colSpan={colSpan} {...rest}>
<div css={tdContent()}>{children}</div>
</td>
);
Expand Down
4 changes: 3 additions & 1 deletion src/components/Table/components/TH/TH.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ import { generateStylesFromTokens } from 'components/Typography/utils';

export const thContainer =
({
isCheckbox,
rowSize,
width,
hasVisibleOptions,
sx,
}: Pick<TableProps<any>, 'rowSize'> & {
isCheckbox?: boolean;
width?: number;
hasVisibleOptions?: boolean;
sx?: CSSObject;
}) =>
(theme: Theme): SerializedStyles => {
return css`
width: ${width ? `${width}%` : '100%'};
width: ${isCheckbox ? '52px' : width ? `${width}%` : '100%'};
height: ${getMinHeight(rowSize)(theme)};
align-content: center;
text-align: left;
Expand Down
5 changes: 4 additions & 1 deletion src/components/Table/components/TH/TH.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Props = {
sx?: CSSObject;
};

const TH: React.FCC<Props & Pick<DivProps, 'onClick'>> = ({
const TH: React.FCC<Props & Pick<DivProps, 'onClick' | 'id'>> = ({
width,
rowSize = 'sm',
children,
Expand All @@ -39,9 +39,11 @@ const TH: React.FCC<Props & Pick<DivProps, 'onClick'>> = ({
colSortingState,
resetSorting,
sx,
id,
...rest
}) => {
const isSortable = Boolean(onSort);
const isCheckbox = id === 'checkbox_select';

const [hasVisibleOptions, setHasVisibleOptions] = React.useState(false);

Expand Down Expand Up @@ -80,6 +82,7 @@ const TH: React.FCC<Props & Pick<DivProps, 'onClick'>> = ({
return (
<th
css={thContainer({
isCheckbox,
rowSize,
width,
hasVisibleOptions: hasVisibleOptions || Boolean(colSortingState),
Expand Down
4 changes: 4 additions & 0 deletions src/components/Table/components/TTitle/TTitle.style.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { SerializedStyles, Theme } from '@emotion/react';
import { css } from '@emotion/react';

/** @TODO update styles with tokens */

export const tTitleContainer =
() =>
(theme: Theme): SerializedStyles => {
Expand All @@ -9,5 +11,7 @@ export const tTitleContainer =
border-bottom: 1px solid ${theme.tokens.colors.get('borderColor.decorative.default')};
display: flex;
justify-content: space-between;
height: 44px;
box-sizing: border-box;
`;
};
65 changes: 60 additions & 5 deletions src/components/Table/components/TTitle/TTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,72 @@
import React from 'react';
import React, { useMemo } from 'react';
import isEqual from 'react-fast-compare';

import ColumnChooser from './components/ColumnChooser';
import { tTitleContainer } from './TTitle.style';
import type { TableProps } from 'components/Table/types';
import Typography from 'components/Typography';

type Props = Pick<TableProps<any>, 'columnsConfig' | 'columns'>;
type Props = Pick<TableProps<any>, 'columnsConfig' | 'columns' | 'rowsConfig' | 'type'> & {
/** Element that that serves as the positioning boundary of the ColumnChooser Menu */
containerRef: React.MutableRefObject<any>;
/** Number of rows */
rowsCount?: number;
};

const TTitle: React.FCC<Props> = ({
type = 'read-only',
columns,
columnsConfig,
rowsConfig,
rowsCount,
containerRef,
}) => {
const { hasRowsCount, rowSelection, bulkActions, defaultAction } = rowsConfig ?? {};

const isSelectable = type === 'interactive' && rowSelection;

const itemsToDisplay = isSelectable
? Object.keys(rowSelection).length
: hasRowsCount
? rowsCount
: 0;

const hasTitle = isSelectable || hasRowsCount;
const hasVisibleActions = isSelectable && itemsToDisplay > 0;

const title = useMemo(
() => (
<div>
<Typography type={isSelectable ? 'active' : 'primary'} variant="label02">
{itemsToDisplay}
</Typography>
<Typography variant="label02"> items {isSelectable ? 'selected' : ''}</Typography>
</div>
),
[isSelectable, itemsToDisplay]
);

const TTitle: React.FCC<Props> = ({ columns, columnsConfig, children }) => {
return (
<div css={tTitleContainer()}>
<div>{children}</div>
<ColumnChooser columns={columns} columnsConfig={columnsConfig} />
<div css={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
{hasTitle && (
<>
{title}
{hasVisibleActions && bulkActions}
</>
)}
</div>

<div>
{hasVisibleActions && defaultAction}
{columnsConfig && (
<ColumnChooser
columns={columns}
columnsConfig={columnsConfig}
containerRef={containerRef}
/>
)}
</div>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import { MenuItemWrapper, MenuWrapper, popoverStyle } from 'components/Menu/Menu
import MenuItemDivider from 'components/Menu/MenuItemDivider';
import type { TableProps } from 'components/Table/types';

type Props = Pick<TableProps<any>, 'columns' | 'columnsConfig'>;
type Props = Pick<TableProps<any>, 'columns' | 'columnsConfig'> & {
/** Element that that serves as the positioning boundary of the ColumnChooser Menu */
containerRef: React.MutableRefObject<any>;
};

/** @TODO create a generic Popover component */

const ColumnChooser: React.FC<Props> = ({ columns, columnsConfig }) => {
const ColumnChooser: React.FC<Props> = ({ columns, columnsConfig, containerRef }) => {
const [isBtnOpen, setBtnOpen] = React.useState<boolean>(false);

const options = flattenColumns(columns);
Expand Down Expand Up @@ -64,7 +67,9 @@ const ColumnChooser: React.FC<Props> = ({ columns, columnsConfig }) => {
aria-controls={isBtnOpen ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={isBtnOpen ? 'true' : undefined}
iconLeftName="columnChooser"
/** @TODO add iconLeft functionality to compact Button */
// iconLeftName="columnChooser"
size="compact"
type="secondary"
>
Edit Columns
Expand All @@ -75,6 +80,9 @@ const ColumnChooser: React.FC<Props> = ({ columns, columnsConfig }) => {
isOpen={isBtnOpen}
onOpenChange={() => setBtnOpen((isOpen) => !isOpen)}
shouldCloseOnInteractOutside={() => true}
boundaryElement={containerRef.current}
/** @TODO adjust this when compact Button with iconLeft is implemented */
crossOffset={-31}
>
<MenuWrapper
aria-label="Menu"
Expand Down
Loading

0 comments on commit b3b6d0a

Please sign in to comment.