Skip to content

Commit

Permalink
Implement a reusable table for ephemeral data edition (saleor#4830)
Browse files Browse the repository at this point in the history
* (wip) DashboardTable base implementation

* (wip) disable example table on refund page

* Colgroup & Col as Table children

* Rename to GridTable

* Add changeset

* Add unit test

* Fix thead definition in col & colgroup refs

* Update macaw-ui

* Move forwarded ref to table element

* Improve typing

* Wrap context into hook

* Revamp table design

* Disable zebra stripes as default

* Remove zebra entirely

* Remove headers & improve typing

* Spread props on inner table

* Fix typo in test description
  • Loading branch information
Droniu authored May 6, 2024
1 parent 16b47e2 commit e02050a
Show file tree
Hide file tree
Showing 14 changed files with 302 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/fair-brooms-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---

This change adds a reusable GridTable component for ephemeral data edition.
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@material-ui/styles": "^4.11.4",
"@reach/auto-id": "^0.16.0",
"@saleor/macaw-ui": "npm:@saleor/[email protected]",
"@saleor/macaw-ui-next": "npm:@saleor/[email protected].3",
"@saleor/macaw-ui-next": "npm:@saleor/[email protected].4",
"@saleor/sdk": "0.6.0",
"@sentry/react": "^7.83.0",
"@sentry/vite-plugin": "^2.15.0",
Expand Down
16 changes: 16 additions & 0 deletions src/components/GridTable/Body.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Box } from "@saleor/macaw-ui-next";
import React, { HTMLAttributes } from "react";

import { GridTableProps } from "./types";

type GridTableBodyElement = React.ElementRef<"tbody">;
type GridTableBodyProps = GridTableProps<HTMLAttributes<HTMLTableSectionElement>>;

export const GridTableBody = React.forwardRef<GridTableBodyElement, GridTableBodyProps>(
({ children, ...props }, forwardedRef) => (
<Box as="tbody" ref={forwardedRef} {...props}>
{children}
</Box>
),
);
GridTableBody.displayName = "GridTable.Body";
29 changes: 29 additions & 0 deletions src/components/GridTable/Cell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Box } from "@saleor/macaw-ui-next";
import React, { TdHTMLAttributes } from "react";

import { GridTableProps } from "./types";

type GridTableCellElement = React.ElementRef<"td">;
type GridTableCellProps = GridTableProps<TdHTMLAttributes<HTMLTableCellElement>>;

export const GridTableCell = React.forwardRef<GridTableCellElement, GridTableCellProps>(
({ children, ...props }, forwardedRef) => {
return (
<Box
as="td"
ref={forwardedRef}
height="100%"
padding={2}
borderTopStyle="solid"
borderBottomStyle="solid"
borderCollapse="collapse"
borderColor="default1"
borderWidth={1}
{...props}
>
{children}
</Box>
);
},
);
GridTableCell.displayName = "GridTable.Cell";
16 changes: 16 additions & 0 deletions src/components/GridTable/Col.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Box } from "@saleor/macaw-ui-next";
import React, { ColHTMLAttributes } from "react";

import { GridTableProps } from "./types";

type GridTableColElement = React.ElementRef<"col">;
type GridTableColProps = GridTableProps<ColHTMLAttributes<HTMLTableColElement>>;

export const GridTableCol = React.forwardRef<GridTableColElement, GridTableColProps>(
({ children, ...props }, forwardedRef) => (
<Box as="col" ref={forwardedRef} {...props}>
{children}
</Box>
),
);
GridTableCol.displayName = "GridTable.Col";
16 changes: 16 additions & 0 deletions src/components/GridTable/Colgroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Box } from "@saleor/macaw-ui-next";
import React, { ColgroupHTMLAttributes } from "react";

import { GridTableProps } from "./types";

type GridTableColgroupElement = React.ElementRef<"colgroup">;
type GridTableColgroupProps = GridTableProps<ColgroupHTMLAttributes<HTMLElement>>;

export const GridTableColgroup = React.forwardRef<GridTableColgroupElement, GridTableColgroupProps>(
({ children, ...props }, forwardedRef) => (
<Box as="colgroup" ref={forwardedRef} {...props}>
{children}
</Box>
),
);
GridTableColgroup.displayName = "GridTable.Colgroup";
37 changes: 37 additions & 0 deletions src/components/GridTable/GridTable.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { render, screen } from "@testing-library/react";
import React from "react";

import { GridTable } from "./index";

describe("GridTable", () => {
it("renders table with rows", () => {
// Arrange
const data = [
{ id: 1, name: "John", age: 30 },
{ id: 2, name: "Jane", age: 25 },
];

// Act
render(
<GridTable>
<GridTable.Body>
{data.map((item, index) => (
<GridTable.Row key={item.id} data-test-id={`row-${index}`}>
<GridTable.Cell data-test-id={`row-${index}-cell-0`}>{item.id}</GridTable.Cell>
<GridTable.Cell data-test-id={`row-${index}-cell-1`}>{item.name}</GridTable.Cell>
<GridTable.Cell data-test-id={`row-${index}-cell-2`}>{item.age}</GridTable.Cell>
</GridTable.Row>
))}
</GridTable.Body>
</GridTable>,
);

// Assert
expect(screen.getByTestId("row-0-cell-0").textContent).toBe("1");
expect(screen.getByTestId("row-0-cell-1").textContent).toBe("John");
expect(screen.getByTestId("row-0-cell-2").textContent).toBe("30");
expect(screen.getByTestId("row-1-cell-0").textContent).toBe("2");
expect(screen.getByTestId("row-1-cell-1").textContent).toBe("Jane");
expect(screen.getByTestId("row-1-cell-2").textContent).toBe("25");
});
});
35 changes: 35 additions & 0 deletions src/components/GridTable/Root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Box } from "@saleor/macaw-ui-next";
import React, { TableHTMLAttributes } from "react";

import { GridTableProps } from "./types";

type GridTableRootElement = React.ElementRef<"div">;
type GridTableRootProps = GridTableProps<TableHTMLAttributes<HTMLTableElement>>;

export const GridTableRoot = React.forwardRef<GridTableRootElement, GridTableRootProps>(
(props, forwardedRef) => {
const { children, ...rest } = props;

return (
<Box height={0}>
<Box
ref={forwardedRef}
as="table"
width="100%"
borderTopStyle="solid"
borderBottomStyle="solid"
borderCollapse="collapse"
borderColor="default1"
borderWidth={1}
style={{
borderSpacing: 0,
}}
{...rest}
>
{children}
</Box>
</Box>
);
},
);
GridTableRoot.displayName = "GridTable.Root";
18 changes: 18 additions & 0 deletions src/components/GridTable/Row.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Box } from "@saleor/macaw-ui-next";
import React, { HTMLAttributes } from "react";

import { GridTableProps } from "./types";

type GridTableRowElement = React.ElementRef<"tr">;
type GridTableRowProps = GridTableProps<HTMLAttributes<HTMLTableRowElement>>;

export const GridTableRow = React.forwardRef<GridTableRowElement, GridTableRowProps>(
({ children, ...props }, forwardedRef) => {
return (
<Box as="tr" ref={forwardedRef} {...props}>
{children}
</Box>
);
},
);
GridTableRow.displayName = "GridTable.Row";
14 changes: 14 additions & 0 deletions src/components/GridTable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { GridTableBody as Body } from "./Body";
import { GridTableCell as Cell } from "./Cell";
import { GridTableCol as Col } from "./Col";
import { GridTableColgroup as Colgroup } from "./Colgroup";
import { GridTableRoot } from "./Root";
import { GridTableRow as Row } from "./Row";

export const GridTable = Object.assign(GridTableRoot, {
Body,
Row,
Cell,
Col,
Colgroup,
});
5 changes: 5 additions & 0 deletions src/components/GridTable/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PropsWithBox } from "@saleor/macaw-ui-next";

type RemovedProps = "color" | "height" | "width";

export type GridTableProps<T> = PropsWithBox<Omit<T, RemovedProps>>;
99 changes: 99 additions & 0 deletions src/orders/components/OrderTransactionRefundPage/ExampleTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { GridTable } from "@dashboard/components/GridTable";
import { formatMoney } from "@dashboard/components/Money";
import { OrderDetailsGrantRefundFragment } from "@dashboard/graphql";
import useLocale from "@dashboard/hooks/useLocale";
import { Box, Button, Input, Text } from "@saleor/macaw-ui-next";
import React from "react";

interface ExampleTableProps {
order: OrderDetailsGrantRefundFragment | undefined;
}

/**
* This is example usage of GridTable component.
* TODO: Remove when implementing MERX-360.
*/
export const ExampleTable: React.FC<ExampleTableProps> = ({ order }) => {
const locale = useLocale();

if (!order?.lines[0]) {
return null;
}

const line = order.lines[0];

return (
<GridTable height="100%" paddingX={6}>
<GridTable.Colgroup>
<GridTable.Col __width="45%" />
<GridTable.Col __width="20%" />
<GridTable.Col __width="25%" />
<GridTable.Col __width="10%" />
</GridTable.Colgroup>
<GridTable.Body>
{Array(5)
.fill(null)
.map((_, ix) => (
<GridTable.Row key={line.id + "row"}>
<GridTable.Cell>
<Box
display="grid"
__gridTemplateColumns="minmax(0, 1fr) auto"
gap={10}
alignItems="center"
>
<Box minWidth={8}>
<img src={line.thumbnail?.url} />
</Box>
<Box overflow="hidden" minWidth={0}>
<Text size={2}>{line.productName}</Text>
<Box overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">
<Text size={2} color="default2" textOverflow="ellipsis" overflow="hidden">
{"500 ml / With Aloe Vera / White packaging / Mango - Passion fruit"}
</Text>
</Box>
</Box>
</Box>
</GridTable.Cell>
<GridTable.Cell>
<Box
display="flex"
flexDirection="column"
overflow="hidden"
flexShrink="1"
__minWidth="0px"
>
{`${ix === 3 ? 5 : 999} ⨉ `}
{formatMoney(line.unitPrice.gross, locale.locale)}
{ix === 3 && (
<Text size={2} whiteSpace="nowrap" color="default2">
2 already refunded
</Text>
)}
</Box>
</GridTable.Cell>
<GridTable.Cell>
<Box backgroundColor="default1" display="flex" gap={2}>
<Input
__minWidth="40px"
placeholder="0"
endAdornment={<Box whiteSpace="nowrap">of {ix === 3 ? 3 : 999}</Box>}
backgroundColor="default1"
size="small"
/>
<Button variant="secondary" size="medium">
All
</Button>
</Box>
</GridTable.Cell>
<GridTable.Cell>
<Button variant="secondary" whiteSpace="nowrap">
{ix % 2 === 0 ? "Add reason" : "Edit reason"}
</Button>
</GridTable.Cell>
</GridTable.Row>
))}
</GridTable.Body>
</GridTable>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ const OrderTransactionRefundPage: React.FC<OrderTransactionRefundPageProps> = ({
onMaxQtySet={onSetMaximumQty}
linesToRefund={linesToRefund}
/>
{/* <DashboardCard>
<ExampleTable order={order} />
</DashboardCard> */}
<DashboardCard marginBottom={5}>
<DashboardCard.Content>
<Text fontWeight="medium" as="p" marginTop={5}>
Expand Down

0 comments on commit e02050a

Please sign in to comment.