Skip to content

Commit

Permalink
Add strong row typing support, add action column support
Browse files Browse the repository at this point in the history
  • Loading branch information
jamerst committed Aug 13, 2022
1 parent 60e00a6 commit 3ea5a63
Show file tree
Hide file tree
Showing 17 changed files with 1,027 additions and 909 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ Please report any issues that you find, and feel free to make feature requests.
## Installation
ODataGrid can be installed using the appropriate npm package ([`o-data-grid`](https://www.npmjs.com/package/o-data-grid) or [`o-data-grid-pro`](https://www.npmjs.com/package/o-data-grid-pro)).

The following material-ui packages must also be installed. I recommend using the latest versions where possible, but they must be v5.x.x. **These are peer dependencies so won't be installed automatically.**
The following mui packages must also be installed. I recommend using the latest versions where possible, the minimum required version for the data-grid packages is **v5.8.0**, but any of the other mui packages must be v5.x.x.

**These are peer dependencies so won't be installed automatically.**
- `@mui/system`
- `@mui/material`
- `@mui/x-date-pickers`
- `@mui/x-data-grid` for `o-data-grid`
- `@mui/x-data-grid-pro` for `o-data-grid-pro`
- `@mui/x-data-grid` (minimum `v5.8.0`) for `o-data-grid`
- `@mui/x-data-grid-pro` (minimum `v5.8.0`) for `o-data-grid-pro`
- `@mui/icons-material`

## Usage
Expand All @@ -48,7 +50,7 @@ From there you can start to customise the grid to your needs using the propertie
If the same reference is not kept, this may trigger duplicate OData requests.

### Helpful Tips
- Nested properties are supported, to define a column for a nested property flatten it as you would for the query string. E.g. to access `Child` from `{ Parent: { Child: "foo" } }`, use `Parent/Child` as the value for `field` in the column definition.
- Nested properties are supported, to define a column for a nested property flatten it as you would for the query string. E.g. to access `Child` from `{ Parent: { Child: "foo" } }`, use `Parent/Child` as the value for `field` in the column definition. Strong typing is still supported through the `result` property of the row.

### Examples
The demo site isn't ready yet, but you can see some examples of usage here on my GitHub:
Expand Down Expand Up @@ -102,6 +104,10 @@ _* = required property_
### <a id="ODataGridColDef">ODataGridColDef</a>
The column definition is again similar to the standard [GridColDef](https://mui.com/components/data-grid/columns/).

Strong typing is supported through the `TRow` generic type. The original unflattened type can be accessed using the `result` property of the grid row parameters. Action columns are also supported using the `ODataGridColumns<T>` type.

The `TDate` generic type is also available to specify the date provider type for dateTime columns with a date picker filter.

#### Modifications
| Name | Change | Description |
| ---- | ------ | ----------- |
Expand Down
2 changes: 1 addition & 1 deletion packages/base/FilterBuilder/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ const buildInnerCondition = <TDate,>(schema: BaseFieldDef<TDate>, field: string,
translator = defaultTranslators["default"]!;
}

const result = translator(schema, field, op, value);
const result = translator({ schema, field, op, value });

if (typeof result === "string") {
return {
Expand Down
8 changes: 4 additions & 4 deletions packages/base/FilterBuilder/translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FilterTranslatorCollection } from "./types";
import { escapeODataString } from "./utils";

export const defaultTranslators: FilterTranslatorCollection<any> = {
"contains": (schema, field, op, value) => {
"contains": ({ schema, field, value }) => {
if ((schema.type && schema.type !== "string") || typeof value !== "string") {
console.warn(`Warning: operation "contains" is only supported for fields of type "string"`);
return false;
Expand All @@ -14,15 +14,15 @@ export const defaultTranslators: FilterTranslatorCollection<any> = {
}
},

"null": (schema, field, op, value) => {
"null": ({ field }) => {
return `${field} eq null`;
},

"notnull": (schema, field, op, value) => {
"notnull": ({ field }) => {
return `${field} ne null`;
},

"default": (schema, field, op, value) => {
"default": ({ schema, field, op, value }) => {
if (schema.type === "date") {
return `date(${field}) ${op} ${value}`;
} else if (schema.type === "datetime") {
Expand Down
9 changes: 8 additions & 1 deletion packages/base/FilterBuilder/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,14 @@ export type FilterTranslatorCollection<TDate> = {
[key in Operation | "default"]?: FilterTranslator<TDate>
}

export type FilterTranslator<TDate> = (schema: BaseFieldDef<TDate>, field: string, op: Operation, value: any) => string | boolean;
export type FilterTranslator<TDate> = (params: FilterTranslatorParams<TDate>) => string | boolean;

export type FilterTranslatorParams<TDate> = {
schema: BaseFieldDef<TDate>,
field: string,
op: Operation,
value: any
}

export type Connective = "and" | "or"

Expand Down
26 changes: 16 additions & 10 deletions packages/base/components/ODataGridBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ResponsiveValues, useResponsive } from "../hooks";

import FilterBuilder from "../FilterBuilder/components/FilterBuilder";

import { ODataResponse, ODataGridBaseProps, IGridSortModel, IGridProps, IGridRowModel, ColumnVisibilityModel, Expand } from "../types";
import { ODataResponse, ODataGridBaseProps, IGridSortModel, IGridProps, ColumnVisibilityModel, Expand, ODataRowModel } from "../types";

import { ExpandToQuery, Flatten, GetPageNumber, GetPageSizeOrDefault } from "../utils";

Expand All @@ -16,11 +16,12 @@ import { GridColumnVisibilityModel } from "@mui/x-data-grid";
const ODataGridBase = <ComponentProps extends IGridProps,
SortModel extends IGridSortModel,
ColDef,
TDate,>(props: ODataGridBaseProps<ComponentProps, SortModel, ColDef, TDate>) => {
TRow,
TDate,>(props: ODataGridBaseProps<ComponentProps, SortModel, ColDef, TRow, TDate>) => {

const [pageNumber, setPageNumber] = useState<number>(GetPageNumber());
const [pageSize, setPageSize] = useState<number>(GetPageSizeOrDefault(props.defaultPageSize));
const [rows, setRows] = useState<IGridRowModel[]>([])
const [rows, setRows] = useState<ODataRowModel<TRow>[]>([])
const [rowCount, setRowCount] = useState<number>(0);
const [loading, setLoading] = useState<boolean>(true);
const [sortModel, setSortModel] = useState<SortModel | undefined>(props.defaultSortModel);
Expand Down Expand Up @@ -61,7 +62,7 @@ const ODataGridBase = <ComponentProps extends IGridProps,
// select all fields for visible columns
const fields = new Set(
props.columns
.filter(c => visibleColumns.includes(c.field) && c.expand === undefined && c.filterOnly !== true)
.filter(c => visibleColumns.includes(c.field) && c.expand === undefined && c.filterOnly !== true && c.type !== "actions")
.map(c => c.select ?? c.field)
);

Expand Down Expand Up @@ -105,19 +106,24 @@ const ODataGridBase = <ComponentProps extends IGridProps,
}

if (sortModel && sortModel.length > 0) {
query.append("$orderby", sortModel.map(s => {
const sortCol = props.columns.find(c => c.field === s.field);
return `${sortCol!.sortField ?? sortCol!.field}${s.sort === "desc" ? " desc" : ""}`;
}).join(","));
const sortCols = sortModel
.map(s => ({ col: props.columns.find(c => c.field === s.field), sort: s.sort }))
.filter(c => c.col)
.map(c => `${c.col!.sortField ?? c.col!.field}${c.sort === "desc" ? " desc" : ""}`);

if (sortCols.length > 0) {
query.append("$orderby", sortCols.join(","));
}
}

const response = await fetch(props.url + "?" + query.toString(), props.requestOptions);
if (response.ok) {
const data = await response.json() as ODataResponse;
const data = await response.json() as ODataResponse<TRow>;

// flatten object so that the DataGrid can access all the properties
// i.e. { Person: { name: "John" } } becomes { "Person/name": "John" }
const rows = data.value.map(v => Flatten(v, "/"));
// keep the original object in the "result" property so that it can still be accessed via strong typing
const rows: ODataRowModel<TRow>[] = data.value.map((v) => ({ result: v, ...Flatten(v, "/") }));

if (data["@odata.count"]) {
setRowCount(data["@odata.count"]);
Expand Down
23 changes: 19 additions & 4 deletions packages/base/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type ODataGridBaseProps<
ComponentProps extends IGridProps,
SortModel extends IGridSortModel,
ColDef,
TRow,
TDate
> =
OmitGridProps<ComponentProps>
Expand Down Expand Up @@ -45,18 +46,32 @@ type OmitGridProps<T> = Omit<T,
| "rowCount"
| "sortingMode"
| "sortModel"
>
>

export type ODataGridBaseColDef<ColDef, TDate> = Omit<ColDef, "filterOperators" | "hide" | "sortComparator"> & FieldDef<TDate> & {
type ODataColumn<T, TDate> = Omit<T, "filterOperators" | "hide" | "sortComparator"> & FieldDef<TDate> & {
select?: string,
expand?: Expand | Expand[],
hide?: ResponsiveValues<boolean> | boolean,
filterOnly?: boolean
}

export type ODataResponse = {
// type for rows when displayed in datagrid
// allows object to be flattened for convenience, but still allows strong typing through "result" property
export type ODataRowModel<T> = {
result: T,
[key: string]: any
}

export type ODataGridBaseColDef<ColDef, TDate> = ODataColumn<ColDef, TDate>
export type ODataGridBaseEnrichedColDef<ColDef, ActionsColDef, TDate> =
| ODataColumn<ColDef, TDate>
| ODataColumn<ActionsColDef, TDate>;

export type ODataBaseGridColumns<EnrichedColDef, ActionsColDef, TDate> = ODataGridBaseEnrichedColDef<EnrichedColDef, ActionsColDef, TDate>[]

export type ODataResponse<T> = {
"@odata.count"?: number,
value: IGridRowModel[]
value: T[]
}

export type Expand = {
Expand Down
14 changes: 10 additions & 4 deletions packages/o-data-grid-pro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ Please report any issues that you find, and feel free to make feature requests.
## Installation
ODataGrid can be installed using the appropriate npm package ([`o-data-grid`](https://www.npmjs.com/package/o-data-grid) or [`o-data-grid-pro`](https://www.npmjs.com/package/o-data-grid-pro)).

The following material-ui packages must also be installed. I recommend using the latest versions where possible, but they must be v5.x.x. **These are peer dependencies so won't be installed automatically.**
The following mui packages must also be installed. I recommend using the latest versions where possible, the minimum required version for the data-grid packages is **v5.8.0**, but any of the other mui packages must be v5.x.x.

**These are peer dependencies so won't be installed automatically.**
- `@mui/system`
- `@mui/material`
- `@mui/x-date-pickers`
- `@mui/x-data-grid` for `o-data-grid`
- `@mui/x-data-grid-pro` for `o-data-grid-pro`
- `@mui/x-data-grid` (minimum `v5.8.0`) for `o-data-grid`
- `@mui/x-data-grid-pro` (minimum `v5.8.0`) for `o-data-grid-pro`
- `@mui/icons-material`

## Usage
Expand All @@ -48,7 +50,7 @@ From there you can start to customise the grid to your needs using the propertie
If the same reference is not kept, this may trigger duplicate OData requests.

### Helpful Tips
- Nested properties are supported, to define a column for a nested property flatten it as you would for the query string. E.g. to access `Child` from `{ Parent: { Child: "foo" } }`, use `Parent/Child` as the value for `field` in the column definition.
- Nested properties are supported, to define a column for a nested property flatten it as you would for the query string. E.g. to access `Child` from `{ Parent: { Child: "foo" } }`, use `Parent/Child` as the value for `field` in the column definition. Strong typing is still supported through the `result` property of the row.

### Examples
The demo site isn't ready yet, but you can see some examples of usage here on my GitHub:
Expand Down Expand Up @@ -102,6 +104,10 @@ _* = required property_
### <a id="ODataGridColDef">ODataGridColDef</a>
The column definition is again similar to the standard [GridColDef](https://mui.com/components/data-grid/columns/).

Strong typing is supported through the `TRow` generic type. The original unflattened type can be accessed using the `result` property of the grid row parameters. Action columns are also supported using the `ODataGridColumns<T>` type.

The `TDate` generic type is also available to specify the date provider type for dateTime columns with a date picker filter.

#### Modifications
| Name | Change | Description |
| ---- | ------ | ----------- |
Expand Down
4 changes: 2 additions & 2 deletions packages/o-data-grid-pro/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "o-data-grid-pro",
"version": "1.2.1",
"version": "1.3.0",
"description": "A React Data Grid and Query Builder for OData APIs. Based on the Material-UI DataGridPro.",
"main": "build/o-data-grid-pro-cjs.js",
"module": "build/o-data-grid-pro-esm.js",
Expand Down Expand Up @@ -40,7 +40,7 @@
"@mui/icons-material": "^5.2.5",
"@mui/material": "^5.2.8",
"@mui/system": "^5.2.8",
"@mui/x-data-grid-pro": "^5.4.0",
"@mui/x-data-grid-pro": "^5.8.0",
"@mui/x-date-pickers": "^5.0.0-beta.2",
"react": "^17.0.2"
},
Expand Down
6 changes: 3 additions & 3 deletions packages/o-data-grid-pro/src/ODataGridProProps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ODataGridBaseProps } from "../../base/types";
import { ODataGridBaseProps, ODataRowModel } from "../../base/types";
import { DataGridProProps, GridColDef, GridSortModel } from "@mui/x-data-grid-pro";

export type ODataGridProProps<TDate = any> = Omit<
ODataGridBaseProps<DataGridProProps, GridSortModel, GridColDef, TDate>,
export type ODataGridProProps<TRow = any, TDate = any> = Omit<
ODataGridBaseProps<DataGridProProps<ODataRowModel<TRow>>, GridSortModel, GridColDef<ODataRowModel<TRow>>, TRow, TDate>,
"component"
>;
12 changes: 8 additions & 4 deletions packages/o-data-grid-pro/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GridColDef } from "@mui/x-data-grid-pro";
import { GridActionsColDef, GridColDef, GridEnrichedColDef } from "@mui/x-data-grid-pro";

import ODataGridPro from "./ODataGridPro";
import { ODataGridBaseColDef } from "../../base/types";
import { ODataBaseGridColumns, ODataGridBaseColDef, ODataGridBaseEnrichedColDef, ODataRowModel } from "../../base/types";
import FilterBuilder from "../../base/FilterBuilder/components/FilterBuilder";
import { allOperators, numericOperators } from "../../base/FilterBuilder/constants";

Expand All @@ -13,7 +13,10 @@ export {
}

export type { ODataGridProProps } from "./ODataGridProProps"
export type ODataGridColDef<TDate = any> = ODataGridBaseColDef<GridColDef, TDate>
export type ODataGridColDef<TRow = any, TDate = any> = ODataGridBaseColDef<GridColDef<ODataRowModel<TRow>>, TDate>

export type ODataGridEnrichedColDef<TRow = any, V = any, F = any, TDate = any> = ODataGridBaseEnrichedColDef<GridColDef<ODataRowModel<TRow>, V, F>, GridActionsColDef<ODataRowModel<TRow>>, TDate>;
export type ODataGridColumns<TRow = any, TDate = any> = ODataBaseGridColumns<GridEnrichedColDef<ODataRowModel<TRow>>, GridActionsColDef<ODataRowModel<TRow>>, TDate>;

export type { SelectOption, ValueOption, ODataColumnVisibilityModel } from "../../base/types";
export type {
Expand All @@ -32,4 +35,5 @@ export type {
} from "../../base/FilterBuilder/types";
export type { FilterBuilderProps } from "../../base/FilterBuilder/components/FilterBuilder";

export { escapeODataString } from "../../base/FilterBuilder/utils"
export { escapeODataString } from "../../base/FilterBuilder/utils";
export { defaultTranslators } from "../../base/FilterBuilder/translation";
14 changes: 10 additions & 4 deletions packages/o-data-grid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ Please report any issues that you find, and feel free to make feature requests.
## Installation
ODataGrid can be installed using the appropriate npm package ([`o-data-grid`](https://www.npmjs.com/package/o-data-grid) or [`o-data-grid-pro`](https://www.npmjs.com/package/o-data-grid-pro)).

The following material-ui packages must also be installed. I recommend using the latest versions where possible, but they must be v5.x.x. **These are peer dependencies so won't be installed automatically.**
The following mui packages must also be installed. I recommend using the latest versions where possible, the minimum required version for the data-grid packages is **v5.8.0**, but any of the other mui packages must be v5.x.x.

**These are peer dependencies so won't be installed automatically.**
- `@mui/system`
- `@mui/material`
- `@mui/x-date-pickers`
- `@mui/x-data-grid` for `o-data-grid`
- `@mui/x-data-grid-pro` for `o-data-grid-pro`
- `@mui/x-data-grid` (minimum `v5.8.0`) for `o-data-grid`
- `@mui/x-data-grid-pro` (minimum `v5.8.0`) for `o-data-grid-pro`
- `@mui/icons-material`

## Usage
Expand All @@ -48,7 +50,7 @@ From there you can start to customise the grid to your needs using the propertie
If the same reference is not kept, this may trigger duplicate OData requests.

### Helpful Tips
- Nested properties are supported, to define a column for a nested property flatten it as you would for the query string. E.g. to access `Child` from `{ Parent: { Child: "foo" } }`, use `Parent/Child` as the value for `field` in the column definition.
- Nested properties are supported, to define a column for a nested property flatten it as you would for the query string. E.g. to access `Child` from `{ Parent: { Child: "foo" } }`, use `Parent/Child` as the value for `field` in the column definition. Strong typing is still supported through the `result` property of the row.

### Examples
The demo site isn't ready yet, but you can see some examples of usage here on my GitHub:
Expand Down Expand Up @@ -102,6 +104,10 @@ _* = required property_
### <a id="ODataGridColDef">ODataGridColDef</a>
The column definition is again similar to the standard [GridColDef](https://mui.com/components/data-grid/columns/).

Strong typing is supported through the `TRow` generic type. The original unflattened type can be accessed using the `result` property of the grid row parameters. Action columns are also supported using the `ODataGridColumns<T>` type.

The `TDate` generic type is also available to specify the date provider type for dateTime columns with a date picker filter.

#### Modifications
| Name | Change | Description |
| ---- | ------ | ----------- |
Expand Down
Loading

0 comments on commit 3ea5a63

Please sign in to comment.