You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/01/03 15:50:02 UTC

[isis] 02/04: ISIS-2476 - fleshes out custom UI example

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

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

commit d2b9d48d246ccc41faaf8ceb1c4ae7bd516a05e6
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Dec 17 18:21:10 2020 +0000

    ISIS-2476 - fleshes out custom UI example
---
 .../src/main/java/demoapp/dom/menubars.layout.xml  |  4 +
 .../java/demoapp/dom/ui/custom/CustomUiMenu.java   | 60 +++++++++++++++
 .../java/demoapp/dom/ui/custom/CustomUiVm.java     | 86 ++++++++++++++++++++++
 .../main/java/demoapp/dom/ui/custom/LatLng.java    | 28 +++++++
 .../main/java/demoapp/dom/ui/custom/Latitude.java  | 37 ++++++++++
 .../main/java/demoapp/dom/ui/custom/Longitude.java | 34 +++++++++
 .../java/demoapp/dom/ui/custom/PositiveNumber.java | 33 +++++++++
 .../demoapp/dom/ui/custom/TabDemo-description.adoc |  8 ++
 .../java/demoapp/dom/ui/custom/TabDemo.layout.xml  | 68 +++++++++++++++++
 .../java/demoapp/dom/ui/custom/LatLngTest.java     | 15 ++++
 .../java/demoapp/webapp/wicket/DemoAppWicket.java  | 21 +++---
 .../{MyEntityPanel.html => CustomUiPanel.html}     | 13 +++-
 .../{MyEntityPanel.java => CustomUiPanel.java}     | 30 +++++++-
 .../wicket/customview/CustomUiPanelFactory.java    | 35 +++++++++
 .../wicket/customview/MyEntityPanelFactory.java    | 43 -----------
 15 files changed, 458 insertions(+), 57 deletions(-)

diff --git a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
index 0173768..ca8adcc 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
@@ -225,6 +225,10 @@ For latest we use: https://raw.githubusercontent.com/apache/isis/master/antora/s
                 <mb3:named>Tabs</mb3:named>
                 <mb3:serviceAction objectType="demo.TabMenu" id="tabDemo" />
             </mb3:section>
+            <mb3:section>
+                <mb3:named>Custom UI</mb3:named>
+                <mb3:serviceAction objectType="demo.CustomUiMenu" id="customUiVm" />
+            </mb3:section>
         </mb3:menu>
 
         <mb3:menu>
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java
new file mode 100644
index 0000000..817b2c2
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java
@@ -0,0 +1,60 @@
+/*
+ *  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 demoapp.dom.ui.custom;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.SemanticsOf;
+
+import lombok.val;
+
+@DomainService(nature=NatureOfService.VIEW, objectType = "demo.CustomUiMenu")
+public class CustomUiMenu {
+
+    @Action(semantics = SemanticsOf.SAFE)
+    @ActionLayout(
+            cssClassFa="fa-globe",
+            describedAs="Opens a Custom UI page displaying a map"
+    )
+    public CustomUiVm whereInTheWorld(
+            @Latitude final String latitude,
+            @Longitude final String longitude,
+            @PositiveNumber final int scale
+    ){
+        val vm = new CustomUiVm();
+        vm.setLatitude(latitude);
+        vm.setLongitude(longitude);
+        vm.setScale(scale);
+
+        return vm;
+    }
+
+    public String default0CustomUiVm() {
+        return "51.753500";
+    }
+    public String default1CustomUiVm() {
+        return "-1.253640";
+    }
+    public int default2CustomUiVm() {
+        return 1;
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java
new file mode 100644
index 0000000..1688807
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java
@@ -0,0 +1,86 @@
+/*
+ *  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 demoapp.dom.ui.custom;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+
+@XmlRootElement(name = "demo.CustomUiVm")
+@XmlType
+@XmlAccessorType(XmlAccessType.FIELD)
+@DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.CustomUiVm")
+public class CustomUiVm implements HasAsciiDocDescription {
+
+    public String title() {
+        return "Custom UI";
+    }
+
+    @Latitude
+    @Getter @Setter
+    private String latitude;
+
+    @Longitude
+    @Getter @Setter
+    private String longitude;
+
+    @PositiveNumber
+    @Getter @Setter
+    private int scale;
+
+    @Data
+    public static class BoundingBox {
+        @Getter
+        private final String minimumLatitude;
+        @Getter
+        private final String minimumLongitude;
+        @Getter
+        private final String maximumLatitude;
+        @Getter
+        private final String maximumLongitude;
+
+        public final String toUrl(String divider) {
+            return getMinimumLongitude() + divider + getMinimumLatitude() + divider + getMaximumLongitude() + divider + getMaximumLatitude();
+        }
+        public final String toUrl() {
+            return toUrl("%2C");
+        }
+    }
+    /**
+     * @link https://wiki.openstreetmap.org/wiki/Bounding_Box
+     */
+    public BoundingBox getBoundingBox() {
+        String minLat = LatLng.add(getLatitude(), -getScale());
+        String maxLat = LatLng.add(getLatitude(), +getScale());
+        String minLng = LatLng.add(getLongitude(), -getScale());
+        String maxLng = LatLng.add(getLongitude(), +getScale());
+        return new BoundingBox(minLat, minLng, maxLat, maxLng);
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/LatLng.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/LatLng.java
new file mode 100644
index 0000000..4353797
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/LatLng.java
@@ -0,0 +1,28 @@
+package demoapp.dom.ui.custom;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import lombok.val;
+import lombok.experimental.UtilityClass;
+
+
+@UtilityClass
+public class LatLng {
+
+    public static BigDecimal toBigDecimal(final String val) {
+        return new BigDecimal(val);
+    }
+
+    public static String toString(BigDecimal val) {
+        return val.toPlainString();
+    }
+
+    public static String add(final String val, final int hundredths) {
+        val scaleBd = new BigDecimal(hundredths).setScale(2, RoundingMode.HALF_UP);
+        val scaleDividedBy100 = scaleBd.divide(new BigDecimal(100), RoundingMode.HALF_UP);
+        val bd = toBigDecimal(val);
+        val newVal = bd.add(scaleDividedBy100);
+        return toString(newVal);
+    }
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Latitude.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Latitude.java
new file mode 100644
index 0000000..cd303aa
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Latitude.java
@@ -0,0 +1,37 @@
+package demoapp.dom.ui.custom;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
+
+import lombok.val;
+
+@Property(
+        regexPattern = Latitude.PATTERN
+        , regexPatternReplacement = "Does not match format of latitude"
+)
+@Parameter(
+        regexPattern = Latitude.PATTERN
+        , regexPatternReplacement = "Does not match format of latitude"
+)
+@Inherited
+@Target({
+        ElementType.FIELD,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.ANNOTATION_TYPE
+})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Latitude {
+
+    String PATTERN = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$";
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Longitude.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Longitude.java
new file mode 100644
index 0000000..a0b31f9
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Longitude.java
@@ -0,0 +1,34 @@
+package demoapp.dom.ui.custom;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
+
+@Property(
+        regexPattern = Longitude.PATTERN
+        , regexPatternReplacement = "Does not match format of longitude"
+)
+@Parameter(
+        regexPattern = Longitude.PATTERN
+        , regexPatternReplacement = "Does not match format of longitude"
+)
+@Inherited
+@Target({
+        ElementType.FIELD,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.ANNOTATION_TYPE
+})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Longitude {
+
+    String PATTERN = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$";
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/PositiveNumber.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/PositiveNumber.java
new file mode 100644
index 0000000..99cacc5
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/PositiveNumber.java
@@ -0,0 +1,33 @@
+package demoapp.dom.ui.custom;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.spec.AbstractSpecification;
+
+@Property(mustSatisfy = PositiveNumber.Specification.class)
+@Parameter(mustSatisfy = PositiveNumber.Specification.class)
+@Inherited
+@Target({
+        ElementType.FIELD,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.ANNOTATION_TYPE
+})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PositiveNumber {
+
+    class Specification extends AbstractSpecification<Integer> {
+
+        @Override
+        public String satisfiesSafely(Integer candidate) {
+            return candidate <= 0 ? "Must be a positive number" : null;
+        }
+    }
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo-description.adoc
new file mode 100644
index 0000000..cf204ed
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo-description.adoc
@@ -0,0 +1,8 @@
+:Notice: 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 ag [...]
+
+(since 1.x)
+
+//TODO
+
+See the sources for this demo here:
+link:${SOURCES_DEMO}/demoapp/dom/tabs[sources]
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo.layout.xml
new file mode 100644
index 0000000..2c20a0e
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo.layout.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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. -->
+<bs3:grid xmlns:bs3="http://isis.apache.org/applib/layout/grid/bootstrap3"
+          xmlns:cpt="http://isis.apache.org/applib/layout/component"
+>
+    <bs3:row>
+        <bs3:col span="12" unreferencedActions="true">
+            <cpt:domainObject/>
+            <cpt:action id="clearHints"/>
+            <cpt:action id="downloadLayoutXml"/>
+            <cpt:action id="openRestApi"/>
+            <cpt:action id="rebuildMetamodel"/>
+
+            <cpt:action id="doHideField"/>
+            <cpt:action id="doShowField"/>
+
+        </bs3:col>
+    </bs3:row>
+    <bs3:row>
+        <bs3:col span="6">
+
+        	<bs3:tabGroup>
+
+				<bs3:tab name="Tab 1">
+					<bs3:row>
+						<bs3:col span="12">
+							<cpt:fieldSet id="fs1" name="Hideable Field">
+								<cpt:property id="field1"/>
+							</cpt:fieldSet>
+						</bs3:col>
+					</bs3:row>
+				</bs3:tab>
+
+				<bs3:tab name="Tab 2">
+					<bs3:row>
+						<bs3:col span="12">
+							<cpt:fieldSet id="fs2">
+								<cpt:property id="field2"/>
+							</cpt:fieldSet>
+						</bs3:col>
+					</bs3:row>
+				</bs3:tab>
+			</bs3:tabGroup>
+
+<!--         	<cpt:fieldSet name="Tabs" id="tabs"> -->
+<!--                 <cpt:property id="field"/> -->
+<!--             </cpt:fieldSet> -->
+
+        </bs3:col>
+        <bs3:col span="6" unreferencedCollections="true">
+
+            <cpt:fieldSet name="Description" id="description" unreferencedProperties="true">
+                <cpt:property id="description" labelPosition="NONE"/>
+
+            </cpt:fieldSet>
+
+        </bs3:col>
+    </bs3:row>
+</bs3:grid>
diff --git a/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngTest.java b/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngTest.java
new file mode 100644
index 0000000..4aa8031
--- /dev/null
+++ b/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngTest.java
@@ -0,0 +1,15 @@
+package demoapp.dom.ui.custom;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import lombok.val;
+
+class LatLngTest {
+
+    @Test
+    void add() {
+        val add = LatLng.add("51.753500", 1);
+        Assertions.assertThat(add).isEqualTo("51.763500");
+    }
+}
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java
index 1216076..723d360 100644
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java
@@ -18,6 +18,11 @@
  */
 package demoapp.webapp.wicket;
 
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Import;
+
 import org.apache.isis.extensions.viewer.wicket.pdfjs.ui.IsisModuleExtPdfjsUi;
 import org.apache.isis.valuetypes.asciidoc.metamodel.IsisModuleValAsciidocMetaModel;
 import org.apache.isis.valuetypes.asciidoc.persistence.jdo.dn5.IsisModuleValAsciidocPersistenceJdoDn5;
@@ -26,13 +31,9 @@ import org.apache.isis.valuetypes.markdown.persistence.jdo.dn5.IsisModuleValMark
 import org.apache.isis.valuetypes.markdown.ui.wkt.IsisModuleValMarkdownUiWkt;
 import org.apache.isis.valuetypes.sse.ui.wkt.IsisModuleValSseUiWkt;
 import org.apache.isis.viewer.wicket.viewer.IsisModuleViewerWicketViewer;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
-import org.springframework.context.annotation.Import;
 
 import demoapp.web.DemoAppManifest;
-import demoapp.webapp.wicket.customview.MyEntityPanelFactory;
+import demoapp.webapp.wicket.customview.CustomUiPanelFactory;
 
 /**
  * Bootstrap the application.
@@ -56,24 +57,24 @@ import demoapp.webapp.wicket.customview.MyEntityPanelFactory;
     IsisModuleValMarkdownPersistenceJdoDn5.class,
 
     // @Component's
-    MyEntityPanelFactory.class
+    CustomUiPanelFactory.class
 })
 //@Log4j2
 public class DemoAppWicket extends SpringBootServletInitializer {
 
     /**
-     * 
+     *
      * @param args
-     * @implNote this is to support the <em>Spring Boot Maven Plugin</em>, which auto-detects an 
+     * @implNote this is to support the <em>Spring Boot Maven Plugin</em>, which auto-detects an
      * entry point by searching for classes having a {@code main(...)}
      */
     public static void main(String[] args) {
         //IsisPresets.prototyping();
         //DebugLoggingPreset.PERSISTENCE.apply();
         //DebugLoggingPreset.ISIS_SESSION.apply();
-        
+
         SpringApplication.run(new Class[] { DemoAppWicket.class }, args);
-        
+
     }
 
 
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.html b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.html
similarity index 73%
rename from examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.html
rename to examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.html
index 0e9b129..fd76620 100644
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.html
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.html
@@ -20,8 +20,17 @@
 <html xmlns:wicket="http://wicket.apache.org">
 <body>
 <wicket:panel>
-    <div class="myEntityPanel">
-        <div><p>MY CUSTOM ENTITY</p></div>
+    <div class="customUiPanel">
+        <div>
+            <p>Where in the world?</p>
+            <iframe wicket:id="iframe"
+                    id="inlineFrameExample"
+                    title="Where in the world ?"
+                    width="1200"
+                    height="800"
+                    >
+            </iframe>
+        </div>
     </div>
 </wicket:panel>
 </body>
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java
similarity index 64%
rename from examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.java
rename to examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java
index 13c2911..0699661 100644
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.java
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java
@@ -19,18 +19,25 @@
 
 package demoapp.webapp.wicket.customview;
 
+import org.apache.wicket.markup.html.link.InlineFrame;
+
+import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 
-public class MyEntityPanel extends PanelAbstract<EntityModel>  {
+import lombok.val;
+
+import demoapp.dom.ui.custom.CustomUiVm;
+
+public class CustomUiPanel extends PanelAbstract<EntityModel>  {
 
 
     private static final long serialVersionUID = 1L;
 
 
-    public MyEntityPanel(
+    public CustomUiPanel(
             final String id,
             final EntityModel model,
             final ComponentFactory componentFactory) {
@@ -51,6 +58,25 @@ public class MyEntityPanel extends PanelAbstract<EntityModel>  {
     @Override
     public void onInitialize() {
         super.onInitialize();
+
+        val managedObject = (ManagedObject) getModelObject();
+        val customUiVm = (CustomUiVm) managedObject.getPojo();
+
+        val iframe = new InlineFrame("iframe", getPage()) {
+            @Override
+            protected CharSequence getURL() {
+                val boundingBox = customUiVm.getBoundingBox();
+                val url = boundingBox.toUrl();
+                return String.format(
+                        "https://www.openstreetmap.org/export/embed.html?bbox=%s&layer=%s"
+                        , url
+                        , "mapnik");
+            }
+        };
+
+        addOrReplace(iframe);
     }
 
+
+
 }
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java
new file mode 100644
index 0000000..dcad85d
--- /dev/null
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java
@@ -0,0 +1,35 @@
+package demoapp.webapp.wicket.customview;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+import org.springframework.core.annotation.Order;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+import org.apache.isis.viewer.wicket.ui.components.entity.EntityComponentFactoryAbstract;
+
+import demoapp.dom.ui.custom.CustomUiVm;
+
+@org.springframework.stereotype.Component
+@Order(OrderPrecedence.EARLY)
+public class CustomUiPanelFactory extends EntityComponentFactoryAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    public CustomUiPanelFactory() {
+        super(ComponentType.ENTITY, CustomUiPanel.class);
+    }
+
+    @Override
+    protected ApplicationAdvice doAppliesTo(EntityModel entityModel) {
+        return ApplicationAdvice.appliesIf(entityModel.getObject().getPojo() instanceof CustomUiVm);
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        final EntityModel entityModel = (EntityModel) model;
+
+        return new CustomUiPanel(id, entityModel, this);
+    }
+}
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanelFactory.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanelFactory.java
deleted file mode 100644
index 2859bec..0000000
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanelFactory.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package demoapp.webapp.wicket.customview;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.model.IModel;
-
-import org.apache.isis.applib.layout.grid.Grid;
-import org.apache.isis.applib.layout.grid.bootstrap3.BS3Grid;
-import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.viewer.wicket.model.models.EntityModel;
-import org.apache.isis.viewer.wicket.ui.ComponentType;
-import org.apache.isis.viewer.wicket.ui.components.entity.EntityComponentFactoryAbstract;
-import org.apache.isis.viewer.wicket.ui.components.layout.bs3.BS3GridPanel;
-
-import lombok.val;
-
-@org.springframework.stereotype.Component
-public class MyEntityPanelFactory  extends EntityComponentFactoryAbstract {
-
-    private static final long serialVersionUID = 1L;
-
-    public MyEntityPanelFactory() {
-        super(ComponentType.ENTITY, MyEntityPanel.class);
-    }
-
-    @Override
-    public Component createComponent(final String id, final IModel<?> model) {
-        final EntityModel entityModel = (EntityModel) model;
-
-        val objectAdapter = entityModel.getObject();
-        final ObjectSpecification specification = entityModel.getTypeOfSpecification();
-        final GridFacet facet = specification.getFacet(GridFacet.class);
-
-        final Grid grid = facet.getGrid(objectAdapter);
-        if (grid != null) {
-            if(grid instanceof BS3Grid) {
-                final BS3Grid bs3Grid = (BS3Grid) grid;
-                return new BS3GridPanel(id, entityModel, bs3Grid);
-            }
-        }
-        return new MyEntityPanel(id, entityModel, this);
-    }
-}