@proulr/sdui-components 0.1.82 → 0.1.85
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/package.json
CHANGED
|
@@ -177,5 +177,27 @@ describe('compileBdcToWire', () => {
|
|
|
177
177
|
expect(block).toEqual({ id: '0', type: 'text', props: { text: 'x' } });
|
|
178
178
|
expect(viaIndex(screen({ id: 'i' }, [])).screen_id).toBe('i');
|
|
179
179
|
});
|
|
180
|
+
|
|
181
|
+
it('flattens _fragment children instead of dropping them', () => {
|
|
182
|
+
const wire = compileBdcToWire(
|
|
183
|
+
screen({ id: 'demo.fragment' }, [
|
|
184
|
+
{
|
|
185
|
+
type: 'column',
|
|
186
|
+
props: {},
|
|
187
|
+
children: [
|
|
188
|
+
{
|
|
189
|
+
type: '_fragment',
|
|
190
|
+
props: {},
|
|
191
|
+
children: [
|
|
192
|
+
{ type: 'text', props: { text: 'a' }, children: [] },
|
|
193
|
+
{ type: 'text', props: { text: 'b' }, children: [] },
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
]),
|
|
199
|
+
);
|
|
200
|
+
expect(wire.blocks[0]?.children?.map((c) => c.props?.text)).toEqual(['a', 'b']);
|
|
201
|
+
});
|
|
180
202
|
});
|
|
181
203
|
|
|
@@ -61,6 +61,19 @@ export function compileBdcBlock(element: BdcElement, path = '0'): SduiBlockWire
|
|
|
61
61
|
return mapBlock(element, path);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
function flattenBlockChildren(children: BdcElement[]): BdcElement[] {
|
|
65
|
+
const out: BdcElement[] = [];
|
|
66
|
+
for (const child of children) {
|
|
67
|
+
if (child.type === '_text_node') continue;
|
|
68
|
+
if (child.type === '_fragment') {
|
|
69
|
+
out.push(...flattenBlockChildren(child.children));
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
out.push(child);
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
|
|
64
77
|
function mapBlock(element: BdcElement, path: string): SduiBlockWire {
|
|
65
78
|
if (element.type === '_text_node') {
|
|
66
79
|
return { id: path, type: 'text', props: { text: String(element.props.value ?? '') } };
|
|
@@ -76,9 +89,7 @@ function mapBlock(element: BdcElement, path: string): SduiBlockWire {
|
|
|
76
89
|
props.text = textFromChildren(element.children);
|
|
77
90
|
}
|
|
78
91
|
|
|
79
|
-
const blockChildren = element.children
|
|
80
|
-
(child) => child.type !== '_text_node' && child.type !== '_fragment',
|
|
81
|
-
);
|
|
92
|
+
const blockChildren = flattenBlockChildren(element.children);
|
|
82
93
|
|
|
83
94
|
return {
|
|
84
95
|
id: typeof element.props.id === 'string' ? element.props.id : path,
|
|
@@ -114,9 +125,9 @@ export function compileBdcToWire(root: BdcElement): SduiScreenWire {
|
|
|
114
125
|
|
|
115
126
|
void rest;
|
|
116
127
|
|
|
117
|
-
const blocks = root.children
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
const blocks = flattenBlockChildren(root.children).map((child, index) =>
|
|
129
|
+
mapBlock(child, String(index)),
|
|
130
|
+
);
|
|
120
131
|
|
|
121
132
|
const compiledHeader = header ? mapHeaderToWire(header as Record<string, unknown>) : undefined;
|
|
122
133
|
if (compiledHeader && headerSlot) {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { resolveTextStyleProps, resolveViewStyleProps } from './resolve-style-props';
|
|
4
|
+
import { m3Colors, m3Shape, spacing } from './proulr-theme';
|
|
5
|
+
|
|
6
|
+
vi.mock('react-native', () => ({
|
|
7
|
+
Platform: {
|
|
8
|
+
OS: 'web',
|
|
9
|
+
select: (spec: Record<string, unknown>) => spec.default ?? spec.web ?? spec.ios ?? {},
|
|
10
|
+
},
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe('resolveViewStyleProps', () => {
|
|
14
|
+
it('resolves snake_case StyleWire tokens', () => {
|
|
15
|
+
const style = resolveViewStyleProps({
|
|
16
|
+
background_color: 'surfaceContainerHigh',
|
|
17
|
+
padding_horizontal: 'lg',
|
|
18
|
+
padding_vertical: 'md',
|
|
19
|
+
margin_top: 'sm',
|
|
20
|
+
gap: 'md',
|
|
21
|
+
border_radius: 'cornerExtraLarge',
|
|
22
|
+
border_width: 1,
|
|
23
|
+
border_color: 'outlineVariant',
|
|
24
|
+
align_items: 'center',
|
|
25
|
+
flex: 1,
|
|
26
|
+
}) as Record<string, unknown>;
|
|
27
|
+
|
|
28
|
+
expect(style.backgroundColor).toBe(m3Colors.surfaceContainerHigh);
|
|
29
|
+
expect(style.paddingHorizontal).toBe(spacing.lg);
|
|
30
|
+
expect(style.paddingVertical).toBe(spacing.md);
|
|
31
|
+
expect(style.marginTop).toBe(spacing.sm);
|
|
32
|
+
expect(style.gap).toBe(spacing.md);
|
|
33
|
+
expect(style.borderRadius).toBe(m3Shape.cornerExtraLarge);
|
|
34
|
+
expect(style.borderWidth).toBe(1);
|
|
35
|
+
expect(style.borderColor).toBe(m3Colors.outlineVariant);
|
|
36
|
+
expect(style.alignItems).toBe('center');
|
|
37
|
+
expect(style.flex).toBe(1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('accepts camelCase aliases without dropping values', () => {
|
|
41
|
+
const style = resolveViewStyleProps({
|
|
42
|
+
backgroundColor: 'primaryContainer',
|
|
43
|
+
paddingHorizontal: 'sm',
|
|
44
|
+
alignItems: 'flex-end',
|
|
45
|
+
} as Record<string, unknown>) as Record<string, unknown>;
|
|
46
|
+
|
|
47
|
+
expect(style.backgroundColor).toBe(m3Colors.primaryContainer);
|
|
48
|
+
expect(style.paddingHorizontal).toBe(spacing.sm);
|
|
49
|
+
expect(style.alignItems).toBe('flex-end');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('resolveTextStyleProps', () => {
|
|
54
|
+
it('resolves color and text_align from snake_case wire', () => {
|
|
55
|
+
const style = resolveTextStyleProps(
|
|
56
|
+
{ color: 'primary', text_align: 'center', typography: 'titleSmall' },
|
|
57
|
+
'body',
|
|
58
|
+
) as Record<string, unknown>;
|
|
59
|
+
|
|
60
|
+
expect(style.color).toBe(m3Colors.primary);
|
|
61
|
+
expect(style.textAlign).toBe('center');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -24,18 +24,23 @@ function resolveTypography(token: TypeToken | undefined): TextStyle | undefined
|
|
|
24
24
|
return m3Type[token];
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
function
|
|
28
|
-
return key.replace(/
|
|
27
|
+
function camelToSnake(key: string): string {
|
|
28
|
+
return key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Canonical StyleWire is snake_case. Accept camelCase inputs from older wires
|
|
33
|
+
* without dropping underscore keys (previous normalize converted to camel and
|
|
34
|
+
* broke every `*_` token read).
|
|
35
|
+
*/
|
|
31
36
|
function normalizeStyleWire(style: StyleWire | Record<string, unknown> | undefined): StyleWire {
|
|
32
37
|
if (!style) return {};
|
|
33
|
-
const normalized:
|
|
38
|
+
const normalized: Record<string, unknown> = {};
|
|
34
39
|
for (const [key, value] of Object.entries(style)) {
|
|
35
|
-
const
|
|
36
|
-
|
|
40
|
+
const snakeKey = key.includes('_') ? key : camelToSnake(key);
|
|
41
|
+
normalized[snakeKey] = value;
|
|
37
42
|
}
|
|
38
|
-
return normalized;
|
|
43
|
+
return normalized as StyleWire;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
export function resolveViewStyleProps(
|