Skip to content

Commit 6041549

Browse files
committed
refactor: rewrite dg rows renderer
1 parent 185ba89 commit 6041549

File tree

9 files changed

+118
-65
lines changed

9 files changed

+118
-65
lines changed

packages/pluggableWidgets/datagrid-web/src/components/Row.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import classNames from "classnames";
22
import { ObjectItem } from "mendix";
33
import { ReactElement } from "react";
44
import { SelectActionHelper } from "../helpers/SelectActionHelper";
5-
import { CellComponent, EventsController } from "../typings/CellComponent";
5+
import { EventsController } from "../typings/CellComponent";
66
import { GridColumn } from "../typings/GridColumn";
7+
import { Cell } from "./Cell";
78
import { CheckboxCell } from "./CheckboxCell";
89
import { SelectorCell } from "./SelectorCell";
910

10-
export interface RowProps<C extends GridColumn> {
11+
export interface RowProps {
1112
className?: string;
12-
CellComponent: CellComponent<C>;
13-
columns: C[];
13+
columns: GridColumn[];
1414
item: ObjectItem;
1515
index: number;
1616
showSelectorCell?: boolean;
@@ -20,8 +20,8 @@ export interface RowProps<C extends GridColumn> {
2020
eventsController: EventsController;
2121
}
2222

23-
export function Row<C extends GridColumn>(props: RowProps<C>): ReactElement {
24-
const { CellComponent: Cell, selectActionHelper, totalRows, eventsController } = props;
23+
export function Row(props: RowProps): ReactElement {
24+
const { selectActionHelper, totalRows, eventsController } = props;
2525
const selected = selectActionHelper.isSelected(props.item);
2626
const ariaSelected = selectActionHelper.selectionType === "None" ? undefined : selected;
2727
const borderTop = props.index === 0;

packages/pluggableWidgets/datagrid-web/src/components/RowsRenderer.tsx

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,35 @@
11
import { KeyNavProvider } from "@mendix/widget-plugin-grid/keyboard-navigation/context";
2-
import { FocusTargetController } from "@mendix/widget-plugin-grid/keyboard-navigation/FocusTargetController";
32
import { ObjectItem } from "mendix";
43
import { observer } from "mobx-react-lite";
54
import { ReactElement } from "react";
6-
import { SelectActionHelper } from "../helpers/SelectActionHelper";
7-
import { useColumnsStore } from "../model/hooks/injection-hooks";
8-
import { CellComponent, EventsController } from "../typings/CellComponent";
9-
import { GridColumn } from "../typings/GridColumn";
5+
import { useLegacyContext } from "../helpers/root-context";
6+
import { useColumnsStore, useDatagridConfig, useRowClass } from "../model/hooks/injection-hooks";
107
import { Row } from "./Row";
118

129
interface RowsRendererProps {
13-
Cell: CellComponent<GridColumn>;
14-
columnsHidable: boolean;
15-
eventsController: EventsController;
16-
focusController: FocusTargetController;
17-
interactive: boolean;
18-
preview: boolean;
19-
rowClass?: (item: ObjectItem) => string;
2010
rows: ObjectItem[];
21-
selectActionHelper: SelectActionHelper;
2211
}
2312

24-
export const RowsRenderer = observer(function RowsRenderer(props: RowsRendererProps): ReactElement {
13+
export const RowsRenderer = observer(function RowsRenderer({ rows }: RowsRendererProps): ReactElement {
14+
const config = useDatagridConfig();
2515
const { visibleColumns } = useColumnsStore();
16+
const rowClass = useRowClass();
17+
const { cellEventsController, focusController, selectActionHelper } = useLegacyContext();
2618
return (
27-
<KeyNavProvider focusController={props.focusController}>
28-
{props.rows.map((item, rowIndex) => {
19+
<KeyNavProvider focusController={focusController}>
20+
{rows.map((item, rowIndex) => {
2921
return (
3022
<Row
31-
totalRows={props.rows.length}
32-
clickable={props.interactive}
33-
selectActionHelper={props.selectActionHelper}
34-
eventsController={props.eventsController}
35-
CellComponent={props.Cell}
36-
className={props.rowClass?.(item)}
23+
totalRows={rows.length}
24+
clickable={config.isInteractive}
25+
selectActionHelper={selectActionHelper}
26+
eventsController={cellEventsController}
27+
className={rowClass.class.get(item)}
3728
columns={visibleColumns}
3829
index={rowIndex}
3930
item={item}
4031
key={`row_${item.id}`}
41-
showSelectorCell={props.columnsHidable}
32+
showSelectorCell={config.columnsHidable}
4233
/>
4334
);
4435
})}

packages/pluggableWidgets/datagrid-web/src/components/Widget.tsx

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import { SelectAllBar } from "../features/select-all/SelectAllBar";
1010
import { SelectionProgressDialog } from "../features/select-all/SelectionProgressDialog";
1111
import { SelectActionHelper } from "../helpers/SelectActionHelper";
1212
import { useBasicData } from "../model/hooks/injection-hooks";
13-
import { CellComponent, EventsController } from "../typings/CellComponent";
14-
import { GridColumn } from "../typings/GridColumn";
13+
import { EventsController } from "../typings/CellComponent";
1514
import { ExportWidget } from "./ExportWidget";
1615
import { Grid } from "./Grid";
1716
import { GridBody } from "./GridBody";
@@ -23,17 +22,15 @@ import { WidgetHeader } from "./WidgetHeader";
2322
import { WidgetRoot } from "./WidgetRoot";
2423
import { WidgetTopBar } from "./WidgetTopBar";
2524

26-
export interface WidgetProps<C extends GridColumn, T extends ObjectItem = ObjectItem> {
27-
CellComponent: CellComponent<C>;
25+
export interface WidgetProps {
2826
className: string;
2927
columnsDraggable: boolean;
3028
columnsFilterable: boolean;
3129
columnsHidable: boolean;
3230
columnsResizable: boolean;
3331
columnsSortable: boolean;
34-
data: T[];
32+
data: ObjectItem[];
3533
exporting: boolean;
36-
filterRenderer: (renderWrapper: (children: ReactNode) => ReactElement, columnIndex: number) => ReactElement;
3734
headerContent?: ReactNode;
3835
headerTitle?: string;
3936
id: string;
@@ -43,7 +40,7 @@ export interface WidgetProps<C extends GridColumn, T extends ObjectItem = Object
4340
loadMoreButtonCaption?: string;
4441

4542
processedRows: number;
46-
rowClass?: (item: T) => string;
43+
4744
styles?: CSSProperties;
4845
rowAction?: ListActionValue;
4946
isFirstLoad: boolean;
@@ -59,7 +56,7 @@ export interface WidgetProps<C extends GridColumn, T extends ObjectItem = Object
5956
focusController: FocusTargetController;
6057
}
6158

62-
export const Widget = observer(<C extends GridColumn>(props: WidgetProps<C>): ReactElement => {
59+
export const Widget = observer(function Widget(props: WidgetProps) {
6360
const { className, exporting, numberOfItems, onExportCancel, selectActionHelper } = props;
6461
const basicData = useBasicData();
6562
const selectionEnabled = selectActionHelper.selectionType !== "None";
@@ -89,19 +86,8 @@ export const Widget = observer(<C extends GridColumn>(props: WidgetProps<C>): Re
8986
);
9087
});
9188

92-
const Main = observer(<C extends GridColumn>(props: WidgetProps<C>): ReactElement => {
93-
const {
94-
CellComponent,
95-
columnsHidable,
96-
data: rows,
97-
headerContent,
98-
headerTitle,
99-
loadMoreButtonCaption,
100-
showRefreshIndicator,
101-
selectActionHelper
102-
} = props;
103-
104-
const basicData = useBasicData();
89+
const Main = observer((props: WidgetProps): ReactElement => {
90+
const { data: rows, headerContent, headerTitle, loadMoreButtonCaption, showRefreshIndicator } = props;
10591

10692
return (
10793
<Fragment>
@@ -113,17 +99,7 @@ const Main = observer(<C extends GridColumn>(props: WidgetProps<C>): ReactElemen
11399
<SelectAllBar />
114100
{showRefreshIndicator ? <RefreshIndicator /> : null}
115101
<GridBody>
116-
<RowsRenderer
117-
preview={false}
118-
interactive={basicData.gridInteractive}
119-
Cell={CellComponent}
120-
columnsHidable={columnsHidable}
121-
rows={rows}
122-
rowClass={props.rowClass}
123-
selectActionHelper={selectActionHelper}
124-
focusController={props.focusController}
125-
eventsController={props.cellEventsController}
126-
/>
102+
<RowsRenderer rows={rows} />
127103
<EmptyPlaceholder />
128104
</GridBody>
129105
</Grid>

packages/pluggableWidgets/datagrid-web/src/model/containers/Datagrid.container.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { ColumnGroupStore } from "../../helpers/state/ColumnGroupStore";
1414
import { GridBasicData } from "../../helpers/state/GridBasicData";
1515
import { GridPersonalizationStore } from "../../helpers/state/GridPersonalizationStore";
1616
import { DatagridConfig } from "../configs/Datagrid.config";
17-
import { gridStyleAtom } from "../models/grid.model";
17+
import { gridStyleAtom, rowClassProvider } from "../models/grid.model";
1818
import { DatasourceParamsController } from "../services/DatasourceParamsController";
1919
import { DerivedLoaderController } from "../services/DerivedLoaderController";
2020
import { PaginationController } from "../services/PaginationController";
@@ -35,6 +35,7 @@ injected(emptyStateWidgetsAtom, CORE.mainGate, CORE.atoms.itemCount);
3535
injected(SelectionGate, CORE.mainGate);
3636
injected(createSelectionHelper, CORE.setupService, DG.selectionGate, CORE.config.optional);
3737
injected(gridStyleAtom, CORE.columnsStore, CORE.config);
38+
injected(rowClassProvider, CORE.mainGate);
3839

3940
injected(
4041
SelectionCounterViewModel,
@@ -81,6 +82,8 @@ export class DatagridContainer extends Container {
8182
this.bind(DG.selectionGate).toInstance(SelectionGate).inTransientScope();
8283
// Selection helper
8384
this.bind(DG.selectionHelper).toInstance(createSelectionHelper).inSingletonScope();
85+
// Row class provider
86+
this.bind(DG.rowClass).toInstance(rowClassProvider).inTransientScope();
8487
}
8588

8689
/**

packages/pluggableWidgets/datagrid-web/src/model/hooks/injection-hooks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export const [useVisibleColumnsCount] = createInjectionHooks(CORE.atoms.visibleC
1616
export const [useItemCount] = createInjectionHooks(CORE.atoms.itemCount);
1717
export const [useColumn] = createInjectionHooks(CORE.column);
1818
export const [useTexts] = createInjectionHooks(CORE.texts);
19+
export const [useRowClass] = createInjectionHooks(DG.rowClass);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { listExpression, obj } from "@mendix/widget-plugin-test-utils";
2+
import { configure, isObservable, observable } from "mobx";
3+
import { MainGateProps } from "../../../../typings/MainGateProps";
4+
import { rowClassProvider } from "../grid.model";
5+
6+
describe("rowClassProvider", () => {
7+
configure({
8+
enforceActions: "never"
9+
});
10+
11+
it("returns empty string when rowClass is not defined", () => {
12+
const gate = observable({ props: {} as MainGateProps });
13+
const atom = rowClassProvider(gate);
14+
15+
expect(atom.class.get(obj())).toBe("");
16+
});
17+
18+
it("returns the class from rowClass expression", () => {
19+
const gate = observable({
20+
props: {
21+
rowClass: listExpression(() => "custom-row-class")
22+
} as MainGateProps
23+
});
24+
const atom = rowClassProvider(gate);
25+
26+
expect(atom.class.get(obj())).toBe("custom-row-class");
27+
});
28+
29+
it("updates reactively when rowClass expression changes", () => {
30+
const rowClassExpression = listExpression(() => "initial-class");
31+
const gate = observable({
32+
props: {
33+
rowClass: rowClassExpression
34+
} as MainGateProps
35+
});
36+
const atom = rowClassProvider(gate);
37+
38+
expect(atom.class.get(obj())).toBe("initial-class");
39+
40+
gate.props.rowClass = listExpression(() => "updated-class");
41+
expect(atom.class.get(obj())).toBe("updated-class");
42+
});
43+
44+
it("class property is not observable itself", () => {
45+
const gate = observable({
46+
props: {
47+
rowClass: listExpression(() => "some-class")
48+
} as MainGateProps
49+
});
50+
51+
const atom = rowClassProvider(gate);
52+
expect(isObservable(atom.class)).toBe(false);
53+
});
54+
});

packages/pluggableWidgets/datagrid-web/src/model/models/grid.model.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { ComputedAtom } from "@mendix/widget-plugin-mobx-kit/main";
2-
import { computed } from "mobx";
1+
import { ComputedAtom, DerivedPropsGate } from "@mendix/widget-plugin-mobx-kit/main";
2+
import { ObjectItem } from "mendix";
3+
import { computed, observable } from "mobx";
34
import { CSSProperties } from "react";
5+
import { MainGateProps } from "../../../typings/MainGateProps";
46
import { ColumnGroupStore } from "../../helpers/state/ColumnGroupStore";
57
import { DatagridConfig } from "../configs/Datagrid.config";
68

@@ -38,3 +40,25 @@ function gridStyle(
3840
gridTemplateColumns: sizes.join(" ")
3941
};
4042
}
43+
44+
export interface RowClassProvider {
45+
class: {
46+
get(item: ObjectItem): string;
47+
};
48+
}
49+
50+
/** @injectable */
51+
export function rowClassProvider(gate: DerivedPropsGate<MainGateProps>): RowClassProvider {
52+
const atom = {
53+
get class() {
54+
return {
55+
get(item: ObjectItem): string {
56+
if (!gate.props.rowClass) return "";
57+
return gate.props.rowClass.get(item).value ?? "";
58+
}
59+
};
60+
}
61+
};
62+
63+
return observable(atom, { class: computed });
64+
}

packages/pluggableWidgets/datagrid-web/src/model/tokens.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { GridPersonalizationStore } from "../helpers/state/GridPersonalizationSt
2929
import { DatasourceParamsController } from "../model/services/DatasourceParamsController";
3030
import { GridColumn } from "../typings/GridColumn";
3131
import { DatagridConfig } from "./configs/Datagrid.config";
32+
import { RowClassProvider } from "./models/grid.model";
3233
import { DatagridSetupService } from "./services/DatagridSetup.service";
3334
import { DerivedLoaderController, DerivedLoaderControllerConfig } from "./services/DerivedLoaderController";
3435
import { PaginationConfig, PaginationController } from "./services/PaginationController";
@@ -105,7 +106,9 @@ export const DG_TOKENS = {
105106
selectionGate: token<DerivedPropsGate<SelectionDynamicProps>>("@gate:GateForSelectionHelper"),
106107
selectionHelper: token<SelectionHelperService | undefined>("SelectionHelperService"),
107108

108-
gridColumnsStyle: token<ComputedAtom<CSSProperties>>("@computed:GridColumnsStyle")
109+
gridColumnsStyle: token<ComputedAtom<CSSProperties>>("@computed:GridColumnsStyle"),
110+
111+
rowClass: token<RowClassProvider>("@store:RowClassProvider")
109112
};
110113

111114
/** "Select all" module tokens. */

packages/pluggableWidgets/datagrid-web/typings/MainGateProps.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ export type MainGateProps = Pick<
2929
| "showNumberOfRows"
3030
| "showPagingButtons"
3131
| "storeFiltersInPersonalization"
32+
| "rowClass"
3233
>;

0 commit comments

Comments
 (0)