Skip to content

Commit

Permalink
Merge pull request #39 from qoretechnologies/feature/table
Browse files Browse the repository at this point in the history
Firs <Table /> version implemented.
  • Loading branch information
Foxhoundn authored Feb 24, 2021
2 parents cbd1f6e + 41dc805 commit 2abd1e9
Show file tree
Hide file tree
Showing 16 changed files with 2,880 additions and 67 deletions.
122 changes: 122 additions & 0 deletions __tests__/table.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { ReqoreLayoutContent, ReqoreTable, ReqoreUIProvider } from '../src';
import { IReqoreTableColumn, IReqoreTableSort } from '../src/components/Table';
import tableData from '../src/mock/tableData';

test('Renders basic <Table /> properly', () => {
render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreTable {...tableData} />
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-table-header-cell').length).toBe(7);
expect(document.querySelectorAll('.reqore-table-row').length).toBe(10);
});

test('Renders <Table /> with grouped columns properly', () => {
const data = {
...tableData,
columns: [
{
dataId: 'id',
header: 'ID',
width: 50,
align: 'center',
},
{
header: 'Name',
dataId: 'name',
grow: 3,
columns: [
{ dataId: 'firstName', header: 'First Name', width: 150, grow: 2 },
{ dataId: 'lastName', header: 'Last Name', width: 150, grow: 1 },
],
},
{ dataId: 'address', header: 'Address', width: 300, grow: 2 },
{
dataId: 'age',
header: 'Really long age header',
width: 50,
align: 'center',
},
{ dataId: 'occupation', header: 'Ocuppation', width: 200 },
{ dataId: 'group', header: 'Group', width: 150 },
] as IReqoreTableColumn[],
};

render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreTable {...data} />
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.reqore-table-column-group').length).toBe(
1
);
expect(document.querySelectorAll('.reqore-table-header-cell').length).toBe(7);
});

test('Sorting on <Table /> works properly', () => {
const data = {
...tableData,
columns: [
{
dataId: 'id',
header: 'ID',
width: 50,
align: 'center',
sortable: true,
},
{ dataId: 'firstName', header: 'First Name', width: 150 },
{ dataId: 'lastName', header: 'Last Name', width: 150 },
{ dataId: 'address', header: 'Address', width: 300, grow: 2 },
{
dataId: 'age',
header: 'Really long age header',
width: 50,
align: 'center',
sortable: true,
},
{ dataId: 'occupation', header: 'Ocuppation', width: 200 },
{ dataId: 'group', header: 'Group', width: 150 },
] as IReqoreTableColumn[],
sort: {
by: 'id',
direction: 'desc',
} as IReqoreTableSort,
};

const fn = jest.fn();

render(
<ReqoreUIProvider>
<ReqoreLayoutContent>
<ReqoreTable {...data} onSortChange={fn} />
</ReqoreLayoutContent>
</ReqoreUIProvider>
);

const firstRow = document.querySelector('.reqore-table-row');
const idCell = firstRow.querySelector('.reqore-table-cell');

expect(idCell.textContent).toBe('99');

fireEvent.click(document.querySelectorAll('.reqore-table-header-cell')[4]);

const ageCell = firstRow.querySelectorAll('.reqore-table-cell')[4];

expect(fn).toHaveBeenCalledWith({ by: 'age', direction: 'desc' });
expect(ageCell.textContent).toBe('99');

fireEvent.click(document.querySelectorAll('.reqore-table-header-cell')[4]);

expect(fn).toHaveBeenLastCalledWith({ by: 'age', direction: 'asc' });
expect(ageCell.textContent).toBe('0');
expect(idCell.textContent).toBe('99');
});
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@qoretechnologies/reqore",
"version": "0.3.0",
"version": "0.4.0",
"description": "ReQore is a UI library of components for Qorus connected apps",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -71,7 +71,9 @@
"react-popper": "^2.2.4",
"react-scrollbar": "^0.5.6",
"react-use": "^15.3.4",
"react-window": "^1.8.6",
"shortid": "^2.2.16",
"styled-components": "^5.2.1"
"styled-components": "^5.2.1",
"thenby": "^1.3.4"
}
}
1 change: 1 addition & 0 deletions src/components/Content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const StyledReqoreContent = styled.div<IReqoreContentStyle>`
box-shadow: 0 0 4px 1px ${darken(0.1, theme.main)};
overflow: auto;
margin: 0 15px 15px 15px;
align-items: flex-start;
`}
`;

Expand Down
1 change: 1 addition & 0 deletions src/components/InternalPopover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const StyledPopoverWrapper = styled.div<{ theme: IReqoreTheme }>`
const defaultColor: string = theme.popover?.main || theme.main;
return css`
z-index: 999999;
background-color: ${defaultColor};
color: ${getReadableColor(defaultColor)};
border-radius: 3.5px;
Expand Down
47 changes: 47 additions & 0 deletions src/components/Table/body.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { Dispatch, SetStateAction, useEffect, useRef } from 'react';
import { useScroll } from 'react-use';
import { FixedSizeList as List } from 'react-window';
import { IReqoreTableColumn } from '.';
import ReqoreTableRow from './row';

export interface IReqoreTableSectionBodyProps {
hover?: boolean;
striped?: boolean;
bordered?: boolean;
data?: any[];
columns?: IReqoreTableColumn[];
setLeftScroll: Dispatch<SetStateAction<number>>;
height: number;
}

const ReqoreTableBody = ({
data,
columns,
setLeftScroll,
height,
}: IReqoreTableSectionBodyProps) => {
const ref = useRef(null);
const { x }: { x: number } = useScroll(ref);

useEffect(() => {
setLeftScroll(x);
}, [x]);

return (
<List
outerRef={ref}
itemCount={data.length}
height={height}
className='reqore-table-body'
itemSize={40}
itemData={{
columns,
data,
}}
>
{ReqoreTableRow}
</List>
);
};

export default ReqoreTableBody;
55 changes: 55 additions & 0 deletions src/components/Table/cell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { useState } from 'react';
import styled, { css } from 'styled-components';
import { IReqoreTheme } from '../../constants/theme';
import ReqoreThemeProvider from '../../containers/ThemeProvider';
import usePopover from '../../hooks/usePopover';

export interface IReqoreTableCellProps
extends React.HTMLAttributes<HTMLTableCellElement> {
children: any;
colspan?: number;
tooltip?: string;
width?: number;
}

export interface IReqoreTableCellStyle {
theme: IReqoreTheme;
width?: number;
}

const StyledTableCell = styled.td<IReqoreTableCellStyle>`
${({ width }) => css`
width: ${width ? `${width}px` : 'auto'};
padding: 5px 10px;
height: 40px;
`}
`;

const ReqoreTableCell = ({
colspan,
className,
children,
width,
tooltip,
...rest
}: IReqoreTableCellProps) => {
const [ref, setRef] = useState(null);

usePopover(ref, tooltip, undefined, undefined, !!tooltip);

return (
<ReqoreThemeProvider>
<StyledTableCell
{...rest}
width={width}
ref={setRef}
colSpan={colspan}
className={`${className || ''} reqore-table-cell`}
>
{children}
</StyledTableCell>
</ReqoreThemeProvider>
);
};

export default ReqoreTableCell;
125 changes: 125 additions & 0 deletions src/components/Table/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/* @flow */
import React from 'react';
import styled, { css } from 'styled-components';
import { IReqoreTableColumn, IReqoreTableSort } from '.';
import { IReqoreTheme } from '../../constants/theme';
import { changeLightness } from '../../helpers/colors';
import ReqoreTableHeaderCell, { StyledTableHeader } from './headerCell';
import { alignToFlex } from './row';

export interface IReqoreTableSectionProps {
columns: IReqoreTableColumn[];
leftScroll: number;
onSortChange?: (sort: string) => void;
sortData: IReqoreTableSort;
}

const StyledTableHeaderWrapper = styled.div<{ leftScroll?: number }>`
${({ leftScroll }) => css`
display: flex;
flex-flow: column;
width: calc(100% - 15px);
transform: translate3d(${-leftScroll}px, 0, 0);
`}
`;

export interface IReqoreTableHeaderStyle {
width?: number;
grow?: number;
theme: IReqoreTheme;
align?: 'center' | 'left' | 'right';
}

const StyledColumnGroupHeader = styled.div<IReqoreTableHeaderStyle>`
${({ align }) => css`
justify-content: ${align ? alignToFlex[align] : 'flex-start'};
`}
`;

const StyledColumnGroup = styled.div<IReqoreTableHeaderStyle>`
display: flex;
flex-flow: column;
width: ${({ width }) => width}px;
${({ grow }) =>
grow &&
css`
flex-grow: ${grow};
`}
`;

const StyledColumnGroupHeaders = styled.div`
display: flex;
`;

const StyledTableHeaderRow = styled.div<{ theme: IReqoreTheme }>`
display: flex;
flex: 1;
${StyledTableHeader}, ${StyledColumnGroupHeader} {
${({ theme }: IReqoreTableHeaderStyle) => css`
border-bottom: 1px solid ${changeLightness(theme.main, 0.07)};
font-size: 13px;
font-weight: 500;
padding: 10px;
display: flex;
flex-shrink: 0;
align-items: center;
`}
}
`;

const ReqoreTableHeader = ({
columns,
leftScroll,
onSortChange,
sortData,
}: IReqoreTableSectionProps) => {
const renderColumns = (columns: IReqoreTableColumn[]) =>
columns.map(
({ grow, header, props = {}, columns: cols, align, ...rest }, index) =>
cols ? (
<StyledColumnGroup
width={cols.reduce((wid, col) => wid + (col.width || 80), 0)}
grow={grow}
key={index}
className='reqore-table-column-group'
>
<StyledColumnGroupHeader
align={align}
{...props}
className='reqore-table-column-group-header'
>
{header}
</StyledColumnGroupHeader>
<StyledColumnGroupHeaders className='reqore-table-headers'>
{renderColumns(cols)}
</StyledColumnGroupHeaders>
</StyledColumnGroup>
) : (
<ReqoreTableHeaderCell
{...props}
{...rest}
key={index}
sortData={sortData}
grow={grow}
align={align}
header={header}
onSortChange={onSortChange}
/>
)
);

return (
<StyledTableHeaderWrapper
className='reqore-table-header-wrapper'
leftScroll={leftScroll}
>
<StyledTableHeaderRow>{renderColumns(columns)}</StyledTableHeaderRow>
</StyledTableHeaderWrapper>
);
};

export default ReqoreTableHeader;
Loading

0 comments on commit 2abd1e9

Please sign in to comment.