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

Issues/implementation #46

Merged
merged 5 commits into from
Apr 8, 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
12 changes: 9 additions & 3 deletions src/components/data/datagrid/datagrid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
}

&__caption {
padding: var(--spacing-v) var(--spacing-h);
text-align: start;
}

Expand Down Expand Up @@ -70,15 +69,22 @@
top: 50%;
transform: translate(-50%, -50%);
width: 100%;
z-index: 1;
z-index: 1000;

.mykn-input {
width: 100%;
}
}

&__cell--editable:not(#{&}__cell--type-boolean) {
padding: 0;
padding-block: 0;
}
base &__cell--editable:not(#{&}__cell--type-boolean):not(#{&}__cell--link) {
padding-inline: 0;
}

&__cell--link .mykn-button {
width: calc(100% - 1em - 3 * var(--spacing-h));
}

&__foot {
Expand Down
97 changes: 58 additions & 39 deletions src/components/data/datagrid/datagrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
SerializedFormData,
TypedField,
formatMessage,
isPrimitive,
serializeForm,
typedFieldByFields,
useIntl,
Expand Down Expand Up @@ -110,6 +109,9 @@ export type DataGridProps = {
attributeData: DataGridProps["objectList"][number],
) => void;

/** Gets called when a row value is edited. */
onChange?: (event: React.ChangeEvent) => void;

/** Gets called when a row value is edited. */
onEdit?: (rowData: SerializedFormData) => void;

Expand Down Expand Up @@ -148,10 +150,11 @@ const getRenderableFields = (
fields: Array<Field | TypedField>,
objectList: AttributeData[],
urlFields: string[],
editable: DataGridProps["editable"],
): TypedField[] =>
typedFieldByFields(fields, objectList).filter(
(f) => !urlFields.includes(String(f.name)),
);
typedFieldByFields(fields, objectList, {
editable: Boolean(editable),
}).filter((f) => !urlFields.includes(String(f.name)));

/**
* A subset of `PaginatorProps` that act as aliases.
Expand All @@ -176,7 +179,7 @@ export const DataGrid: React.FC<DataGridProps> = ({
boolProps,
objectList,
fields = objectList?.length ? Object.keys(objectList[0]) : [],
editable = Boolean(fields.find((f) => !isPrimitive(f) && f.editable)),
editable = undefined,
paginatorProps,
showPaginator = Boolean(paginatorProps),
pProps,
Expand All @@ -187,6 +190,7 @@ export const DataGrid: React.FC<DataGridProps> = ({
urlFields = DEFAULT_URL_FIELDS,
labelSelect,
labelSelectAll,
onChange,
onEdit,
onSelect,
onSelectionChange,
Expand Down Expand Up @@ -214,12 +218,6 @@ export const DataGrid: React.FC<DataGridProps> = ({
[string, "ASC" | "DESC"] | undefined
>();

// Trigger onSelectionChange when selectedState changes.
useEffect(() => {
const dirty = selectedState && selected !== selectedState;
dirty && onSelectionChange?.(selectedState as AttributeData[]);
}, [selectedState]);

// Update selectedState when selected prop changes.
useEffect(() => {
selected && setSelectedState(selected);
Expand All @@ -232,7 +230,12 @@ export const DataGrid: React.FC<DataGridProps> = ({
}
}, [sort]);

const renderableFields = getRenderableFields(fields, objectList, urlFields);
const renderableFields = getRenderableFields(
fields,
objectList,
urlFields,
editable,
);
const sortField = sortState?.[0];
const sortDirection = sortState?.[1];
const titleId = title ? `${id}-caption` : undefined;
Expand All @@ -253,20 +256,22 @@ export const DataGrid: React.FC<DataGridProps> = ({
const value = allSelected ? [] : renderableRows;
setSelectedState(value);
onSelect?.(value, !allSelected);
onSelectionChange?.(value);
};

const handleSelect = (attributeData: AttributeData) => {
const currentlySelected = selectedState || [];

const isAttributeDataCurrentlySelected =
currentlySelected.includes(attributeData);

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

setSelectedState(newSelectedState);
onSelect?.([attributeData], !isAttributeDataCurrentlySelected);
onSelectionChange?.(newSelectedState);
};

/**
Expand Down Expand Up @@ -337,20 +342,20 @@ export const DataGrid: React.FC<DataGridProps> = ({
amountSelected={selectedState?.length || 0}
count={count || 0}
dataGridId={id}
editable={editable}
editable={Boolean(renderableFields.find((f) => f.editable))}
editingRow={editingState[0]}
editingFieldIndex={editingState[1]}
fields={fields}
handleSelect={handleSelect}
labelSelect={labelSelect || ""}
onChange={onChange}
onClick={onClick}
onEdit={onEdit}
page={page || 1}
renderableFields={renderableFields}
renderableRows={renderableRows}
setEditingState={setEditingState}
selectable={selectable}
selectedRows={selected || []}
selectedRows={selectedState || []}
sortDirection={sortDirection}
sortField={sortField}
urlFields={urlFields}
Expand Down Expand Up @@ -458,9 +463,9 @@ export type DataGridBodyProps = {
editable: boolean;
editingRow: AttributeData | null;
editingFieldIndex: number | null;
fields: Array<Field | TypedField>;
handleSelect: (attributeData: AttributeData) => void;
labelSelect: string;
onChange: DataGridProps["onChange"];
onClick: DataGridProps["onClick"];
onEdit: DataGridProps["onEdit"];
page: number;
Expand Down Expand Up @@ -488,9 +493,9 @@ export const DataGridBody: React.FC<DataGridBodyProps> = ({
editable,
editingRow,
editingFieldIndex,
fields,
handleSelect,
labelSelect,
onChange,
onClick,
onEdit,
page,
Expand Down Expand Up @@ -545,8 +550,9 @@ export const DataGridBody: React.FC<DataGridBodyProps> = ({
editingFieldIndex === renderableFields.indexOf(field)
}
field={field}
fields={fields}
renderableFields={renderableFields}
urlFields={urlFields}
onChange={onChange}
onClick={(e, rowData) => {
if (editable) {
setEditingState([rowData, renderableFields.indexOf(field)]);
Expand Down Expand Up @@ -675,8 +681,9 @@ export type DataGridContentCellProps = {
dataGridId: string;
rowData: AttributeData;
field: TypedField;
fields: DataGridProps["fields"];
renderableFields: TypedField[];
urlFields: DataGridProps["urlFields"];
onChange: DataGridProps["onChange"];
onClick: DataGridProps["onClick"];
onEdit: DataGridProps["onEdit"];
};
Expand All @@ -694,18 +701,18 @@ export const DataGridContentCell: React.FC<DataGridContentCellProps> = ({
isEditingRow,
isEditingField,
field,
fields = [],
renderableFields = [],
rowData,
urlFields = DEFAULT_URL_FIELDS,
onChange,
onClick,
onEdit,
}) => {
const [pristine, setPristine] = useState<boolean>(true);

const fieldEditable =
typeof field.editable === "boolean" ? field.editable : editable;
const renderableFields = getRenderableFields(fields, [rowData], urlFields);
const fieldIndex = renderableFields.indexOf(field);
const fieldIndex = renderableFields.findIndex((f) => f.name === field.name);
const urlField = urlFields.find((f) => rowData[f]);
const rowUrl = urlField ? rowData[urlField] : null;
const value = rowData[field.name];
Expand Down Expand Up @@ -753,9 +760,15 @@ export const DataGridContentCell: React.FC<DataGridContentCellProps> = ({
value={(value || "").toString()}
form={`${dataGridId}-editable-form`}
required={true}
onChange={() => setPristine(false)}
onChange={(e: React.ChangeEvent) => {
setPristine(false);
onChange?.(e);
}}
onBlur={(e: React.FocusEvent<HTMLInputElement | HTMLSelectElement>) => {
const data = serializeForm(e.target.form as HTMLFormElement, true);
const data = Object.assign(
rowData,
serializeForm(e.target.form as HTMLFormElement, true),
);
!pristine && onEdit?.(data);
}}
/>
Expand Down Expand Up @@ -784,15 +797,20 @@ export const DataGridContentCell: React.FC<DataGridContentCellProps> = ({
/**
* Renders the value according to Value component
*/
const renderValue = () => (
<Value
aProps={aProps}
badgeProps={badgeProps}
boolProps={boolProps as BoolProps}
pProps={pProps}
value={value}
/>
);
const renderValue = () => {
// Support label from select
const label = field.options?.find((o) => o.value === value)?.label;

return (
<Value
aProps={aProps}
badgeProps={badgeProps}
boolProps={boolProps as BoolProps}
pProps={pProps}
value={label || value}
/>
);
};

return (
<td
Expand All @@ -802,16 +820,17 @@ export const DataGridContentCell: React.FC<DataGridContentCellProps> = ({
{
"mykn-datagrid__cell--editable": fieldEditable,
"mykn-datagrid__cell--editing": isEditingField,
"mykn-datagrid__cell--link": link,
},
)}
aria-description={field2Caption(field.name)}
>
{isEditingRow && !isEditingField && renderHiddenInput()}
{link && (
<A href={link} aria-label={link} onClick={(e) => onClick?.(e, rowData)}>
<Outline.ArrowTopRightOnSquareIcon />
</A>
)}
{isEditingRow && !isEditingField && renderHiddenInput()}
{isEditingField && renderFormControl()}
{!isEditingField && fieldEditable && renderButton()}
{!isEditingField && !fieldEditable && renderValue()}
Expand Down
8 changes: 7 additions & 1 deletion src/components/form/form/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { forceArray } from "../../../lib/format/array";
import { ucFirst } from "../../../lib/format/string";
import { useIntl } from "../../../lib/i18n/useIntl";
import { Button } from "../../button";
import { Toolbar, ToolbarItem } from "../../toolbar";
import { Toolbar, ToolbarItem, ToolbarProps } from "../../toolbar";
import { ErrorMessage } from "../errormessage";
import { FormControl } from "../formcontrol";
import { InputProps } from "../input";
Expand Down Expand Up @@ -51,6 +51,9 @@ export type FormProps = React.ComponentProps<"form"> & {
/** Whether to show the form actions. */
showActions?: boolean;

/** Props to pass to Toolbar. */
toolbarProps?: Partial<ToolbarProps>;

/** The submit form label. */
labelSubmit?: string;

Expand Down Expand Up @@ -90,6 +93,7 @@ export type FormProps = React.ComponentProps<"form"> & {
* @param onChange
* @param onSubmit
* @param showActions
* @param toolbarProps
* @param useTypedResults
* @param validate
* @param validateOnChange
Expand All @@ -110,6 +114,7 @@ export const Form: React.FC<FormProps> = ({
onChange,
onSubmit,
showActions = true,
toolbarProps,
useTypedResults = false,
validate,
validateOnChange = false,
Expand Down Expand Up @@ -237,6 +242,7 @@ export const Form: React.FC<FormProps> = ({
align={secondaryActions.length ? "space-between" : "end"}
variant={"transparent"}
items={secondaryActions}
{...toolbarProps}
>
<Button
type="submit"
Expand Down
4 changes: 2 additions & 2 deletions src/components/form/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,9 @@ const BaseSelectDropdown: React.FC<SelectDropdownProps> = ({
* Renders the `SelectOption` components
*/
const renderOptions = () =>
options.map(({ label }, i) => (
options.map(({ label, value }, i) => (
<SelectOption
key={label}
key={`${label}-${value || label}`}
ref={(node) => {
listRef.current[i] = node;
}}
Expand Down
2 changes: 1 addition & 1 deletion src/components/toolbar/toolbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
row-gap: 12px;
text-align: start;
width: 100%;
z-index: 1;
z-index: 10;

&--compact {
gap: 0;
Expand Down
8 changes: 7 additions & 1 deletion src/components/toolbar/toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export type ToolbarProps = React.PropsWithChildren<
/** Aligns the contents based on the current direction. */
align?: "start" | "center" | "end" | "space-between";

/** The position of `children` compared to `items`. */
childrenPosition?: "before" | "after";

/** Whether the toolbar shows items horizontally or vertically, mobile devices always use vertical. */
direction?: "horizontal" | "vertical";

Expand Down Expand Up @@ -44,6 +47,7 @@ export type ToolbarProps = React.PropsWithChildren<
* A flexible and customizable toolbar component for arranging and aligning
* various interactive elements such as `A`, `Button`, `ButtonLink` and `Dropdown`.
* @param children
* @param childrenPosition
* @param align
* @param compact
* @param direction
Expand All @@ -57,6 +61,7 @@ export type ToolbarProps = React.PropsWithChildren<
*/
export const Toolbar: React.FC<ToolbarProps> = ({
children,
childrenPosition = "after",
align = "start",
compact = false,
direction = "horizontal",
Expand Down Expand Up @@ -128,8 +133,9 @@ export const Toolbar: React.FC<ToolbarProps> = ({
role="toolbar"
{...props}
>
{childrenPosition === "before" && children}
{items.map(renderItem)}
{children}
{childrenPosition === "after" && children}
</nav>
);
};
Loading