Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/improve types #157

Merged
merged 6 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions src/components/data/attributegrid/attributegrid.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,37 @@
import React from "react";

import {
AttributeData,
FieldSet,
attributeDataByFieldsets,
slugify,
} from "../../../lib";
import { FieldSet, dataByFieldsets, slugify } from "../../../lib";
import { Column, Grid } from "../../layout";
import { Toolbar } from "../../toolbar";
import { Body, H2, Hr } from "../../typography";
import { AttributeList, AttributeListProps } from "../attributelist";
import "./attributegrid.scss";

export type AttributeGridProps = {
object: AttributeData;
fieldsets: FieldSet[];
export type AttributeGridProps<T extends object = object> = {
object: T;
fieldsets: FieldSet<T>[];
generateTitleIds?: boolean;
title?: React.ReactNode;
};

/**
* AttributeGrid component, renders multiple `AttributeList`s in a `Grid` component based on `fieldsets`.
* AttributeGrid Component
*
* Uses multiple `AttributeList`s to render lists of data within a `Grid`.
*
* @typeParam T - The shape of a single data item.
*/
export const AttributeGrid: React.FC<AttributeGridProps> = ({
export const AttributeGrid = <T extends object = object>({
object,
fieldsets,
generateTitleIds = false,
title,
...props
}) => {
}: AttributeGridProps<T>) => {
const objectList =
object && fieldsets?.length
? attributeDataByFieldsets(object, fieldsets)
: [];
object && fieldsets?.length ? dataByFieldsets(object, fieldsets) : [];

// The grid datastructure:
// The grid data structure:
//
// Built an Array of Array of tuples, each Array (1) represents a row with a max span of 12, the total span is defined
// by the sum of the first key of each tuple in Array (2).
Expand Down
48 changes: 25 additions & 23 deletions src/components/data/attributelist/attributelist.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,64 @@
import React from "react";

import { AttributeData } from "../../../lib/data/attributedata";
import { field2Title } from "../../../lib/format/string";
import { Field, string2Title } from "../../../lib";
import { H3 } from "../../typography";
import { Value } from "../value";
import "./attributelist.scss";

export type AttributeListProps = React.ComponentPropsWithoutRef<"div"> & {
/** The object to show object attributes of. */
object: AttributeData;
export type AttributeListProps<T extends object = object> =
React.ComponentPropsWithoutRef<"div"> & {
/** The object to show object attributes of. */
object: T;

/** The fields in object to show. */
fields?: Array<keyof AttributeData>;
/** The fields in object to show. */
fields?: Array<keyof T>;

/** A title for the attribute list. */
title?: string;
/** A title for the attribute list. */
title?: string;

/** An optional id for the title. */
titleId?: string;
};
/** An optional id for the title. */
titleId?: string;
};

/**
* AttributeList component, shows multiple `fields` in `object`.
* TODO: tooltip
*/
export const AttributeList: React.FC<AttributeListProps> = ({
export const AttributeList = <T extends object = object>({
title = "",
titleId,
object = {},
fields = Object.keys(object),
object = {} as T,
fields = Object.keys(object) as Field<T>[],
...props
}) => (
}: AttributeListProps<T>) => (
<div className="mykn-attributelist" {...props}>
{title && <H3 id={titleId}>{title}</H3>}

<dl className="mykn-attributelist__list">
{fields.map((f) => (
<AttributePair key={f} object={object} field={f} />
<AttributePair<T> key={f.toString()} object={object} field={f} />
))}
</dl>
</div>
);

export type AttributePairProps = {
object: AttributeData;
field: keyof AttributeData;
export type AttributePairProps<T extends object = object> = {
object: T;
field: keyof T;
};

/**
* A single attribute in an AttributeList
*/
export const AttributePair: React.FC<AttributePairProps> = ({
export const AttributePair = <T extends object = object>({
object,
field,
}) => {
}: AttributePairProps<T>) => {
return (
<>
<dt className="mykn-attributelist__key">{field2Title(field)}</dt>
<dt className="mykn-attributelist__key">
{string2Title(field as string)}
</dt>
<dd className="mykn-attributelist__value">
<Value value={object[field]} />
</dd>
Expand Down
61 changes: 37 additions & 24 deletions src/components/data/attributetable/attributetable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import clsx from "clsx";
import React, { useId, useState } from "react";

import {
AttributeData,
Field,
LabeledAttributeData,
TypedField,
field2Title,
string2Title,
typedFieldByFields,
useIntl,
} from "../../../lib";
Expand All @@ -15,29 +13,38 @@ import { Form, FormControl, FormProps } from "../../form";
import { Value } from "../value";
import "./attributetable.scss";

export type AttributeTableProps = {
object?: AttributeData;
labeledObject?: LabeledAttributeData;
export type AttributeTableProps<T extends object = object> = {
object?: T;
// TODO: Deprecate?
labeledObject?: Record<string, { label: string; value: unknown }>;
editable?: boolean;
fields?: Field[] | TypedField[];
fields?: Field<T>[] | TypedField<T>[];
formProps?: FormProps;
labelCancel?: string;
labelEdit?: string;
valign?: "middle" | "start";
compact?: boolean;
};
export const AttributeTable: React.FC<AttributeTableProps> = ({
object = {},

/**
* AttributeTable Component
*
* Shows key/value pairs, optionally grouped by `title`.
*
* @typeParam T - The shape of a single item.
*/
export const AttributeTable = <T extends object = object>({
object = {} as T,
labeledObject = {},
editable = false,
fields = Object.keys(object).concat(Object.keys(labeledObject)),
fields = Object.keys(object).concat(Object.keys(labeledObject)) as Field<T>[],
formProps,
labelCancel,
labelEdit,
valign = "middle",
compact = false,
...props
}) => {
}: AttributeTableProps<T>) => {
const intl = useIntl();
const [isFormOpenState, setIsFormOpenState] = useState(false);
const typedFields = typedFieldByFields(fields, [object]);
Expand Down Expand Up @@ -79,8 +86,8 @@ export const AttributeTable: React.FC<AttributeTableProps> = ({

const renderRows = () =>
typedFields.map((field) => (
<AttributeTableRow
key={field.name}
<AttributeTableRow<T>
key={field.name.toString()}
editable={editable}
field={field}
isFormOpen={isFormOpenState}
Expand All @@ -106,33 +113,39 @@ export const AttributeTable: React.FC<AttributeTableProps> = ({
);
};

export type AttributeTableRowProps = {
export type AttributeTableRowProps<T extends object = object> = {
editable?: boolean;
object?: AttributeData;
labeledObject?: LabeledAttributeData;
field: TypedField;
object?: T;
// TODO: Deprecate in favor of using TypedField for labels in the future?
labeledObject?: Record<string, { label: string; value: unknown }>;
field: TypedField<T>;
isFormOpen: boolean;
labelEdit?: string;
onClick: React.MouseEventHandler;
compact?: boolean;
};
export const AttributeTableRow: React.FC<AttributeTableRowProps> = ({

export const AttributeTableRow = <T extends object = object>({
editable = false,
field,
isFormOpen,
object = {},
object = {} as T,
labeledObject = {},
labelEdit,
compact = false,
onClick,
}) => {
}: AttributeTableRowProps<T>) => {
const id = useId();
const intl = useIntl();
const [isEditingState, setIsEditingState] = useState(false);
const name = field.name;
const fieldInObject = Object.keys(object).includes(name);
const label = fieldInObject ? field2Title(name) : labeledObject[name].label;
const rawValue = fieldInObject ? object[name] : labeledObject[name].value;
const fieldInObject = Object.keys(object).includes(name.toString());
const label = fieldInObject
? string2Title(name.toString())
: labeledObject[name as string].label;
const rawValue = fieldInObject
? object[name as keyof T]
: labeledObject[name as string].value;
const isEditing = isFormOpen && isEditingState;

const handleCLick: React.MouseEventHandler = (e) => {
Expand Down Expand Up @@ -180,7 +193,7 @@ export const AttributeTableRow: React.FC<AttributeTableRowProps> = ({
field.type === "boolean" ? Boolean(rawValue) : undefined
}
hidden={!isEditing}
name={name}
name={name.toString()}
options={field.options}
required={true}
type={field.type === "number" ? "number" : undefined}
Expand Down
Loading
Loading