Skip to content

Commit cdedfa8

Browse files
TASK-1881605: Multiselect component.
1 parent a02b784 commit cdedfa8

File tree

13 files changed

+1058
-47
lines changed

13 files changed

+1058
-47
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.pega.constellation.sdk.kmp.core.components.ComponentTypes.Group
2020
import com.pega.constellation.sdk.kmp.core.components.ComponentTypes.Integer
2121
import com.pega.constellation.sdk.kmp.core.components.ComponentTypes.ListView
2222
import com.pega.constellation.sdk.kmp.core.components.ComponentTypes.ModalViewContainer
23+
import com.pega.constellation.sdk.kmp.core.components.ComponentTypes.Multiselect
2324
import com.pega.constellation.sdk.kmp.core.components.ComponentTypes.OneColumn
2425
import com.pega.constellation.sdk.kmp.core.components.ComponentTypes.Phone
2526
import com.pega.constellation.sdk.kmp.core.components.ComponentTypes.RadioButtons
@@ -71,6 +72,7 @@ import com.pega.constellation.sdk.kmp.core.components.fields.UrlComponent
7172
import com.pega.constellation.sdk.kmp.core.components.widgets.ActionButtonsComponent
7273
import com.pega.constellation.sdk.kmp.core.components.widgets.AlertBannerComponent
7374
import com.pega.constellation.sdk.kmp.core.components.widgets.AutoCompleteComponent
75+
import com.pega.constellation.sdk.kmp.core.components.widgets.MultiselectComponent
7476
import com.pega.constellation.sdk.kmp.core.components.widgets.UnsupportedComponent
7577
import com.pega.constellation.sdk.kmp.core.api.ComponentDefinition as Def
7678

@@ -102,6 +104,7 @@ object ComponentRegistry {
102104
Def(Integer) { IntegerComponent(it) },
103105
Def(ListView) { ListViewComponent(it) },
104106
Def(ModalViewContainer) { ModalViewContainerComponent(it) },
107+
Def(Multiselect) { MultiselectComponent(it) },
105108
Def(OneColumn) { OneColumnComponent(it) },
106109
Def(Phone) { PhoneComponent(it) },
107110
Def(RadioButtons) { RadioButtonsComponent(it) },

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ object ComponentTypes {
4848
val ActionButtons = ComponentType("ActionButtons")
4949
val AlertBanner = ComponentType("AlertBanner")
5050
val AutoComplete = ComponentType("AutoComplete")
51+
val Multiselect = ComponentType("Multiselect")
5152

5253
// unsupported
5354
val Unsupported = ComponentType("Unsupported")
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.pega.constellation.sdk.kmp.core.components.widgets
2+
3+
import androidx.compose.runtime.getValue
4+
import androidx.compose.runtime.mutableStateOf
5+
import androidx.compose.runtime.setValue
6+
import com.pega.constellation.sdk.kmp.core.api.ComponentContext
7+
import com.pega.constellation.sdk.kmp.core.api.ComponentEvent
8+
import com.pega.constellation.sdk.kmp.core.components.fields.SelectableComponent
9+
import kotlinx.serialization.json.jsonArray
10+
import kotlinx.serialization.json.jsonPrimitive
11+
import kotlinx.serialization.json.JsonObject
12+
13+
class MultiselectComponent(context: ComponentContext) : SelectableComponent(context) {
14+
var selectedKeys: List<String> by mutableStateOf(emptyList())
15+
private set
16+
17+
override fun applyProps(props: JsonObject) {
18+
super.applyProps(props)
19+
selectedKeys = props["selectedKeys"]?.jsonArray
20+
?.map { it.jsonPrimitive.content }
21+
?: emptyList()
22+
}
23+
24+
fun addSelection(key: String) {
25+
if (selectedKeys.contains(key)) return
26+
selectedKeys = selectedKeys + key
27+
context.sendComponentEvent(ComponentEvent.multiselectAddItem(key))
28+
}
29+
30+
fun removeSelection(key: String) {
31+
if (!selectedKeys.contains(key)) return
32+
selectedKeys = selectedKeys - key
33+
context.sendComponentEvent(ComponentEvent.multiselectRemoveItem(key))
34+
}
35+
}
36+
37+
private fun ComponentEvent.Companion.multiselectAddItem(key: String) =
38+
ComponentEvent("MultiselectEvent", eventData = mapOf("type" to "addItem", "key" to key))
39+
40+
private fun ComponentEvent.Companion.multiselectRemoveItem(key: String) =
41+
ComponentEvent("MultiselectEvent", eventData = mapOf("type" to "removeItem", "key" to key))

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

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import androidx.compose.ui.test.ComposeUiTest
44
import androidx.compose.ui.test.ExperimentalTestApi
55
import androidx.compose.ui.test.onNodeWithText
66
import androidx.compose.ui.test.performClick
7+
import androidx.compose.ui.test.performTextInput
78
import androidx.compose.ui.test.runComposeUiTest
89
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.ComposeTest
910
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.waitForNode
@@ -12,54 +13,96 @@ import com.pega.constellation.sdk.kmp.test.mock.PegaVersion
1213
import kotlin.test.Test
1314

1415
@OptIn(ExperimentalTestApi::class)
15-
class DataReferenceListOfRecordsTest : ComposeTest(PegaVersion.v25_1) {
16+
class DataReferenceListOfRecordsTest : ComposeTest(PegaVersion.v25_1) {
1617
val columns = listOf("BRAND", "MODEL")
1718

1819
@Test
19-
fun test_table_simple_table() = runComposeUiTest {
20-
val cars = listOf(
21-
listOf("Ford", "Focus"),
22-
listOf("Toyota", "Corolla"),
23-
listOf("Fiat", "126p"),
24-
listOf("Skoda", "Octavia"),
25-
listOf("Audi", "A5")
26-
)
27-
val cars2 = listOf(
28-
listOf("Ford", "Focus"),
29-
listOf("Fiat", "126p"),
30-
)
31-
32-
setupApp("O40M3A-MarekCo-Work-DataReferenceListOfRecordsTest")
33-
34-
// create case
35-
onNodeWithText("New Service").performClick()
36-
37-
// Editable Table
38-
waitForNode("DataReference ListOfRecords - Table", substring = true)
39-
waitForNodes("DataReferenceListOfRecordsCars", count = 1)
40-
verifyTable(cars)
41-
onNodeWithText("Next").performClick()
42-
43-
// Editable SimpleTable
44-
waitForNode("DataReference ListOfRecords - SimpleTable", substring = true)
45-
waitForNode("DataReferenceListOfRecordsCars")
46-
verifyTable(cars)
47-
onNodeWithText("Next").performClick()
48-
49-
// Readonly Table
50-
waitForNode("DataReference ListOfRecords - Table readonly", substring = true)
51-
waitForNode("DataReferenceListOfRecordsCars")
52-
verifyTable(cars2)
53-
onNodeWithText("Next").performClick()
54-
55-
// Readonly SimpleTable
56-
waitForNode("DataReference ListOfRecords - SimpleTable readonly", substring = true)
57-
waitForNode("DataReferenceListOfRecordsCars")
58-
verifyTable(cars2)
59-
}
20+
fun test_table_simple_table() =
21+
runComposeUiTest {
22+
val cars =
23+
listOf(
24+
listOf("Ford", "Focus"),
25+
listOf("Toyota", "Corolla"),
26+
listOf("Fiat", "126p"),
27+
listOf("Skoda", "Octavia"),
28+
listOf("Audi", "A5"),
29+
)
30+
val cars2 =
31+
listOf(
32+
listOf("Ford", "Focus"),
33+
listOf("Fiat", "126p"),
34+
)
35+
36+
setupApp("O40M3A-MarekCo-Work-DataReferenceListOfRecordsTest")
37+
38+
// create case
39+
onNodeWithText("New Service").performClick()
40+
41+
// Editable Table
42+
waitForNode("DataReference ListOfRecords - Table", substring = true)
43+
waitForNodes("DataReferenceListOfRecordsCars", count = 1)
44+
verifyTable(cars)
45+
onNodeWithText("Next").performClick()
46+
47+
// Editable SimpleTable
48+
waitForNode("DataReference ListOfRecords - SimpleTable", substring = true)
49+
waitForNode("DataReferenceListOfRecordsCars")
50+
verifyTable(cars)
51+
onNodeWithText("Next").performClick()
52+
53+
// Readonly Table
54+
waitForNode("DataReference ListOfRecords - Table readonly", substring = true)
55+
waitForNode("DataReferenceListOfRecordsCars")
56+
verifyTable(cars2)
57+
onNodeWithText("Next").performClick()
58+
59+
// Readonly SimpleTable
60+
waitForNode("DataReference ListOfRecords - SimpleTable readonly", substring = true)
61+
waitForNode("DataReferenceListOfRecordsCars")
62+
verifyTable(cars2)
63+
}
6064

6165
private fun ComposeUiTest.verifyTable(data: List<List<String>>) {
6266
columns.forEach { waitForNodes(it, 2) }
6367
data.flatten().forEach { waitForNodes(it, 2) }
6468
}
69+
70+
@Test
71+
fun test_combo_box() =
72+
runComposeUiTest {
73+
setupApp("O40M3A-MarekCo-Work-DataReferenceMultiSelectTest")
74+
75+
// Create case
76+
onNodeWithText("New Service").performClick()
77+
78+
// Wait for multiselect to appear
79+
waitForNode("Cars Selection")
80+
81+
// Open dropdown by clicking on the field
82+
onNodeWithText("Cars Selection").performClick()
83+
84+
// Verify options are loaded from D_carsList
85+
waitForNode("Focus")
86+
waitForNode("Corolla")
87+
waitForNode("126p")
88+
waitForNode("Octavia")
89+
90+
// Test search functionality - type to filter, verify only matching option visible
91+
onNodeWithText("Cars Selection").performTextInput("oct")
92+
waitForNode("Octavia")
93+
94+
// Close dropdown (clears search via onExpandedChange), reopen, then select multiple items
95+
onNodeWithText("Cars Selection").performClick()
96+
onNodeWithText("Cars Selection").performClick()
97+
waitForNode("Focus")
98+
onNodeWithText("Focus").performClick()
99+
onNodeWithText("Octavia").performClick()
100+
101+
// Close dropdown by clicking on the field again
102+
onNodeWithText("Cars Selection").performClick()
103+
waitForNode("Focus, Octavia")
104+
105+
onNodeWithText("Submit").performClick()
106+
waitForNode("Thanks for registration")
107+
}
65108
}

0 commit comments

Comments
 (0)