@player-ui/common-expressions-plugin 0.7.4-next.4 → 0.7.5--canary.434.14868
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/dist/CommonExpressionsPlugin.native.js +11332 -0
- package/dist/CommonExpressionsPlugin.native.js.map +1 -0
- package/dist/cjs/index.cjs +199 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/index.legacy-esm.js +180 -0
- package/dist/index.mjs +180 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +24 -58
- package/src/expressions/__tests__/expressions.test.ts +373 -0
- package/src/expressions/__tests__/toNum.test.ts +56 -0
- package/src/expressions/index.ts +33 -30
- package/src/expressions/toNum.ts +4 -4
- package/src/index.test.ts +31 -0
- package/src/index.ts +7 -7
- package/types/expressions/index.d.ts +39 -0
- package/types/expressions/toNum.d.ts +5 -0
- package/types/index.d.ts +32 -0
- package/dist/common-expressions-plugin.dev.js +0 -10782
- package/dist/common-expressions-plugin.prod.js +0 -2
- package/dist/index.cjs.js +0 -167
- package/dist/index.d.ts +0 -72
- package/dist/index.esm.js +0 -163
- package/dist/xlr/ceil.json +0 -33
- package/dist/xlr/concat.json +0 -19
- package/dist/xlr/containsAny.json +0 -44
- package/dist/xlr/findProperty.json +0 -92
- package/dist/xlr/findPropertyIndex.json +0 -70
- package/dist/xlr/floor.json +0 -33
- package/dist/xlr/isEmpty.json +0 -26
- package/dist/xlr/isNotEmpty.json +0 -26
- package/dist/xlr/length.json +0 -24
- package/dist/xlr/lowerCase.json +0 -24
- package/dist/xlr/manifest.js +0 -31
- package/dist/xlr/manifest.json +0 -36
- package/dist/xlr/number.json +0 -31
- package/dist/xlr/replace.json +0 -37
- package/dist/xlr/round.json +0 -33
- package/dist/xlr/sentenceCase.json +0 -24
- package/dist/xlr/size.json +0 -24
- package/dist/xlr/sum.json +0 -27
- package/dist/xlr/titleCase.json +0 -24
- package/dist/xlr/trim.json +0 -24
- package/dist/xlr/upperCase.json +0 -24
package/package.json
CHANGED
|
@@ -1,67 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@player-ui/common-expressions-plugin",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"
|
|
5
|
-
"publishConfig": {
|
|
6
|
-
"registry": "https://registry.npmjs.org"
|
|
7
|
-
},
|
|
3
|
+
"version": "0.7.5--canary.434.14868",
|
|
4
|
+
"main": "dist/cjs/index.cjs",
|
|
8
5
|
"peerDependencies": {
|
|
9
|
-
"@player-ui/player": "0.7.
|
|
6
|
+
"@player-ui/player": "0.7.5--canary.434.14868"
|
|
7
|
+
},
|
|
8
|
+
"devDependencies": {
|
|
9
|
+
"@player-ui/make-flow": "workspace:*"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@player-ui/expression-plugin": "0.7.
|
|
13
|
-
"
|
|
12
|
+
"@player-ui/expression-plugin": "0.7.5--canary.434.14868",
|
|
13
|
+
"tslib": "^2.6.2"
|
|
14
14
|
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
15
|
+
"module": "dist/index.legacy-esm.js",
|
|
16
|
+
"types": "types/index.d.ts",
|
|
17
|
+
"bundle": "dist/CommonExpressionsPlugin.native.js",
|
|
18
18
|
"sideEffects": false,
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
"homepage": "https://player-ui.github.io",
|
|
28
|
-
"contributors": [
|
|
29
|
-
{
|
|
30
|
-
"name": "Adam Dierkens",
|
|
31
|
-
"url": "https://github.com/adierkens"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"name": "Spencer Hamm",
|
|
35
|
-
"url": "https://github.com/spentacular"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"name": "Harris Borawski",
|
|
39
|
-
"url": "https://github.com/hborawski"
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"name": "Jeremiah Zucker",
|
|
43
|
-
"url": "https://github.com/sugarmanz"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"name": "Ketan Reddy",
|
|
47
|
-
"url": "https://github.com/KetanReddy"
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
"name": "Brocollie08",
|
|
51
|
-
"url": "https://github.com/brocollie08"
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"name": "Kelly Harrop",
|
|
55
|
-
"url": "https://github.com/kharrop"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"name": "Alejandro Fimbres",
|
|
59
|
-
"url": "https://github.com/lexfm"
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"name": "Rafael Campos",
|
|
63
|
-
"url": "https://github.com/rafbcampos"
|
|
19
|
+
"exports": {
|
|
20
|
+
"./package.json": "./package.json",
|
|
21
|
+
"./dist/index.css": "./dist/index.css",
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./types/index.d.ts",
|
|
24
|
+
"import": "./dist/index.mjs",
|
|
25
|
+
"default": "./dist/cjs/index.cjs"
|
|
64
26
|
}
|
|
65
|
-
|
|
66
|
-
"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"src",
|
|
31
|
+
"types"
|
|
32
|
+
]
|
|
67
33
|
}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { describe, expect, it, test, beforeEach } from "vitest";
|
|
2
|
+
import type { ExpressionContext, DataModelWithParser } from "@player-ui/player";
|
|
3
|
+
import { BindingParser, LocalModel, withParser } from "@player-ui/player";
|
|
4
|
+
import {
|
|
5
|
+
size,
|
|
6
|
+
concat,
|
|
7
|
+
round,
|
|
8
|
+
floor,
|
|
9
|
+
ceil,
|
|
10
|
+
trim,
|
|
11
|
+
upperCase,
|
|
12
|
+
lowerCase,
|
|
13
|
+
replace,
|
|
14
|
+
titleCase,
|
|
15
|
+
sentenceCase,
|
|
16
|
+
isEmpty,
|
|
17
|
+
isNotEmpty,
|
|
18
|
+
findProperty,
|
|
19
|
+
findPropertyIndex,
|
|
20
|
+
sum,
|
|
21
|
+
containsAny,
|
|
22
|
+
} from "..";
|
|
23
|
+
|
|
24
|
+
const parseBinding = new BindingParser().parse;
|
|
25
|
+
describe("expr functions", () => {
|
|
26
|
+
let model: DataModelWithParser;
|
|
27
|
+
let context: ExpressionContext;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
model = withParser(new LocalModel(), parseBinding);
|
|
31
|
+
context = {
|
|
32
|
+
model,
|
|
33
|
+
evaluate: () => undefined,
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("replace", () => {
|
|
38
|
+
test("works on simple things", () => {
|
|
39
|
+
expect(replace(context, "foo-bar-baz", "bar-", "")).toBe("foo-baz");
|
|
40
|
+
expect(replace(context, "adam #1", "1", "2")).toBe("adam #2");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("replaces all occurrences", () => {
|
|
44
|
+
expect(replace(context, "B1 B2 B3 B4", "B", "A")).toBe("A1 A2 A3 A4");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("leaves non-string things alone", () => {
|
|
48
|
+
expect(replace(context, 2, "foo", "bar")).toBe(2);
|
|
49
|
+
expect(replace(context, undefined, undefined)).toBe(undefined);
|
|
50
|
+
expect(replace(context, "foo", undefined)).toBe("foo");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("defaults to empty replacement", () => {
|
|
54
|
+
expect(replace(context, "foo", "o")).toBe("f");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("math", () => {
|
|
59
|
+
it("works for rounding", () => {
|
|
60
|
+
expect(round(context, "1.5")).toBe(2);
|
|
61
|
+
expect(round(context, 1.5)).toBe(2);
|
|
62
|
+
expect(round(context, "-1.7")).toBe(-2);
|
|
63
|
+
expect(round(context, -1.7)).toBe(-2);
|
|
64
|
+
expect(round(context, undefined as any)).toBe(0);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("works for ceil", () => {
|
|
68
|
+
expect(ceil(context, "1.5")).toBe(2);
|
|
69
|
+
expect(ceil(context, 1.3)).toBe(2);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("works for floor", () => {
|
|
73
|
+
expect(floor(context, "1.5")).toBe(1);
|
|
74
|
+
expect(floor(context, 1.3)).toBe(1);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("works for sum", () => {
|
|
78
|
+
expect(sum(context, 1, 2, 3)).toBe(1 + 2 + 3);
|
|
79
|
+
expect(sum(context, "1", 2, "3")).toBe(1 + 2 + 3);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("strings", () => {
|
|
84
|
+
test("trim", () => {
|
|
85
|
+
expect(trim(context, " foo ")).toBe("foo");
|
|
86
|
+
expect(trim(context, 123)).toBe(123);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("upperCase", () => {
|
|
90
|
+
expect(upperCase(context, "foo bar")).toBe("FOO BAR");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("lowerCase", () => {
|
|
94
|
+
expect(lowerCase(context, "FOO BaR")).toBe("foo bar");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("titleCase", () => {
|
|
98
|
+
expect(titleCase(context, "foo bar Baz")).toBe("Foo Bar Baz");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("sentenceCase", () => {
|
|
102
|
+
expect(sentenceCase(context, "foo Bar baz")).toBe("Foo Bar baz");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("size", () => {
|
|
107
|
+
test("good things", () => {
|
|
108
|
+
expect(size(context, "foo")).toBe(3);
|
|
109
|
+
expect(size(context, [1, 2, 3, 4])).toBe(4);
|
|
110
|
+
expect(size(context, { foo: "bar", baz: "blah" })).toBe(2);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("bad things", () => {
|
|
114
|
+
expect(size(context, null)).toBe(0);
|
|
115
|
+
expect(size(context, undefined)).toBe(0);
|
|
116
|
+
expect(size(context, 12)).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("concat", () => {
|
|
121
|
+
test("strings", () => {
|
|
122
|
+
expect(concat(context, "1")).toBe("1");
|
|
123
|
+
expect(concat(context, "1", 1)).toBe("11");
|
|
124
|
+
expect(concat(context, 1, 1)).toBe("11");
|
|
125
|
+
expect(concat(context, "1", "2", "3")).toBe("123");
|
|
126
|
+
expect(concat(context, 1, undefined)).toBe("1");
|
|
127
|
+
expect(concat(context, 0, 1, undefined)).toBe("01");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("arrays", () => {
|
|
131
|
+
expect(concat(context, [1, 2], [3, 4])).toStrictEqual([1, 2, 3, 4]);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("isNotEmpty", () => {
|
|
136
|
+
test("isNotEmpty", () => {
|
|
137
|
+
expect(isNotEmpty(context, "")).toBe(false);
|
|
138
|
+
expect(isNotEmpty(context, undefined)).toBe(false);
|
|
139
|
+
expect(isNotEmpty(context, null)).toBe(false);
|
|
140
|
+
expect(isNotEmpty(context, "foo")).toBe(true);
|
|
141
|
+
expect(isNotEmpty(context, 12)).toBe(true);
|
|
142
|
+
expect(isNotEmpty(context, [])).toBe(false);
|
|
143
|
+
expect(isNotEmpty(context, [100])).toBe(true);
|
|
144
|
+
expect(isNotEmpty(context, [{ foo: "bar" }, { foo: "bar1" }])).toBe(true);
|
|
145
|
+
expect(isNotEmpty(context, {})).toBe(false);
|
|
146
|
+
expect(isNotEmpty(context, { foo: "bar" })).toBe(true);
|
|
147
|
+
// eslint-disable-next-line prefer-const
|
|
148
|
+
let dummy;
|
|
149
|
+
expect(isNotEmpty(context, dummy)).toBe(false);
|
|
150
|
+
dummy = { key: "1" };
|
|
151
|
+
expect(isNotEmpty(context, dummy)).toBe(true);
|
|
152
|
+
expect(isNotEmpty(context, dummy.key)).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("isEmpty", () => {
|
|
157
|
+
expect(isEmpty(context, "")).toBe(true);
|
|
158
|
+
expect(isEmpty(context, undefined)).toBe(true);
|
|
159
|
+
expect(isEmpty(context, null)).toBe(true);
|
|
160
|
+
expect(isEmpty(context, "foo")).toBe(false);
|
|
161
|
+
expect(isEmpty(context, 12)).toBe(false);
|
|
162
|
+
expect(isEmpty(context, [])).toBe(true);
|
|
163
|
+
expect(isEmpty(context, [100])).toBe(false);
|
|
164
|
+
expect(isEmpty(context, [{ foo: "bar" }, { foo: "bar1" }])).toBe(false);
|
|
165
|
+
expect(isEmpty(context, {})).toBe(true);
|
|
166
|
+
expect(isEmpty(context, { foo: "bar" })).toBe(false);
|
|
167
|
+
expect(isEmpty(context, undefined)).toBe(true);
|
|
168
|
+
const dummy = { key: "1" };
|
|
169
|
+
expect(isEmpty(context, dummy)).toBe(false);
|
|
170
|
+
expect(isEmpty(context, dummy.key)).toBe(false);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("findProperty", () => {
|
|
174
|
+
beforeEach(() => {
|
|
175
|
+
model.set([
|
|
176
|
+
[
|
|
177
|
+
"people",
|
|
178
|
+
[
|
|
179
|
+
{
|
|
180
|
+
name: "Adam",
|
|
181
|
+
pet: "dog",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "Frodo",
|
|
185
|
+
pet: "cat",
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
],
|
|
189
|
+
["names", ["Adam", "Spencer", "Ketan", "Harris"]],
|
|
190
|
+
]);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("finds the right object when using a model reference", () => {
|
|
194
|
+
const property = findProperty(
|
|
195
|
+
context,
|
|
196
|
+
"people",
|
|
197
|
+
"name",
|
|
198
|
+
"Frodo",
|
|
199
|
+
"pet",
|
|
200
|
+
undefined,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const propertyIndex = findPropertyIndex(
|
|
204
|
+
context,
|
|
205
|
+
"people",
|
|
206
|
+
"name",
|
|
207
|
+
"Frodo",
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
expect(property).toBe("cat");
|
|
211
|
+
expect(propertyIndex).toBe(1);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("finds the right object when using direct arrays", () => {
|
|
215
|
+
const arr = [
|
|
216
|
+
{
|
|
217
|
+
name: "Adam",
|
|
218
|
+
pet: "dog",
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "Frodo",
|
|
222
|
+
pet: "cat",
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
const property = findProperty(
|
|
227
|
+
context,
|
|
228
|
+
arr,
|
|
229
|
+
"name",
|
|
230
|
+
"Frodo",
|
|
231
|
+
"pet",
|
|
232
|
+
undefined,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const propertyIndex = findPropertyIndex(context, arr, "name", "Frodo");
|
|
236
|
+
|
|
237
|
+
expect(property).toBe("cat");
|
|
238
|
+
expect(propertyIndex).toBe(1);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("index not found", () => {
|
|
242
|
+
const propertyIndex = findPropertyIndex(
|
|
243
|
+
context,
|
|
244
|
+
"people",
|
|
245
|
+
"name",
|
|
246
|
+
"Spencer",
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
expect(propertyIndex).toBe(-1);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("undefined binding", () => {
|
|
253
|
+
const propertyIndex = findPropertyIndex(
|
|
254
|
+
context,
|
|
255
|
+
undefined as any,
|
|
256
|
+
"name",
|
|
257
|
+
"Spencer",
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
expect(propertyIndex).toBe(-1);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("non-existant model ref", () => {
|
|
264
|
+
expect(findPropertyIndex(context, "not-there", "name", "Adam")).toBe(-1);
|
|
265
|
+
expect(
|
|
266
|
+
findProperty(
|
|
267
|
+
context,
|
|
268
|
+
"not-there",
|
|
269
|
+
"name",
|
|
270
|
+
"Adam",
|
|
271
|
+
undefined,
|
|
272
|
+
"defaultVal",
|
|
273
|
+
),
|
|
274
|
+
).toBe("defaultVal");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("fallback value", () => {
|
|
278
|
+
const property = findProperty(
|
|
279
|
+
context,
|
|
280
|
+
"people",
|
|
281
|
+
"name",
|
|
282
|
+
"Spencer",
|
|
283
|
+
"pet",
|
|
284
|
+
"rabbit",
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
expect(property).toBe("rabbit");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("works for value arrays", () => {
|
|
291
|
+
expect(findPropertyIndex(context, "names", undefined, "Adam")).toBe(0);
|
|
292
|
+
expect(
|
|
293
|
+
findProperty(context, "names", undefined, "Adam", undefined, undefined),
|
|
294
|
+
).toBe("Adam");
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
describe("containsAny", () => {
|
|
298
|
+
beforeEach(() => {
|
|
299
|
+
model.set([
|
|
300
|
+
[
|
|
301
|
+
"people",
|
|
302
|
+
[
|
|
303
|
+
{
|
|
304
|
+
name: "Adam",
|
|
305
|
+
pet: "dog",
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
name: "Frodo",
|
|
309
|
+
pet: "cat",
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
],
|
|
313
|
+
["names", ["Adam", "Spencer", "Ketan", "Harris"]],
|
|
314
|
+
]);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("find a keyword from a string", () => {
|
|
318
|
+
const str = "The quick brown fox jumps over the lazy dog";
|
|
319
|
+
expect(containsAny(context, str, "fox")).toBe(true);
|
|
320
|
+
expect(containsAny(context, str, "Fox")).toBe(false);
|
|
321
|
+
});
|
|
322
|
+
test("find a keyword from an array", () => {
|
|
323
|
+
const str = "The quick brown fox jumps over the lazy dog";
|
|
324
|
+
expect(containsAny(context, str, ["fox"])).toBe(true);
|
|
325
|
+
expect(containsAny(context, str, ["Fox"])).toBe(false);
|
|
326
|
+
expect(containsAny(context, str, ["Fox", "dog"])).toBe(true);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test("should return true since keyword exists (second arg is string)", () => {
|
|
330
|
+
expect(containsAny(context, "foo", "foo")).toBe(true);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test("should return true since keyword exists (second arg is array)", () => {
|
|
334
|
+
expect(containsAny(context, "foo", ["foo", "bar"])).toBe(true);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("should return false since keyword does not exist (second arg is array)", () => {
|
|
338
|
+
expect(containsAny(context, "fuba", ["foo", "bar"])).toBe(false);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("should return false since keyword does not exist (second arg is string)", () => {
|
|
342
|
+
expect(containsAny(context, "foo", "bar")).toBe(false);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test("should return false since first argument is bad", () => {
|
|
346
|
+
expect(containsAny(context, null, "bar")).toBe(false);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("should return false since second argument is bad", () => {
|
|
350
|
+
expect(containsAny(context, "foo", null)).toBe(false);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("should return true since second argument as blank is acceptable", () => {
|
|
354
|
+
expect(containsAny(context, "foo", "")).toBe(true);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("should return true since both arguments as blank is acceptable", () => {
|
|
358
|
+
expect(containsAny(context, "", "")).toBe(true);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("should return false since first argument is blank and second is not", () => {
|
|
362
|
+
expect(containsAny(context, "", "bar")).toBe(false);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test("should return false since first agument is blank and second argument is an empty array", () => {
|
|
366
|
+
expect(containsAny(context, "", [])).toBe(false);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("should return false since second argument is an empty array", () => {
|
|
370
|
+
expect(containsAny(context, "foo", [])).toBe(false);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { toNum } from "../toNum";
|
|
3
|
+
|
|
4
|
+
describe("toNum", () => {
|
|
5
|
+
test("returns the argument when provided a number", () => {
|
|
6
|
+
expect(toNum(40)).toBe(40);
|
|
7
|
+
expect(toNum(1.0)).toBe(1.0);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("ignores all space", () => {
|
|
11
|
+
expect(toNum(" \r\n\t50\t\n\r ")).toBe(50);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("ignores all commas", () => {
|
|
15
|
+
expect(toNum("400,000,000.00")).toBe(400000000.0);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("ignores a single currency symbols", () => {
|
|
19
|
+
expect(toNum("¥500")).toBe(500);
|
|
20
|
+
expect(toNum("£-1")).toBe(-1);
|
|
21
|
+
expect(toNum("$33.33")).toBe(33.33);
|
|
22
|
+
expect(toNum("€20,000")).toBe(20000);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("returns undef when the parameter has multiple currency symbols", () => {
|
|
26
|
+
expect(toNum("¥50¥0")).toBe(undefined);
|
|
27
|
+
expect(toNum("£-1£")).toBe(undefined);
|
|
28
|
+
expect(toNum("$33.33$")).toBe(undefined);
|
|
29
|
+
expect(toNum("€€20,000")).toBe(undefined);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("returns undef if the parameter cannot be converted to number", () => {
|
|
33
|
+
expect(toNum("hello")).toBe(undefined);
|
|
34
|
+
expect(toNum({})).toBe(undefined);
|
|
35
|
+
expect(toNum(undefined)).toBe(undefined);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("returns 0 if the provided parameter is falsy and the 2nd parameter flag is true", () => {
|
|
39
|
+
expect(toNum("", true)).toBe(0);
|
|
40
|
+
expect(toNum(null, true)).toBe(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("returns undef if the provided string parameter is empty and the 2nd parameter is false", () => {
|
|
44
|
+
expect(toNum("", false)).toBe(undefined);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("returns undef when only part of the input contains a number", () => {
|
|
48
|
+
expect(toNum("123abc")).toBe(undefined);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("does not parse hex, octal, or binary strings", () => {
|
|
52
|
+
expect(toNum("0x123")).toBe(undefined);
|
|
53
|
+
expect(toNum("0o123")).toBe(undefined);
|
|
54
|
+
expect(toNum("0b123")).toBe(undefined);
|
|
55
|
+
});
|
|
56
|
+
});
|
package/src/expressions/index.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type {
|
|
2
|
+
ExpressionHandler,
|
|
3
|
+
ExpressionContext,
|
|
4
|
+
Binding,
|
|
5
|
+
} from "@player-ui/player";
|
|
6
|
+
import { withoutContext } from "@player-ui/player";
|
|
7
|
+
import { toNum } from "./toNum";
|
|
5
8
|
|
|
6
9
|
/** Returns a function that executes the given function only if the first argument is a string */
|
|
7
10
|
function ifString(fn: (arg: string) => unknown) {
|
|
8
11
|
return (arg: unknown) => {
|
|
9
|
-
if (typeof arg ===
|
|
12
|
+
if (typeof arg === "string") {
|
|
10
13
|
return fn(arg);
|
|
11
14
|
}
|
|
12
15
|
|
|
@@ -17,11 +20,11 @@ function ifString(fn: (arg: string) => unknown) {
|
|
|
17
20
|
/** Generic Types */
|
|
18
21
|
|
|
19
22
|
export const size = withoutContext((val: unknown): number => {
|
|
20
|
-
if (typeof val ===
|
|
23
|
+
if (typeof val === "string") {
|
|
21
24
|
return val.length;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
if (typeof val ===
|
|
27
|
+
if (typeof val === "object" && val !== null) {
|
|
25
28
|
return Object.keys(val).length;
|
|
26
29
|
}
|
|
27
30
|
|
|
@@ -36,7 +39,7 @@ export const isEmpty: ExpressionHandler<[unknown], boolean> = (ctx, val) => {
|
|
|
36
39
|
return true;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
if (typeof val ===
|
|
42
|
+
if (typeof val === "object" || typeof val === "string") {
|
|
40
43
|
return size(ctx, val) === 0;
|
|
41
44
|
}
|
|
42
45
|
|
|
@@ -58,7 +61,7 @@ export const concat = withoutContext((...args: Array<unknown>) => {
|
|
|
58
61
|
});
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
return args.reduce((merged: any, next) => merged + (next ??
|
|
64
|
+
return args.reduce((merged: any, next) => merged + (next ?? ""), "");
|
|
62
65
|
});
|
|
63
66
|
|
|
64
67
|
/** String Types */
|
|
@@ -67,31 +70,31 @@ export const trim = withoutContext(ifString((str) => str.trim()));
|
|
|
67
70
|
export const upperCase = withoutContext(ifString((str) => str.toUpperCase()));
|
|
68
71
|
export const lowerCase = withoutContext(ifString((str) => str.toLowerCase()));
|
|
69
72
|
export const replace = withoutContext(
|
|
70
|
-
(str: unknown, pattern: unknown, replacement: unknown =
|
|
73
|
+
(str: unknown, pattern: unknown, replacement: unknown = "") => {
|
|
71
74
|
if (
|
|
72
|
-
typeof str ===
|
|
73
|
-
typeof pattern ===
|
|
74
|
-
typeof replacement ===
|
|
75
|
+
typeof str === "string" &&
|
|
76
|
+
typeof pattern === "string" &&
|
|
77
|
+
typeof replacement === "string"
|
|
75
78
|
) {
|
|
76
|
-
const replacementRegex = new RegExp(pattern,
|
|
79
|
+
const replacementRegex = new RegExp(pattern, "g");
|
|
77
80
|
|
|
78
81
|
return str.replace(replacementRegex, replacement);
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
return str;
|
|
82
|
-
}
|
|
85
|
+
},
|
|
83
86
|
);
|
|
84
87
|
export const titleCase = withoutContext(
|
|
85
88
|
ifString((str) =>
|
|
86
89
|
str
|
|
87
|
-
.split(
|
|
90
|
+
.split(" ")
|
|
88
91
|
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
89
|
-
.join(
|
|
90
|
-
)
|
|
92
|
+
.join(" "),
|
|
93
|
+
),
|
|
91
94
|
);
|
|
92
95
|
|
|
93
96
|
export const sentenceCase = withoutContext(
|
|
94
|
-
ifString((str) => str.replace(/\b[a-zA-Z]/, (word) => word.toUpperCase()))
|
|
97
|
+
ifString((str) => str.replace(/\b[a-zA-Z]/, (word) => word.toUpperCase())),
|
|
95
98
|
);
|
|
96
99
|
|
|
97
100
|
/** Math Types */
|
|
@@ -99,15 +102,15 @@ export const sentenceCase = withoutContext(
|
|
|
99
102
|
export const number = withoutContext(toNum);
|
|
100
103
|
|
|
101
104
|
export const round = withoutContext<[number | string], number>((num) =>
|
|
102
|
-
Math.round(toNum(num, true) ?? 0)
|
|
105
|
+
Math.round(toNum(num, true) ?? 0),
|
|
103
106
|
);
|
|
104
107
|
|
|
105
108
|
export const floor = withoutContext<[number | string], number>((num) =>
|
|
106
|
-
Math.floor(toNum(num, true) ?? 0)
|
|
109
|
+
Math.floor(toNum(num, true) ?? 0),
|
|
107
110
|
);
|
|
108
111
|
|
|
109
112
|
export const ceil = withoutContext<[number | string], number>((num) =>
|
|
110
|
-
Math.ceil(toNum(num, true) ?? 0)
|
|
113
|
+
Math.ceil(toNum(num, true) ?? 0),
|
|
111
114
|
);
|
|
112
115
|
|
|
113
116
|
export const sum = withoutContext<Array<number | string>, number>((...args) => {
|
|
@@ -124,7 +127,7 @@ export const findPropertyIndex: ExpressionHandler<
|
|
|
124
127
|
context: ExpressionContext,
|
|
125
128
|
bindingOrModel: Binding | Array<Record<string, T>> | undefined,
|
|
126
129
|
propToCheck: string | undefined,
|
|
127
|
-
valueToCheck: T
|
|
130
|
+
valueToCheck: T,
|
|
128
131
|
) => {
|
|
129
132
|
if (bindingOrModel === undefined) {
|
|
130
133
|
return -1;
|
|
@@ -140,7 +143,7 @@ export const findPropertyIndex: ExpressionHandler<
|
|
|
140
143
|
|
|
141
144
|
return searchArray.findIndex((value) => {
|
|
142
145
|
const propVal =
|
|
143
|
-
typeof value ===
|
|
146
|
+
typeof value === "object" && propToCheck !== undefined
|
|
144
147
|
? value[propToCheck]
|
|
145
148
|
: value;
|
|
146
149
|
|
|
@@ -158,7 +161,7 @@ export const findProperty: ExpressionHandler<
|
|
|
158
161
|
propToCheck: string | undefined,
|
|
159
162
|
valueToCheck: T,
|
|
160
163
|
propToReturn?: string,
|
|
161
|
-
defaultValue?: T
|
|
164
|
+
defaultValue?: T,
|
|
162
165
|
) => {
|
|
163
166
|
const searchArray: Array<Record<string, T>> = Array.isArray(bindingOrModel)
|
|
164
167
|
? bindingOrModel
|
|
@@ -170,7 +173,7 @@ export const findProperty: ExpressionHandler<
|
|
|
170
173
|
|
|
171
174
|
const foundValue = searchArray.find((value) => {
|
|
172
175
|
const propVal =
|
|
173
|
-
typeof value ===
|
|
176
|
+
typeof value === "object" && propToCheck !== undefined
|
|
174
177
|
? value[propToCheck]
|
|
175
178
|
: value;
|
|
176
179
|
|
|
@@ -181,7 +184,7 @@ export const findProperty: ExpressionHandler<
|
|
|
181
184
|
return defaultValue;
|
|
182
185
|
}
|
|
183
186
|
|
|
184
|
-
if (typeof foundValue ===
|
|
187
|
+
if (typeof foundValue === "object" && propToReturn) {
|
|
185
188
|
return foundValue[propToReturn] ?? defaultValue;
|
|
186
189
|
}
|
|
187
190
|
|
|
@@ -197,8 +200,8 @@ export const findProperty: ExpressionHandler<
|
|
|
197
200
|
export const containsAny = withoutContext<[string, string[] | string], boolean>(
|
|
198
201
|
(str, keywords) => {
|
|
199
202
|
if (
|
|
200
|
-
!(typeof str ===
|
|
201
|
-
!(typeof keywords ===
|
|
203
|
+
!(typeof str === "string") ||
|
|
204
|
+
!(typeof keywords === "string" || Array.isArray(keywords))
|
|
202
205
|
) {
|
|
203
206
|
return false;
|
|
204
207
|
}
|
|
@@ -208,5 +211,5 @@ export const containsAny = withoutContext<[string, string[] | string], boolean>(
|
|
|
208
211
|
}
|
|
209
212
|
|
|
210
213
|
return str.indexOf(keywords) > -1;
|
|
211
|
-
}
|
|
214
|
+
},
|
|
212
215
|
);
|