Skip to content

Commit

Permalink
♻️ - refactor: split datagrid filter into separate component
Browse files Browse the repository at this point in the history
  • Loading branch information
svenvandescheur committed Oct 10, 2024
1 parent eadd13c commit b004fdb
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 112 deletions.
111 changes: 111 additions & 0 deletions src/components/data/datagrid/datagridfilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import clsx from "clsx";
import React, { useContext, useEffect, useRef, useState } from "react";

import {
AttributeData,
field2Title,
formatMessage,
serializeForm,
useIntl,
} from "../../../lib";
import { FormControl } from "../../form";
import { Outline } from "../../icon";
import { DataGridContext } from "./datagrid";
import { TRANSLATIONS } from "./translations";

/**
* DataGrid filter, encapsulates a set of FormControl's allowing the user to
* filter items on the grid.
*/
export const DataGridFilter: React.FC = () => {
const intl = useIntl();
const onFilterTimeoutRef = useRef<NodeJS.Timeout>();
const [filterState, setFilterState] = useState<AttributeData>();

const {
dataGridId,
filterTransform,
labelFilterField,
onFilter,
renderableFields,
selectable,
} = useContext(DataGridContext);

// Debounce filter
useEffect(() => {
const handler = () => {
// No filter state.
if (filterState === undefined) {
return;
}
onFilter(filterState || {});
};
onFilterTimeoutRef.current && clearTimeout(onFilterTimeoutRef.current);
onFilterTimeoutRef.current = setTimeout(handler, 300);
}, [filterState]);

return (
<tr className="mykn-datagrid__row mykn-datagrid__row--filter" role="row">
{selectable && (
<th
className={clsx(
"mykn-datagrid__cell",
"mykn-datagrid__cell--filter",
"mykn-datagrid__cell--checkbox",
)}
></th>
)}
{renderableFields.map((field) => {
const placeholder = field2Title(field.name, { lowerCase: false });

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { options, valueTransform, ...context } = field;

const _labelFilterField = labelFilterField
? formatMessage(labelFilterField, context)
: intl.formatMessage(TRANSLATIONS.LABEL_FILTER_FIELD, context);

return (
<th
key={`${dataGridId}-filter-${field2Title(field.name, { lowerCase: false })}`}
className={clsx(
"mykn-datagrid__cell",
"mykn-datagrid__c" + "ell--filter",
)}
style={field.width ? { width: field.width } : {}}
>
{field.filterable !== false && (
<FormControl
aria-label={_labelFilterField}
icon={
!field.options &&
field.type !== "daterange" && <Outline.MagnifyingGlassIcon />
}
form={`${dataGridId}-filter-form`}
name={field.filterLookup || field.name}
options={field.options}
min={!field.options && field.type === "number" ? 0 : undefined}
pad={field.type === "daterange" ? "v" : undefined}
placeholder={placeholder}
type={field.type}
value={field.filterValue}
onChange={(
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>,
) => {
e.preventDefault();
const data = serializeForm(
e.target.form as HTMLFormElement,
) as AttributeData;
const _data = filterTransform ? filterTransform(data) : data;

// Reset page on filter (length of dataset may change).
setFilterState(_data);
}}
/>
)}
</th>
);
})}
</tr>
);
};
116 changes: 4 additions & 112 deletions src/components/data/datagrid/datagridthead.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
import clsx from "clsx";
import React, {
CSSProperties,
useContext,
useEffect,
useRef,
useState,
} from "react";
import React, { CSSProperties, useContext, useEffect, useRef } from "react";

import {
AttributeData,
field2Title,
formatMessage,
serializeForm,
useIntl,
} from "../../../lib";
import { FormControl } from "../../form";
import { Outline } from "../../icon";
import { field2Title } from "../../../lib";
import { DataGridContext, scrollPaneRef } from "./datagrid";
import { DataGridFilter } from "./datagridfilter";
import { DataGridHeadingCell } from "./datagridheadingcell";
import { TRANSLATIONS } from "./translations";

/**
* DataGrid table head, encapsulates a set of table rows, indicating that they
* comprise the head of a table with information about the table's columns.
*/
export const DataGridTHead: React.FC = () => {
const intl = useIntl();
const onFilterTimeoutRef = useRef<NodeJS.Timeout>();
const ref = useRef<HTMLTableSectionElement>(null);
const [filterState, setFilterState] = useState<AttributeData>();

const {
dataGridId,
filterable,
filterTransform,
height,
labelFilterField,
onFilter,
renderableFields,
selectable,
toolbarRef,
Expand Down Expand Up @@ -85,19 +64,6 @@ export const DataGridTHead: React.FC = () => {
});
};

// Debounce filter
useEffect(() => {
const handler = () => {
// No filter state.
if (filterState === undefined) {
return;
}
onFilter(filterState || {});
};
onFilterTimeoutRef.current && clearTimeout(onFilterTimeoutRef.current);
onFilterTimeoutRef.current = setTimeout(handler, 300);
}, [filterState]);

return (
<thead
ref={ref}
Expand Down Expand Up @@ -127,81 +93,7 @@ export const DataGridTHead: React.FC = () => {
</tr>

{/* Filters */}
{filterable && (
<tr
className="mykn-datagrid__row mykn-datagrid__row--filter"
role="row"
>
{selectable && (
<th
className={clsx(
"mykn-datagrid__cell",
"mykn-datagrid__cell--filter",
"mykn-datagrid__cell--checkbox",
)}
></th>
)}
{renderableFields.map((field) => {
const placeholder = field2Title(field.name, { lowerCase: false });

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { options, valueTransform, ...context } = field;

const _labelFilterField = labelFilterField
? formatMessage(labelFilterField, context)
: intl.formatMessage(TRANSLATIONS.LABEL_FILTER_FIELD, context);

return (
<th
key={`${dataGridId}-filter-${field2Title(field.name, { lowerCase: false })}`}
className={clsx(
"mykn-datagrid__cell",
"mykn-datagrid__c" + "ell--filter",
)}
style={field.width ? { width: field.width } : {}}
>
{field.filterable !== false && (
<FormControl
aria-label={_labelFilterField}
icon={
!field.options &&
!field.type.includes("date") && (
<Outline.MagnifyingGlassIcon />
)
}
form={`${dataGridId}-filter-form`}
name={field.filterLookup || field.name}
options={field.options}
min={
!field.options && field.type === "number" ? 0 : undefined
}
pad={field.type === "daterange" ? "v" : undefined}
placeholder={placeholder}
type={field.type}
value={field.filterValue}
onChange={(
e: React.ChangeEvent<
HTMLInputElement | HTMLSelectElement
>,
) => {
e.preventDefault();
const data = serializeForm(
e.target.form as HTMLFormElement,
) as AttributeData;
const _data = filterTransform
? filterTransform(data)
: data;

// Reset page on filter (length of dataset may change).
setFilterState(_data);
}}
/>
)}
</th>
);
})}
</tr>
)}
{filterable && <DataGridFilter />}
</thead>
);
};

0 comments on commit b004fdb

Please sign in to comment.