Skip to content

Commit

Permalink
Merge pull request #68 from maykinmedia/feature/react-nodes-tabular-l…
Browse files Browse the repository at this point in the history
…ayout

Use react nodes in tabular layout
  • Loading branch information
SilviaAmAm authored Jun 3, 2024
2 parents ceb9936 + d4897e6 commit 5f8d71c
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 32 deletions.
60 changes: 53 additions & 7 deletions src/components/data/attributetable/attributetable.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { Meta, StoryObj } from "@storybook/react";
import * as React from "react";

import { ButtonLink } from "../../button";
import { Outline } from "../../icon";
import { AttributeTable } from "./attributetable";

const meta: Meta<typeof AttributeTable> = {
Expand All @@ -10,17 +13,60 @@ const meta: Meta<typeof AttributeTable> = {
export default meta;
type Story = StoryObj<typeof meta>;

export const LabeledAttributeTableComponent: Story = {
args: {
labeledObject: {
url: { label: "Url", value: "https://www.example.com" },
omschrijving: { label: "Omschrijving", value: "Afvalpas vervangen" },
zaaktype: { label: "Zaaktype", value: "https://www.example.com" },
versie: { label: "Versie", value: 2 },
opmerkingen: { label: "Opmerkingen", value: null },
actief: { label: "Actief", value: false },
toekomstig: { label: "Toekomstig", value: false },
concept: { label: "Concept", value: true },
},
},
};

export const LabeledAttributeTableComponentWithNodes: Story = {
args: {
labeledObject: {
button: {
label: "A button!",
value: (
<ButtonLink
href="https://www.example.com"
target="_blank"
variant="transparent"
>
<Outline.AcademicCapIcon />
Click me!
</ButtonLink>
),
},
labelWithIcon: {
label: (
<div>
A label with icon <Outline.AcademicCapIcon />
</div>
),
value: "Some value",
},
},
},
};

export const AttributeTableComponent: Story = {
args: {
object: {
url: "https://www.example.com",
Omschrijving: "Afvalpas vervangen",
Zaaktype: "https://www.example.com",
Versie: 2,
Opmerkingen: null,
Actief: false,
Toekomstig: false,
Concept: true,
omschrijving: "Afvalpas vervangen",
zaaktype: "https://www.example.com",
versie: 2,
opmerkingen: null,
actief: false,
toekomstig: false,
concept: true,
},
},
};
59 changes: 41 additions & 18 deletions src/components/data/attributetable/attributetable.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,65 @@
import React from "react";

import { AttributeData } from "../../../lib";
import { field2Title } from "../../../lib/format/string";
import {
Attribute,
AttributeData,
LabeledAttributeData,
field2Title,
isPrimitive,
} from "../../../lib";
import { Value } from "../value";
import "./attributetable.scss";

export type AttributeTableProps = {
object: AttributeData;
fields?: Array<keyof AttributeData>;
object?: AttributeData;
labeledObject?: LabeledAttributeData;
fields?: Array<keyof LabeledAttributeData | keyof AttributeData>;
};

export type AttributeTableRowProps = {
object: AttributeData;
field: keyof AttributeData;
object?: AttributeData;
labeledObject?: LabeledAttributeData;
field: keyof LabeledAttributeData | keyof AttributeData;
};

export const AttributeTable: React.FC<AttributeTableProps> = ({
object = {},
fields = Object.keys(object),
labeledObject = {},
fields = Object.keys(object).concat(Object.keys(labeledObject)),
...props
}) => (
<div className="mykn-attributetable" {...props}>
{fields.map((field) => (
<AttributeTableRow key={field} field={field} object={object} />
<AttributeTableRow
key={field}
field={field}
object={object}
labeledObject={labeledObject}
/>
))}
</div>
);

export const AttributeTableRow: React.FC<AttributeTableRowProps> = ({
object,
object = {},
labeledObject = {},
field,
}) => (
<div className="mykn-attributetable__row">
<div className="mykn-attributetable__cell mykn-attributetable__key">
{field2Title(field)}
</div>
<div className="mykn-attributetable__cell">
<Value value={object[field]} />
}) => {
const fieldInObject = Object.keys(object).includes(field);
let value = fieldInObject ? object[field] : labeledObject[field].value;

if (isPrimitive(value) || value === null) {
value = <Value value={value as Attribute} />;
}

const label = fieldInObject ? field2Title(field) : labeledObject[field].label;

return (
<div className="mykn-attributetable__row">
<div className="mykn-attributetable__cell mykn-attributetable__key">
{label}
</div>
<div className="mykn-attributetable__cell">{value}</div>
</div>
</div>
);
);
};
48 changes: 46 additions & 2 deletions src/components/data/datagrid/datagrid.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,15 @@ export const JSONPlaceholderExample: Story = {
]}
selected={
// SelectableRows story
args.selectable &&
objectList.length > 0 && [objectList[1], objectList[3], objectList[4]]
args.selected ||
(args.selectable &&
objectList.length > 0 && [
objectList[1],
objectList[3],
objectList[4],
])
}
equalityChecker={args.equalityChecker}
onPageChange={setPage}
onPageSizeChange={setPageSize}
onSort={(field) => setSort(field)}
Expand Down Expand Up @@ -275,6 +281,44 @@ export const SelectableRows: Story = {
},
};

export const SelectableRowsWithCustomMatchFunction: Story = {
...JSONPlaceholderExample,
args: {
...JSONPlaceholderExample.args,
fields: ["userId", "id", "title"],
selectable: true,
selectionActions: [
{
children: "Aanmaken",
name: "create",
onClick: ({ detail }) => {
alert(`${detail.length} items selected.`);
},
},
],
selected: [
{
userId: 1,
id: 1,
title:
"sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
body: "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto",
},
{
userId: 1,
id: 5,
title: "nesciunt quas odio",
body: "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque",
},
],
equalityChecker: (item1, item2) => item1.id === item2.id,
},
argTypes: {
onSelect: { action: "onSelect" },
onSelectionChange: { action: "onSelectionChange" },
},
};

export const EditableRows: Story = {
...JSONPlaceholderExample,
args: {
Expand Down
24 changes: 19 additions & 5 deletions src/components/data/datagrid/datagrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ export type DataGridProps = {
/** References to the selected items in `objectList`, setting this preselects the items. */
selected?: AttributeData[];

/** Can be used to specify how to compare the selected items and the items in the data grid */
equalityChecker?: (item1: AttributeData, item2: AttributeData) => boolean;

/** Renders buttons allowing to perform actions on selection, `onClick` is called with selection array. */
selectionActions?: ButtonProps[];

Expand Down Expand Up @@ -198,6 +201,7 @@ export const DataGrid: React.FC<DataGridProps> = ({
selectable = false,
fieldsSelectable = false,
selected,
equalityChecker = (item1, item2) => item1 === item2,
selectionActions = [],
sort,
title = "",
Expand Down Expand Up @@ -302,11 +306,12 @@ export const DataGrid: React.FC<DataGridProps> = ({
const handleSelect = (attributeData: AttributeData) => {
const currentlySelected = selectedState || [];

const isAttributeDataCurrentlySelected =
currentlySelected.includes(attributeData);
const isAttributeDataCurrentlySelected = currentlySelected.find((element) =>
equalityChecker(element, attributeData),
);

const newSelectedState = isAttributeDataCurrentlySelected
? [...currentlySelected].filter((a) => a !== attributeData)
? [...currentlySelected].filter((a) => !equalityChecker(a, attributeData))
: [...currentlySelected, attributeData];

setSelectedState(newSelectedState);
Expand Down Expand Up @@ -431,6 +436,7 @@ export const DataGrid: React.FC<DataGridProps> = ({
setEditingState={setEditingState}
selectable={selectable}
selectedRows={selectedState || []}
equalityChecker={equalityChecker}
sortDirection={sortDirection}
sortField={sortField}
urlFields={urlFields}
Expand Down Expand Up @@ -765,6 +771,7 @@ export type DataGridBodyProps = {
sortDirection: "ASC" | "DESC" | undefined;
sortField: string | undefined;
urlFields: string[];
equalityChecker?: (item1: AttributeData, item2: AttributeData) => boolean;
};

/**
Expand Down Expand Up @@ -792,6 +799,7 @@ export const DataGridBody: React.FC<DataGridBodyProps> = ({
renderableRows,
selectable,
selectedRows,
equalityChecker = (item1, item2) => item1 === item2,
setEditingState,
sortDirection,
sortField,
Expand All @@ -802,7 +810,9 @@ export const DataGridBody: React.FC<DataGridBodyProps> = ({
<tr
key={`${dataGridId}-row-${index}`}
className={clsx("mykn-datagrid__row", {
"mykn-datagrid__row--selected": selectedRows?.includes(rowData),
"mykn-datagrid__row--selected": !!selectedRows.find((element) =>
equalityChecker(element, rowData),
),
})}
>
{selectable && (
Expand All @@ -814,7 +824,11 @@ export const DataGridBody: React.FC<DataGridBodyProps> = ({
>
<DataGridSelectionCheckbox
amountSelected={amountSelected}
checked={selectedRows?.includes(rowData) || false}
checked={
!!selectedRows.find((element) =>
equalityChecker(element, rowData),
) || false
}
count={count}
handleSelect={handleSelect}
labelSelect={labelSelect}
Expand Down
10 changes: 10 additions & 0 deletions src/lib/data/attributedata.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ReactNode } from "react";

import { ChoiceFieldProps } from "../../components";

/**
Expand All @@ -15,6 +17,14 @@ export const DEFAULT_URL_FIELDS = [
/** An object with key/value pairs describing various attributes to be presented. */
export type AttributeData<T = Attribute> = Record<string, T>;

/** An attribute with its corresponding label */
export type LabeledAttribute = {
label: string | ReactNode;
value: Attribute | ReactNode;
};

export type LabeledAttributeData<T = LabeledAttribute> = Record<string, T>;

/** A value in `AttributeData`. */
export type Attribute = boolean | number | string | null;

Expand Down

0 comments on commit 5f8d71c

Please sign in to comment.