@teselagen/ui 0.8.8 → 0.9.3
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.
- package/DataTable/utils/filterLocalEntitiesToHasura.d.ts +5 -0
- package/DataTable/utils/getAllRows.d.ts +1 -1
- package/DataTable/utils/getRowCopyText.d.ts +1 -3
- package/DataTable/utils/handleCopyColumn.d.ts +1 -1
- package/DataTable/utils/handleCopyRows.d.ts +1 -5
- package/DataTable/utils/handleCopyTable.d.ts +1 -1
- package/DataTable/utils/index.d.ts +0 -1
- package/DataTable/utils/initializeHasuraWhereAndFilter.d.ts +1 -0
- package/DataTable/utils/queryParams.d.ts +16 -12
- package/DataTable/utils/rowClick.d.ts +1 -1
- package/DataTable/utils/tableQueryParamsToHasuraClauses.d.ts +26 -0
- package/FormComponents/Uploader.d.ts +1 -3
- package/FormComponents/tryToMatchSchemas.d.ts +1 -1
- package/MenuBar/index.d.ts +1 -3
- package/README.md +1 -1
- package/ResizableDraggableDialog/index.d.ts +1 -3
- package/TagSelect/index.d.ts +1 -1
- package/index.cjs.js +40098 -36878
- package/index.d.ts +2 -0
- package/index.es.js +39112 -35892
- package/package.json +2 -4
- package/src/DataTable/Columns.js +2 -2
- package/src/DataTable/DisplayOptions.js +1 -1
- package/src/DataTable/FilterAndSortMenu.js +27 -30
- package/src/DataTable/index.js +113 -90
- package/src/DataTable/style.css +1 -1
- package/src/DataTable/utils/filterLocalEntitiesToHasura.js +356 -0
- package/src/DataTable/utils/filterLocalEntitiesToHasura.test.js +1285 -0
- package/src/DataTable/utils/getAllRows.js +2 -6
- package/src/DataTable/utils/handleCopyColumn.js +2 -2
- package/src/DataTable/utils/handleCopyTable.js +2 -2
- package/src/DataTable/utils/initializeHasuraWhereAndFilter.js +15 -0
- package/src/DataTable/utils/queryParams.js +153 -770
- package/src/DataTable/utils/tableQueryParamsToHasuraClauses.js +277 -0
- package/src/DataTable/utils/tableQueryParamsToHasuraClauses.test.js +245 -0
- package/src/DataTable/utils/withTableParams.js +3 -16
- package/src/FormComponents/index.js +2 -2
- package/src/TgSelect/index.js +15 -0
- package/src/index.js +2 -0
- package/src/utils/determineBlackOrWhiteTextColor.js +8 -1
- package/ui.css +10537 -0
- package/utils/determineBlackOrWhiteTextColor.d.ts +1 -2
- package/utils/hotkeyUtils.d.ts +1 -3
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { camelCase, set } from "lodash-es";
|
|
2
|
+
|
|
3
|
+
export function tableQueryParamsToHasuraClauses({
|
|
4
|
+
page,
|
|
5
|
+
pageSize,
|
|
6
|
+
searchTerm,
|
|
7
|
+
filters,
|
|
8
|
+
order,
|
|
9
|
+
schema, // Add schema as a parameter
|
|
10
|
+
additionalFilter
|
|
11
|
+
}) {
|
|
12
|
+
const ccFields = getFieldsMappedByCCDisplayName(schema);
|
|
13
|
+
let where = {};
|
|
14
|
+
const order_by = [];
|
|
15
|
+
const limit = pageSize || 25;
|
|
16
|
+
const offset = page && pageSize ? (page - 1) * pageSize : 0;
|
|
17
|
+
|
|
18
|
+
if (searchTerm) {
|
|
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
|
+
|
|
26
|
+
schema.fields.forEach(field => {
|
|
27
|
+
const { type, path, searchDisabled } = field;
|
|
28
|
+
if (uniqueFieldsByPath[path]) return; // Skip if already added
|
|
29
|
+
uniqueFieldsByPath[path] = true;
|
|
30
|
+
if (searchDisabled || field.filterDisabled || type === "color") return;
|
|
31
|
+
|
|
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
|
+
}
|
|
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);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (searchTermFilters.length > 0) {
|
|
66
|
+
if (Object.keys(where).length > 0) {
|
|
67
|
+
where = { _and: [where, { _or: searchTermFilters }] };
|
|
68
|
+
} else {
|
|
69
|
+
where = { _or: searchTermFilters };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (filters && filters.length > 0) {
|
|
75
|
+
const filterClauses = filters.map(filter => {
|
|
76
|
+
let { selectedFilter, filterOn, filterValue } = filter;
|
|
77
|
+
const fieldSchema = ccFields[filterOn] || {};
|
|
78
|
+
|
|
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(";");
|
|
96
|
+
|
|
97
|
+
if (type === "number" || type === "integer") {
|
|
98
|
+
filterValue = Array.isArray(filterValue)
|
|
99
|
+
? filterValue.map(val => Number(val))
|
|
100
|
+
: Number(filterValue);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (fieldSchema.normalizeFilter) {
|
|
104
|
+
filterValue = fieldSchema.normalizeFilter(
|
|
105
|
+
filterValue,
|
|
106
|
+
selectedFilter,
|
|
107
|
+
filterOn
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
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
|
|
133
|
+
return {
|
|
134
|
+
_not: {
|
|
135
|
+
[filterOn.split(".")[0]]: {}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
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
|
+
}
|
|
174
|
+
}
|
|
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])
|
|
210
|
+
}
|
|
211
|
+
}
|
|
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
|
+
});
|
|
230
|
+
|
|
231
|
+
if (filterClauses.length > 0) {
|
|
232
|
+
if (Object.keys(where).length > 0) {
|
|
233
|
+
where = { _and: [where, ...filterClauses] };
|
|
234
|
+
} else {
|
|
235
|
+
where = { _and: filterClauses };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (order && order.length > 0) {
|
|
241
|
+
order.forEach(item => {
|
|
242
|
+
const field = item.startsWith("-") ? item.substring(1) : item;
|
|
243
|
+
const direction = item.startsWith("-") ? "desc" : "asc";
|
|
244
|
+
order_by.push({ [field]: direction });
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (additionalFilter) {
|
|
249
|
+
where = { _and: [where, additionalFilter] };
|
|
250
|
+
}
|
|
251
|
+
return { where, order_by, limit, offset };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Takes a schema and returns an object with the fields mapped by their camelCased display name.
|
|
256
|
+
* If the displayName is not set or is a jsx element, the path is used instead.
|
|
257
|
+
* The same conversion must be done when using the result of this method
|
|
258
|
+
*/
|
|
259
|
+
export function getFieldsMappedByCCDisplayName(schema) {
|
|
260
|
+
if (!schema || !schema.fields) return {};
|
|
261
|
+
return schema.fields.reduce((acc, field) => {
|
|
262
|
+
const ccDisplayName = getCCDisplayName(field);
|
|
263
|
+
acc[ccDisplayName] = field;
|
|
264
|
+
return acc;
|
|
265
|
+
}, {});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
*
|
|
270
|
+
* @param {object} field
|
|
271
|
+
* @returns the camelCase display name of the field, to be used for filters, sorting, etc
|
|
272
|
+
*/
|
|
273
|
+
export function getCCDisplayName(field) {
|
|
274
|
+
return camelCase(
|
|
275
|
+
typeof field.displayName === "string" ? field.displayName : field.path
|
|
276
|
+
);
|
|
277
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { tableQueryParamsToHasuraClauses } from "./tableQueryParamsToHasuraClauses";
|
|
2
|
+
|
|
3
|
+
describe("tableQueryParamsToHasuraClauses", () => {
|
|
4
|
+
const schema = {
|
|
5
|
+
fields: [
|
|
6
|
+
{ path: "name", type: "string" },
|
|
7
|
+
{ path: "age", type: "number" },
|
|
8
|
+
{ path: "isActive", type: "boolean" },
|
|
9
|
+
{ path: "email", type: "string" }
|
|
10
|
+
]
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
it("should handle empty query params", () => {
|
|
14
|
+
const result = tableQueryParamsToHasuraClauses({});
|
|
15
|
+
expect(result).toEqual({
|
|
16
|
+
where: {},
|
|
17
|
+
order_by: [],
|
|
18
|
+
limit: 25,
|
|
19
|
+
offset: 0
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should handle page and pageSize", () => {
|
|
24
|
+
const result = tableQueryParamsToHasuraClauses({ page: 2, pageSize: 10 });
|
|
25
|
+
expect(result).toEqual({
|
|
26
|
+
where: {},
|
|
27
|
+
order_by: [],
|
|
28
|
+
limit: 10,
|
|
29
|
+
offset: 10
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should handle searchTerm with string fields", () => {
|
|
34
|
+
const result = tableQueryParamsToHasuraClauses({
|
|
35
|
+
searchTerm: "test",
|
|
36
|
+
schema
|
|
37
|
+
});
|
|
38
|
+
expect(result).toEqual({
|
|
39
|
+
where: {
|
|
40
|
+
_or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
|
|
41
|
+
},
|
|
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: [],
|
|
82
|
+
limit: 25,
|
|
83
|
+
offset: 0
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should handle searchTerm with number fields", () => {
|
|
88
|
+
const result = tableQueryParamsToHasuraClauses({
|
|
89
|
+
searchTerm: "30",
|
|
90
|
+
schema
|
|
91
|
+
});
|
|
92
|
+
expect(result).toEqual({
|
|
93
|
+
where: {
|
|
94
|
+
_or: [
|
|
95
|
+
{ name: { _ilike: "%30%" } },
|
|
96
|
+
{ age: { _eq: 30 } },
|
|
97
|
+
{ email: { _ilike: "%30%" } }
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
order_by: [],
|
|
101
|
+
limit: 25,
|
|
102
|
+
offset: 0
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should handle searchTerm with boolean fields", () => {
|
|
107
|
+
const result = tableQueryParamsToHasuraClauses({
|
|
108
|
+
searchTerm: "true",
|
|
109
|
+
schema
|
|
110
|
+
});
|
|
111
|
+
expect(result).toEqual({
|
|
112
|
+
where: {
|
|
113
|
+
_or: [
|
|
114
|
+
{ name: { _ilike: "%true%" } },
|
|
115
|
+
{ isActive: { _eq: true } },
|
|
116
|
+
{ email: { _ilike: "%true%" } }
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
order_by: [],
|
|
120
|
+
limit: 25,
|
|
121
|
+
offset: 0
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("should handle searchTerm with multiple field types", () => {
|
|
126
|
+
const result = tableQueryParamsToHasuraClauses({
|
|
127
|
+
searchTerm: "test",
|
|
128
|
+
schema
|
|
129
|
+
});
|
|
130
|
+
expect(result).toEqual({
|
|
131
|
+
where: {
|
|
132
|
+
_or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
|
|
133
|
+
},
|
|
134
|
+
order_by: [],
|
|
135
|
+
limit: 25,
|
|
136
|
+
offset: 0
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should handle contains filter", () => {
|
|
141
|
+
const result = tableQueryParamsToHasuraClauses({
|
|
142
|
+
filters: [
|
|
143
|
+
{
|
|
144
|
+
selectedFilter: "contains",
|
|
145
|
+
filterOn: "name",
|
|
146
|
+
filterValue: "test"
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
});
|
|
150
|
+
expect(result).toEqual({
|
|
151
|
+
where: { _and: [{ name: { _ilike: "%test%" } }] },
|
|
152
|
+
order_by: [],
|
|
153
|
+
limit: 25,
|
|
154
|
+
offset: 0
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should handle equalTo filter for number", () => {
|
|
159
|
+
const result = tableQueryParamsToHasuraClauses({
|
|
160
|
+
filters: [
|
|
161
|
+
{ selectedFilter: "equalTo", filterOn: "age", filterValue: "30" }
|
|
162
|
+
],
|
|
163
|
+
schema
|
|
164
|
+
});
|
|
165
|
+
expect(result).toEqual({
|
|
166
|
+
where: { _and: [{ age: { _eq: 30 } }] },
|
|
167
|
+
order_by: [],
|
|
168
|
+
limit: 25,
|
|
169
|
+
offset: 0
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("should handle order", () => {
|
|
174
|
+
const result = tableQueryParamsToHasuraClauses({ order: ["name", "-age"] });
|
|
175
|
+
expect(result).toEqual({
|
|
176
|
+
where: {},
|
|
177
|
+
order_by: [{ name: "asc" }, { age: "desc" }],
|
|
178
|
+
limit: 25,
|
|
179
|
+
offset: 0
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("should combine all params", () => {
|
|
184
|
+
const result = tableQueryParamsToHasuraClauses({
|
|
185
|
+
page: 2,
|
|
186
|
+
pageSize: 10,
|
|
187
|
+
searchTerm: "test",
|
|
188
|
+
filters: [
|
|
189
|
+
{
|
|
190
|
+
selectedFilter: "greaterThan",
|
|
191
|
+
filterOn: "age",
|
|
192
|
+
filterValue: "30"
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
order: ["name"],
|
|
196
|
+
schema
|
|
197
|
+
});
|
|
198
|
+
expect(result).toEqual({
|
|
199
|
+
where: {
|
|
200
|
+
_and: [
|
|
201
|
+
{
|
|
202
|
+
_or: [
|
|
203
|
+
{ name: { _ilike: "%test%" } },
|
|
204
|
+
{ email: { _ilike: "%test%" } }
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
{ age: { _gt: 30 } }
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
order_by: [{ name: "asc" }],
|
|
211
|
+
limit: 10,
|
|
212
|
+
offset: 10
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("should combine searchTerm and filters", () => {
|
|
217
|
+
const result = tableQueryParamsToHasuraClauses({
|
|
218
|
+
searchTerm: "test",
|
|
219
|
+
filters: [
|
|
220
|
+
{
|
|
221
|
+
selectedFilter: "greaterThan",
|
|
222
|
+
filterOn: "age",
|
|
223
|
+
filterValue: "30"
|
|
224
|
+
}
|
|
225
|
+
],
|
|
226
|
+
schema
|
|
227
|
+
});
|
|
228
|
+
expect(result).toEqual({
|
|
229
|
+
where: {
|
|
230
|
+
_and: [
|
|
231
|
+
{
|
|
232
|
+
_or: [
|
|
233
|
+
{ name: { _ilike: "%test%" } },
|
|
234
|
+
{ email: { _ilike: "%test%" } }
|
|
235
|
+
]
|
|
236
|
+
},
|
|
237
|
+
{ age: { _gt: 30 } }
|
|
238
|
+
]
|
|
239
|
+
},
|
|
240
|
+
order_by: [],
|
|
241
|
+
limit: 25,
|
|
242
|
+
offset: 0
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|
|
@@ -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())
|
|
@@ -32,7 +32,6 @@ import { branch, compose } from "recompose";
|
|
|
32
32
|
export const useTableParams = props => {
|
|
33
33
|
const {
|
|
34
34
|
additionalFilter,
|
|
35
|
-
additionalOrFilter,
|
|
36
35
|
controlled_pageSize,
|
|
37
36
|
defaults: _defaults,
|
|
38
37
|
doNotCoercePageSize,
|
|
@@ -166,16 +165,6 @@ export const useTableParams = props => {
|
|
|
166
165
|
);
|
|
167
166
|
|
|
168
167
|
const queryParams = useMemo(() => {
|
|
169
|
-
const additionalFilterToUse =
|
|
170
|
-
typeof additionalFilter === "function"
|
|
171
|
-
? additionalFilter
|
|
172
|
-
: () => additionalFilter;
|
|
173
|
-
|
|
174
|
-
const additionalOrFilterToUse =
|
|
175
|
-
typeof additionalOrFilter === "function"
|
|
176
|
-
? additionalOrFilter
|
|
177
|
-
: () => additionalOrFilter;
|
|
178
|
-
|
|
179
168
|
return getQueryParams({
|
|
180
169
|
doNotCoercePageSize,
|
|
181
170
|
currentParams,
|
|
@@ -185,8 +174,7 @@ export const useTableParams = props => {
|
|
|
185
174
|
schema: convertedSchema,
|
|
186
175
|
isInfinite: isInfinite || (isSimple && !withPaging),
|
|
187
176
|
isLocalCall,
|
|
188
|
-
additionalFilter
|
|
189
|
-
additionalOrFilter: additionalOrFilterToUse,
|
|
177
|
+
additionalFilter,
|
|
190
178
|
noOrderError,
|
|
191
179
|
isCodeModel,
|
|
192
180
|
ownProps: passingProps
|
|
@@ -194,7 +182,6 @@ export const useTableParams = props => {
|
|
|
194
182
|
}, [
|
|
195
183
|
additionalFilter,
|
|
196
184
|
passingProps,
|
|
197
|
-
additionalOrFilter,
|
|
198
185
|
doNotCoercePageSize,
|
|
199
186
|
currentParams,
|
|
200
187
|
entities,
|
|
@@ -114,14 +114,14 @@ function removeUnwantedProps(props) {
|
|
|
114
114
|
|
|
115
115
|
const LabelWithTooltipInfo = ({ label, tooltipInfo, labelStyle }) =>
|
|
116
116
|
tooltipInfo ? (
|
|
117
|
-
<
|
|
117
|
+
<span style={{ display: "flex", alignItems: "center", ...labelStyle }}>
|
|
118
118
|
{label}{" "}
|
|
119
119
|
<InfoHelper
|
|
120
120
|
style={{ marginLeft: "5px", marginTop: "-6px" }}
|
|
121
121
|
size={12}
|
|
122
122
|
content={tooltipInfo}
|
|
123
123
|
/>
|
|
124
|
-
</
|
|
124
|
+
</span>
|
|
125
125
|
) : (
|
|
126
126
|
label || null
|
|
127
127
|
);
|
package/src/TgSelect/index.js
CHANGED
|
@@ -390,6 +390,21 @@ class TgSelect extends React.Component {
|
|
|
390
390
|
onKeyDown: e => {
|
|
391
391
|
const { which } = e;
|
|
392
392
|
e.persist();
|
|
393
|
+
// Handle Cmd+Enter (macOS) or Ctrl+Enter (Windows/Linux)
|
|
394
|
+
if ((e.metaKey || e.ctrlKey) && which === Keys.ENTER) {
|
|
395
|
+
e.preventDefault();
|
|
396
|
+
// Try to select the active item, or the first enabled item
|
|
397
|
+
const items = options || [];
|
|
398
|
+
let activeItem = this.state.activeItem;
|
|
399
|
+
if (!activeItem) {
|
|
400
|
+
// Find the first enabled item
|
|
401
|
+
activeItem = items.find(opt => !itemDisabled(opt));
|
|
402
|
+
}
|
|
403
|
+
if (activeItem) {
|
|
404
|
+
this.handleItemSelect(activeItem, e);
|
|
405
|
+
}
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
393
408
|
if (which === Keys.ENTER) {
|
|
394
409
|
e.preventDefault();
|
|
395
410
|
// e.stopPropagation();
|
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
|
|
@@ -85,3 +86,4 @@ const noop = () => undefined;
|
|
|
85
86
|
export { noop };
|
|
86
87
|
export { default as showDialogOnDocBody } from "./showDialogOnDocBody";
|
|
87
88
|
export { default as TableFormTrackerContext } from "./DataTable/TableFormTrackerContext";
|
|
89
|
+
export { initializeHasuraWhereAndFilter } from "./DataTable/utils/initializeHasuraWhereAndFilter";
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
/* Copyright (C) 2018 TeselaGen Biotechnology, Inc. */
|
|
2
2
|
import Color from "color";
|
|
3
3
|
|
|
4
|
-
export default
|
|
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
|
+
}
|