@teselagen/ui 0.7.33-beta.4 → 0.7.33-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,5 @@
1
+ import { camelCase, set } from "lodash-es";
2
+
1
3
  export function tableQueryParamsToHasuraClauses({
2
4
  page,
3
5
  pageSize,
@@ -7,6 +9,7 @@ export function tableQueryParamsToHasuraClauses({
7
9
  schema, // Add schema as a parameter
8
10
  additionalFilter
9
11
  }) {
12
+ const ccFields = getFieldsMappedByCCDisplayName(schema);
10
13
  let where = {};
11
14
  const order_by = {};
12
15
  const limit = pageSize || 25;
@@ -20,9 +23,8 @@ export function tableQueryParamsToHasuraClauses({
20
23
  const filterValue = searchTerm; // No cleaning needed here, we're using _ilike
21
24
 
22
25
  if (type === "string" || type === "lookup") {
23
- searchTermFilters.push({
24
- [path]: { _ilike: `%${filterValue}%` }
25
- });
26
+ const o = set({}, path, { _ilike: `%${filterValue}%` });
27
+ searchTermFilters.push(o);
26
28
  } else if (type === "boolean") {
27
29
  let regex;
28
30
  try {
@@ -32,22 +34,19 @@ export function tableQueryParamsToHasuraClauses({
32
34
  }
33
35
  if (regex) {
34
36
  if ("true".replace(regex, "") !== "true") {
35
- searchTermFilters.push({
36
- [path]: { _eq: true }
37
- });
37
+ const o = set({}, path, { _eq: true });
38
+ searchTermFilters.push(o);
38
39
  } else if ("false".replace(regex, "") !== "false") {
39
- searchTermFilters.push({
40
- [path]: { _eq: false }
41
- });
40
+ const o = set({}, path, { _eq: false });
41
+ searchTermFilters.push(o);
42
42
  }
43
43
  }
44
44
  } else if (
45
45
  (type === "number" || type === "integer") &&
46
46
  !isNaN(filterValue)
47
47
  ) {
48
- searchTermFilters.push({
49
- [path]: { _eq: parseFloat(filterValue) }
50
- });
48
+ const o = set({}, path, { _eq: parseFloat(filterValue) });
49
+ searchTermFilters.push(o);
51
50
  }
52
51
  });
53
52
  if (searchTermFilters.length > 0) {
@@ -60,34 +59,150 @@ export function tableQueryParamsToHasuraClauses({
60
59
  }
61
60
 
62
61
  if (filters && filters.length > 0) {
63
- const filterClauses = filters.map(filter => {
64
- const { selectedFilter, filterOn, filterValue } = filter;
65
- switch (selectedFilter) {
66
- case "textContains":
67
- return { [filterOn]: { _ilike: `%${filterValue}%` } };
68
- case "textEquals":
69
- return { [filterOn]: { _eq: filterValue } };
70
- case "textNotEquals":
71
- return { [filterOn]: { _neq: filterValue } };
72
- case "numberEquals":
73
- return { [filterOn]: { _eq: parseFloat(filterValue) } };
74
- case "numberGreaterThan":
75
- return { [filterOn]: { _gt: parseFloat(filterValue) } };
76
- case "numberLessThan":
77
- return { [filterOn]: { _lt: parseFloat(filterValue) } };
78
- case "numberGreaterThanEquals":
79
- return { [filterOn]: { _gte: parseFloat(filterValue) } };
80
- case "numberLessThanEquals":
81
- return { [filterOn]: { _lte: parseFloat(filterValue) } };
82
- case "isNull":
83
- return { [filterOn]: { _is_null: true } };
84
- case "isNotNull":
85
- return { [filterOn]: { _is_null: false } };
86
- default:
87
- console.warn(`Unsupported filter type: ${selectedFilter}`);
88
- return {};
89
- }
90
- });
62
+ const filterClauses = filters
63
+ .map(filter => {
64
+ let { selectedFilter, filterOn, filterValue } = filter;
65
+ const fieldSchema = ccFields[filterOn] || {};
66
+
67
+ const { path, reference, type } = fieldSchema;
68
+ let stringFilterValue =
69
+ filterValue && filterValue.toString
70
+ ? filterValue.toString()
71
+ : filterValue;
72
+ if (stringFilterValue === false) {
73
+ // we still want to be able to search for the string "false" which will get parsed to false
74
+ stringFilterValue = "false";
75
+ } else {
76
+ stringFilterValue = stringFilterValue || "";
77
+ }
78
+ const arrayFilterValue = Array.isArray(filterValue)
79
+ ? filterValue
80
+ : stringFilterValue.split(";");
81
+
82
+ if (type === "number" || type === "integer") {
83
+ filterValue = Array.isArray(filterValue)
84
+ ? filterValue.map(val => Number(val))
85
+ : Number(filterValue);
86
+ }
87
+
88
+ if (fieldSchema.normalizeFilter) {
89
+ filterValue = fieldSchema.normalizeFilter(
90
+ filterValue,
91
+ selectedFilter,
92
+ filterOn
93
+ );
94
+ }
95
+
96
+ if (reference) {
97
+ filterOn = reference.sourceField;
98
+ } else {
99
+ filterOn = path || filterOn;
100
+ }
101
+ switch (selectedFilter) {
102
+ case "none":
103
+ return {};
104
+ case "startsWith":
105
+ return { [filterOn]: { _ilike: `${filterValue}%` } };
106
+ case "endsWith":
107
+ return { [filterOn]: { _ilike: `%${filterValue}` } };
108
+ case "contains":
109
+ return { [filterOn]: { _ilike: `%${filterValue}%` } };
110
+ case "notContains":
111
+ return { [filterOn]: { _not_ilike: `%${filterValue}%` } };
112
+ case "isExactly":
113
+ return { [filterOn]: { _eq: filterValue } };
114
+ case "isEmpty":
115
+ return {
116
+ _or: [
117
+ { [filterOn]: { _eq: "" } },
118
+ { [filterOn]: { _is_null: true } }
119
+ ]
120
+ };
121
+ case "notEmpty":
122
+ return {
123
+ _and: [
124
+ { [filterOn]: { _neq: "" } },
125
+ { [filterOn]: { _is_null: false } }
126
+ ]
127
+ };
128
+ case "inList":
129
+ return { [filterOn]: { _in: filterValue } };
130
+ case "notInList":
131
+ return { [filterOn]: { _nin: filterValue } };
132
+ case "true":
133
+ return { [filterOn]: { _eq: true } };
134
+ case "false":
135
+ return { [filterOn]: { _eq: false } };
136
+ case "dateIs":
137
+ return { [filterOn]: { _eq: filterValue } };
138
+ case "notBetween":
139
+ return {
140
+ _or: [
141
+ {
142
+ [filterOn]: {
143
+ _lt: new Date(arrayFilterValue[0])
144
+ }
145
+ },
146
+ {
147
+ [filterOn]: {
148
+ _gt: new Date(
149
+ new Date(arrayFilterValue[1]).setHours(23, 59)
150
+ )
151
+ }
152
+ }
153
+ ]
154
+ };
155
+ case "isBetween":
156
+ return {
157
+ [filterOn]: {
158
+ _gte: new Date(arrayFilterValue[0]),
159
+ _lte: new Date(new Date(arrayFilterValue[1]).setHours(23, 59))
160
+ }
161
+ };
162
+ case "isBefore":
163
+ return { [filterOn]: { _lt: new Date(filterValue) } };
164
+ case "isAfter":
165
+ return { [filterOn]: { _gt: new Date(filterValue) } };
166
+ case "greaterThan":
167
+ return { [filterOn]: { _gt: parseFloat(filterValue) } };
168
+ case "lessThan":
169
+ return { [filterOn]: { _lt: parseFloat(filterValue) } };
170
+ case "inRange":
171
+ return {
172
+ [filterOn]: {
173
+ _gte: parseFloat(arrayFilterValue[0]),
174
+ _lte: parseFloat(arrayFilterValue[1])
175
+ }
176
+ };
177
+ case "outsideRange":
178
+ return {
179
+ _or: [
180
+ {
181
+ [filterOn]: {
182
+ _lt: parseFloat(arrayFilterValue[0])
183
+ }
184
+ },
185
+ {
186
+ [filterOn]: {
187
+ _gt: parseFloat(arrayFilterValue[1])
188
+ }
189
+ }
190
+ ]
191
+ };
192
+ case "equalTo":
193
+ return { [filterOn]: { _eq: parseFloat(filterValue) } };
194
+ case "regex":
195
+ return { [filterOn]: { _regex: filterValue } };
196
+ default:
197
+ console.warn(`Unsupported filter type: ${selectedFilter}`);
198
+ return {};
199
+ }
200
+ })
201
+ .map(filter => {
202
+ const o = {};
203
+ set(o, Object.keys(filter)[0], filter[Object.keys(filter)[0]]);
204
+ return o;
205
+ });
91
206
 
92
207
  if (filterClauses.length > 0) {
93
208
  if (Object.keys(where).length > 0) {
@@ -111,3 +226,28 @@ export function tableQueryParamsToHasuraClauses({
111
226
  }
112
227
  return { where, order_by, limit, offset };
113
228
  }
229
+
230
+ /**
231
+ * Takes a schema and returns an object with the fields mapped by their camelCased display name.
232
+ * If the displayName is not set or is a jsx element, the path is used instead.
233
+ * The same conversion must be done when using the result of this method
234
+ */
235
+ export function getFieldsMappedByCCDisplayName(schema) {
236
+ if (!schema || !schema.fields) return {};
237
+ return schema.fields.reduce((acc, field) => {
238
+ const ccDisplayName = getCCDisplayName(field);
239
+ acc[ccDisplayName] = field;
240
+ return acc;
241
+ }, {});
242
+ }
243
+
244
+ /**
245
+ *
246
+ * @param {object} field
247
+ * @returns the camelCase display name of the field, to be used for filters, sorting, etc
248
+ */
249
+ export function getCCDisplayName(field) {
250
+ return camelCase(
251
+ typeof field.displayName === "string" ? field.displayName : field.path
252
+ );
253
+ }
@@ -98,11 +98,11 @@ describe("tableQueryParamsToHasuraClauses", () => {
98
98
  });
99
99
  });
100
100
 
101
- it("should handle textContains filter", () => {
101
+ it("should handle contains filter", () => {
102
102
  const result = tableQueryParamsToHasuraClauses({
103
103
  filters: [
104
104
  {
105
- selectedFilter: "textContains",
105
+ selectedFilter: "contains",
106
106
  filterOn: "name",
107
107
  filterValue: "test"
108
108
  }
@@ -116,25 +116,12 @@ describe("tableQueryParamsToHasuraClauses", () => {
116
116
  });
117
117
  });
118
118
 
119
- it("should handle textEquals filter", () => {
119
+ it("should handle equalTo filter for number", () => {
120
120
  const result = tableQueryParamsToHasuraClauses({
121
121
  filters: [
122
- { selectedFilter: "textEquals", filterOn: "name", filterValue: "test" }
123
- ]
124
- });
125
- expect(result).toEqual({
126
- where: { _and: [{ name: { _eq: "test" } }] },
127
- order_by: {},
128
- limit: 25,
129
- offset: 0
130
- });
131
- });
132
-
133
- it("should handle numberEquals filter", () => {
134
- const result = tableQueryParamsToHasuraClauses({
135
- filters: [
136
- { selectedFilter: "numberEquals", filterOn: "age", filterValue: "30" }
137
- ]
122
+ { selectedFilter: "equalTo", filterOn: "age", filterValue: "30" }
123
+ ],
124
+ schema
138
125
  });
139
126
  expect(result).toEqual({
140
127
  where: { _and: [{ age: { _eq: 30 } }] },
@@ -161,7 +148,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
161
148
  searchTerm: "test",
162
149
  filters: [
163
150
  {
164
- selectedFilter: "numberGreaterThan",
151
+ selectedFilter: "greaterThan",
165
152
  filterOn: "age",
166
153
  filterValue: "30"
167
154
  }
@@ -192,7 +179,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
192
179
  searchTerm: "test",
193
180
  filters: [
194
181
  {
195
- selectedFilter: "numberGreaterThan",
182
+ selectedFilter: "greaterThan",
196
183
  filterOn: "age",
197
184
  filterValue: "30"
198
185
  }
@@ -6,13 +6,13 @@ import {
6
6
  makeDataTableHandlers,
7
7
  getQueryParams,
8
8
  setCurrentParamsOnUrl,
9
- getCurrentParamsFromUrl,
10
- getCCDisplayName
9
+ getCurrentParamsFromUrl
11
10
  } from "./queryParams";
12
11
  import { withRouter } from "react-router-dom";
13
12
  import getTableConfigFromStorage from "./getTableConfigFromStorage";
14
13
  import { useDeepEqualMemo } from "../../utils/hooks/useDeepEqualMemo";
15
14
  import { branch, compose } from "recompose";
15
+ import { getCCDisplayName } from "./tableQueryParamsToHasuraClauses";
16
16
 
17
17
  /**
18
18
  * Note all these options can be passed at Design Time or at Runtime (like reduxForm())
@@ -1 +0,0 @@
1
- export function simplifyHasuraWhere(whereClause: any): {};
@@ -1,80 +0,0 @@
1
- export function simplifyHasuraWhere(whereClause) {
2
- const simplifiedWhere = {};
3
-
4
- for (const key in whereClause) {
5
- // eslint-disable-next-line no-prototype-builtins
6
- if (whereClause.hasOwnProperty(key)) {
7
- const value = whereClause[key];
8
-
9
- if (
10
- typeof value === "object" &&
11
- value !== null &&
12
- !Array.isArray(value)
13
- ) {
14
- if (key.includes(".")) {
15
- // Handle dot-nested where clauses
16
- const keys = key.split(".");
17
- let currentObj = simplifiedWhere;
18
- for (let i = 0; i < keys.length - 1; i++) {
19
- const nestedKey = keys[i];
20
- if (!currentObj[nestedKey]) {
21
- currentObj[nestedKey] = {};
22
- }
23
- currentObj = currentObj[nestedKey];
24
- }
25
- if (typeof value === "object" && value !== null && "_eq" in value) {
26
- currentObj[keys[keys.length - 1]] = value;
27
- } else {
28
- currentObj[keys[keys.length - 1]] = { _eq: value };
29
- }
30
- } else {
31
- // Handle regular Hasura operators or already nested objects.
32
- if (
33
- typeof value === "object" &&
34
- value !== null &&
35
- !("_eq" in value) &&
36
- !("_gt" in value) &&
37
- !("_lt" in value) &&
38
- !("_gte" in value) &&
39
- !("_lte" in value) &&
40
- !("_in" in value) &&
41
- !("_nin" in value) &&
42
- !("_neq" in value) &&
43
- !("_like" in value) &&
44
- !("_nlike" in value) &&
45
- !("_ilike" in value) &&
46
- !("_nilike" in value) &&
47
- !("_similar" in value) &&
48
- !("_nsimilar" in value) &&
49
- !("_regex" in value) &&
50
- !("_nregex" in value) &&
51
- !("_iregex" in value) &&
52
- !("_niregex" in value)
53
- ) {
54
- simplifiedWhere[key] = simplifyHasuraWhere(value);
55
- } else {
56
- simplifiedWhere[key] = value;
57
- }
58
- }
59
- } else {
60
- // Handle simplified _eq where clauses
61
- if (key.includes(".")) {
62
- const keys = key.split(".");
63
- let currentObj = simplifiedWhere;
64
- for (let i = 0; i < keys.length - 1; i++) {
65
- const nestedKey = keys[i];
66
- if (!currentObj[nestedKey]) {
67
- currentObj[nestedKey] = {};
68
- }
69
- currentObj = currentObj[nestedKey];
70
- }
71
- currentObj[keys[keys.length - 1]] = { _eq: value };
72
- } else {
73
- simplifiedWhere[key] = { _eq: value };
74
- }
75
- }
76
- }
77
- }
78
-
79
- return simplifiedWhere;
80
- }
@@ -1,73 +0,0 @@
1
- import { simplifyHasuraWhere } from "./simplifyHasuraWhere";
2
-
3
- describe("simplifyHasuraWhere", () => {
4
- it("should handle empty where clause", () => {
5
- expect(simplifyHasuraWhere({})).toEqual({});
6
- });
7
-
8
- it("should simplify simple _eq where clauses", () => {
9
- const input = { id: 123, name: "John Doe" };
10
- const expected = { id: { _eq: 123 }, name: { _eq: "John Doe" } };
11
- expect(simplifyHasuraWhere(input)).toEqual(expected);
12
- });
13
-
14
- it("should handle existing _eq where clauses without simplification", () => {
15
- const input = { id: { _eq: 123 }, name: { _eq: "John Doe" } };
16
- const expected = { id: { _eq: 123 }, name: { _eq: "John Doe" } };
17
- expect(simplifyHasuraWhere(input)).toEqual(expected);
18
- });
19
-
20
- it("should handle other Hasura operators", () => {
21
- const input = { age: { _gt: 30 }, isActive: { _eq: true } };
22
- const expected = { age: { _gt: 30 }, isActive: { _eq: true } };
23
- expect(simplifyHasuraWhere(input)).toEqual(expected);
24
- });
25
-
26
- it("should handle dot-nested where clauses", () => {
27
- const input = { "address.city": { _eq: "New York" }, "address.zip": 10001 };
28
- const expected = {
29
- address: { city: { _eq: "New York" }, zip: { _eq: 10001 } }
30
- };
31
- expect(simplifyHasuraWhere(input)).toEqual(expected);
32
- });
33
-
34
- it("should handle deeply nested dot-nested where clauses", () => {
35
- const input = { "nested.prop.value": { _eq: "test" } };
36
- const expected = { nested: { prop: { value: { _eq: "test" } } } };
37
- expect(simplifyHasuraWhere(input)).toEqual(expected);
38
- });
39
-
40
- it("should handle a mix of simple, nested, and operator clauses", () => {
41
- const input = {
42
- id: 123,
43
- name: "John Doe",
44
- "address.city": { _eq: "New York" },
45
- age: { _gt: 30 },
46
- "nested.prop.value": { _eq: "test" }
47
- };
48
- const expected = {
49
- id: { _eq: 123 },
50
- name: { _eq: "John Doe" },
51
- address: { city: { _eq: "New York" } },
52
- age: { _gt: 30 },
53
- nested: { prop: { value: { _eq: "test" } } }
54
- };
55
- expect(simplifyHasuraWhere(input)).toEqual(expected);
56
- });
57
-
58
- it("should handle already nested objects with operators", () => {
59
- const input = {
60
- address: {
61
- city: { _eq: "London" },
62
- country: "UK"
63
- }
64
- };
65
- const expected = {
66
- address: {
67
- city: { _eq: "London" },
68
- country: { _eq: "UK" }
69
- }
70
- };
71
- expect(simplifyHasuraWhere(input)).toEqual(expected);
72
- });
73
- });