Skip to content

Commit 830636e

Browse files
authored
feat: add support for array values in preview text (#749)
1 parent 6c0041f commit 830636e

File tree

3 files changed

+110
-5
lines changed

3 files changed

+110
-5
lines changed

.changeset/real-poets-drive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@blinkk/root-cms': patch
3+
---
4+
5+
feat: add support for array values in preview text (#749)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {describe, it, expect} from 'vitest';
2+
import {getNestedValue} from './objects.js';
3+
4+
describe('getNestedValue', () => {
5+
it('returns a nested property when using dot notation', () => {
6+
const data = {meta: {title: 'Foo'}};
7+
8+
expect(getNestedValue(data, 'meta.title')).toBe('Foo');
9+
});
10+
11+
it('returns the first defined value from a list of keys', () => {
12+
const data = {meta: {title: 'Foo', description: 'Bar'}};
13+
14+
expect(
15+
getNestedValue(data, ['meta.missing', 'meta.description', 'meta.title'])
16+
).toBe('Bar');
17+
});
18+
19+
it('resolves bracket notation against real arrays', () => {
20+
const data = {items: [{title: 'First'}, {title: 'Second'}]};
21+
22+
expect(getNestedValue(data, 'items[1].title')).toBe('Second');
23+
});
24+
25+
it('resolves bracket notation against array objects using the _array key', () => {
26+
const data = {
27+
items: {
28+
_array: ['item1', 'item2'],
29+
item1: {title: 'Foo'},
30+
item2: {title: 'Bar'},
31+
},
32+
};
33+
34+
expect(getNestedValue(data, 'items[0].title')).toBe('Foo');
35+
});
36+
37+
it('returns undefined when the array object index does not exist', () => {
38+
const data = {
39+
items: {
40+
_array: ['item1'],
41+
item1: {title: 'Foo'},
42+
},
43+
};
44+
45+
expect(getNestedValue(data, 'items[1].title')).toBeUndefined();
46+
});
47+
});

packages/root-cms/ui/utils/objects.ts

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,70 @@ export function getNestedValue(data: any, key: string | string[]): any {
2828
}
2929
return undefined;
3030
}
31-
if (!key.includes('.')) {
32-
return (data || {})[key];
31+
32+
const segments = key.split('.');
33+
let current = data;
34+
for (const segment of segments) {
35+
if (current === undefined || current === null) {
36+
return undefined;
37+
}
38+
39+
current = resolveSegment(current, segment);
3340
}
34-
const keys = key.split('.');
41+
return current;
42+
}
43+
44+
function resolveSegment(data: any, segment: string): any {
45+
if (!segment) {
46+
return data;
47+
}
48+
49+
// eslint-disable-next-line no-useless-escape
50+
const matcher = /([^\[\]]+)|\[(\d+)\]/g;
3551
let current = data;
36-
for (const segment of keys) {
52+
53+
let match: RegExpExecArray | null;
54+
while ((match = matcher.exec(segment))) {
3755
if (current === undefined || current === null) {
3856
return undefined;
3957
}
40-
current = current[segment];
58+
59+
const property = match[1];
60+
const index = match[2];
61+
if (property !== undefined) {
62+
current = current[property];
63+
continue;
64+
}
65+
66+
if (index === undefined) {
67+
return undefined;
68+
}
69+
const arrayIndex = Number(index);
70+
if (Array.isArray(current)) {
71+
current = current[arrayIndex];
72+
continue;
73+
}
74+
75+
if (isObject(current) && Array.isArray(current._array)) {
76+
const key = current._array[arrayIndex];
77+
if (key === undefined) {
78+
return undefined;
79+
}
80+
current = current[key];
81+
continue;
82+
}
83+
84+
if (
85+
isObject(current) &&
86+
Object.prototype.hasOwnProperty.call(current, String(arrayIndex))
87+
) {
88+
current = current[String(arrayIndex)];
89+
continue;
90+
}
91+
92+
return undefined;
4193
}
94+
4295
return current;
4396
}
4497

0 commit comments

Comments
 (0)