Skip to content

Commit 6833d13

Browse files
committed
feat(eventToShortText): localised composite formatting
Add formatComposite for itemDef type='composite' where every field is a 'select' with options — joins each present field's localised option label with ' · '. Used e.g. for body-vulva-cervix-position so tooltips/diary read 'High · Soft · Open' instead of the raw object. Free-form composites (medication/basic with name/doseValue/doseUnit/route) still fall through to formatObject (unchanged). New tests EST19 / EST19b.
1 parent a843858 commit 6833d13

2 files changed

Lines changed: 58 additions & 0 deletions

File tree

tests/eventToShortText.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,26 @@ describe('[ESTX] eventToShortText', () => {
322322
assert.equal(result, 'Ibuprofen — 400 mg, oral');
323323
});
324324

325+
it('[EST19] cervix-position composite joins selected option labels', () => {
326+
const event = {
327+
content: { height: 1.0, firmness: 0.0, openness: 0.5 },
328+
streamIds: ['body-vulva-cervix-position'],
329+
type: 'cervix-position/3d-vectors'
330+
};
331+
const result = eventToShortText(event);
332+
assert.equal(result, 'High · Firm · Medium');
333+
});
334+
335+
it('[EST19b] cervix-position composite skips missing fields', () => {
336+
const event = {
337+
content: { height: 0.0 },
338+
streamIds: ['body-vulva-cervix-position'],
339+
type: 'cervix-position/3d-vectors'
340+
};
341+
const result = eventToShortText(event);
342+
assert.equal(result, 'Low');
343+
});
344+
325345
// ─── Convertible: mood ───────────────────────────────────────────
326346

327347
describe('[EST20] convertible mood', () => {

ts/HDSModel/eventToShortText.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ function formatWithItemDef (event: any, content: any, itemDef: any, model: any):
100100
return formatSlider(content, itemDef);
101101
}
102102

103+
if (type === 'composite') {
104+
return formatComposite(content, itemDef);
105+
}
106+
103107
// number, text, composite, etc.
104108
if (typeof content === 'number') {
105109
return formatNumber(event.type, content, model);
@@ -195,6 +199,40 @@ function formatSlider (content: any, itemDef: any): string {
195199
return suffix ? `${text} ${suffix}` : text;
196200
}
197201

202+
/**
203+
* Format a composite event by walking the itemDef's `composite` block. Only
204+
* applies when every field is a `select` with options — joins the localised
205+
* option labels with " · ". Used e.g. for `body-vulva-cervix-position`
206+
* (`{ height, firmness, openness }` → "High · Soft · Open").
207+
*
208+
* Falls through to `formatObject` for free-form composites (e.g.
209+
* medication/basic with `name`/`doseValue`/`doseUnit`/`route`) so existing
210+
* shape-specific renderers keep working.
211+
*/
212+
function formatComposite (content: any, itemDef: any): string | null {
213+
if (!content || typeof content !== 'object') return String(content);
214+
const composite = itemDef?.data?.composite;
215+
if (!composite) return formatObject(content);
216+
const fieldKeys = Object.keys(composite);
217+
const allSelectsWithOptions = fieldKeys.length > 0 && fieldKeys.every(k =>
218+
composite[k]?.type === 'select' && Array.isArray(composite[k]?.options)
219+
);
220+
if (!allSelectsWithOptions) return formatObject(content);
221+
const parts: string[] = [];
222+
for (const field of fieldKeys) {
223+
const value = content[field];
224+
if (value === undefined || value === null) continue;
225+
const opt = composite[field].options.find((o: any) => o.value === value);
226+
if (opt?.label) {
227+
const text = typeof opt.label === 'string' ? opt.label : (localizeText(opt.label) || String(value));
228+
parts.push(text);
229+
} else {
230+
parts.push(String(value));
231+
}
232+
}
233+
return parts.length > 0 ? parts.join(' · ') : formatObject(content);
234+
}
235+
198236
function formatSelect (event: any, content: any, itemDef: any): string {
199237
let valueForSelect = content;
200238
let prefix = '';

0 commit comments

Comments
 (0)