Skip to content

Commit

Permalink
Initial nested expand support implemention
Browse files Browse the repository at this point in the history
  • Loading branch information
jamerst committed Jul 24, 2022
1 parent 54e9fd6 commit bd2480d
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 54 deletions.
43 changes: 24 additions & 19 deletions packages/base/components/ODataGridBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,34 @@ const ODataGridBase = <ComponentProps extends IGridProps,
filterSelects.forEach((s) => fields.add(s));
}

// group all expands by the navigation field
const groupedExpands = GroupArrayBy(
props.columns
.filter(c => visibleColumns.includes(c.field) && !!c.expand)
.map(c => c.expand!),
(e) => e.navigationField
);
const expands = props.columns
.filter(c => visibleColumns.includes(c.field) && c.expand)
.map(c => c.expand!);

// construct a single expand for each navigation field, combining nested query options
const expands: Expand[] = [];
groupedExpands.forEach((e, k) => {
expands.push({
navigationField: k,
top: e[0].top,
orderBy: e[0].orderBy,
count: e.some(e2 => e2.count),
select: Array.from(new Set(e.filter(e2 => e2.select).map(e2 => e2.select))).join(","),
});
});
// group all expands by the navigation field
// const groupedExpands = GroupArrayBy(
// props.columns
// .filter(c => visibleColumns.includes(c.field) && !!c.expand)
// .map(c => c.expand!),
// (e) => e.navigationField
// );

// // construct a single expand for each navigation field, combining nested query options
// const expands: Expand[] = [];
// groupedExpands.forEach((e, k) => {
// expands.push({
// navigationField: k,
// top: e.find(e2 => e2.top)?.top,
// orderBy: e.find(e2 => e2.orderBy)?.orderBy,
// count: e.some(e2 => e2.count),
// select: Array.from(new Set(e.filter(e2 => e2.select).map(e2 => e2.select))).join(","),
// expand: e.find(e2 => e2.expand)
// });
// });

const query = new URLSearchParams();
query.append("$select", Array.from(fields).join(","));
query.append("$expand", expands.map(e => ExpandToQuery(e)).join(","));
query.append("$expand", ExpandToQuery(expands));
query.append("$top", pageSize.toString());
query.append("$skip", (pageNumber * pageSize).toString());

Expand Down
2 changes: 1 addition & 1 deletion packages/base/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export type ODataResponse = {
export type Expand = {
navigationField: string,
select?: string,
expand?: Expand,
expand?: Expand[] | Expand,
orderBy?: string,
top?: number,
count?: boolean
Expand Down
53 changes: 39 additions & 14 deletions packages/base/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,51 @@ import { defaultPageSize } from "./constants";
* @param e Expand to convert
* @returns OData expand clause string
*/
export const ExpandToQuery = (e?: Expand) => {
if (e === undefined) {
export const ExpandToQuery = (expand?: Expand[] | Expand): string => {
if (expand === undefined) {
return "";
}

let result = `${e.navigationField}`;
if (!Array.isArray(expand)) {
return ExpandToQuery([expand]);
}

const options = [
{ type: "select", value: e.select },
{ type: "expand", value: ExpandToQuery(e.expand) },
{ type: "orderby", value: e.orderBy },
{ type: "top", value: e.top },
{ type: "count", value: e.count }
];
// group all expands by the navigation field
const groupedExpands = GroupArrayBy(expand, (e) => e.navigationField);

if (options.some(o => o.value)) {
result += `(${options.filter(o => o.value).map(o => `$${o.type}=${o.value}`).join(";")})`
}
// construct a single expand for each navigation field, combining nested query options (where possible)
const expands: Expand[] = [];
groupedExpands.forEach((e, k) => {
expands.push({
navigationField: k,
top: e.find(e2 => e2.top)?.top,
orderBy: e.find(e2 => e2.orderBy)?.orderBy,
count: e.some(e2 => e2.count),
select: Array.from(new Set(e.filter(e2 => e2.select).map(e2 => e2.select))).join(","),
expand: e.filter(e2 => e2.expand)
.map(e2 => e2.expand!)
.reduce((a: Expand[], b) => Array.isArray(b) ? a.concat(b) : [...a, b], [])
});
});

return expands.map(e => {
let result = `${e.navigationField}`;

const options = [
{ type: "select", value: e.select },
{ type: "expand", value: ExpandToQuery(e.expand) },
{ type: "orderby", value: e.orderBy },
{ type: "top", value: e.top },
{ type: "count", value: e.count }
];

if (options.some(o => o.value)) {
result += `(${options.filter(o => o.value).map(o => `$${o.type}=${o.value}`).join(";")})`
}

return result;

return result;
}).join(",")
}

/**
Expand Down
71 changes: 51 additions & 20 deletions packages/o-data-grid/dev/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ODataGridColDef, ODataColumnVisibilityModel, escapeODataString } from "
import ODataGrid from "../src/ODataGrid";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";
import { ExpandToQuery } from "../../base/utils";

const theme = createTheme({
palette: {
Expand All @@ -19,6 +20,18 @@ export const muiCache = createCache({
});

const App = () => {
console.debug(ExpandToQuery({
navigationField: "jobCategories",
expand: {
navigationField: "category",
select: "name",
expand: {
navigationField: "job",
select: "title"
}
}
}));

return (
<CacheProvider value={muiCache}>
<ThemeProvider theme={theme}>
Expand All @@ -27,7 +40,6 @@ const App = () => {
url="http://0.0.0.0:5000/api/odata/job"
columns={columns}
columnVisibilityModel={columnVisibility}
getRowId={(row) => row.Id}
defaultSortModel={defaultSort}
filterBuilderProps={filterBuilderProps}
alwaysSelect={alwaysFetch}
Expand All @@ -47,13 +59,13 @@ const filterBuilderProps = { autocompleteGroups: ["Job", "Company"] };
const alwaysFetch = ["Id", "Archived"];
const columns: ODataGridColDef[] = [
{
field: "Title",
field: "title",
headerName: "Job Title",
flex: 2,
autocompleteGroup: "Job"
},
{
field: "Location",
field: "location",
headerName: "Location",
flex: 1,
renderCustomFilter: (value, setValue) => (
Expand Down Expand Up @@ -94,60 +106,79 @@ const columns: ODataGridColDef[] = [
}
};
},
valueGetter: (params) => `${params.row.Location}${params.row.Distance ? ` (${params.row.Distance.toFixed(1)} mi away)` : ""}`,
valueGetter: (params) => `${params.row.location}${params.row.distance ? ` (${params.row.distance.toFixed(1)} mi away)` : ""}`,
autocompleteGroup: "Job"
},
{
field: "Company/Name",
field: "company/name",
headerName: "Company",
flex: 2,
renderCell: (params) => (
<Grid container spacing={1} alignItems="center" wrap="nowrap">
<Grid item>
{params.value}
</Grid>
{params.row["Company/Recruiter"] && <Grid item><Chip sx={{ cursor: "pointer" }} label="Recruiter" size="small" /></Grid>}
{params.row["Company/Blacklisted"] && <Grid item><Chip sx={{ cursor: "pointer" }} label="Blacklisted" size="small" color="error" /></Grid>}
{params.row["company/recruiter"] && <Grid item><Chip sx={{ cursor: "pointer" }} label="Recruiter" size="small" /></Grid>}
{params.row["company/blacklisted"] && <Grid item><Chip sx={{ cursor: "pointer" }} label="Blacklisted" size="small" color="error" /></Grid>}
</Grid>
),
expand: { navigationField: "Company", select: "Id,Name,Recruiter,Blacklisted,Watched" },
expand: { navigationField: "company", select: "id,name,recruiter,blacklisted,watched" },
autocompleteGroup: "Company"
},
{
field: "Salary",
field: "salary",
headerName: "Salary",
type: "number",
filterField: "AvgYearlySalary",
sortField: "AvgYearlySalary",
filterField: "avgYearlySalary",
sortField: "avgYearlySalary",
label: "Median Annual Salary",
filterType: "number",
filterOperators: ["eq", "ne", "gt", "lt", "ge", "le", "null", "notnull"],
flex: 1,
autocompleteGroup: "Job"
},
{
field: "Status",
field: "status",
headerName: "Status",
type: "singleSelect",
valueOptions: ["Not Applied", "Awaiting Response", "In Progress", "Rejected", "Dropped Out"],
filterOperators: ["eq", "ne"],
autocompleteGroup: "Job"
},
{
field: "JobCategories",
field: "jobCategories",
headerName: "Categories",
label: "Category",
expand: {
navigationField: "JobCategories/Category",
select: "Name"
navigationField: "jobCategories",
expand: {
navigationField: "category",
select: "name",
expand: [
{
navigationField: "companyCategories",
count: true
},
{
navigationField: "companyCategories",
count: true
},
{
navigationField: "jobCategories",
count: true
},
]
}
},
sortable: false,
filterable: false,
flex: 1,
renderCell: (params) => params.row.JobCategories.map((c: any) => c["Category/Name"]).join(", "),
renderCell: (params) => params.row.jobCategories.map((c: any) => c["category/name"]).join(", "),
autocompleteGroup: "Job"
},
{
field: "Source/DisplayName",
expand: { navigationField: "Source", select: "DisplayName" },
field: "source/displayName",
expand: { navigationField: "source", select: "displayName" },
headerName: "Source",
filterable: false,
sortable: false,
Expand All @@ -156,8 +187,8 @@ const columns: ODataGridColDef[] = [
autocompleteGroup: "Job"
},
{
field: "Posted",
select: "Posted,Seen,Archived",
field: "posted",
select: "posted,seen,archived",
headerName: "Posted",
type: "date",
flex: .9,
Expand Down

0 comments on commit bd2480d

Please sign in to comment.