Skip to content

Commit ee3efc6

Browse files
TASK-1830422: ListViewComponent now supports nested items. (#89) (#93)
Co-authored-by: mloct <tomasz.mlocek@pega.com>
1 parent b3fcd8e commit ee3efc6

File tree

11 files changed

+718
-7
lines changed

11 files changed

+718
-7
lines changed

core/src/commonMain/kotlin/com/pega/constellation/sdk/kmp/core/components/containers/ListViewComponent.kt

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import com.pega.constellation.sdk.kmp.core.components.containers.ListViewCompone
1111
import com.pega.constellation.sdk.kmp.core.components.containers.ListViewComponent.SelectionMode.SINGLE
1212
import com.pega.constellation.sdk.kmp.core.components.getJSONArray
1313
import com.pega.constellation.sdk.kmp.core.components.getString
14+
import kotlinx.serialization.json.JsonArray
15+
import kotlinx.serialization.json.JsonElement
1416
import kotlinx.serialization.json.JsonObject
17+
import kotlinx.serialization.json.jsonArray
1518
import kotlinx.serialization.json.jsonObject
1619
import kotlinx.serialization.json.jsonPrimitive
1720

@@ -27,6 +30,8 @@ class ListViewComponent(context: ComponentContext) : BaseComponent(context) {
2730
private set
2831
var columnNames by mutableStateOf(emptyList<String>())
2932
private set
33+
var columnLabels by mutableStateOf(emptyList<String>())
34+
private set
3035
var items by mutableStateOf(emptyList<Item>())
3136
private set
3237

@@ -38,11 +43,10 @@ class ListViewComponent(context: ComponentContext) : BaseComponent(context) {
3843
selectionMode = SINGLE
3944
selectedItemIndex = props.getIntOrNull("selectedItemIndex")
4045
columnNames = props.getJSONArray("columnNames").map { it.jsonPrimitive.content }
46+
columnLabels = props.getJSONArray("columnLabels").map { it.jsonPrimitive.content }
4147
items = props.getJSONArray("items")
42-
.map { item ->
43-
item.jsonObject.entries
44-
.associate { it.key to it.value.jsonPrimitive.content }
45-
.let { Item(it) }
48+
.map {
49+
Item(it.toFoldedItemContent(emptyMap(), ""))
4650
}
4751
}
4852

@@ -64,6 +68,25 @@ class ListViewComponent(context: ComponentContext) : BaseComponent(context) {
6468
private fun JsonObject.getIntOrNull(key: String): Int? =
6569
get(key)?.jsonPrimitive?.content?.takeIf { it.isNotBlank() }?.toIntOrNull()
6670

71+
private fun JsonElement.toFoldedItemContent(
72+
initialResult: Map<String, String>,
73+
currentPath: String
74+
): Map<String, String> =
75+
when (this) {
76+
is JsonObject -> jsonObject.entries.fold(initialResult) { accumulator, element ->
77+
val nextPath = if (currentPath.isNotEmpty()) {
78+
"$currentPath.${element.key}"
79+
} else {
80+
element.key
81+
}
82+
element.value.toFoldedItemContent(accumulator, nextPath)
83+
}
84+
is JsonArray -> jsonArray.foldIndexed(initialResult) { index, accumulator, element ->
85+
element.toFoldedItemContent(accumulator, "$currentPath[$index]")
86+
}
87+
else -> initialResult + mapOf(currentPath to jsonPrimitive.content)
88+
}
89+
6790
private fun String.toSelectionMode() =
6891
when (this.uppercase()) {
6992
"SINGLE" -> SINGLE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.cases
2+
3+
import androidx.compose.ui.test.ExperimentalTestApi
4+
import androidx.compose.ui.test.onNodeWithText
5+
import androidx.compose.ui.test.performClick
6+
import androidx.compose.ui.test.runComposeUiTest
7+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.ComposeTest
8+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.waitForNode
9+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.waitForNodes
10+
import com.pega.constellation.sdk.kmp.test.mock.PegaVersion
11+
import kotlin.test.Test
12+
13+
@OptIn(ExperimentalTestApi::class)
14+
class NestedDataReferenceTest : ComposeTest(PegaVersion.v24_2_2) {
15+
16+
@Test
17+
fun test_nested_json() = runComposeUiTest {
18+
setupApp("O40M3A-MarekCo-Work-NestedJSON")
19+
20+
// create case
21+
onNodeWithText("New Service").performClick()
22+
23+
// verify form title
24+
waitForNode("Create (N-", substring = true)
25+
26+
// verify column names
27+
listOf("CASE ID", "DATA1", "DATA2").forEach { waitForNodes(it, count = 2) }
28+
29+
// verify data
30+
listOf(
31+
listOf("S-32005", "Nested1-1", "Nested1-2"),
32+
listOf("S-32004", "Nested2-1", "Nested2-2"),
33+
listOf("S-32003", "Nested3-1", "Nested3-2"),
34+
listOf("S-32002", "Nested4-1", "Nested4-2"),
35+
listOf("S-32001", "Nested5-1", "Nested5-2")
36+
).flatten().forEach { waitForNodes(it, count = 2) }
37+
}
38+
}

samples/android-cmp-app/src/androidInstrumentedTest/kotlin/com/pega/constellation/sdk/kmp/samples/androidcmpapp/test/cases/ParametrizedDataReferenceTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import kotlin.test.Test
1717

1818
@OptIn(ExperimentalTestApi::class)
1919
class ParametrizedDataReferenceTest : ComposeTest(PegaVersion.v24_2_2) {
20-
private val columns = listOf("KeyName", "KeyLength", "Algorithm")
20+
private val columns = listOf("Key Name", "Key Length", "Algorithm")
2121

2222
private val unfilteredKeys = listOf(
2323
"AeroCrypt",

scripts/dxcomponents/components/containers/templates/listview/list-view.component.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export class ListViewComponent extends BaseComponent {
99
payload;
1010
fields$;
1111
displayedColumns$ = [];
12+
displayedColumnsLabels$ = [];
1213
configProps$;
1314
selectionMode;
1415
selectedItemIndex = null;
@@ -155,6 +156,7 @@ export class ListViewComponent extends BaseComponent {
155156
selectionMode: this.selectionMode,
156157
selectedItemIndex: String(this.selectedItemIndex),
157158
columnNames: this.displayedColumns$,
159+
columnLabels: this.displayedColumnsLabels$,
158160
items: this.response,
159161
};
160162
this.componentsManager.onComponentPropsUpdate(this);
@@ -195,6 +197,7 @@ export class ListViewComponent extends BaseComponent {
195197
const columns = this.#getHeaderCells(columnFields, this.fieldDefs);
196198
this.fields$ = this.#updateFields(this.fields$, fieldsMetaData.data.fields, columns);
197199
this.displayedColumns$ = columns.map((c) => c.id);
200+
this.displayedColumnsLabels$ = columns.map((c) => c.label);
198201
this.response = tableDataResults;
199202

200203
// disabling parsing for now parsing data according to field types like date/date-time/currency

scripts/dxcomponents/components/containers/templates/listview/utils.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,27 @@ function prepareExtraFields(metaFields, configFields, configFieldSet, reportColu
539539

540540
const AssignDashObjects = ["Assign-Worklist", "Assign-WorkBasket"];
541541

542+
function isFLProperty(label) {
543+
return label?.startsWith("@FL");
544+
}
545+
546+
function getFieldLabel(fieldConfig) {
547+
const { label, classID, caption } = fieldConfig;
548+
let fieldLabel = (label ?? caption)?.substring(4);
549+
const labelSplit = fieldLabel?.split(".");
550+
const propertyName = labelSplit?.pop();
551+
const fieldMetaData = PCore.getMetadataUtils().getPropertyMetadata(propertyName, classID) ?? {};
552+
fieldLabel = fieldMetaData.label ?? fieldMetaData.caption ?? propertyName;
553+
const definedOnClassID = fieldMetaData.definedOnClassID;
554+
const localeValue = PCore.getLocaleUtils().getLocaleValue(
555+
fieldLabel,
556+
`${definedOnClassID ?? fieldMetaData.classID ?? classID}.${propertyName}`,
557+
PCore.getLocaleUtils().FIELD_LABELS_BUNDLE_KEY,
558+
null
559+
);
560+
return localeValue || fieldLabel;
561+
}
562+
542563
function populateRenderingOptions(name, config, field) {
543564
const shouldDisplayAsSemanticLink = "displayAsLink" in field.config && field.config.displayAsLink;
544565
if (shouldDisplayAsSemanticLink) {
@@ -565,7 +586,10 @@ export function initializeColumns(fields = [], getMappedProperty = null) {
565586

566587
let label = field.config.label || field.config.caption;
567588
const { show = true, displayAs } = field.config;
568-
if (label.startsWith("@")) {
589+
590+
if (isFLProperty(label)) {
591+
label = getFieldLabel(field.config);
592+
} else if (label.startsWith("@")) {
569593
label = label.substring(3);
570594
}
571595

0 commit comments

Comments
 (0)