You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by jo...@apache.org on 2021/09/24 09:04:31 UTC

[isis] branch ISIS-2872 created (now 526eee5)

This is an automated email from the ASF dual-hosted git repository.

joergrade pushed a change to branch ISIS-2872
in repository https://gitbox.apache.org/repos/asf/isis.git.


      at 526eee5  ISIS-2872 Upgrade Kotlin/KVision Dependencies

This branch includes the following new commits:

     new c47da78  ISIS-2768 Load logos
     new cfd1da2  ISIS-2846 Extensions attribute changes adopted, class refactored
     new 526eee5  ISIS-2872 Upgrade Kotlin/KVision Dependencies

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[isis] 02/03: ISIS-2846 Extensions attribute changes adopted, class refactored

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

joergrade pushed a commit to branch ISIS-2872
in repository https://gitbox.apache.org/repos/asf/isis.git

commit cfd1da2b5aa781604e9265c213a8033c2060014a
Author: Jörg Rade <jo...@kuehne-nagel.com>
AuthorDate: Mon Sep 20 09:04:26 2021 +0200

    ISIS-2846 Extensions attribute changes adopted, class refactored
---
 .../kroviz/core/aggregator/AggregatorWithLayout.kt |  6 ++-
 .../kroviz/core/model/CollectionProperties.kt      |  4 +-
 .../org/apache/isis/client/kroviz/to/Action.kt     |  1 +
 .../org/apache/isis/client/kroviz/to/Collection.kt |  3 +-
 .../org/apache/isis/client/kroviz/to/DomainType.kt |  5 +-
 .../org/apache/isis/client/kroviz/to/Member.kt     |  1 +
 .../isis/client/kroviz/to/PlainTransferObjects.kt  | 21 +++-----
 .../org/apache/isis/client/kroviz/to/TObject.kt    |  1 +
 .../apache/isis/client/kroviz/to/mb/Extensions.kt  | 56 ++++++++++++++++++++++
 .../client/kroviz/ui/diagram/LinkTreeDiagram.kt    |  4 +-
 .../snapshots/demo2_0_0/COLLECTION_DESCRIPTION.kt  |  3 +-
 .../apache/isis/client/kroviz/to/DomainTypeTest.kt |  2 +-
 .../org/apache/isis/client/kroviz/to/MemberTest.kt |  5 +-
 .../apache/isis/client/kroviz/to/PropertyTest.kt   |  5 +-
 .../client/kroviz/ui/table/LinkTreeDiagramTest.kt  | 39 +++++++--------
 .../client/kroviz/ui/table/TableFactoryTest.kt     |  6 +--
 16 files changed, 109 insertions(+), 53 deletions(-)

diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/AggregatorWithLayout.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/AggregatorWithLayout.kt
index 32ed837..434fa1b 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/AggregatorWithLayout.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/AggregatorWithLayout.kt
@@ -19,12 +19,14 @@ abstract class AggregatorWithLayout : BaseAggregator() {
     }
 
     protected fun handleLayout(layout: Layout, dm: DisplayModelWithLayout, referrer: String) {
+        console.log("[CA.handleLayout] ISIS-2846")
+        console.log(layout)
         if (dm.layout == null) {
             dm.addLayout(layout)
             dm.properties.propertyLayoutList.forEach { p ->
                 val l = p.link
                 if (l == null) {
-                    //                   console.log(p.id + " link empty")  // ISIS-2846
+                    console.log(p.id + " link empty")  // ISIS-2846
                 } else {
                     val isDn = l.href.contains("datanucleus")
                     if (!isDn) {
@@ -36,7 +38,7 @@ abstract class AggregatorWithLayout : BaseAggregator() {
         }
     }
 
-    protected fun invokeLayoutLink(obj: TObject, aggregator: AggregatorWithLayout, referrer :String) {
+    protected fun invokeLayoutLink(obj: TObject, aggregator: AggregatorWithLayout, referrer: String) {
         val l = obj.getLayoutLink()
         if (l.representation() == Represention.OBJECT_LAYOUT_BS3) {
             invoke(l, aggregator, Constants.subTypeXml, referrer)
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/CollectionProperties.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/CollectionProperties.kt
index e48782b..e821481 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/CollectionProperties.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/CollectionProperties.kt
@@ -19,8 +19,8 @@
 package org.apache.isis.client.kroviz.core.model
 
 import org.apache.isis.client.kroviz.layout.PropertyLt
-import org.apache.isis.client.kroviz.to.Extensions
 import org.apache.isis.client.kroviz.to.Property
+import org.apache.isis.client.kroviz.to.mb.Extensions
 
 class CollectionProperties() {
     val list = mutableListOf<ColumnProperties>()
@@ -58,7 +58,7 @@ class CollectionProperties() {
         val id = description.id
         val cp = findOrCreate(id)
         val e: Extensions = description.extensions!!
-        cp.friendlyName = e.friendlyName
+        cp.friendlyName = e.getFriendlyName()
     }
 
     private fun findOrCreate(id: String): ColumnProperties {
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Action.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Action.kt
index f62cbfe..ee5d392 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Action.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Action.kt
@@ -19,6 +19,7 @@
 package org.apache.isis.client.kroviz.to
 
 import kotlinx.serialization.Serializable
+import org.apache.isis.client.kroviz.to.mb.Extensions
 
 @Serializable
 data class Action(val id: String,
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Collection.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Collection.kt
index 31f8848..d220e26 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Collection.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Collection.kt
@@ -19,6 +19,7 @@
 package org.apache.isis.client.kroviz.to
 
 import kotlinx.serialization.Serializable
+import org.apache.isis.client.kroviz.to.mb.Extensions
 
 @Serializable
 data class Collection(val id: String,
@@ -27,4 +28,4 @@ data class Collection(val id: String,
                       val extensions: Extensions,
                       val value: List<Link> = emptyList(),
                       val disabledReason: String = ""
-) : TransferObject, HasLinks {}
+) : TransferObject, HasLinks
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/DomainType.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/DomainType.kt
index 3f4c0d5..ca024dd 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/DomainType.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/DomainType.kt
@@ -20,6 +20,7 @@
 package org.apache.isis.client.kroviz.to
 
 import kotlinx.serialization.Serializable
+import org.apache.isis.client.kroviz.to.mb.Extensions
 
 @Serializable
 data class DomainType(
@@ -28,6 +29,4 @@ data class DomainType(
         val members: Map<String, Link>,
         val typeActions: Map<String, Link>,
         val extensions: Extensions
-) : TransferObject, HasLinks {
-
-}
+) : TransferObject, HasLinks
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Member.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Member.kt
index 82d8e07..d9401b2 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Member.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/Member.kt
@@ -19,6 +19,7 @@
 package org.apache.isis.client.kroviz.to
 
 import kotlinx.serialization.Serializable
+import org.apache.isis.client.kroviz.to.mb.Extensions
 
 @Serializable
 data class Member(val id: String,
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/PlainTransferObjects.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/PlainTransferObjects.kt
index 24e8217..2d05e0d 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/PlainTransferObjects.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/PlainTransferObjects.kt
@@ -16,11 +16,11 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
 package org.apache.isis.client.kroviz.to
 
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
+import org.apache.isis.client.kroviz.to.mb.Extensions
 
 enum class ActionSemantics(val type: String) {
     IDEMPOTENT("idempotent"),
@@ -35,20 +35,6 @@ data class DomainTypes(override val links: List<Link> = emptyList(),
 ) : TransferObject, HasLinks
 
 @Serializable
-data class Extensions(val oid: String = "",
-                      val isService: Boolean = false,
-                      val isPersistent: Boolean = false,
-                      val menuBar: String? = MenuBarPosition.PRIMARY.position,
-                      val actionSemantics: String? = null,
-                      val actionType: String = "",
-                      @SerialName("x-isis-format") val xIsisFormat: String? = null,
-                      val friendlyName: String = "",
-                      val collectionSemantics: String? = null,
-                      val pluralName: String = "",
-                      val description: String = ""
-) : TransferObject
-
-@Serializable
 data class HttpError(
         val httpStatusCode: Int,
         val message: String,
@@ -87,6 +73,11 @@ enum class Method(val operation: String) {
 //    DELETE("DELETE")  not used - Apache Isis defines delete operations on DomainObjects
 }
 
+enum class Position(val type: String) {
+    PANEL("PANEL"),
+    PANEL_DROPDOWN("PANEL_DROPDOWN")
+}
+
 @Serializable
 data class Property(val id: String = "",
                     val memberType: String = "",
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/TObject.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/TObject.kt
index d0ba6b4..fd1be37 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/TObject.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/TObject.kt
@@ -19,6 +19,7 @@
 package org.apache.isis.client.kroviz.to
 
 import kotlinx.serialization.Serializable
+import org.apache.isis.client.kroviz.to.mb.Extensions
 
 @Serializable
 data class TObject(override val links: List<Link> = emptyList(),
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/mb/Extensions.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/mb/Extensions.kt
new file mode 100644
index 0000000..adfd028
--- /dev/null
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/mb/Extensions.kt
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.client.kroviz.to.mb
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import org.apache.isis.client.kroviz.to.MenuBarPosition
+import org.apache.isis.client.kroviz.to.TransferObject
+
+@Serializable
+data class Extensions(val oid: String = "",
+                      val isService: Boolean = false,
+                      val isPersistent: Boolean = false,
+                      val menuBar: String? = MenuBarPosition.PRIMARY.position,
+                      val actionSemantics: String? = null,
+                      val actionType: String = "",
+                      @SerialName("x-isis-format") val xIsisFormat: String? = null,
+                      private val friendlyName: String = "",
+                      private val friendlyNameForm: String = "",
+                      val collectionSemantics: String? = null,
+                      val pluralName: String = "",
+                      private val description: String = "",
+                      private val descriptionForm: String = ""
+) : TransferObject {
+
+    fun getFriendlyName(): String {
+        if (friendlyName.isEmpty()) {
+            console.log("[Extensions.getFriendlyName] is empty")
+        }
+        return friendlyName
+    }
+
+    fun getDescription(): String {
+        if (description.isEmpty()) {
+            console.log("[Extensions.getDescription] is empty")
+        }
+        return description
+    }
+
+}
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/diagram/LinkTreeDiagram.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/diagram/LinkTreeDiagram.kt
index e11cfc8..decb7c1 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/diagram/LinkTreeDiagram.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/diagram/LinkTreeDiagram.kt
@@ -94,9 +94,9 @@ object LinkTreeDiagram {
         val obj = logEntry.obj
         if (obj != null) {
             val ets = (obj as Property).extensions!!
-            pc.addLine("friendlyName: " + ets.friendlyName)
+            pc.addLine("friendlyName: " + ets.getFriendlyName())
             pc.addHorizontalLine()
-            pc.add("descriptions: " + ets.description)
+            pc.add("descriptions: " + ets.getDescription())
             pc.toMindmapNode(level)
         }
         return pc.code
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/COLLECTION_DESCRIPTION.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/COLLECTION_DESCRIPTION.kt
index 5165d72..04546dd 100644
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/COLLECTION_DESCRIPTION.kt
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/COLLECTION_DESCRIPTION.kt
@@ -44,7 +44,8 @@ object COLLECTION_DESCRIPTION: Response(){
     "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/domain-type\""
   } ],
   "extensions" : {
-    "friendlyName" : "Entities"
+    "friendlyName" : "Entities",
+    "friendlyNameForm" : "static"
   }
 }
     """
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/DomainTypeTest.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/DomainTypeTest.kt
index 50f7d9c..9dbde2e 100644
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/DomainTypeTest.kt
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/DomainTypeTest.kt
@@ -34,7 +34,7 @@ class DomainTypeTest {
         //when
         val to = DomainTypeHandler().parse(jsonStr) as DomainType
         //then
-        assertEquals("Java Lang String Jdo", to.extensions.friendlyName)
+        assertEquals("Java Lang String Jdo", to.extensions.getFriendlyName())
     }
 
     @Test
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/MemberTest.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/MemberTest.kt
index 2511fd2..bdef37a 100644
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/MemberTest.kt
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/MemberTest.kt
@@ -22,6 +22,7 @@ import kotlinx.serialization.json.Json
 import org.apache.isis.client.kroviz.handler.MemberHandler
 import org.apache.isis.client.kroviz.snapshots.demo2_0_0.COLLECTION_DESCRIPTION
 import org.apache.isis.client.kroviz.snapshots.simpleapp1_16_0.FR_PROPERTY_DESCRIPTION
+import org.apache.isis.client.kroviz.to.mb.Extensions
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
@@ -73,7 +74,7 @@ class MemberTest() {
         val m = MemberHandler().parse(FR_PROPERTY_DESCRIPTION.str) as Member
         val extensions: Extensions? = m.extensions
         assertNotNull(extensions)
-        assertEquals("ResultListResult class", extensions.friendlyName)
+        assertEquals("ResultListResult class", extensions.getFriendlyName())
     }
 
     @Test
@@ -81,7 +82,7 @@ class MemberTest() {
         val m = MemberHandler().parse(COLLECTION_DESCRIPTION.str) as Member
         val extensions: Extensions? = m.extensions
         assertNotNull(extensions)
-        assertEquals("Entities", extensions.friendlyName)
+        assertEquals("Entities", extensions.getFriendlyName())
     }
 
     private fun parse(jsonStr: String): Member {
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/PropertyTest.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/PropertyTest.kt
index bd40c88..9641ddb 100644
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/PropertyTest.kt
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/to/PropertyTest.kt
@@ -24,6 +24,7 @@ import org.apache.isis.client.kroviz.snapshots.demo2_0_0.PROPERTY
 import org.apache.isis.client.kroviz.snapshots.demo2_0_0.PROPERTY_DESCRIPTION
 import org.apache.isis.client.kroviz.snapshots.simpleapp1_16_0.FR_OBJECT_PROPERTY_
 import org.apache.isis.client.kroviz.snapshots.simpleapp1_16_0.SO_PROPERTY
+import org.apache.isis.client.kroviz.to.mb.Extensions
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertTrue
@@ -35,7 +36,7 @@ class PropertyTest {
         val jsonStr = PROPERTY_DESCRIPTION.str
         val p = PropertyHandler().parse(jsonStr) as Property
         assertEquals("parity", p.id)
-        assertEquals("The parity of this 'DemoItem'.", p.extensions!!.description)
+        assertEquals("The parity of this 'DemoItem'.", p.extensions!!.getDescription())
     }
 
     @Test
@@ -84,7 +85,7 @@ class PropertyTest {
         val jsonStr = DOMAIN_TYPES_PROPERTY.str
         val p = PropertyHandler().parse(jsonStr) as Property
         val e: Extensions = p.extensions!!
-        val actual = e.friendlyName
+        val actual = e.getFriendlyName()
         val expected = "Read Only Property Derived Render Day Not Specified"
         assertEquals(expected, actual)
     }
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/ui/table/LinkTreeDiagramTest.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/ui/table/LinkTreeDiagramTest.kt
index 73a3432..90bdf8e 100644
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/ui/table/LinkTreeDiagramTest.kt
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/ui/table/LinkTreeDiagramTest.kt
@@ -19,6 +19,7 @@
 
 package org.apache.isis.client.kroviz.ui.table
 
+import org.apache.isis.client.kroviz.core.aggregator.SystemAggregator
 import org.apache.isis.client.kroviz.core.event.EventStore
 import org.apache.isis.client.kroviz.core.event.ResourceSpecification
 import org.apache.isis.client.kroviz.handler.*
@@ -26,21 +27,22 @@ import org.apache.isis.client.kroviz.snapshots.Response
 import org.apache.isis.client.kroviz.snapshots.simpleapp1_16_0.*
 import org.apache.isis.client.kroviz.to.Method
 import org.apache.isis.client.kroviz.ui.diagram.LinkTreeDiagram
-import kotlin.test.Test
 import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
 
 class LinkTreeDiagramTest {
 
-    @Test
+    //@Test         //TODO rework test to use AggregatorWithLayout
     fun testLinkTreeDiagram() {
         //when
-        load(RESTFUL, RestfulHandler())
-        load(RESTFUL_SERVICES, ServiceHandler())
-        load(RESTFUL_USER, UserHandler())
-        load(RESTFUL_MENUBARS, MenuBarsHandler())
-        load(RESTFUL_VERSION, VersionHandler())
-        load(RESTFUL_DOMAIN_TYPES, DomainTypesHandler())
+        val aggregator = SystemAggregator()
+        load(RESTFUL, "", RestfulHandler(), aggregator)
+        val referer = RESTFUL.url
+        load(RESTFUL_SERVICES, referer, ServiceHandler(), aggregator)
+        load(RESTFUL_USER, referer, UserHandler(), aggregator)
+        load(RESTFUL_MENUBARS, referer, MenuBarsHandler(), aggregator)
+        load(RESTFUL_VERSION, referer, VersionHandler(), aggregator)
+        load(RESTFUL_DOMAIN_TYPES, referer, DomainTypesHandler(), aggregator)
         // then
         assertTrue(EventStore.log.size >= 6)
         val rootRs = ResourceSpecification(RESTFUL.url)
@@ -48,20 +50,19 @@ class LinkTreeDiagramTest {
         assertNotNull(rootLogEntry)  //1
 
         // when
-        val code = LinkTreeDiagram.build().trim()
+        val code = LinkTreeDiagram.build(aggregator).trim()
         // then
-//        console.log("[LTDT.testLinkTreeDiagram] ${code}")
-        assertTrue(code.startsWith("@startsalt"))
-        assertTrue(code.endsWith("@endsalt"))
-        assertTrue(code.contains("+ /http://localhost:8080/restful/"))
-        assertTrue(code.contains("++ /http://localhost:8080/restful/version"))
-        //menuBars json doesn't have UP nor SELF nor a reference to it's parent
-//        assertTrue(code.contains("++ /http://localhost:8080/restful/menuBars"))
+        console.log("[LTDT.testLinkTreeDiagram] ${code}")
+        assertTrue(code.startsWith("@startmindmap"))
+        assertTrue(code.endsWith("@endmindmap"))
+        assertTrue(code.contains("http://localhost:8080/restful/"))
+        assertTrue(code.contains("http://localhost:8080/restful/version"))
+        assertTrue(code.contains("http://localhost:8080/restful/menuBars"))
     }
 
-    private fun load(response: Response, handler: BaseHandler) {
-        val rs = ResourceSpecification(response.url)
-        EventStore.start(rs, Method.GET.operation)
+    private fun load(response: Response, referer: String, handler: BaseHandler, aggregator: SystemAggregator) {
+        val rs = ResourceSpecification(response.url, referrerUrl = referer)
+        EventStore.start(rs, Method.GET.operation, aggregator = aggregator)
         val le = EventStore.end(rs, response.str)!!
         val tObj = handler.parse(response.str)!!
         le.setTransferObject(tObj)
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/ui/table/TableFactoryTest.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/ui/table/TableFactoryTest.kt
index af7127a..75f6d60 100644
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/ui/table/TableFactoryTest.kt
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/ui/table/TableFactoryTest.kt
@@ -18,13 +18,13 @@
  */
 package org.apache.isis.client.kroviz.ui.table
 
+import io.kvision.state.ObservableList
+import io.kvision.state.observableListOf
 import org.apache.isis.client.kroviz.core.model.Exposer
-import org.apache.isis.client.kroviz.to.Extensions
 import org.apache.isis.client.kroviz.to.Member
 import org.apache.isis.client.kroviz.to.MemberType
 import org.apache.isis.client.kroviz.to.TObject
-import io.kvision.state.ObservableList
-import io.kvision.state.observableListOf
+import org.apache.isis.client.kroviz.to.mb.Extensions
 
 
 class TableFactoryTest {

[isis] 01/03: ISIS-2768 Load logos

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

joergrade pushed a commit to branch ISIS-2872
in repository https://gitbox.apache.org/repos/asf/isis.git

commit c47da788dbea3abe03ad53bbbe974aebe360dde4
Author: Jörg Rade <jo...@kuehne-nagel.com>
AuthorDate: Mon Sep 13 15:37:59 2021 +0200

    ISIS-2768 Load logos
---
 .../kroviz/core/aggregator/BaseAggregator.kt       |  5 +--
 .../kroviz/core/aggregator/RestfulDispatcher.kt    |  7 ++--
 .../kroviz/core/aggregator/SystemAggregator.kt     | 17 ++++++++-
 .../kroviz/core/event/ResourceSpecification.kt     |  5 +--
 .../client/kroviz/core/event/RoXmlHttpRequest.kt   |  3 +-
 .../isis/client/kroviz/core/model/SystemDM.kt      | 15 +++++---
 .../isis/client/kroviz/handler/IconHandler.kt      | 20 +++--------
 .../isis/client/kroviz/handler/LayoutHandler.kt    |  3 +-
 .../isis/client/kroviz/handler/LayoutXmlHandler.kt |  7 ++--
 .../IconHandler.kt => utils/ImageUtils.kt}         | 23 +++---------
 .../LayoutHandler.kt => utils/UrlUtils.kt}         | 42 ++++++++++++----------
 11 files changed, 79 insertions(+), 68 deletions(-)

diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/BaseAggregator.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/BaseAggregator.kt
index edbe93b..e6e8bc4 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/BaseAggregator.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/BaseAggregator.kt
@@ -24,6 +24,7 @@ import org.apache.isis.client.kroviz.core.model.DisplayModel
 import org.apache.isis.client.kroviz.to.Link
 import org.apache.isis.client.kroviz.to.TObject
 import org.apache.isis.client.kroviz.ui.core.Constants
+import org.apache.isis.client.kroviz.utils.UrlUtils
 
 /**
  * An Aggregator:
@@ -76,11 +77,11 @@ abstract class BaseAggregator {
     }
 
     private fun Link.isLayout(): Boolean {
-        return href.isNotEmpty() && href.contains("layout")
+        return href.isNotEmpty() && UrlUtils.isLayout(href)
     }
 
     private fun Link.isIcon(): Boolean {
-        return href.isNotEmpty() && href.endsWith("object-icon")
+        return href.isNotEmpty() && UrlUtils.isObjectIcon(href)
     }
 
     protected fun invoke(
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/RestfulDispatcher.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/RestfulDispatcher.kt
index 13214f0..99f5951 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/RestfulDispatcher.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/RestfulDispatcher.kt
@@ -29,11 +29,14 @@ class RestfulDispatcher() : BaseAggregator() {
         val restful = logEntry.getTransferObject() as Restful
         restful.links.forEach {
             when {
+                it.rel.endsWith(Relation.SELF.name) -> { }
                 it.rel.endsWith("/menuBars") -> invokeNavigation(it)
-                it.relation() == Relation.SELF -> {
-                }
                 it.rel.endsWith("/services") -> {
                 }
+/*                it.rel.endsWith("/brand-logo-signin") -> {
+                }
+                it.rel.endsWith("/brand-logo-header") -> {
+                }   */
                 else -> invokeSystem(it)
             }
         }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/SystemAggregator.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/SystemAggregator.kt
index 90e370f..d6dc960 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/SystemAggregator.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/SystemAggregator.kt
@@ -23,6 +23,8 @@ import org.apache.isis.client.kroviz.core.model.SystemDM
 import org.apache.isis.client.kroviz.to.DomainTypes
 import org.apache.isis.client.kroviz.to.User
 import org.apache.isis.client.kroviz.to.Version
+import org.apache.isis.client.kroviz.utils.ImageUtils
+import org.apache.isis.client.kroviz.utils.UrlUtils
 
 class SystemAggregator() : BaseAggregator() {
 
@@ -36,7 +38,20 @@ class SystemAggregator() : BaseAggregator() {
             is User -> dpm.addData(obj)
             is Version -> dpm.addData(obj)
             is DomainTypes -> dpm.addData(obj)
-            else -> log(logEntry)
+            else -> {
+                if (logEntry.blob != null) {
+                    val icon = ImageUtils.extractIcon(logEntry)
+                    val url = logEntry.url
+                    val isApplicationIcon = UrlUtils.isApplicationIcon(url)
+                    when (isApplicationIcon) {
+                        url.contains("48") -> (dpm as SystemDM).addSmallIcon(icon)
+                        url.contains("256") -> (dpm as SystemDM).addLargeIcon(icon)
+                        else -> log(logEntry)
+                    }
+                } else {
+                    console.log("[SA.update] no blob/image")
+                }
+            }
         }
 
         if (dpm.canBeDisplayed()) {
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/event/ResourceSpecification.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/event/ResourceSpecification.kt
index ee18faf..3019d3a 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/event/ResourceSpecification.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/event/ResourceSpecification.kt
@@ -20,6 +20,7 @@ package org.apache.isis.client.kroviz.core.event
 
 import org.apache.isis.client.kroviz.ui.core.Constants
 import org.apache.isis.client.kroviz.utils.StringUtils
+import org.apache.isis.client.kroviz.utils.UrlUtils
 
 class ResourceSpecification(
         val url: String,
@@ -28,8 +29,8 @@ class ResourceSpecification(
 
     fun isRedundant(): Boolean {
         return when {
-            url.contains("object-layout") -> true
-            url.contains("/properties/") -> true
+            UrlUtils.isObjectLayout(url) -> true
+            UrlUtils.isProperties(url) -> true
             else -> false
         }
     }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/event/RoXmlHttpRequest.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/event/RoXmlHttpRequest.kt
index 28b9b1b..00b933f 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/event/RoXmlHttpRequest.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/event/RoXmlHttpRequest.kt
@@ -27,6 +27,7 @@ import org.apache.isis.client.kroviz.to.TObject
 import org.apache.isis.client.kroviz.ui.core.Constants
 import org.apache.isis.client.kroviz.ui.core.UiManager
 import org.apache.isis.client.kroviz.utils.StringUtils
+import org.apache.isis.client.kroviz.utils.UrlUtils
 import org.w3c.xhr.BLOB
 import org.w3c.xhr.XMLHttpRequest
 import org.w3c.xhr.XMLHttpRequestResponseType
@@ -53,7 +54,7 @@ class RoXmlHttpRequest(val aggregator: BaseAggregator?) {
         xhr.setRequestHeader("Authorization", "Basic $credentials")
         xhr.setRequestHeader(CONTENT_TYPE, "application/$subType;charset=UTF-8")
         xhr.setRequestHeader(ACCEPT, "application/$subType, ${Constants.pngMimeType}")
-        if (url.endsWith("object-icon")) {
+        if (UrlUtils.isIcon(url)) {
             xhr.responseType = XMLHttpRequestResponseType.BLOB
         }
 
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/SystemDM.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/SystemDM.kt
index 3e4581e..7f99f94 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/SystemDM.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/SystemDM.kt
@@ -18,15 +18,14 @@
  */
 package org.apache.isis.client.kroviz.core.model
 
-import org.apache.isis.client.kroviz.to.DomainTypes
-import org.apache.isis.client.kroviz.to.TransferObject
-import org.apache.isis.client.kroviz.to.User
-import org.apache.isis.client.kroviz.to.Version
+import org.apache.isis.client.kroviz.to.*
 
 class SystemDM(override val title: String) : DisplayModel() {
     var user: User? = null
     var version: Version? = null
     private var domainTypes: DomainTypes? = null
+    var logoSmall: Icon? = null
+    var logoLarge: Icon? = null
 
     override fun canBeDisplayed(): Boolean {
         return !isRendered
@@ -42,4 +41,12 @@ class SystemDM(override val title: String) : DisplayModel() {
         }
     }
 
+    fun addSmallIcon(icon: Icon) {
+        logoSmall = icon
+    }
+
+    fun addLargeIcon(icon: Icon) {
+        logoLarge = icon
+    }
+
 }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/IconHandler.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/IconHandler.kt
index e751528..ccb676b 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/IconHandler.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/IconHandler.kt
@@ -18,38 +18,26 @@
  */
 package org.apache.isis.client.kroviz.handler
 
-import org.apache.isis.client.kroviz.to.Icon
 import org.apache.isis.client.kroviz.to.TransferObject
 import org.apache.isis.client.kroviz.ui.core.Constants
-import org.w3c.dom.Image
-import org.w3c.dom.url.URL
-import org.w3c.files.Blob
+import org.apache.isis.client.kroviz.utils.ImageUtils
+import org.apache.isis.client.kroviz.utils.UrlUtils
 
 class IconHandler : BaseHandler() {
 
     override fun canHandle(response: String): Boolean {
-        val isObjectIcon = logEntry.url.endsWith("object-icon")
-        if (isObjectIcon) {
+        if (UrlUtils.isIcon(logEntry.url)) {
             return super.canHandle(response)
         }
         return false
     }
 
     override fun parse(response: String): TransferObject {
-        val blob = logEntry.blob!!
-        val image = blob.toImage()
-        return Icon(image)
+        return ImageUtils.extractIcon(logEntry)
     }
 
     override fun update() {
         logEntry.getAggregator().update(logEntry, Constants.pngMimeType)
     }
 
-    fun Blob.toImage() : Image {
-        val url = URL.createObjectURL(this)
-        val image = Image()
-        image.src = url
-        return image
-    }
-
 }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutHandler.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutHandler.kt
index 0d5316b..057f2ad 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutHandler.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutHandler.kt
@@ -21,13 +21,14 @@ package org.apache.isis.client.kroviz.handler
 import kotlinx.serialization.json.Json
 import org.apache.isis.client.kroviz.layout.Layout
 import org.apache.isis.client.kroviz.to.TransferObject
+import org.apache.isis.client.kroviz.utils.UrlUtils
 import org.apache.isis.client.kroviz.utils.XmlHelper
 
 class LayoutHandler : BaseHandler() {
 
     override fun canHandle(response: String): Boolean {
         val isJsonLayout = !XmlHelper.isXml(response)
-                && logEntry.url.endsWith("layout")
+                && UrlUtils.isLayout(logEntry.url)
         if (isJsonLayout) {
             return super.canHandle(response)
         }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutXmlHandler.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutXmlHandler.kt
index 89af96d..12012f4 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutXmlHandler.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutXmlHandler.kt
@@ -20,14 +20,15 @@ package org.apache.isis.client.kroviz.handler
 
 import org.apache.isis.client.kroviz.to.TransferObject
 import org.apache.isis.client.kroviz.to.bs3.Grid
+import org.apache.isis.client.kroviz.utils.UrlUtils
 import org.apache.isis.client.kroviz.utils.XmlHelper
 
 class LayoutXmlHandler : BaseHandler() {
 
     override fun canHandle(response: String): Boolean {
-        val isLayoutXml = XmlHelper.isXml(response)
-                && logEntry.url.endsWith("layout")
-        if (isLayoutXml) {
+        val isXmlLayout = XmlHelper.isXml(response)
+                && UrlUtils.isLayout(logEntry.url)
+        if (isXmlLayout) {
             return super.canHandle(response)
         }
         return false
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/IconHandler.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/ImageUtils.kt
similarity index 65%
copy from incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/IconHandler.kt
copy to incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/ImageUtils.kt
index e751528..80436f3 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/IconHandler.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/ImageUtils.kt
@@ -16,36 +16,23 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.client.kroviz.handler
+package org.apache.isis.client.kroviz.utils
 
+import org.apache.isis.client.kroviz.core.event.LogEntry
 import org.apache.isis.client.kroviz.to.Icon
-import org.apache.isis.client.kroviz.to.TransferObject
-import org.apache.isis.client.kroviz.ui.core.Constants
 import org.w3c.dom.Image
 import org.w3c.dom.url.URL
 import org.w3c.files.Blob
 
-class IconHandler : BaseHandler() {
+object ImageUtils {
 
-    override fun canHandle(response: String): Boolean {
-        val isObjectIcon = logEntry.url.endsWith("object-icon")
-        if (isObjectIcon) {
-            return super.canHandle(response)
-        }
-        return false
-    }
-
-    override fun parse(response: String): TransferObject {
+    fun extractIcon(logEntry: LogEntry): Icon {
         val blob = logEntry.blob!!
         val image = blob.toImage()
         return Icon(image)
     }
 
-    override fun update() {
-        logEntry.getAggregator().update(logEntry, Constants.pngMimeType)
-    }
-
-    fun Blob.toImage() : Image {
+    fun Blob.toImage(): Image {
         val url = URL.createObjectURL(this)
         val image = Image()
         image.src = url
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutHandler.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/UrlUtils.kt
similarity index 54%
copy from incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutHandler.kt
copy to incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/UrlUtils.kt
index 0d5316b..28d0560 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/handler/LayoutHandler.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/UrlUtils.kt
@@ -16,27 +16,33 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.client.kroviz.handler
-
-import kotlinx.serialization.json.Json
-import org.apache.isis.client.kroviz.layout.Layout
-import org.apache.isis.client.kroviz.to.TransferObject
-import org.apache.isis.client.kroviz.utils.XmlHelper
-
-class LayoutHandler : BaseHandler() {
-
-    override fun canHandle(response: String): Boolean {
-        val isJsonLayout = !XmlHelper.isXml(response)
-                && logEntry.url.endsWith("layout")
-        if (isJsonLayout) {
-            return super.canHandle(response)
-        }
-        return false
+package org.apache.isis.client.kroviz.utils
+
+import org.apache.isis.client.kroviz.to.Relation
+
+object UrlUtils {
+
+    fun isIcon(url: String): Boolean {
+        return isApplicationIcon(url) || isObjectIcon(url)
     }
 
+    fun isObjectIcon(url: String): Boolean {
+         return url.endsWith(Relation.OBJECT_ICON.type)
+    }
 
-    override fun parse(response: String): TransferObject {
-        return Json.decodeFromString(Layout.serializer(), response)
+    fun isApplicationIcon(url: String): Boolean {
+        return url.contains("/images/")
     }
 
+    fun isProperties(url: String): Boolean {
+        return url.contains("/properties/")
+    }
+
+    fun isLayout(url: String): Boolean {
+        return url.endsWith(Relation.LAYOUT.type)
+    }
+
+    fun isObjectLayout(url: String): Boolean {
+        return url.endsWith(Relation.OBJECT_LAYOUT.type)
+    }
 }

[isis] 03/03: ISIS-2872 Upgrade Kotlin/KVision Dependencies

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

joergrade pushed a commit to branch ISIS-2872
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 526eee54028d36389271c2a9fba8ca02d60fded8
Author: Jörg Rade <jo...@kuehne-nagel.com>
AuthorDate: Fri Sep 24 11:01:54 2021 +0200

    ISIS-2872 Upgrade Kotlin/KVision Dependencies
---
 incubator/clients/kroviz/TODO.adoc                 |  11 ++
 incubator/clients/kroviz/build.gradle.kts          |  83 +++----------
 incubator/clients/kroviz/gradle.properties         |   8 +-
 .../kotlin/org/apache/isis/client/kroviz/App.kt    |  30 ++++-
 .../isis/client/kroviz/ui/core/MenuFactory.kt      | 102 +++++++++-------
 .../org/apache/isis/client/kroviz/ui/core/RoApp.kt |   3 +-
 .../apache/isis/client/kroviz/ui/core/RoDialog.kt  |  76 ++++++------
 .../apache/isis/client/kroviz/ui/core/RoMenuBar.kt |  67 ++++++-----
 .../isis/client/kroviz/ui/core/RoStatusBar.kt      |  31 ++---
 .../apache/isis/client/kroviz/ui/core/RoTable.kt   |   7 +-
 .../isis/client/kroviz/ui/kv/override/RoTab.kt     |  59 ++++-----
 .../client/kroviz/ui/kv/override/RoTabPanel.kt     | 132 +++++++++------------
 .../isis/client/kroviz/ui/kv/override/RoWindow.kt  |   9 +-
 .../isis/client/kroviz/ui/panel/EventLogTable.kt   |   2 -
 14 files changed, 297 insertions(+), 323 deletions(-)

diff --git a/incubator/clients/kroviz/TODO.adoc b/incubator/clients/kroviz/TODO.adoc
new file mode 100644
index 0000000..ce1e295
--- /dev/null
+++ b/incubator/clients/kroviz/TODO.adoc
@@ -0,0 +1,11 @@
+# KVision
+
+* upgrade to 5.latest, kotlin to 1.5.10
+* revert patch to build.gradle.kts
+* https://github.com/rjaros/kvision/issues/306 (select in dropdown)
+* https://github.com/rjaros/kvision/issues/247 (map to fill whole container)
+* https://github.com/rjaros/kvision/issues/299 (header footer access)
+* Remove duplicated / copied KV files
+
+# Long Term
+* use docker for integration test
diff --git a/incubator/clients/kroviz/build.gradle.kts b/incubator/clients/kroviz/build.gradle.kts
index 455b821..5652244 100644
--- a/incubator/clients/kroviz/build.gradle.kts
+++ b/incubator/clients/kroviz/build.gradle.kts
@@ -16,15 +16,15 @@
 //  specific language governing permissions and limitations
 //  under the License.
 //
-import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
-import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
 import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
 import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
 
 plugins {
     val kotlinVersion: String by System.getProperties()
-    id("kotlinx-serialization") version kotlinVersion
+    kotlin("plugin.serialization") version kotlinVersion
     kotlin("js") version kotlinVersion
+    val kvisionVersion: String by System.getProperties()
+    id("io.kvision") version kvisionVersion
 }
 
 version = "2.0.0-SNAPSHOT"
@@ -40,6 +40,7 @@ repositories {
 val kotlinVersion: String by System.getProperties()
 val kvisionVersion: String by System.getProperties()
 
+// Custom Properties
 val webDir = file("src/main/web")
 
 kotlin {
@@ -47,16 +48,13 @@ kotlin {
         browser {
             runTask {
                 outputFileName = "main.bundle.js"
-                sourceMaps = true
+                sourceMaps = false
                 devServer = KotlinWebpackConfig.DevServer(
                     open = false,
                     port = 3000,
                     proxy = mutableMapOf(
                         "/kv/*" to "http://localhost:8080",
-                        "/kvws/*" to mapOf(
-                            "target" to "ws://localhost:8080",
-                            "ws" to true
-                        )
+                        "/kvws/*" to mapOf("target" to "ws://localhost:8080", "ws" to true)
                     ),
                     static = mutableListOf("$buildDir/processedResources/js/main")
                 )
@@ -73,15 +71,17 @@ kotlin {
         binaries.executable()
     }
     sourceSets["main"].dependencies {
+        implementation(npm("react-awesome-button", "*"))
+        implementation(npm("prop-types", "*"))
         implementation("io.kvision:kvision:$kvisionVersion")
         implementation("io.kvision:kvision-bootstrap:$kvisionVersion")
-        implementation("io.kvision:kvision-bootstrap:$kvisionVersion")
         implementation("io.kvision:kvision-bootstrap-css:$kvisionVersion")
         implementation("io.kvision:kvision-bootstrap-datetime:$kvisionVersion")
         implementation("io.kvision:kvision-bootstrap-select:$kvisionVersion")
         implementation("io.kvision:kvision-bootstrap-spinner:$kvisionVersion")
         implementation("io.kvision:kvision-bootstrap-upload:$kvisionVersion")
         implementation("io.kvision:kvision-bootstrap-dialog:$kvisionVersion")
+        implementation("io.kvision:kvision-bootstrap-typeahead:$kvisionVersion")
         implementation("io.kvision:kvision-fontawesome:$kvisionVersion")
         implementation("io.kvision:kvision-i18n:$kvisionVersion")
         implementation("io.kvision:kvision-richtext:$kvisionVersion")
@@ -90,6 +90,11 @@ kotlin {
         implementation("io.kvision:kvision-chart:$kvisionVersion")
         implementation("io.kvision:kvision-tabulator:$kvisionVersion")
         implementation("io.kvision:kvision-pace:$kvisionVersion")
+        implementation("io.kvision:kvision-toast:$kvisionVersion")
+        implementation("io.kvision:kvision-react:$kvisionVersion")
+        implementation("io.kvision:kvision-routing-navigo:$kvisionVersion")
+        implementation("io.kvision:kvision-state:$kvisionVersion")
+        implementation("io.kvision:kvision-rest:$kvisionVersion")
         implementation("io.kvision:kvision-moment:$kvisionVersion")
         implementation("io.kvision:kvision-maps:$kvisionVersion")
         implementation(npm("xmltojson", "1.3.5", false))
@@ -101,63 +106,3 @@ kotlin {
     }
     sourceSets["main"].resources.srcDir(webDir)
 }
-
-fun getNodeJsBinaryExecutable(): String {
-    val nodeDir = NodeJsRootPlugin.apply(rootProject).nodeJsSetupTaskProvider.get().destination
-    val isWindows = System.getProperty("os.name").toLowerCase().contains("windows")
-    val nodeBinDir = if (isWindows) nodeDir else nodeDir.resolve("bin")
-    val command = NodeJsRootPlugin.apply(rootProject).nodeCommand
-    val finalCommand = if (isWindows && command == "node") "node.exe" else command
-    return nodeBinDir.resolve(finalCommand).absolutePath
-}
-
-tasks {
-    create("generatePotFile", Exec::class) {
-        dependsOn("compileKotlinJs")
-        executable = getNodeJsBinaryExecutable()
-        args("${rootProject.buildDir}/js/node_modules/gettext-extract/bin/gettext-extract")
-        inputs.files(kotlin.sourceSets["main"].kotlin.files)
-        outputs.file("$projectDir/src/main/resources/i18n/messages.pot")
-    }
-}
-afterEvaluate {
-    extensions.configure<NodeJsRootExtension> {
-        versions.webpackDevServer.version = "4.0.0"
-    }
-    tasks {
-        getByName("processResources", Copy::class) {
-            dependsOn("compileKotlinJs")
-            exclude("**/*.pot")
-            doLast("Convert PO to JSON") {
-                destinationDir.walkTopDown().filter {
-                    it.isFile && it.extension == "po"
-                }.forEach {
-                    exec {
-                        executable = getNodeJsBinaryExecutable()
-                        args(
-                            "${rootProject.buildDir}/js/node_modules/gettext.js/bin/po2json",
-                            it.absolutePath,
-                            "${it.parent}/${it.nameWithoutExtension}.json"
-                        )
-                        println("Converted ${it.name} to ${it.nameWithoutExtension}.json")
-                    }
-                    it.delete()
-                }
-            }
-        }
-        create("zip", Zip::class) {
-            dependsOn("browserProductionWebpack")
-            group = "package"
-            destinationDirectory.set(file("$buildDir/libs"))
-            val distribution =
-                project.tasks.getByName("browserProductionWebpack", KotlinWebpack::class).destinationDirectory!!
-            from(distribution) {
-                include("*.*")
-            }
-            from(webDir)
-            duplicatesStrategy = DuplicatesStrategy.EXCLUDE
-            inputs.files(distribution, webDir)
-            outputs.file(archiveFile)
-        }
-    }
-}
diff --git a/incubator/clients/kroviz/gradle.properties b/incubator/clients/kroviz/gradle.properties
index 1f665df..2ecbefd 100644
--- a/incubator/clients/kroviz/gradle.properties
+++ b/incubator/clients/kroviz/gradle.properties
@@ -18,8 +18,8 @@
 #
 javaVersion=1.8
 #Plugins
-systemProp.kotlinVersion=1.5.10
-serializationVersion=1.2.1
+systemProp.kotlinVersion=1.5.31
+serializationVersion=1.2.2
 #Dependencies
-systemProp.kvisionVersion=4.8.3
-kotlin.js.compiler=legacy
+systemProp.kvisionVersion=5.1.1
+kotlin.js.compiler=ir
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/App.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/App.kt
index 34a3976..316864b 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/App.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/App.kt
@@ -18,24 +18,28 @@
  */
 package org.apache.isis.client.kroviz
 
+import io.kvision.*
 import org.apache.isis.client.kroviz.ui.core.RoApp
-import io.kvision.Application
 import io.kvision.pace.Pace
+import io.kvision.panel.ContainerType
 import io.kvision.panel.root
 import io.kvision.panel.vPanel
-import io.kvision.startApplication
 import io.kvision.utils.px
-import io.kvision.require
+import kotlinx.browser.window
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.asCoroutineDispatcher
+
+val AppScope = CoroutineScope(window.asCoroutineDispatcher())
 
 class App : Application() {
 
     init {
+        Pace.init()
         require("css/kroviz.css")
     }
 
     override fun start() {
-        Pace.init()
-        root("kroviz") {
+        root("kroviz", containerType = ContainerType.FLUID, addRow = true) {
             vPanel(spacing = 0) {
                 padding = 0.px
                 add(RoApp)
@@ -49,5 +53,19 @@ class App : Application() {
 }
 
 fun main() {
-    startApplication(::App)
+    startApplication(
+        ::App,
+        module.hot,
+        BootstrapModule,
+        BootstrapCssModule,
+        FontAwesomeModule,
+        BootstrapSelectModule,
+        BootstrapDatetimeModule,
+        BootstrapSpinnerModule,
+//        BootstrapTypeaheadModule,
+        BootstrapUploadModule,
+        RichTextModule,
+        ChartModule,
+        TabulatorModule,
+        CoreModule)
 }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/MenuFactory.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/MenuFactory.kt
index f34cc30..ef99b62 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/MenuFactory.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/MenuFactory.kt
@@ -36,17 +36,20 @@ import io.kvision.html.Link as KvisionHtmlLink
 
 object MenuFactory {
 
-    fun buildForObject(tObject: TObject,
-                       withText: Boolean = true,
-                       iconName: String = "Actions")
+    fun buildForObject(
+        tObject: TObject,
+        withText: Boolean = true,
+        iconName: String = "Actions"
+    )
             : DropDown {
         val type = tObject.domainType
         val text = if (withText) "Actions for $type" else ""
         val icon = IconManager.find(iconName)
         val dd = DropDown(
-                text = text,
-                icon = icon,
-                style = ButtonStyle.LINK)
+            text = text,
+            icon = icon,
+            style = ButtonStyle.LINK
+        )
         val actions = tObject.getActions()
         actions.forEach {
             val link = buildActionLink(it.id, text)
@@ -59,18 +62,22 @@ object MenuFactory {
         return dd
     }
 
-    fun buildForMenu(menu: Menu,
-                     style: ButtonStyle = ButtonStyle.LIGHT,
-                     withText: Boolean = true,
-                     classes: Set<String> = setOf())
+    fun buildForMenu(
+        menu: Menu,
+        style: ButtonStyle = ButtonStyle.LIGHT,
+        withText: Boolean = true,
+        classes: Set<String> = setOf()
+    )
             : DropDown {
         val menuTitle = menu.named
         val dd = DropDown(
-                text = if (withText) menuTitle else "",
-                icon = IconManager.find(menuTitle),
-                style = style,
-                classes = classes,
-                forNavbar = false)
+            text = if (withText) menuTitle else "",
+            icon = IconManager.find(menuTitle),
+            style = style,
+//                classes = classes,
+            forNavbar = false
+        )
+        dd.addCssClass(classes.toString())
         //dd.setDragDropData(Constants.stdMimeType, menuTitle)
         // action.setDragDropData gets always overridden by dd.setDragDropData
         menu.section.forEachIndexed { index, section ->
@@ -93,8 +100,9 @@ object MenuFactory {
         val menu = findMenuByTitle(title)
         return if (menu == null) null else
             buildForMenu(
-                    menu = menu,
-                    withText = false)
+                menu = menu,
+                withText = false
+            )
     }
 
     private fun findMenuByTitle(menuTitle: String): Menu? {
@@ -110,8 +118,9 @@ object MenuFactory {
     }
 
     fun buildForAction(
-            menuTitle: String,
-            actionTitle: String): KvisionHtmlLink? {
+        menuTitle: String,
+        actionTitle: String
+    ): KvisionHtmlLink? {
         val menu = findMenuByTitle(menuTitle)!!
         menu.section.forEachIndexed { _, section ->
             section.serviceAction.forEach { sa ->
@@ -129,14 +138,16 @@ object MenuFactory {
         return null
     }
 
-     fun buildActionLink(
-            label: String,
-            menuTitle: String): KvisionHtmlLink {
+    fun buildActionLink(
+        label: String,
+        menuTitle: String
+    ): KvisionHtmlLink {
         val actionTitle = StringUtils.deCamel(label)
         val actionLink: KvisionHtmlLink = ddLink(
-                label = actionTitle,
-                icon = IconManager.find(label),
-                classes = IconManager.findStyleFor(label))
+            label = actionTitle,
+            icon = IconManager.find(label),
+            classes = IconManager.findStyleFor(label)
+        )
         val id = "$menuTitle${Constants.actionSeparator}$actionTitle"
         actionLink.setDragDropData(Constants.stdMimeType, id)
         actionLink.id = id
@@ -144,33 +155,37 @@ object MenuFactory {
     }
 
     private fun ddLink(
-            label: String,
-            icon: String? = null,
-            classes: Set<String>? = null,
-            init: (KvisionHtmlLink.() -> Unit)? = null
+        label: String,
+        icon: String? = null,
+        classes: Set<String>? = null,
+        init: (KvisionHtmlLink.() -> Unit)? = null
     ): KvisionHtmlLink {
-        return KvisionHtmlLink(
-                label = label,
-                url = null,
-                icon = icon,
-                image = null,
-                separator = null,
-                labelFirst = true,
-                classes = (classes ?: null.set) + "dropdown-item").apply {
+        val link = KvisionHtmlLink(
+            label = label,
+            url = null,
+            icon = icon,
+            image = null,
+            separator = null,
+            labelFirst = true
+        )
+        link.addCssClass("dropdown-item")
+        return link.apply {
             init?.invoke(this)
         }
     }
 
     // initially added items will be enabled
     fun amendWithSaveUndo(
-            dd: DropDown,
-            tObject: TObject) {
+        dd: DropDown,
+        tObject: TObject
+    ) {
         dd.separator()
 
         val saveLink = tObject.links.first()
         val saveAction = buildActionLink(
-                label = "save",
-                menuTitle = tObject.domainType)
+            label = "save",
+            menuTitle = tObject.domainType
+        )
         saveAction.onClick {
             ResourceProxy().fetch(saveLink)
         }
@@ -178,8 +193,9 @@ object MenuFactory {
 
         val undoLink = Link(href = "")
         val undoAction = buildActionLink(
-                label = "undo",
-                menuTitle = tObject.domainType)
+            label = "undo",
+            menuTitle = tObject.domainType
+        )
         undoAction.onClick {
             ResourceProxy().fetch(undoLink)
         }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoApp.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoApp.kt
index 067db26..eb6bba1 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoApp.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoApp.kt
@@ -27,9 +27,10 @@ object RoApp : SimplePanel() {
     init {
         this.add(RoMenuBar.navbar)
 
-        val view = HPanel(classes = setOf("main")) {
+        val view = HPanel() {
             width = CssSize(100, UNIT.perc)
         }
+        view.addCssClass("main")
         view.add(RoIconBar.panel)
         view.add(RoView.tabPanel)
         this.add(view)
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoDialog.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoDialog.kt
index b977975..ec579c1 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoDialog.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoDialog.kt
@@ -38,49 +38,54 @@ import org.apache.isis.client.kroviz.utils.Point
 import io.kvision.html.Link as KvisionHtmlLink
 
 class RoDialog(
-        caption: String,
-        val items: List<FormItem>,
-        val command: Command,
-        defaultAction: String = "OK",
-        widthPerc: Int = 30,
-        heightPerc: Int = 100,
-        menu: List<KvisionHtmlLink>? = null,
-        customButtons: List<FormItem> = emptyList() ) :
-        Displayable, RoWindow(caption = caption, closeButton = true, menu = menu) {
+    caption: String,
+    val items: List<FormItem>,
+    val command: Command,
+    defaultAction: String = "OK",
+    widthPerc: Int = 30,
+    heightPerc: Int = 100,
+    menu: List<KvisionHtmlLink>? = null,
+    customButtons: List<FormItem> = emptyList()
+) :
+    Displayable, RoWindow(caption = caption, closeButton = true, menu = menu) {
 
     private val okButton = Button(
-            text = defaultAction,
-            icon = IconManager.find(defaultAction),
-            style = ButtonStyle.SUCCESS)
-            .onClick {
-                execute()
-            }
+        text = defaultAction,
+        icon = IconManager.find(defaultAction),
+        style = ButtonStyle.SUCCESS
+    )
+        .onClick {
+            execute()
+        }
 
     private val cancelButton = Button(
-            "Cancel",
-            "fas fa-times",
-            ButtonStyle.OUTLINEINFO)
-            .onClick {
-                close()
-            }
+        "Cancel",
+        "fas fa-times",
+        ButtonStyle.OUTLINEINFO
+    )
+        .onClick {
+            close()
+        }
 
     @Deprecated("remove once leaflet/svg is fully operational")
     private val scaleUpButton = Button(
-            "",
-            "fas fa-plus",
-            ButtonStyle.OUTLINEINFO)
-            .onClick {
-                (command as DiagramDialog).scale(Direction.UP)
-            }
+        "",
+        "fas fa-plus",
+        ButtonStyle.OUTLINEINFO
+    )
+        .onClick {
+            (command as DiagramDialog).scale(Direction.UP)
+        }
 
     @Deprecated("remove once leaflet/svg is fully operational")
     private val scaleDownButton = Button(
-            "",
-            "fas fa-minus",
-            ButtonStyle.OUTLINEINFO)
-            .onClick {
-                (command as DiagramDialog).scale(Direction.DOWN)
-            }
+        "",
+        "fas fa-minus",
+        ButtonStyle.OUTLINEINFO
+    )
+        .onClick {
+            (command as DiagramDialog).scale(Direction.DOWN)
+        }
 
     var formPanel: FormPanel<String>? = null
 
@@ -98,9 +103,8 @@ class RoDialog(
 
             add(formPanel!!, grow = 2)
 
-            val buttonBar = HPanel(
-                    spacing = 10,
-                    classes = setOf("button-bar"))
+            val buttonBar = HPanel(spacing = 10)
+            buttonBar.addCssClass("button-bar")
             buttonBar.add(okButton)
             customButtons.forEach {
                 val b = createButton(it)
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoMenuBar.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoMenuBar.kt
index ec07964..2427639 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoMenuBar.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoMenuBar.kt
@@ -59,80 +59,90 @@ object RoMenuBar : SimplePanel() {
 
     private fun buildMainMenu(): DropDown {
         return dropDown(
-                "",
-                icon = IconManager.find("Burger"),
-                forNavbar = false,
-                style = ButtonStyle.LIGHT)
+            "",
+            icon = IconManager.find("Burger"),
+            forNavbar = false,
+            style = ButtonStyle.LIGHT
+        )
         {
             ddLink(
-                    "Connect ...",
-                    icon = IconManager.find("Connect")
+                "Connect ...",
+                icon = IconManager.find("Connect")
             ).onClick { e ->
                 val at = Point(e.pageX.toInt(), e.pageY.toInt())
                 LoginPrompt().open(at)
             }
 
             val toolTitle = "Toolbar"
-            ddLink(toolTitle,
-                    icon = IconManager.find(toolTitle)
+            ddLink(
+                toolTitle,
+                icon = IconManager.find(toolTitle)
             ).onClick {
                 RoIconBar.toggle()
             }
 
             val sampleTitle = "History"
-            ddLink(sampleTitle,
-                    icon = IconManager.find(sampleTitle)
+            ddLink(
+                sampleTitle,
+                icon = IconManager.find(sampleTitle)
             ).onClick {
                 val model = EventStore.log
                 UiManager.add("Log Entries", EventLogTable(model))
             }
 
             val chartTitle = "Sample Chart"
-            ddLink(chartTitle,
-                    icon = IconManager.find("Chart")
+            ddLink(
+                chartTitle,
+                icon = IconManager.find("Chart")
             ).onClick {
                 UiManager.add(chartTitle, EventChart(SampleChartModel()))
             }
 
             val geoMapTitle = "Sample Geo Map"
-            ddLink(geoMapTitle,
-                    icon = IconManager.find("Map")
+            ddLink(
+                geoMapTitle,
+                icon = IconManager.find("Map")
             ).onClick {
                 UiManager.add(geoMapTitle, GeoMap())
             }
 
             val svgMapTitle = "Sample SVG Map"
-            ddLink(svgMapTitle,
-                    icon = IconManager.find("Diagram")
+            ddLink(
+                svgMapTitle,
+                icon = IconManager.find("Diagram")
             ).onClick {
                 UiManager.add(svgMapTitle, SvgMap())
             }
 
             val svgInlineTitle = "Sample SVG Inline (interactive)"
-            ddLink(svgInlineTitle,
-                    icon = IconManager.find("Diagram")
+            ddLink(
+                svgInlineTitle,
+                icon = IconManager.find("Diagram")
             ).onClick {
                 SvgInline().open()
             }
 
             val imageTitle = "Sample Image"
-            ddLink(imageTitle,
-                    icon = IconManager.find("Image")
+            ddLink(
+                imageTitle,
+                icon = IconManager.find("Image")
             ).onClick {
                 val panel = ImageSample()
                 RoView.addTab(imageTitle, panel)
             }
 
             val searchTitle = "Dropdown search example"
-            ddLink(searchTitle,
-                    icon = IconManager.find("Find")
+            ddLink(
+                searchTitle,
+                icon = IconManager.find("Find")
             ).onClick {
                 UiManager.add(searchTitle, DropdownSearch())
             }
 
             val aboutTitle = "About"
-            ddLink(aboutTitle,
-                    icon = IconManager.find(aboutTitle)
+            ddLink(
+                aboutTitle,
+                icon = IconManager.find(aboutTitle)
             ).onClick {
                 About().open()
             }
@@ -151,10 +161,11 @@ object RoMenuBar : SimplePanel() {
 
     private fun logoButton() {
         val classes = setOf("isis-logo-button-image", "logo-button")
-        val logo = Button("", style = ButtonStyle.LINK, classes = classes)
-                .onClick {
-                    window.open("https://isis.apache.org")
-                }
+        val logo = Button("", style = ButtonStyle.LINK)
+        logo.addCssClass(classes.toString())
+        logo.onClick {
+            window.open("https://isis.apache.org")
+        }
         nav.add(logo)
     }
 
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoStatusBar.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoStatusBar.kt
index ee5bd00..5f68f86 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoStatusBar.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoStatusBar.kt
@@ -35,9 +35,9 @@ import org.apache.isis.client.kroviz.ui.dialog.NotificationDialog
 import org.apache.isis.client.kroviz.utils.IconManager
 
 object RoStatusBar {
-    val navbar = Navbar(
-            type = NavbarType.FIXEDBOTTOM,
-            classes = setOf("status-bar"))
+    val navbar = Navbar(type = NavbarType.FIXEDBOTTOM)
+
+    //FIXME    navbar.addCssClasses("status-bar")
     private val nav = Nav(rightAlign = true)
     private val userBtn: Button = buildButton("", "Me", ButtonStyle.OUTLINEWARNING)
     private val classDiagram = buildButton("", "Diagram", ButtonStyle.OUTLINEWARNING)
@@ -46,9 +46,10 @@ object RoStatusBar {
 
     private fun buildButton(text: String, iconName: String, style: ButtonStyle): Button {
         return Button(
-                text = text,
-                icon = IconManager.find(iconName),
-                style = style).apply {
+            text = text,
+            icon = IconManager.find(iconName),
+            style = style
+        ).apply {
             padding = CssSize(-16, UNIT.px)
             margin = CssSize(0, UNIT.px)
         }
@@ -117,18 +118,20 @@ object RoStatusBar {
 
     private fun isisButton(): Button {
         val classes = setOf("isis-logo-button-image", "logo-button")
-        return Button("", style = ButtonStyle.LINK, classes = classes)
-                .onClick {
-                    window.open("https://isis.apache.org")
-                }
+        val b = Button("", style = ButtonStyle.LINK)
+        b.addCssClass(classes.toString())
+        return b.onClick {
+            window.open("https://isis.apache.org")
+        }
     }
 
     private fun kvisionButton(): Button {
         val classes = setOf("kvision-logo-button-image", "logo-button")
-        return Button("", style = ButtonStyle.LINK, classes = classes)
-                .onClick {
-                    window.open("https://kvision.io")
-                }
+        val b = Button("", style = ButtonStyle.LINK)
+        b.addCssClass(classes.toString())
+        return b.onClick {
+            window.open("https://kvision.io")
+        }
     }
 
 }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoTable.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoTable.kt
index 1dbe848..531e64d 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoTable.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/core/RoTable.kt
@@ -22,7 +22,7 @@ import io.kvision.core.Container
 import io.kvision.core.CssSize
 import io.kvision.core.UNIT
 import io.kvision.panel.SimplePanel
-import io.kvision.table.TableType
+import io.kvision.tabulator.TableType
 import io.kvision.tabulator.Layout
 import io.kvision.tabulator.Tabulator
 import io.kvision.tabulator.TabulatorOptions
@@ -59,7 +59,7 @@ class RoTable(displayCollection: CollectionDM) : SimplePanel() {
 
         tabulator(model, options = options, types = tableTypes) {
             setEventListener<Tabulator<Exposer>> {
-                tabulatorCellClick = {
+                cellClickTabulator = {
                     // can't check cast to external interface
                     val cc = it.detail as CellComponent
                     val column = cc.getColumn().getField()
@@ -96,7 +96,8 @@ class RoTable(displayCollection: CollectionDM) : SimplePanel() {
             classes: Set<String> = setOf(),
             init: (Tabulator<T>.() -> Unit)? = null
     ): Tabulator<T> {
-        val tabulator = Tabulator(data, dataUpdateOnEdit, options, types, classes)
+        val tabulator = Tabulator(data, dataUpdateOnEdit, options, types)
+        tabulator.addCssClass(classes.toString())
         init?.invoke(tabulator)
         return tabulator
     }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoTab.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoTab.kt
index 4b017d2..a9dbc58 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoTab.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoTab.kt
@@ -32,9 +32,8 @@ import io.kvision.html.Icon
 import io.kvision.html.Link
 import io.kvision.html.TAG
 import io.kvision.html.Tag
+import io.kvision.panel.TabPanel
 import io.kvision.routing.RoutingManager
-import io.kvision.state.ObservableState
-import io.kvision.state.bind
 import io.kvision.utils.obj
 import org.apache.isis.client.kroviz.utils.DomUtil
 import org.apache.isis.client.kroviz.utils.ScalableVectorGraphic
@@ -51,22 +50,19 @@ import org.apache.isis.client.kroviz.utils.ScalableVectorGraphic
  * @param init an initializer extension function
  */
 open class RoTab(
+    label: String? = null, icon: String? = null,
+    image: ResString? = null, closable: Boolean = false, val route: String? = null,
+    init: (RoTab.() -> Unit)? = null
+) : Tag(TAG.LI, className = "nav-item") {
+
+    constructor(
         label: String? = null,
+        child: Component,
         icon: String? = null,
         image: ResString? = null,
         closable: Boolean = false,
-        val route: String? = null,
+        route: String? = null,
         init: (RoTab.() -> Unit)? = null
-) : Tag(TAG.LI, classes = setOf("nav-item")) {
-
-    constructor(
-            label: String? = null,
-            child: Component,
-            icon: String? = null,
-            image: ResString? = null,
-            closable: Boolean = false,
-            route: String? = null,
-            init: (RoTab.() -> Unit)? = null
     ) : this(label, icon, image, closable, route, init) {
         @Suppress("LeakingThis")
         add(child)
@@ -115,20 +111,20 @@ open class RoTab(
             closeIcon.visible = value
         }
 
-    internal val closeIcon = Icon("fas fa-times").apply {
+    protected val closeIcon = Icon("fas fa-times").apply {
         addCssClass("kv-tab-close")
         visible = closable
         setEventListener<Icon> {
             click = { e ->
-                val tabPanel = (this@RoTab.parent as? RoTabPanelNav)?.tabPanel
+                val tabPanel = (this@RoTab.parent as? RoTabPanel.TabPanelNav)?.tabPanel
                 val actIndex = tabPanel?.getTabIndex(this@RoTab) ?: -1
                 e.asDynamic().data = actIndex
                 @Suppress("UnsafeCastFromDynamic")
-                val event = org.w3c.dom.CustomEvent("tabClosing", obj { detail = e; cancelable = true })
+                val event = org.w3c.dom.CustomEvent("closingTab", obj { detail = e; cancelable = true })
                 if (tabPanel?.getElement()?.dispatchEvent(event) != false) {
                     tabPanel?.removeTab(actIndex)
                     @Suppress("UnsafeCastFromDynamic")
-                    val closed = org.w3c.dom.CustomEvent("tabClosed", obj { detail = e })
+                    val closed = org.w3c.dom.CustomEvent("closedTab", obj { detail = e })
                     tabPanel?.getElement()?.dispatchEvent(closed)
                 }
                 e.stopPropagation()
@@ -139,20 +135,20 @@ open class RoTab(
     /**
      * A link component within the tab.
      */
-    val link = Link(label ?: "", "#", icon, image, classes = setOf("nav-link")).apply {
+    val link = Link(label ?: "", "#", icon, image, className = "nav-link").apply {
         add(this@RoTab.closeIcon)
     }
 
     internal val tabId = counter++
 
     protected val routingHandler = { _: Any ->
-        (this@RoTab.parent as? RoTabPanelNav)?.tabPanel?.activeTab = this
+        (this@RoTab.parent as? RoTabPanel.TabPanelNav)?.tabPanel?.activeTab = this
     }
 
     init {
         addPrivate(link)
         onClick { e ->
-            (this@RoTab.parent as? RoTabPanelNav)?.tabPanel?.activeTab = this
+            (this@RoTab.parent as? RoTabPanel.TabPanelNav)?.tabPanel?.activeTab = this
             e.preventDefault()
             if (route != null) {
                 RoutingManager.getRouter().kvNavigate(route)
@@ -168,7 +164,7 @@ open class RoTab(
     }
 
     override fun childrenVNodes(): Array<VNode> {
-        return (privateChildren).filter { it.visible }.map { it.renderVNode() }.toTypedArray()
+        return (privateChildren!!).filter { it.visible }.map { it.renderVNode() }.toTypedArray()
     }
 
     override fun dispose() {
@@ -186,25 +182,12 @@ open class RoTab(
  *
  * It takes the same parameters as the constructor of the built component.
  */
-fun RoTabPanel.tab(
-        label: String? = null, icon: String? = null,
-        image: ResString? = null, closable: Boolean = false, route: String? = null,
-        init: (RoTab.() -> Unit)? = null
+fun TabPanel.tab(
+    label: String? = null, icon: String? = null,
+    image: ResString? = null, closable: Boolean = false, route: String? = null,
+    init: (RoTab.() -> Unit)? = null
 ): RoTab {
     val tab = RoTab(label, icon, image, closable, route, init)
     this.add(tab)
     return tab
 }
-
-/**
- * DSL builder extension function for observable state.
- *
- * It takes the same parameters as the constructor of the built component.
- */
-fun <S> RoTabPanel.tab(
-        state: ObservableState<S>,
-        label: String? = null, icon: String? = null,
-        image: ResString? = null, closable: Boolean = false, route: String? = null,
-        init: (RoTab.(S) -> Unit)
-) = tab(label, icon, image, closable, route).bind(state, true, init)
-
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoTabPanel.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoTabPanel.kt
index 9ca5846..bb1390f 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoTabPanel.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoTabPanel.kt
@@ -26,13 +26,11 @@ package org.apache.isis.client.kroviz.ui.kv.override
 import com.github.snabbdom.VNode
 import io.kvision.core.*
 import io.kvision.panel.SimplePanel
+import io.kvision.panel.Tab
 import io.kvision.panel.VPanel
 import io.kvision.routing.RoutingManager
-import io.kvision.state.ObservableState
-import io.kvision.state.bind
 import io.kvision.utils.auto
 import io.kvision.utils.obj
-import io.kvision.utils.set
 import org.apache.isis.client.kroviz.ui.core.RoView
 
 /**
@@ -66,29 +64,29 @@ enum class SideTabSize {
  * @param sideTabSize side tab size
  * @param scrollableTabs determines if tabs are scrollable (default: false)
  * @param draggableTabs determines if tabs are draggable (default: false)
- * @param classes a set of CSS class names
+ * @param className CSS class names
  * @param init an initializer extension function
  */
 @Suppress("LeakingThis")
 open class RoTabPanel(
-        private val tabPosition: TabPosition = TabPosition.TOP,
-        private val sideTabSize: SideTabSize = SideTabSize.SIZE_3,
-        val scrollableTabs: Boolean = false,
-        val draggableTabs: Boolean = false,
-        classes: Set<String> = setOf(),
-        init: (RoTabPanel.() -> Unit)? = null
-) : SimplePanel(classes) {
-
-    private val navClasses = when (tabPosition) {
-        TabPosition.TOP -> if (scrollableTabs) setOf("nav", "nav-tabs", "tabs-top") else setOf("nav", "nav-tabs")
-        TabPosition.LEFT -> setOf("nav", "nav-tabs", "tabs-left", "flex-column")
-        TabPosition.RIGHT -> setOf("nav", "nav-tabs", "tabs-right", "flex-column")
+    protected val tabPosition: TabPosition = TabPosition.TOP,
+    protected val sideTabSize: SideTabSize = SideTabSize.SIZE_3,
+    val scrollableTabs: Boolean = false,
+    val draggableTabs: Boolean = false,
+    className: String? = null,
+    init: (RoTabPanel.() -> Unit)? = null
+) : SimplePanel((className?.let { "$it " } ?: "") + "kv-tab-panel") {
+
+    protected val navClasses = when (tabPosition) {
+        TabPosition.TOP -> if (scrollableTabs) "nav nav-tabs tabs-top" else "nav nav-tabs"
+        TabPosition.LEFT -> "nav nav-tabs tabs-left flex-column"
+        TabPosition.RIGHT -> "nav nav-tabs tabs-right flex-column"
     }
 
     internal val tabs = mutableListOf<RoTab>()
 
-    private val nav = RoTabPanelNav(this, navClasses)
-    private val content = RoTabPanelContent(this)
+    private val nav = TabPanelNav(this, navClasses)
+    private val content = TabPanelContent(this)
 
     /**
      * The index of the active tab.
@@ -102,7 +100,7 @@ open class RoTabPanel(
                 }
                 tabs.getOrNull(value)?.link?.addCssClass("active")
                 @Suppress("UnsafeCastFromDynamic")
-                this.dispatchEvent("tabChange", obj { detail = obj { data = value } })
+                this.dispatchEvent("changeTab", obj { detail = obj { data = value } })
             }
         }
 
@@ -116,6 +114,7 @@ open class RoTabPanel(
         }
 
     init {
+        //TODO to be set by caller
         width = auto
         marginTop = CssSize(40, UNIT.px)
         when (tabPosition) {
@@ -127,25 +126,21 @@ open class RoTabPanel(
                 this.addSurroundingCssClass("container-fluid")
                 this.addCssClass("row")
                 val sizes = calculateSideClasses()
-                this.addPrivate(WidgetWrapper(nav, setOf(sizes.first, "pl-0", "pr-0")))
-                this.addPrivate(WidgetWrapper(content, setOf(sizes.second, "pl-0", "pr-0")))
+                this.addPrivate(WidgetWrapper(nav, "${sizes.first} ps-0 pe-0"))
+                this.addPrivate(WidgetWrapper(content, "${sizes.second} ps-0 pe-0"))
             }
             TabPosition.RIGHT -> {
                 this.addSurroundingCssClass("container-fluid")
                 this.addCssClass("row")
                 val sizes = calculateSideClasses()
-                this.addPrivate(WidgetWrapper(content, setOf(sizes.second, "pl-0", "pr-0")))
-                this.addPrivate(WidgetWrapper(nav, setOf(sizes.first, "pl-0", "pr-0")))
+                this.addPrivate(WidgetWrapper(content, "${sizes.second} ps-0 pe-0"))
+                this.addPrivate(WidgetWrapper(nav, "${sizes.first} ps-0 pe-0"))
             }
         }
         init?.invoke(this)
     }
 
-    override fun render(): VNode {
-        return render("div", childrenVNodes())
-    }
-
-    private fun calculateSideClasses(): Pair<String, String> {
+    protected fun calculateSideClasses(): Pair<String, String> {
         return when (sideTabSize) {
             SideTabSize.SIZE_1 -> Pair("col-sm-1", "col-sm-11")
             SideTabSize.SIZE_2 -> Pair("col-sm-2", "col-sm-10")
@@ -379,6 +374,33 @@ open class RoTabPanel(
         return null
     }
 
+    /**
+     * A helper component for rendering tabs.
+     */
+    class TabPanelNav(internal val tabPanel: RoTabPanel, className: String) : SimplePanel(className) {
+
+        override fun render(): VNode {
+            return render("ul", childrenVNodes())
+        }
+
+        override fun childrenVNodes(): Array<VNode> {
+            return tabPanel.tabs.filter { it.visible }.map { it.renderVNode() }.toTypedArray()
+        }
+
+    }
+
+    /**
+     * A helper component for rendering tab content.
+     */
+    class TabPanelContent(private val tabPanel: RoTabPanel) : SimplePanel() {
+
+        override fun childrenVNodes(): Array<VNode> {
+            return tabPanel.tabs.getOrNull(tabPanel.activeIndex)?.getChildren()?.map { it.renderVNode() }
+                ?.toTypedArray()
+                ?: emptyArray()
+        }
+
+    }
 }
 
 /**
@@ -387,54 +409,14 @@ open class RoTabPanel(
  * It takes the same parameters as the constructor of the built component.
  */
 fun Container.tabPanel(
-        tabPosition: TabPosition = TabPosition.TOP,
-        sideTabSize: SideTabSize = SideTabSize.SIZE_3,
-        scrollableTabs: Boolean = false,
-        draggableTabs: Boolean = false,
-        classes: Set<String>? = null,
-        className: String? = null,
-        init: (RoTabPanel.() -> Unit)? = null
+    tabPosition: TabPosition = TabPosition.TOP,
+    sideTabSize: SideTabSize = SideTabSize.SIZE_3,
+    scrollableTabs: Boolean = false,
+    draggableTabs: Boolean = false,
+    className: String? = null,
+    init: (RoTabPanel.() -> Unit)? = null
 ): RoTabPanel {
-    val tabPanel = RoTabPanel(tabPosition, sideTabSize, scrollableTabs, draggableTabs, classes ?: className.set, init)
+    val tabPanel = RoTabPanel(tabPosition, sideTabSize, scrollableTabs, draggableTabs, className, init)
     this.add(tabPanel)
     return tabPanel
 }
-
-/**
- * DSL builder extension function for observable state.
- *
- * It takes the same parameters as the constructor of the built component.
- */
-fun <S> Container.tabPanel(
-        state: ObservableState<S>,
-        tabPosition: TabPosition = TabPosition.TOP,
-        sideTabSize: SideTabSize = SideTabSize.SIZE_3,
-        scrollableTabs: Boolean = false,
-        draggableTabs: Boolean = false,
-        classes: Set<String>? = null,
-        className: String? = null,
-        init: (RoTabPanel.(S) -> Unit)
-) = tabPanel(tabPosition, sideTabSize, scrollableTabs, draggableTabs, classes, className).bind(state, true, init)
-
-
-internal class RoTabPanelNav(internal val tabPanel: RoTabPanel, classes: Set<String>) : SimplePanel(classes) {
-
-    override fun render(): VNode {
-        return render("ul", childrenVNodes())
-    }
-
-    override fun childrenVNodes(): Array<VNode> {
-        return tabPanel.tabs.filter { it.visible }.map { it.renderVNode() }.toTypedArray()
-    }
-
-}
-
-internal class RoTabPanelContent(private val tabPanel: RoTabPanel) : SimplePanel() {
-
-    override fun childrenVNodes(): Array<VNode> {
-        return tabPanel.tabs.getOrNull(tabPanel.activeIndex)?.getChildren()?.map { it.renderVNode() }?.toTypedArray()
-                ?: emptyArray()
-    }
-
-}
-
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoWindow.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoWindow.kt
index 5f5d1f1..146cbbf 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoWindow.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/override/RoWindow.kt
@@ -71,7 +71,7 @@ open class RoWindow(
         menu: List<KvisionHtmlLink>? = null,
         init: (RoWindow.() -> Unit)? = null
 ) :
-        SimplePanel(classes + setOf("modal-content", "kv-window")) {
+    SimplePanel(classes.toString() +" modal-content kv-window") {
 
     /**
      * Window caption text.
@@ -160,7 +160,7 @@ open class RoWindow(
             windowIcon.visible = (value != null && value != "")
         }
 
-    private val header = SimplePanel(setOf("modal-header"))
+    private val header = SimplePanel("modal-header")
 
     /**
      * @suppress
@@ -173,10 +173,11 @@ open class RoWindow(
     private val closeIcon = CloseIcon()
     private val maximizeIcon = MaximizeIcon()
     private val minimizeIcon = MinimizeIcon()
-    private val captionTag = Tag(TAG.H5, caption, classes = setOf("modal-title")).apply {
+    private val captionTag = Tag(TAG.H5, caption).apply {
+        addCssClass("modal-title")
         alignSelf = AlignItems.START
     }
-    private val iconsContainer = SimplePanel(setOf("kv-window-icons-container"))
+    private val iconsContainer = SimplePanel("kv-window-icons-container")
 
     private val windowIcon = Icon(icon ?: "").apply {
         addCssClass("window-icon")
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/panel/EventLogTable.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/panel/EventLogTable.kt
index 12885e8..db3505b 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/panel/EventLogTable.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/panel/EventLogTable.kt
@@ -154,8 +154,6 @@ class EventLogTable(val model: List<LogEntry>) : VPanel() {
 
         tabulator = tabulator(model, options = options) {
             setEventListener<Tabulator<LogEntry>> {
-                tabulatorRowClick = {
-                }
             }
         }
     }