@teselagen/ui 0.8.6-beta.2 → 0.8.6-beta.20

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.
@@ -11,44 +11,57 @@ export function tableQueryParamsToHasuraClauses({
11
11
  }) {
12
12
  const ccFields = getFieldsMappedByCCDisplayName(schema);
13
13
  let where = {};
14
- const order_by = {};
14
+ const order_by = [];
15
15
  const limit = pageSize || 25;
16
16
  const offset = page && pageSize ? (page - 1) * pageSize : 0;
17
17
 
18
18
  if (searchTerm) {
19
19
  const searchTermFilters = [];
20
+ // Create a map to deduplicate fields by path
21
+ const uniqueFieldsByPath = {};
22
+
23
+ // Split the search term by comma to support multi-term searching
24
+ const searchTerms = searchTerm.split(",");
25
+
20
26
  schema.fields.forEach(field => {
21
27
  const { type, path, searchDisabled } = field;
28
+ if (uniqueFieldsByPath[path]) return; // Skip if already added
29
+ uniqueFieldsByPath[path] = true;
22
30
  if (searchDisabled || field.filterDisabled || type === "color") return;
23
- const filterValue = searchTerm; // No cleaning needed here, we're using _ilike
24
31
 
25
- if (type === "string" || type === "lookup") {
26
- const o = set({}, path, { _ilike: `%${filterValue}%` });
27
- searchTermFilters.push(o);
28
- } else if (type === "boolean") {
29
- let regex;
30
- try {
31
- regex = new RegExp("^" + searchTerm, "ig");
32
- } catch (error) {
33
- //ignore
34
- }
35
- if (regex) {
36
- if ("true".replace(regex, "") !== "true") {
37
- const o = set({}, path, { _eq: true });
38
- searchTermFilters.push(o);
39
- } else if ("false".replace(regex, "") !== "false") {
40
- const o = set({}, path, { _eq: false });
41
- searchTermFilters.push(o);
32
+ // Process each search term
33
+ searchTerms.forEach(term => {
34
+ const filterValue = term.trim(); // Trim the term to handle spaces after commas
35
+
36
+ if (type === "string" || type === "lookup") {
37
+ const o = set({}, path, { _ilike: `%${filterValue}%` });
38
+ searchTermFilters.push(o);
39
+ } else if (type === "boolean") {
40
+ let regex;
41
+ try {
42
+ regex = new RegExp("^" + filterValue, "ig");
43
+ } catch (error) {
44
+ //ignore
45
+ }
46
+ if (regex) {
47
+ if ("true".replace(regex, "") !== "true") {
48
+ const o = set({}, path, { _eq: true });
49
+ searchTermFilters.push(o);
50
+ } else if ("false".replace(regex, "") !== "false") {
51
+ const o = set({}, path, { _eq: false });
52
+ searchTermFilters.push(o);
53
+ }
42
54
  }
55
+ } else if (
56
+ (type === "number" || type === "integer") &&
57
+ !isNaN(filterValue)
58
+ ) {
59
+ const o = set({}, path, { _eq: parseFloat(filterValue) });
60
+ searchTermFilters.push(o);
43
61
  }
44
- } else if (
45
- (type === "number" || type === "integer") &&
46
- !isNaN(filterValue)
47
- ) {
48
- const o = set({}, path, { _eq: parseFloat(filterValue) });
49
- searchTermFilters.push(o);
50
- }
62
+ });
51
63
  });
64
+
52
65
  if (searchTermFilters.length > 0) {
53
66
  if (Object.keys(where).length > 0) {
54
67
  where = { _and: [where, { _or: searchTermFilters }] };
@@ -59,157 +72,161 @@ export function tableQueryParamsToHasuraClauses({
59
72
  }
60
73
 
61
74
  if (filters && filters.length > 0) {
62
- const filterClauses = filters
63
- .map(filter => {
64
- let { selectedFilter, filterOn, filterValue } = filter;
65
- const fieldSchema = ccFields[filterOn] || {};
75
+ const filterClauses = filters.map(filter => {
76
+ let { selectedFilter, filterOn, filterValue } = filter;
77
+ const fieldSchema = ccFields[filterOn] || {};
66
78
 
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(";");
79
+ const { path, reference, type, customColumnFilter } = fieldSchema;
80
+ if (customColumnFilter) {
81
+ return customColumnFilter(filterValue);
82
+ }
83
+ let stringFilterValue =
84
+ filterValue && filterValue.toString
85
+ ? filterValue.toString()
86
+ : filterValue;
87
+ if (stringFilterValue === false) {
88
+ // we still want to be able to search for the string "false" which will get parsed to false
89
+ stringFilterValue = "false";
90
+ } else {
91
+ stringFilterValue = stringFilterValue || "";
92
+ }
93
+ const arrayFilterValue = Array.isArray(filterValue)
94
+ ? filterValue
95
+ : stringFilterValue.split(";");
81
96
 
82
- if (type === "number" || type === "integer") {
83
- filterValue = Array.isArray(filterValue)
84
- ? filterValue.map(val => Number(val))
85
- : Number(filterValue);
86
- }
97
+ if (type === "number" || type === "integer") {
98
+ filterValue = Array.isArray(filterValue)
99
+ ? filterValue.map(val => Number(val))
100
+ : Number(filterValue);
101
+ }
87
102
 
88
- if (fieldSchema.normalizeFilter) {
89
- filterValue = fieldSchema.normalizeFilter(
90
- filterValue,
91
- selectedFilter,
92
- filterOn
93
- );
94
- }
103
+ if (fieldSchema.normalizeFilter) {
104
+ filterValue = fieldSchema.normalizeFilter(
105
+ filterValue,
106
+ selectedFilter,
107
+ filterOn
108
+ );
109
+ }
95
110
 
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":
111
+ if (reference) {
112
+ filterOn = reference.sourceField;
113
+ } else {
114
+ filterOn = path || filterOn;
115
+ }
116
+ switch (selectedFilter) {
117
+ case "none":
118
+ return {};
119
+ case "startsWith":
120
+ return { [filterOn]: { _ilike: `${filterValue}%` } };
121
+ case "endsWith":
122
+ return { [filterOn]: { _ilike: `%${filterValue}` } };
123
+ case "contains":
124
+ return { [filterOn]: { _ilike: `%${filterValue}%` } };
125
+ case "notContains":
126
+ return { [filterOn]: { _nilike: `%${filterValue}%` } };
127
+ case "isExactly":
128
+ return { [filterOn]: { _eq: filterValue } };
129
+ case "isEmpty":
130
+ if (filterOn.includes(".")) {
131
+ // if we're filtering on a nested field, like a sequence table with parts.name
132
+ // we really want to just query on the top level field's existence
156
133
  return {
157
- [filterOn]: {
158
- _gte: new Date(arrayFilterValue[0]),
159
- _lte: new Date(new Date(arrayFilterValue[1]).setHours(23, 59))
134
+ _not: {
135
+ [filterOn.split(".")[0]]: {}
160
136
  }
161
137
  };
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])
138
+ }
139
+ return {
140
+ _or: [
141
+ { [filterOn]: { _eq: "" } },
142
+ { [filterOn]: { _is_null: true } }
143
+ ]
144
+ };
145
+ case "notEmpty":
146
+ return {
147
+ _and: [
148
+ { [filterOn]: { _neq: "" } },
149
+ { [filterOn]: { _is_null: false } }
150
+ ]
151
+ };
152
+ case "inList":
153
+ return { [filterOn]: { _in: filterValue } };
154
+ case "notInList":
155
+ return { [filterOn]: { _nin: filterValue } };
156
+ case "true":
157
+ return { [filterOn]: { _eq: true } };
158
+ case "false":
159
+ return { [filterOn]: { _eq: false } };
160
+ case "dateIs":
161
+ return { [filterOn]: { _eq: filterValue } };
162
+ case "notBetween":
163
+ return {
164
+ _or: [
165
+ {
166
+ [filterOn]: {
167
+ _lt: new Date(arrayFilterValue[0])
168
+ }
169
+ },
170
+ {
171
+ [filterOn]: {
172
+ _gt: new Date(new Date(arrayFilterValue[1]).setHours(23, 59))
173
+ }
175
174
  }
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
- }
175
+ ]
176
+ };
177
+ case "isBetween":
178
+ return {
179
+ [filterOn]: {
180
+ _gte: new Date(arrayFilterValue[0]),
181
+ _lte: new Date(new Date(arrayFilterValue[1]).setHours(23, 59))
182
+ }
183
+ };
184
+ case "isBefore":
185
+ return { [filterOn]: { _lt: new Date(filterValue) } };
186
+ case "isAfter":
187
+ return { [filterOn]: { _gt: new Date(filterValue) } };
188
+ case "greaterThan":
189
+ return { [filterOn]: { _gt: parseFloat(filterValue) } };
190
+ case "lessThan":
191
+ return { [filterOn]: { _lt: parseFloat(filterValue) } };
192
+ case "inRange":
193
+ return {
194
+ [filterOn]: {
195
+ _gte: parseFloat(arrayFilterValue[0]),
196
+ _lte: parseFloat(arrayFilterValue[1])
197
+ }
198
+ };
199
+ case "outsideRange":
200
+ return {
201
+ _or: [
202
+ {
203
+ [filterOn]: {
204
+ _lt: parseFloat(arrayFilterValue[0])
205
+ }
206
+ },
207
+ {
208
+ [filterOn]: {
209
+ _gt: parseFloat(arrayFilterValue[1])
189
210
  }
190
- ]
191
- };
192
- case "equalTo":
193
- return {
194
- [filterOn]: {
195
- _eq:
196
- type === "number" || type === "integer"
197
- ? parseFloat(filterValue)
198
- : filterValue
199
211
  }
200
- };
201
- case "regex":
202
- return { [filterOn]: { _regex: filterValue } };
203
- default:
204
- console.warn(`Unsupported filter type: ${selectedFilter}`);
205
- return {};
206
- }
207
- })
208
- .map(filter => {
209
- const o = {};
210
- set(o, Object.keys(filter)[0], filter[Object.keys(filter)[0]]);
211
- return o;
212
- });
212
+ ]
213
+ };
214
+ case "equalTo":
215
+ return {
216
+ [filterOn]: {
217
+ _eq:
218
+ type === "number" || type === "integer"
219
+ ? parseFloat(filterValue)
220
+ : filterValue
221
+ }
222
+ };
223
+ case "regex":
224
+ return { [filterOn]: { _regex: filterValue } };
225
+ default:
226
+ console.warn(`Unsupported filter type: ${selectedFilter}`);
227
+ return {};
228
+ }
229
+ });
213
230
 
214
231
  if (filterClauses.length > 0) {
215
232
  if (Object.keys(where).length > 0) {
@@ -224,7 +241,7 @@ export function tableQueryParamsToHasuraClauses({
224
241
  order.forEach(item => {
225
242
  const field = item.startsWith("-") ? item.substring(1) : item;
226
243
  const direction = item.startsWith("-") ? "desc" : "asc";
227
- order_by[field] = direction;
244
+ order_by.push({ [field]: direction });
228
245
  });
229
246
  }
230
247
 
@@ -14,7 +14,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
14
14
  const result = tableQueryParamsToHasuraClauses({});
15
15
  expect(result).toEqual({
16
16
  where: {},
17
- order_by: {},
17
+ order_by: [],
18
18
  limit: 25,
19
19
  offset: 0
20
20
  });
@@ -24,7 +24,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
24
24
  const result = tableQueryParamsToHasuraClauses({ page: 2, pageSize: 10 });
25
25
  expect(result).toEqual({
26
26
  where: {},
27
- order_by: {},
27
+ order_by: [],
28
28
  limit: 10,
29
29
  offset: 10
30
30
  });
@@ -39,7 +39,46 @@ describe("tableQueryParamsToHasuraClauses", () => {
39
39
  where: {
40
40
  _or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
41
41
  },
42
- order_by: {},
42
+ order_by: [],
43
+ limit: 25,
44
+ offset: 0
45
+ });
46
+ });
47
+ it("should handle searchTerm with string fields with a comma in them", () => {
48
+ const result = tableQueryParamsToHasuraClauses({
49
+ searchTerm: "test,test2",
50
+ schema
51
+ });
52
+ expect(result).toEqual({
53
+ where: {
54
+ _or: [
55
+ { name: { _ilike: "%test%" } },
56
+ { name: { _ilike: "%test2%" } },
57
+ { email: { _ilike: "%test%" } },
58
+ { email: { _ilike: "%test2%" } }
59
+ ]
60
+ },
61
+ order_by: [],
62
+ limit: 25,
63
+ offset: 0
64
+ });
65
+ });
66
+ it("should flatten queries with dup paths", () => {
67
+ const result = tableQueryParamsToHasuraClauses({
68
+ searchTerm: "test",
69
+ schema: {
70
+ fields: [
71
+ ...schema.fields,
72
+ { path: "name", type: "string" },
73
+ { path: "name", type: "string" }
74
+ ]
75
+ }
76
+ });
77
+ expect(result).toEqual({
78
+ where: {
79
+ _or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
80
+ },
81
+ order_by: [],
43
82
  limit: 25,
44
83
  offset: 0
45
84
  });
@@ -58,7 +97,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
58
97
  { email: { _ilike: "%30%" } }
59
98
  ]
60
99
  },
61
- order_by: {},
100
+ order_by: [],
62
101
  limit: 25,
63
102
  offset: 0
64
103
  });
@@ -77,7 +116,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
77
116
  { email: { _ilike: "%true%" } }
78
117
  ]
79
118
  },
80
- order_by: {},
119
+ order_by: [],
81
120
  limit: 25,
82
121
  offset: 0
83
122
  });
@@ -92,7 +131,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
92
131
  where: {
93
132
  _or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
94
133
  },
95
- order_by: {},
134
+ order_by: [],
96
135
  limit: 25,
97
136
  offset: 0
98
137
  });
@@ -110,7 +149,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
110
149
  });
111
150
  expect(result).toEqual({
112
151
  where: { _and: [{ name: { _ilike: "%test%" } }] },
113
- order_by: {},
152
+ order_by: [],
114
153
  limit: 25,
115
154
  offset: 0
116
155
  });
@@ -125,7 +164,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
125
164
  });
126
165
  expect(result).toEqual({
127
166
  where: { _and: [{ age: { _eq: 30 } }] },
128
- order_by: {},
167
+ order_by: [],
129
168
  limit: 25,
130
169
  offset: 0
131
170
  });
@@ -135,7 +174,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
135
174
  const result = tableQueryParamsToHasuraClauses({ order: ["name", "-age"] });
136
175
  expect(result).toEqual({
137
176
  where: {},
138
- order_by: { name: "asc", age: "desc" },
177
+ order_by: [{ name: "asc" }, { age: "desc" }],
139
178
  limit: 25,
140
179
  offset: 0
141
180
  });
@@ -168,7 +207,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
168
207
  { age: { _gt: 30 } }
169
208
  ]
170
209
  },
171
- order_by: { name: "asc" },
210
+ order_by: [{ name: "asc" }],
172
211
  limit: 10,
173
212
  offset: 10
174
213
  });
@@ -198,7 +237,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
198
237
  { age: { _gt: 30 } }
199
238
  ]
200
239
  },
201
- order_by: {},
240
+ order_by: [],
202
241
  limit: 25,
203
242
  offset: 0
204
243
  });
package/src/index.js CHANGED
@@ -41,6 +41,7 @@ export { default as popoverOverflowModifiers } from "./utils/popoverOverflowModi
41
41
  export * from "./utils/tgFormValues";
42
42
  export { default as tgFormValues } from "./utils/tgFormValues";
43
43
  export { default as withStore } from "./utils/withStore";
44
+ export { default as determineBlackOrWhiteTextColor } from "./utils/determineBlackOrWhiteTextColor";
44
45
  export {
45
46
  default as withTableParams,
46
47
  useTableParams
@@ -1,4 +1,11 @@
1
1
  /* Copyright (C) 2018 TeselaGen Biotechnology, Inc. */
2
2
  import Color from "color";
3
3
 
4
- export default c => (Color(c).isLight() ? "#000000" : "#FFFFFF");
4
+ export default function determineBlackOrWhiteTextColor(c) {
5
+ try {
6
+ return Color(c).isLight() ? "#000000" : "#FFFFFF";
7
+ } catch (e) {
8
+ console.error("Error in color parsing:", e);
9
+ return "#000000"; // Fallback to black if color parsing fails
10
+ }
11
+ }
@@ -1,2 +1 @@
1
- declare function _default(c: any): "#000000" | "#FFFFFF";
2
- export default _default;
1
+ export default function determineBlackOrWhiteTextColor(c: any): "#000000" | "#FFFFFF";