Skip to content

Commit

Permalink
MDS-738 Table & Checkbox Integration (#2434)
Browse files Browse the repository at this point in the history
* feat: a new table with expanded selectable rows and checkboxes has been created

* fix: table control structure

* fix: row and branch selection and highlighting

* fix: row and branch selection and highlighting

* fix: burst selection/deselection

* fix: burst select/deselect

* feat: the Expandable table with Checkboxes

* fix: rows are being selected via checkbox only

* fix: some code improvements in the examples

* fmt

* fix: code improvement by the remarks

* fix: code improvement after the remarks

* fix: another code correction

* fix: type of the variable has been removed

* fix: a tiny fix at the Table component

* fmt
  • Loading branch information
karl-kallavus authored Oct 18, 2023
1 parent 1d465d9 commit 6968938
Show file tree
Hide file tree
Showing 13 changed files with 17,178 additions and 8,222 deletions.
8 changes: 7 additions & 1 deletion next-docs/pages/components/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import CustomContent from '../../public/examples/table/CustomContent';
import DeepTable from '../../public/examples/table/DeepTable';
import Default from '../../public/examples/table/Default';
import Editable from '../../public/examples/table/Editable';
import ExpandableCheckboxes from '../../public/examples/table/ExpandableCheckboxes';
import ExpandedRows from '../../public/examples/table/ExpandedRows';
import ExpandedWithModals from '../../public/examples/table/ExpandedWithModals';
import LongData from '../../public/examples/table/LongData';
Expand Down Expand Up @@ -108,10 +109,15 @@ const PageTable = () => {
code={examples ? examples.DeepTable : 'Loading'}
/>
<Preview
title="Expanded Rows with calls the modal windows"
title="Expanded rows with calls the modal windows"
preview={<ExpandedWithModals />}
code={examples ? examples.ExpandedWithModals : 'Loading'}
/>
<Preview
title="Expanded selectable rows with checkboxes"
preview={<ExpandableCheckboxes />}
code={examples ? examples.ExpandableCheckboxes : 'Loading'}
/>
<Preview
title="Expanded rows with custom content"
preview={<CustomContent />}
Expand Down
317 changes: 317 additions & 0 deletions next-docs/public/examples/table/ExpandableCheckboxes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
import { Checkbox, Chip, Tooltip, mergeClassnames } from "@heathmont/moon-core-tw";
import { OtherFrame } from "@heathmont/moon-icons-tw";
import { Table } from "@heathmont/moon-table-tw";
import React, { useState } from "react";

interface HeaderProps {
rows: [];
rowsById: { [key: string]: boolean };
isAllRowsExpanded: boolean;
getToggleAllRowsExpandedProps: () => React.HTMLAttributes<HTMLSpanElement>;
}

const Example = () => {
/**
* The PREFIX is necessary if you use several different tables with checkboxes on the same page.
* Each table should have its own unique PREFIX to avoid assigning identical indexes to elements.
* When using only one table, the PREFIX can be omitted.
*/
const PREFIX = "any__unique__string__for__each__table";

const [selected, setSelected] = useState<{ [key: string]: boolean }>({});

const columnShift = (depth: number) => {
const shiftMap: { [key: number]: string } = [
'ps-0',
'ps-6',
'ps-12',
];

return shiftMap[depth];
}

const checkIfSelected = (id: string, canExpand: boolean, rowsById: { [key: string]: boolean }) => {
return canExpand
? Object.keys(rowsById)
.filter((rowId) => rowId.indexOf(id) === 0 && rowId !== id)
.every((rowId) => selected[rowId] === true)
: selected[id] === true;
}

const checkIfIndeterminate = (id: string, rowsById: { [key: string]: boolean }) => {
const matches = Object.keys(rowsById)
.filter((rowId) => rowId.indexOf(id) === 0 && rowId !== id);
return !matches.every((rowId) => selected[rowId] === true) && matches.some((rowId) => selected[rowId] === true);
}

const columnsInitial = [
{
'Header': 'Expand/Select',
'sticky': 'left',
columns: [
{
'id': 'any_unique_string_required',
Header: ({
rows,
rowsById,
getToggleAllRowsExpandedProps,
isAllRowsExpanded,
}: HeaderProps) => (
<div className="flex items-center gap-x-1">
<div className="flex items-center h-full">
<Checkbox
id={PREFIX && PREFIX.length ? `${PREFIX}_root` : 'root'}
checked={(Object.keys(rowsById).length === Object.keys(selected).length)}
indeterminate={!!Object.keys(selected).length && Object.keys(selected).length < Object.keys(rowsById).length}
onClick={(e) => { e.stopPropagation() }}
/>
</div>
{<span {...getToggleAllRowsExpandedProps()}>
{isAllRowsExpanded ? '👇' : '👉'}
</span>}
</div>
),
Cell: ({ row, rowsById }: any) => (
<div className={mergeClassnames(
"flex items-center gap-x-1",
columnShift(row.depth),
)}
onClick={(e) => {
if ((e.target as unknown as HTMLElement).tagName === 'SPAN') e.stopPropagation();
}}
>
<div className="flex items-center h-full">
<Checkbox
id={PREFIX && PREFIX.length ? `${PREFIX}_${row.id}` : row.id}
checked={checkIfSelected(row.id, row.canExpand, rowsById)}
indeterminate={checkIfIndeterminate(row.id, rowsById)}
onClick={(e) => e.stopPropagation()}
/>
</div>
{row.canExpand ? (
<span {...row.getToggleRowExpandedProps()}>
{row.isExpanded ? '👇' : '👉'}
</span>
) : null}
</div>
)
},
],
},
{
Header: 'Name',
sticky: 'left',
columns: [
{
Header: 'First Name',
accessor: 'firstName',
},
],
},
{
Header: 'Info',
columns: [
{
Header: 'Age',
accessor: 'age',
width: 50,
},
{
Header: 'Visits',
accessor: 'visits',
},
{
Header: 'Activity',
accessor: 'activity',
},
{
Header: 'Status',
width: 60,
accessor: 'status',
},
{
Header: 'Profile Progress',
Footer: 'Profile Progress',
accessor: 'progress',
},
],
},
{
Header: 'Actions',
sticky: 'right',
columns: [
{
Header: 'Actions',
accessor: 'actions',
Footer: '',
},
],
},
];

const tooltip = () => (
<Tooltip>
<Tooltip.Trigger className="max-h-6">
<Chip
variant="ghost"
iconOnly={<OtherFrame className="text-moon-24 max-h-6" />}
/>
</Tooltip.Trigger>
<Tooltip.Content position="top-start">
Round details
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip>
);

const makeData = () => [
{
firstName: 'Test lvl1',
age: <span>36</span>,
visits: <span>50</span>,
progress: <span>20</span>,
status: 19,
activity: 54,
actions: tooltip(),
subRows: [
{
firstName: 'Sub test lvl2',
age: <span>96</span>,
visits: <span>8</span>,
progress: <span>2</span>,
status: 97,
activity: 23,
actions: tooltip(),
subRows: [
{
firstName: 'Sub test lvl3',
age: <span>63</span>,
visits: <span>82</span>,
progress: <span>59</span>,
status: 52,
activity: 46,
actions: tooltip(),
},
{
firstName: 'Sub test lvl3',
age: <span>64</span>,
visits: <span>35</span>,
progress: <span>78</span>,
status: 65,
activity: 5,
actions: tooltip(),
},
{
firstName: 'Sub test lvl3',
age: <span>12</span>,
visits: <span>4</span>,
progress: <span>44</span>,
status: 98,
activity: 5,
actions: tooltip(),
},
],
},
{
firstName: 'Sub test lvl2',
age: <span>74</span>,
visits: <span>5</span>,
progress: <span>1</span>,
status: 86,
activity: 2,
actions: tooltip(),
subRows: [
{
firstName: 'Sub test lvl3',
age: <span>89</span>,
visits: <span>98</span>,
progress: <span>54</span>,
status: 24,
activity: 43,
actions: tooltip(),
},
{
firstName: 'Sub test lvl3',
age: <span>52</span>,
visits: <span>25</span>,
progress: <span>25</span>,
status: 97,
activity: 35,
actions: tooltip(),
},
{
firstName: 'Sub test lvl3',
age: <span>55</span>,
visits: <span>54</span>,
progress: <span>24</span>,
status: 56,
activity: 33,
actions: tooltip(),
},
],
},
{
firstName: 'Sub test lvl2',
age: <span>53</span>,
visits: <span>63</span>,
progress: <span>24</span>,
status: 48,
activity: 3,
actions: tooltip(),
subRows: [
{
firstName: 'Sub test lvl3',
age: <span>4</span>,
visits: <span>653</span>,
progress: <span>36</span>,
status: 44,
activity: 43,
actions: tooltip(),
},
{
firstName: 'Sub test lvl3',
age: <span>49</span>,
visits: <span>45</span>,
progress: <span>454</span>,
status: 35,
activity: 4,
actions: tooltip(),
},
],
},
],
}
];

const columns = React.useMemo(() => columnsInitial, [selected]);
const data = React.useMemo(() => makeData(), []);

const defaultColumn = React.useMemo(
() => ({
minWidth: 100,
width: 150,
maxWidth: 400,
}), []);

return (
<Table
columns={columns}
defaultColumn={defaultColumn}
data={data}
width={800}
height={400}
selectable={true}
useCheckbox={true}
expandedByDefault={true}
getOnRowSelect={() => (rows) => {
setSelected(rows.reduce((acc: { [key: string]: boolean }, item) => {
acc[item.id] = true;
return acc;
}, {})
);
}}
/>
);
}

export default Example;
6 changes: 3 additions & 3 deletions next-docs/public/examples/table/ExpandedWithModals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { ReactNode, useState } from 'react';
import { Table } from '@heathmont/moon-table-tw';
import { Button, Chip, Modal, Tooltip } from '@heathmont/moon-core-tw';
import { OtherFrame } from '@heathmont/moon-icons-tw';
import { number } from 'zod';

interface HeaderProps {
isAllRowsExpanded: boolean;
Expand Down Expand Up @@ -107,7 +106,8 @@ const Example = () => {
onClick={() => {
setTitle(modal.title);
setPanel(modal.panel);
openModal(); }}
openModal();
}}
/>
</Tooltip.Trigger>
<Tooltip.Content position="top-start">
Expand All @@ -128,7 +128,7 @@ const Example = () => {
<li>Activity: {Math.floor(index * 100)}</li>
<li>Actions: {Math.floor(index * 100)}</li>
</ul>
)
)
};
}

Expand Down
2 changes: 0 additions & 2 deletions next-docs/public/examples/table/LongData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ const Example = () => {
'Header': 'Currency',
'accessor': 'currency',
'Footer': '',
/*'maxWidth': Number.MAX_SAFE_INTEGER,
'width': 80 */
},
],
},
Expand Down
Loading

0 comments on commit 6968938

Please sign in to comment.