You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by po...@apache.org on 2023/09/21 14:30:00 UTC

[incubator-kie-tools-temporary-rnd-do-not-use] 08/27: NO-ISSUE: merge main into KOGITO-8015-feature-preview (#1461)

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

porcelli pushed a commit to branch KOGITO-8015-feature-preview
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools-temporary-rnd-do-not-use.git

commit 9bc804ea709147694e994ae30f250a390606785d
Author: Fabrizio Antonangeli <fa...@gmail.com>
AuthorDate: Wed Feb 15 10:07:14 2023 +0100

    NO-ISSUE: merge main into KOGITO-8015-feature-preview (#1461)
    
    Co-authored-by: Gianluca <gi...@gmail.com>
    Co-authored-by: Gianluca <gz...@redhat.com>
    Co-authored-by: Wojtek Trocki <wt...@redhat.com>
    Co-authored-by: renovate[bot] <29...@users.noreply.github.com>
    Co-authored-by: Renovate Bot <bo...@renovateapp.com>
    Co-authored-by: Luiz Motta <lu...@gmail.com>
    Co-authored-by: Riccardo Forina <rf...@redhat.com>
    Co-authored-by: Rachel Yordán <ka...@users.noreply.github.com>
    Co-authored-by: Jozef Marko <jo...@redhat.com>
    Co-authored-by: Wagner Scholl Lemos <jo...@gmail.com>
    Co-authored-by: Kumar Aditya Raj <se...@gmail.com>
    Co-authored-by: Thiago Lugli <th...@users.noreply.github.com>
    Co-authored-by: Tiago Bento <15...@users.noreply.github.com>
    Co-authored-by: Yeser Amer <ya...@redhat.com>
    Co-authored-by: Kirill Gaevskii <kg...@redhat.com>
    Co-authored-by: Jaime Enriquez <ja...@inodesoft.com>
    Co-authored-by: Pere Fernández <pe...@users.noreply.github.com>
---
 .github/actions/setup-env/action.yml               |    2 +-
 README.md                                          |    2 +-
 .../uniforms-patternfly/env/index.js               |   24 +-
 examples/uniforms-patternfly/package.json          |   44 +
 examples/uniforms-patternfly/src/App.tsx           |   55 +
 .../uniforms-patternfly/src/CodeBlock.tsx          |   27 +-
 examples/uniforms-patternfly/src/Form.tsx          |   53 +
 .../uniforms-patternfly/src/index.tsx              |   10 +-
 .../uniforms-patternfly/src/schemas/json-schema.ts |  113 ++
 .../src/schemas/simple-schema-2.ts                 |   80 +
 examples/uniforms-patternfly/static/index.html     |   29 +
 examples/uniforms-patternfly/static/logo.png       |  Bin 0 -> 30305 bytes
 .../static/resources/styles.css                    |   17 +-
 examples/uniforms-patternfly/tsconfig.json         |    9 +
 examples/uniforms-patternfly/webpack.config.js     |   49 +
 package.json                                       |    4 +-
 .../dashbuilder-component-table/static/index.css   |    4 +-
 .../dashbuilder-component-uniforms/package.json    |    6 +-
 .../src/Uniforms.tsx                               |    2 +-
 packages/form-dmn/package.json                     |    6 +-
 .../form-dmn/src/uniforms/DmnAutoFieldProvider.tsx |    2 +-
 packages/form-dmn/tests/DmnForm.test.tsx           |   10 +-
 .../tests/__snapshots__/DmnForm.test.tsx.snap      |   60 +-
 packages/form-generation-tool/package.json         |    4 +-
 packages/form/package.json                         |    6 +-
 packages/form/src/FormBase.tsx                     |    4 +-
 packages/kn-plugin-workflow/README.md              |    2 +-
 .../src/devDeployments/DevDeploymentsDropdown.tsx  |    4 +-
 packages/online-editor/src/index.tsx               |    3 -
 .../src/workspace/startupBlockers/FsChanged.tsx    |  148 --
 .../startupBlockers/IncompatibleBrowser.tsx        |    3 +-
 packages/serverless-logic-web-tools/src/index.tsx  |    3 -
 .../src/workspace/startupBlockers/FsChanged.tsx    |  145 --
 .../startupBlockers/IncompatibleBrowser.tsx        |    3 +-
 .../core/shape/toolbox/items/tooltip/Tooltip.java  |    6 +-
 .../org/kie/lienzo/client/BasicWiresExample.java   |   81 +-
 .../java/org/kie/lienzo/client/EventExample.java   |   61 +-
 .../org/kie/lienzo/client/WiresDockingExample.java |    3 -
 .../common/stunner/sw/definition/ModelUtils.java   |    1 -
 .../stunner/sw/definition/ParallelStateBranch.java |   12 +-
 .../common/stunner/sw/definition/State.java        |   28 +-
 .../common/stunner/sw/definition/Timeout.java      |    1 -
 .../stunner/sw/marshall/StateMarshalling.java      |    8 -
 .../stunner/sw/marshall/StateMarshallingTest.java  |    5 +-
 .../common/stunner/sw/client/ShapeFactory.java     |    2 +-
 .../sw/client/shapes/CallbackStateShape.java       |   58 +
 .../common/stunner/sw/client/shapes/EndShape.java  |    5 +-
 .../stunner/sw/client/shapes/EndShapeView.java     |    2 +-
 .../stunner/sw/client/shapes/EventStateShape.java  |   58 +
 .../sw/client/shapes/ForEachStateShape.java        |   57 +
 .../stunner/sw/client/shapes/InjectStateShape.java |   39 +
 .../sw/client/shapes/OperationStateShape.java      |   57 +
 .../sw/client/shapes/ParallelStateShape.java       |   57 +
 .../sw/client/shapes/ServerlessWorkflowShape.java  |   55 +
 ...Shape.java => ServerlessWorkflowShapeView.java} |   68 +-
 .../stunner/sw/client/shapes/SleepStateShape.java  |   53 +
 .../stunner/sw/client/shapes/StartShape.java       |    5 +-
 .../stunner/sw/client/shapes/StartShapeView.java   |    2 +-
 .../stunner/sw/client/shapes/StateShape.java       |  190 ++-
 .../stunner/sw/client/shapes/StateShapeView.java   |   10 +-
 .../stunner/sw/client/shapes/SwitchStateShape.java |   57 +
 .../stunner/sw/client/shapes/TransitionView.java   |    8 +-
 .../stunner/sw/client/shapes/icons/CornerIcon.java |   80 +
 .../stunner/sw/client/shapes/icons/IconPath.java   |   21 +
 .../common/stunner/sw/client/shapes/StateNode.java |   73 +
 .../stunner/sw/client/shapes/StateShapeTest.java   |  297 +++-
 .../common/stunner/sw/client/shapes/StateView.java |   49 +
 .../ast/visitor/ASTHeuristicCheckerVisitor.java    |   72 +
 .../kie-wb-common-dmn-client/pom.xml               |   14 +-
 .../DecisionTableEditorDefinitionEnricher.java     |    2 +-
 .../actions/AbstractActionsToolboxView.java        |    1 +
 .../customproperties/AssociationList.java          |   21 +-
 .../processes/ProcessConverterDelegate.java        |   43 +-
 packages/uniforms-bootstrap4-codegen/package.json  |    4 +-
 .../src/__tests__/_createContext.ts                |   12 +-
 .../src/uniforms/BootstrapCodeGenContext.tsx       |   12 +-
 packages/uniforms-patternfly-codegen/package.json  |    4 +-
 .../src/__tests__/_createContext.ts                |   12 +-
 .../src/uniforms/CodeGenContext.tsx                |   12 +-
 packages/uniforms-patternfly/.vscode/launch.json   |   29 +
 packages/uniforms-patternfly/README.md             |   97 ++
 .../uniforms-patternfly/__mocks__/styleMock.js     |    1 +
 .../env/index.js}                                  |   13 +-
 packages/uniforms-patternfly/jest.config.js        |   34 +
 .../package.json                                   |   39 +-
 .../src}/AutoField.tsx                             |   32 +-
 packages/uniforms-patternfly/src/AutoFields.tsx    |   40 +
 .../src/AutoForm.tsx}                              |   15 +-
 packages/uniforms-patternfly/src/BaseForm.tsx      |   39 +
 packages/uniforms-patternfly/src/BoolField.tsx     |   56 +
 packages/uniforms-patternfly/src/DateField.tsx     |  184 +++
 packages/uniforms-patternfly/src/ErrorField.tsx    |   47 +
 packages/uniforms-patternfly/src/ErrorsField.tsx   |   50 +
 packages/uniforms-patternfly/src/HiddenField.tsx   |   46 +
 packages/uniforms-patternfly/src/ListAddField.tsx  |   60 +
 packages/uniforms-patternfly/src/ListDelField.tsx  |   54 +
 packages/uniforms-patternfly/src/ListField.tsx     |  105 ++
 packages/uniforms-patternfly/src/ListItemField.tsx |   48 +
 packages/uniforms-patternfly/src/LongTextField.tsx |   60 +
 packages/uniforms-patternfly/src/NestField.tsx     |   50 +
 packages/uniforms-patternfly/src/NumField.tsx      |   60 +
 packages/uniforms-patternfly/src/QuickForm.tsx     |   43 +
 packages/uniforms-patternfly/src/RadioField.tsx    |   56 +
 .../src/SelectCheckboxField.tsx                    |   67 +
 packages/uniforms-patternfly/src/SelectField.tsx   |   53 +
 .../uniforms-patternfly/src/SelectInputsField.tsx  |  133 ++
 packages/uniforms-patternfly/src/SubmitField.tsx   |   41 +
 packages/uniforms-patternfly/src/TextField.tsx     |  187 +++
 .../src/ValidatedForm.tsx}                         |   15 +-
 .../src/ValidatedQuickForm.tsx}                    |    8 +-
 .../src/__tests__/AutoField.test.tsx               |  107 ++
 .../src/__tests__/AutoFields.test.tsx              |  105 ++
 .../src/__tests__/AutoForm.test.tsx}               |   12 +-
 .../src/__tests__/BaseForm.test.tsx}               |   12 +-
 .../src/__tests__/BoolField.test.tsx               |  108 ++
 .../src/__tests__/DateField.test.tsx               |  188 +++
 .../src/__tests__/ErrorField.test.tsx              |   51 +
 .../src/__tests__/ErrorsField.test.tsx             |   58 +
 .../src/__tests__/HiddenField.test.tsx             |  131 ++
 .../src/__tests__/ListAddField.test.tsx            |   56 +
 .../src/__tests__/ListDelField.test.tsx            |   56 +
 .../src/__tests__/ListField.test.tsx               |   93 ++
 .../src/__tests__/ListItemField.test.tsx           |   55 +
 .../src/__tests__/LongTextField.test.tsx           |  129 ++
 .../src/__tests__/NestField.test.tsx               |   70 +
 .../src/__tests__/NumField.test.tsx                |  240 +++
 .../src/__tests__/QuickForm.test.tsx}              |   12 +-
 .../src/__tests__/RadioField.test.tsx              |  218 +++
 .../src/__tests__/SelectField.test.tsx             |  498 ++++++
 .../src/__tests__/SubmitField.test.tsx             |   40 +
 .../src/__tests__/TextField.test.tsx               |  357 +++++
 .../src/__tests__/ValidateQuickForm.test.tsx}      |   12 +-
 .../src/__tests__/ValidatedForm.test.tsx}          |   12 +-
 .../src/__tests__/index.test.ts                    |   45 +
 .../src/__tests__/jest.setup.ts}                   |    6 +-
 .../src/__tests__/test-utils.tsx                   |   62 +
 packages/uniforms-patternfly/src/index.ts          |   39 +
 packages/uniforms-patternfly/src/wrapField.tsx     |   71 +
 packages/uniforms-patternfly/tsconfig.cjs.json     |   13 +
 packages/uniforms-patternfly/tsconfig.esm.json     |   13 +
 packages/unitables-dmn/package.json                |    5 +-
 packages/unitables/package.json                    |    6 +-
 packages/unitables/src/uniforms/AutoField.tsx      |    2 +-
 pnpm-lock.yaml                                     | 1594 +++++++++++++++++---
 repo/graph.dot                                     |   15 +-
 repo/graph.json                                    |   25 +-
 146 files changed, 7972 insertions(+), 957 deletions(-)

diff --git a/.github/actions/setup-env/action.yml b/.github/actions/setup-env/action.yml
index 9afb7a6b1f..f1bde8db42 100644
--- a/.github/actions/setup-env/action.yml
+++ b/.github/actions/setup-env/action.yml
@@ -25,7 +25,7 @@ runs:
     - name: "Setup Node"
       uses: actions/setup-node@v2
       with:
-        node-version: 16.13.2
+        node-version: 18.14.0
 
     - name: "Setup JDK 11"
       uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc
diff --git a/README.md b/README.md
index de610ee98a..7ae9f768e4 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ This repository contains tooling applications and libraries for KIE projects.
 
 To start building the KIE Tools project, you're going to need:
 
-- Node `16` _(To install, follow these instructions: https://nodejs.org/en/download/package-manager/)_
+- Node `18` _(To install, follow these instructions: https://nodejs.org/en/download/package-manager/)_
 - pnpm `7.26.3` _(To install, follow these instructions: https://pnpm.io/installation)_
 - Maven `3.8.6`
 - Java `11`
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java b/examples/uniforms-patternfly/env/index.js
similarity index 56%
copy from packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java
copy to examples/uniforms-patternfly/env/index.js
index 06bac41e46..00d6364f4d 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java
+++ b/examples/uniforms-patternfly/env/index.js
@@ -1,11 +1,11 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed 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
+ *        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,
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-package org.kie.workbench.common.stunner.sw.client.shapes;
+const { varsWithName, composeEnv } = require("@kie-tools-scripts/build-env");
 
-import org.kie.workbench.common.stunner.core.client.shape.impl.NodeShapeImpl;
-
-public class StartShape extends NodeShapeImpl {
-
-    public StartShape() {
-        super(new StartShapeView().asAbstractShape());
-    }
-}
+module.exports = composeEnv([require("@kie-tools/root-env/env")], {
+  vars: varsWithName({}),
+  get env() {
+    return {
+      exampleUniformsPatternflyPort: {
+        port: 9101,
+      },
+    };
+  },
+});
diff --git a/examples/uniforms-patternfly/package.json b/examples/uniforms-patternfly/package.json
new file mode 100644
index 0000000000..234e211670
--- /dev/null
+++ b/examples/uniforms-patternfly/package.json
@@ -0,0 +1,44 @@
+{
+  "private": true,
+  "name": "@kie-tools-examples/uniforms-patternfly",
+  "version": "0.0.0",
+  "license": "Apache-2.0",
+  "scripts": {
+    "build": "rimraf dist && webpack",
+    "build:dev": "run-script-if --bool \"$(build-env examples.build)\" --then \"pnpm build --env dev\"",
+    "build:prod": "run-script-if --bool \"$(build-env examples.build)\" --then \"pnpm build\"",
+    "start": "webpack serve --host 0.0.0.0 --env dev"
+  },
+  "dependencies": {
+    "@kie-tools-core/patternfly-base": "workspace:*",
+    "@kie-tools/uniforms-patternfly": "workspace:*",
+    "@patternfly/react-core": "^4.157.3",
+    "@patternfly/react-icons": "^4.11.17",
+    "ajv": "^6.12.6",
+    "react": "^17.0.2",
+    "react-dom": "^17.0.2",
+    "simpl-schema": "^1.12.0",
+    "uniforms": "^3.10.2",
+    "uniforms-bridge-json-schema": "^3.10.2",
+    "uniforms-bridge-simple-schema": "^3.10.2",
+    "uniforms-bridge-simple-schema-2": "^3.10.2"
+  },
+  "devDependencies": {
+    "@kie-tools-core/webpack-base": "workspace:*",
+    "@kie-tools/root-env": "workspace:*",
+    "@kie-tools/tsconfig": "workspace:*",
+    "@types/react": "^17.0.6",
+    "@types/react-dom": "^17.0.5",
+    "@types/react-router": "^5.1.14",
+    "@types/react-router-dom": "^5.1.7",
+    "@types/simpl-schema": "^1.12.0",
+    "copy-webpack-plugin": "^11.0.0",
+    "react-router-dom": "^5.2.1",
+    "rimraf": "^3.0.2",
+    "typescript": "^4.4.2",
+    "webpack": "^5.36.2",
+    "webpack-cli": "^4.10.0",
+    "webpack-dev-server": "^4.7.3",
+    "webpack-merge": "^5.8.0"
+  }
+}
\ No newline at end of file
diff --git a/examples/uniforms-patternfly/src/App.tsx b/examples/uniforms-patternfly/src/App.tsx
new file mode 100644
index 0000000000..1edc8c61c0
--- /dev/null
+++ b/examples/uniforms-patternfly/src/App.tsx
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import "@patternfly/react-core/dist/styles/base.css";
+import * as React from "react";
+import "../static/resources/styles.css";
+import { useState } from "react";
+import { Brand, Nav, NavItem, NavList, Page, PageHeader } from "@patternfly/react-core";
+import { Form } from "./Form";
+
+export enum SchemaType {
+  JsonSchema,
+  SimpleSchema,
+}
+
+export function App() {
+  const [schemaType, setSchemaType] = useState<SchemaType>(SchemaType.JsonSchema);
+
+  return (
+    <Page
+      header={
+        <PageHeader
+          logo={<Brand src={"logo.png"} alt="Logo" />}
+          topNav={
+            <Nav onSelect={(e) => setSchemaType(e.itemId as SchemaType)} aria-label="Nav" variant="horizontal">
+              <NavList>
+                <NavItem itemId={SchemaType.JsonSchema} isActive={schemaType === SchemaType.JsonSchema}>
+                  JSON Schema
+                </NavItem>
+                <NavItem itemId={SchemaType.SimpleSchema} isActive={schemaType === SchemaType.SimpleSchema}>
+                  Simple Schema
+                </NavItem>
+              </NavList>
+            </Nav>
+          }
+        />
+      }
+    >
+      <Form schemaType={schemaType} />
+    </Page>
+  );
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java b/examples/uniforms-patternfly/src/CodeBlock.tsx
similarity index 51%
copy from packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java
copy to examples/uniforms-patternfly/src/CodeBlock.tsx
index 06bac41e46..d52c96189c 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java
+++ b/examples/uniforms-patternfly/src/CodeBlock.tsx
@@ -1,11 +1,11 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed 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
+ *        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,
@@ -14,13 +14,24 @@
  * limitations under the License.
  */
 
-package org.kie.workbench.common.stunner.sw.client.shapes;
+import * as React from "react";
 
-import org.kie.workbench.common.stunner.core.client.shape.impl.NodeShapeImpl;
+interface Props {
+  model: any;
+}
 
-public class StartShape extends NodeShapeImpl {
+export function CodeBlock(props: Props) {
+  if (!props.model)
+    return (
+      <>
+        <span></span>
+      </>
+    );
 
-    public StartShape() {
-        super(new StartShapeView().asAbstractShape());
-    }
+  return (
+    <div style={{ marginBottom: "1em" }}>
+      <h2>Result:</h2>
+      <pre style={{ background: "#eee", padding: "1rem" }}>{JSON.stringify(props.model, null, 2)}</pre>
+    </div>
+  );
 }
diff --git a/examples/uniforms-patternfly/src/Form.tsx b/examples/uniforms-patternfly/src/Form.tsx
new file mode 100644
index 0000000000..ac9f495f22
--- /dev/null
+++ b/examples/uniforms-patternfly/src/Form.tsx
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { useState, useMemo } from "react";
+import { SchemaType } from "./App";
+import { bridge as jsonSchema } from "./schemas/json-schema";
+import { bridge as simpleSchema } from "./schemas/simple-schema-2";
+import { CodeBlock } from "./CodeBlock";
+import AutoForm from "@kie-tools/uniforms-patternfly/dist/esm/AutoForm";
+
+interface Props {
+  schemaType: SchemaType;
+}
+
+export function Form(props: Props) {
+  const [model, setModel] = useState<any>(undefined);
+
+  const schema = useMemo(() => {
+    if (props.schemaType === SchemaType.SimpleSchema) {
+      return simpleSchema;
+    }
+    return jsonSchema;
+  }, [props.schemaType]);
+
+  return (
+    <div className={"form--page"}>
+      <div className={"form--container"}>
+        <CodeBlock model={model} />
+        <AutoForm
+          placeholder
+          model={model}
+          schema={schema}
+          onSubmit={(model: any) => setModel(model)}
+          showInlineError
+        />
+      </div>
+    </div>
+  );
+}
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/examples/uniforms-patternfly/src/index.tsx
similarity index 65%
copy from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to examples/uniforms-patternfly/src/index.tsx
index 7f7980f259..148f255eab 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/examples/uniforms-patternfly/src/index.tsx
@@ -1,11 +1,11 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed 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
+ *        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,
@@ -14,4 +14,8 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+import * as React from "react";
+import * as ReactDOM from "react-dom";
+import { App } from "./App";
+
+ReactDOM.render(<App />, document.getElementById("app"));
diff --git a/examples/uniforms-patternfly/src/schemas/json-schema.ts b/examples/uniforms-patternfly/src/schemas/json-schema.ts
new file mode 100644
index 0000000000..22bd1408ba
--- /dev/null
+++ b/examples/uniforms-patternfly/src/schemas/json-schema.ts
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as Ajv from "ajv";
+import { JSONSchemaBridge } from "uniforms-bridge-json-schema";
+
+const ajv = new Ajv({ allErrors: true, useDefaults: true });
+
+function createValidator(schema: any) {
+  const validator = ajv.compile(schema);
+  return (model: any) => {
+    validator(model);
+    if (validator.errors && validator.errors.length) {
+      throw { details: validator.errors };
+    }
+  };
+}
+
+const schema = {
+  type: "object",
+  properties: {
+    flight: {
+      type: "object",
+      properties: {
+        flightNumber: {
+          type: "string",
+        },
+        seat: {
+          type: "string",
+        },
+        gate: {
+          type: "string",
+        },
+        departure: {
+          type: "string",
+          format: "date-time",
+        },
+        arrival: {
+          type: "string",
+          format: "date-time",
+        },
+      },
+      disabled: true,
+    },
+    hotel: {
+      type: "object",
+      properties: {
+        name: {
+          type: "string",
+        },
+        addresses: {
+          type: "array",
+          items: {
+            $ref: "#/definitions/address",
+          },
+        },
+        phone: {
+          type: "string",
+        },
+        bookingNumber: {
+          type: "string",
+        },
+        room: {
+          type: "string",
+        },
+        numberOfBeds: {
+          placeholder: "Select...",
+          enum: [1, 2, 3],
+          type: "number",
+        },
+      },
+    },
+  },
+  definitions: {
+    address: {
+      type: "object",
+      properties: {
+        street: {
+          type: "string",
+        },
+        city: {
+          type: "string",
+        },
+        zipCode: {
+          type: "string",
+        },
+        country: {
+          placeholder: "Select...",
+          enum: ["Brazil", "Ireland", "USA"],
+          type: "string",
+        },
+      },
+    },
+  },
+  phases: ["complete", "release"],
+};
+
+const schemaValidator = createValidator(schema);
+
+export const bridge = new JSONSchemaBridge(schema, schemaValidator);
diff --git a/examples/uniforms-patternfly/src/schemas/simple-schema-2.ts b/examples/uniforms-patternfly/src/schemas/simple-schema-2.ts
new file mode 100644
index 0000000000..284777d046
--- /dev/null
+++ b/examples/uniforms-patternfly/src/schemas/simple-schema-2.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import SimpleSchema2Bridge from "uniforms-bridge-simple-schema-2";
+import SimpleSchema from "simpl-schema";
+
+const schema = new SimpleSchema({
+  date: {
+    type: Date,
+    defaultValue: new Date(),
+  },
+
+  adult: {
+    type: Boolean,
+  },
+
+  size: {
+    type: String,
+    defaultValue: "m",
+    allowedValues: ["xs", "s", "m", "l", "xl"],
+  },
+
+  rating: {
+    type: Number,
+    allowedValues: [1, 2, 3, 4, 5],
+    uniforms: {
+      checkboxes: true,
+    },
+  },
+
+  hello: {
+    type: Object,
+  },
+
+  "hello.something": {
+    type: String,
+  },
+
+  "hello.somethingelse": {
+    type: String,
+  },
+
+  friends: {
+    type: Array,
+    minCount: 1,
+  },
+
+  "friends.$": {
+    type: Object,
+    uniforms: {
+      label: false,
+    },
+  },
+
+  "friends.$.name": {
+    type: String,
+    min: 3,
+  },
+
+  "friends.$.age": {
+    type: Number,
+    min: 0,
+    max: 150,
+  },
+} as any);
+
+export const bridge = new SimpleSchema2Bridge(schema);
diff --git a/examples/uniforms-patternfly/static/index.html b/examples/uniforms-patternfly/static/index.html
new file mode 100644
index 0000000000..bf8755ddf9
--- /dev/null
+++ b/examples/uniforms-patternfly/static/index.html
@@ -0,0 +1,29 @@
+<!--
+  ~ Copyright 2020 Red Hat, Inc. and/or its affiliates.
+  ~
+  ~ Licensed 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.
+  -->
+
+<!DOCTYPE html>
+<html lang="en" class="pf-m-redhat-font">
+  <head>
+    <title>Uniforms Patternfly Examples</title>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
+  <body>
+    <div id="app" style="height: 100vh"></div>
+    <script src="index.js"></script>
+  </body>
+</html>
diff --git a/examples/uniforms-patternfly/static/logo.png b/examples/uniforms-patternfly/static/logo.png
new file mode 100644
index 0000000000..3691516354
Binary files /dev/null and b/examples/uniforms-patternfly/static/logo.png differ
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/examples/uniforms-patternfly/static/resources/styles.css
similarity index 64%
copy from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to examples/uniforms-patternfly/static/resources/styles.css
index 7f7980f259..a45a12fe6a 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/examples/uniforms-patternfly/static/resources/styles.css
@@ -1,11 +1,11 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed 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
+ *        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,
@@ -14,4 +14,15 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+.form--page {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100vw;
+}
+
+.form--container {
+  width: 60%;
+  background-color: white;
+  padding: 20px;
+}
diff --git a/examples/uniforms-patternfly/tsconfig.json b/examples/uniforms-patternfly/tsconfig.json
new file mode 100644
index 0000000000..12548a6f43
--- /dev/null
+++ b/examples/uniforms-patternfly/tsconfig.json
@@ -0,0 +1,9 @@
+{
+  "extends": "@kie-tools/tsconfig/tsconfig.json",
+  "compilerOptions": {
+    "declaration": false,
+    "declarationMap": false,
+    "moduleResolution": "node",
+    "target": "es6"
+  }
+}
diff --git a/examples/uniforms-patternfly/webpack.config.js b/examples/uniforms-patternfly/webpack.config.js
new file mode 100644
index 0000000000..4249153f0c
--- /dev/null
+++ b/examples/uniforms-patternfly/webpack.config.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+const path = require("path");
+const CopyPlugin = require("copy-webpack-plugin");
+const patternflyBase = require("@kie-tools-core/patternfly-base");
+const common = require("@kie-tools-core/webpack-base/webpack.common.config");
+const { merge } = require("webpack-merge");
+const { env } = require("./env");
+const buildEnv = env;
+
+module.exports = (env) => [
+  merge(common(env), {
+    entry: {
+      index: "./src/index.tsx",
+    },
+    output: {
+      publicPath: "/",
+    },
+    module: {
+      rules: [...patternflyBase.webpackModuleRules],
+    },
+    plugins: [
+      new CopyPlugin({
+        patterns: [{ from: "./static", to: "." }],
+      }),
+    ],
+    devServer: {
+      historyApiFallback: false,
+      static: [{ directory: path.join(__dirname, "./dist") }, { directory: path.join(__dirname, "./static") }],
+      compress: true,
+      port: buildEnv.exampleUniformsPatternflyPort.port,
+    },
+    ignoreWarnings: [/Failed to parse source map/],
+  }),
+];
diff --git a/package.json b/package.json
index 1c1ff4e362..b3f35b0a6d 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
     "@kie-tools-scripts/sparse-checkout": "workspace:*",
     "@kie-tools-scripts/update-version": "workspace:*",
     "@nice-move/prettier-plugin-package-json": "^0.6.1",
-    "@types/node": "^15.0.2",
+    "@types/node": "^18.13.0",
     "filemanager-webpack-plugin": "^7.0.0",
     "husky": "^6.0.0",
     "postinstall-postinstall": "^2.1.0",
@@ -31,7 +31,7 @@
     "react-dropzone": "^11.4.2"
   },
   "engines": {
-    "node": ">=16",
+    "node": ">=18",
     "pnpm": "7.26.3"
   },
   "pnpm": {
diff --git a/packages/dashbuilder-component-table/static/index.css b/packages/dashbuilder-component-table/static/index.css
index 3661509f9b..b6c2e78cd3 100644
--- a/packages/dashbuilder-component-table/static/index.css
+++ b/packages/dashbuilder-component-table/static/index.css
@@ -42,7 +42,9 @@
   --pf-global--BackgroundColor--100: #100c2a !important;
 }
 
-[mode="dark"] .pf-c-options-menu__menu {
+[mode="dark"] .pf-c-options-menu__menu,
+[mode="dark"] .pf-c-pagination__nav,
+[mode="dark"] .pf-c-pagination__total-items {
   background-color: #100c2a !important;
   color: #eef1fa;
 }
diff --git a/packages/dashbuilder-component-uniforms/package.json b/packages/dashbuilder-component-uniforms/package.json
index ebe8499ac6..94b5f30ba3 100644
--- a/packages/dashbuilder-component-uniforms/package.json
+++ b/packages/dashbuilder-component-uniforms/package.json
@@ -24,15 +24,15 @@
   "dependencies": {
     "@kie-tools-core/patternfly-base": "workspace:*",
     "@kie-tools/dashbuilder-component-api": "workspace:*",
+    "@kie-tools/uniforms-patternfly": "workspace:*",
     "@patternfly/react-core": "^4.157.3",
     "@patternfly/react-table": "^4.27.15",
     "ajv": "^6.12.6",
     "lodash": "^4.17.21",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
-    "uniforms": "^3.5.5",
-    "uniforms-bridge-json-schema": "^3.5.5",
-    "uniforms-patternfly": "^4.7.9"
+    "uniforms": "^3.10.2",
+    "uniforms-bridge-json-schema": "^3.10.2"
   },
   "devDependencies": {
     "@babel/core": "^7.16.0",
diff --git a/packages/dashbuilder-component-uniforms/src/Uniforms.tsx b/packages/dashbuilder-component-uniforms/src/Uniforms.tsx
index 10520f1308..55ad7b8017 100644
--- a/packages/dashbuilder-component-uniforms/src/Uniforms.tsx
+++ b/packages/dashbuilder-component-uniforms/src/Uniforms.tsx
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 import { JSONSchemaBridge } from "uniforms-bridge-json-schema";
-import { AutoForm } from "uniforms-patternfly/dist/es6";
+import { AutoForm } from "@kie-tools/uniforms-patternfly/dist/esm";
 import * as Ajv from "ajv";
 import * as React from "react";
 import { useCallback } from "react";
diff --git a/packages/form-dmn/package.json b/packages/form-dmn/package.json
index cd493340ce..dc3e4670f9 100644
--- a/packages/form-dmn/package.json
+++ b/packages/form-dmn/package.json
@@ -29,6 +29,7 @@
     "@kie-tools-core/notifications": "workspace:*",
     "@kie-tools/form": "workspace:*",
     "@kie-tools/i18n-common-dictionary": "workspace:*",
+    "@kie-tools/uniforms-patternfly": "workspace:*",
     "@patternfly/react-core": "^4.157.3",
     "@patternfly/react-icons": "^4.11.17",
     "ajv": "^6.12.6",
@@ -37,9 +38,8 @@
     "lodash": "^4.17.21",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
-    "uniforms": "^3.5.5",
-    "uniforms-bridge-json-schema": "^3.5.5",
-    "uniforms-patternfly": "^4.7.9"
+    "uniforms": "^3.10.2",
+    "uniforms-bridge-json-schema": "^3.10.2"
   },
   "devDependencies": {
     "@babel/core": "^7.16.0",
diff --git a/packages/form-dmn/src/uniforms/DmnAutoFieldProvider.tsx b/packages/form-dmn/src/uniforms/DmnAutoFieldProvider.tsx
index d34ae060fd..b94d5ac826 100644
--- a/packages/form-dmn/src/uniforms/DmnAutoFieldProvider.tsx
+++ b/packages/form-dmn/src/uniforms/DmnAutoFieldProvider.tsx
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import { AutoField, AutoFields } from "uniforms-patternfly";
+import { AutoField, AutoFields } from "@kie-tools/uniforms-patternfly/dist/esm";
 import { DmnFeelContextField } from "./DmnFeelContextField";
 import * as React from "react";
 import { FEEL_CONTEXT } from "./DmnFormJsonSchemaBridge";
diff --git a/packages/form-dmn/tests/DmnForm.test.tsx b/packages/form-dmn/tests/DmnForm.test.tsx
index 27a2d6e3a7..47470c10ee 100644
--- a/packages/form-dmn/tests/DmnForm.test.tsx
+++ b/packages/form-dmn/tests/DmnForm.test.tsx
@@ -58,7 +58,7 @@ describe("DmnForm tests", () => {
 
     const { findByTestId } = render(<DmnForm {...newProps} />);
 
-    expect(await findByTestId("base-form")).toMatchSnapshot();
+    expect(await findByTestId("form-base")).toMatchSnapshot();
   });
 
   it("should submit the formData", async () => {
@@ -68,7 +68,7 @@ describe("DmnForm tests", () => {
 
     const { findByTestId } = render(<DmnForm {...props} onSubmit={onSubmit} formRef={formRef} formInputs={formData} />);
 
-    expect(await findByTestId("base-form")).toMatchSnapshot();
+    expect(await findByTestId("form-base")).toMatchSnapshot();
 
     await act(async () => {
       formRef.current?.submit();
@@ -84,7 +84,7 @@ describe("DmnForm tests", () => {
 
     const { findByTestId } = render(<DmnForm {...props} onSubmit={onSubmit} formRef={formRef} formInputs={formData} />);
 
-    expect(await findByTestId("base-form")).toMatchSnapshot();
+    expect(await findByTestId("form-base")).toMatchSnapshot();
 
     await act(async () => {
       formRef.current?.submit();
@@ -102,7 +102,7 @@ describe("DmnForm tests", () => {
       <DmnForm {...props} formRef={formRef} onValidate={onValidate} formInputs={formData} />
     );
 
-    expect(await findByTestId("base-form")).toMatchSnapshot();
+    expect(await findByTestId("form-base")).toMatchSnapshot();
 
     await act(async () => {
       formRef.current?.submit();
@@ -120,7 +120,7 @@ describe("DmnForm tests", () => {
       <DmnForm {...props} formRef={formRef} onValidate={onValidate} formInputs={formData} />
     );
 
-    expect(await findByTestId("base-form")).toMatchSnapshot();
+    expect(await findByTestId("form-base")).toMatchSnapshot();
 
     await act(async () => {
       formRef.current?.submit();
diff --git a/packages/form-dmn/tests/__snapshots__/DmnForm.test.tsx.snap b/packages/form-dmn/tests/__snapshots__/DmnForm.test.tsx.snap
index ad6e340733..03d8a34534 100644
--- a/packages/form-dmn/tests/__snapshots__/DmnForm.test.tsx.snap
+++ b/packages/form-dmn/tests/__snapshots__/DmnForm.test.tsx.snap
@@ -19,15 +19,17 @@ exports[`DmnForm tests should have placeholder 1`] = `
 exports[`DmnForm tests should remove require parameter 1`] = `
 <div>
   <div
-    data-testid="base-form"
+    data-testid="form-base"
   >
     <form
       class="pf-c-form"
+      data-testid="base-form"
       novalidate=""
     >
       <div>
         <div
           class="pf-c-form__group"
+          data-testid="wrapper-field"
         >
           <div
             class="pf-c-form__group-label"
@@ -53,6 +55,7 @@ exports[`DmnForm tests should remove require parameter 1`] = `
               data-ouia-component-id="OUIA-Generated-TextInputBase-22"
               data-ouia-component-type="PF4/TextInput"
               data-ouia-safe="true"
+              data-testid="text-field"
               id="uniforms-0007-0001"
               name="name"
               placeholder=""
@@ -70,15 +73,17 @@ exports[`DmnForm tests should remove require parameter 1`] = `
 
 exports[`DmnForm tests should render the DMN Form 1`] = `
 <div
-  data-testid="base-form"
+  data-testid="form-base"
 >
   <form
     class="pf-c-form"
+    data-testid="base-form"
     novalidate=""
   >
     <div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -104,6 +109,7 @@ exports[`DmnForm tests should render the DMN Form 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-1"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0000-0001"
             name="name"
             placeholder=""
@@ -115,6 +121,7 @@ exports[`DmnForm tests should render the DMN Form 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -140,6 +147,7 @@ exports[`DmnForm tests should render the DMN Form 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-2"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0000-0003"
             name="lastName"
             placeholder=""
@@ -151,6 +159,7 @@ exports[`DmnForm tests should render the DMN Form 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -176,6 +185,7 @@ exports[`DmnForm tests should render the DMN Form 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-3"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0000-0005"
             name="daysAndTimeDuration"
             placeholder=""
@@ -187,6 +197,7 @@ exports[`DmnForm tests should render the DMN Form 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -212,6 +223,7 @@ exports[`DmnForm tests should render the DMN Form 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-4"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0000-0007"
             name="yearsAndMonthDuration"
             placeholder=""
@@ -228,15 +240,17 @@ exports[`DmnForm tests should render the DMN Form 1`] = `
 
 exports[`DmnForm tests should submit the formData 1`] = `
 <div
-  data-testid="base-form"
+  data-testid="form-base"
 >
   <form
     class="pf-c-form"
+    data-testid="base-form"
     novalidate=""
   >
     <div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -262,6 +276,7 @@ exports[`DmnForm tests should submit the formData 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-5"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0001-0001"
             name="name"
             placeholder=""
@@ -273,6 +288,7 @@ exports[`DmnForm tests should submit the formData 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -298,6 +314,7 @@ exports[`DmnForm tests should submit the formData 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-6"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0001-0003"
             name="lastName"
             placeholder=""
@@ -309,6 +326,7 @@ exports[`DmnForm tests should submit the formData 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -334,6 +352,7 @@ exports[`DmnForm tests should submit the formData 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-7"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0001-0005"
             name="daysAndTimeDuration"
             placeholder=""
@@ -345,6 +364,7 @@ exports[`DmnForm tests should submit the formData 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -370,6 +390,7 @@ exports[`DmnForm tests should submit the formData 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-8"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0001-0007"
             name="yearsAndMonthDuration"
             placeholder=""
@@ -386,15 +407,17 @@ exports[`DmnForm tests should submit the formData 1`] = `
 
 exports[`DmnForm tests should validate the formData - invalid 1`] = `
 <div
-  data-testid="base-form"
+  data-testid="form-base"
 >
   <form
     class="pf-c-form"
+    data-testid="base-form"
     novalidate=""
   >
     <div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -420,6 +443,7 @@ exports[`DmnForm tests should validate the formData - invalid 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-17"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0004-0001"
             name="name"
             placeholder=""
@@ -431,6 +455,7 @@ exports[`DmnForm tests should validate the formData - invalid 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -456,6 +481,7 @@ exports[`DmnForm tests should validate the formData - invalid 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-18"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0004-0003"
             name="lastName"
             placeholder=""
@@ -467,6 +493,7 @@ exports[`DmnForm tests should validate the formData - invalid 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -492,6 +519,7 @@ exports[`DmnForm tests should validate the formData - invalid 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-19"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0004-0005"
             name="daysAndTimeDuration"
             placeholder=""
@@ -509,6 +537,7 @@ exports[`DmnForm tests should validate the formData - invalid 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -534,6 +563,7 @@ exports[`DmnForm tests should validate the formData - invalid 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-20"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0004-0007"
             name="yearsAndMonthDuration"
             placeholder=""
@@ -550,15 +580,17 @@ exports[`DmnForm tests should validate the formData - invalid 1`] = `
 
 exports[`DmnForm tests should validate the formData - success 1`] = `
 <div
-  data-testid="base-form"
+  data-testid="form-base"
 >
   <form
     class="pf-c-form"
+    data-testid="base-form"
     novalidate=""
   >
     <div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -584,6 +616,7 @@ exports[`DmnForm tests should validate the formData - success 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-13"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0003-0001"
             name="name"
             placeholder=""
@@ -595,6 +628,7 @@ exports[`DmnForm tests should validate the formData - success 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -620,6 +654,7 @@ exports[`DmnForm tests should validate the formData - success 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-14"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0003-0003"
             name="lastName"
             placeholder=""
@@ -631,6 +666,7 @@ exports[`DmnForm tests should validate the formData - success 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -656,6 +692,7 @@ exports[`DmnForm tests should validate the formData - success 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-15"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0003-0005"
             name="daysAndTimeDuration"
             placeholder=""
@@ -667,6 +704,7 @@ exports[`DmnForm tests should validate the formData - success 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -692,6 +730,7 @@ exports[`DmnForm tests should validate the formData - success 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-16"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0003-0007"
             name="yearsAndMonthDuration"
             placeholder=""
@@ -708,15 +747,17 @@ exports[`DmnForm tests should validate the formData - success 1`] = `
 
 exports[`DmnForm tests shouldn't submit the formData 1`] = `
 <div
-  data-testid="base-form"
+  data-testid="form-base"
 >
   <form
     class="pf-c-form"
+    data-testid="base-form"
     novalidate=""
   >
     <div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -742,6 +783,7 @@ exports[`DmnForm tests shouldn't submit the formData 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-9"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0002-0001"
             name="name"
             placeholder=""
@@ -753,6 +795,7 @@ exports[`DmnForm tests shouldn't submit the formData 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -778,6 +821,7 @@ exports[`DmnForm tests shouldn't submit the formData 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-10"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0002-0003"
             name="lastName"
             placeholder=""
@@ -789,6 +833,7 @@ exports[`DmnForm tests shouldn't submit the formData 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -814,6 +859,7 @@ exports[`DmnForm tests shouldn't submit the formData 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-11"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0002-0005"
             name="daysAndTimeDuration"
             placeholder=""
@@ -831,6 +877,7 @@ exports[`DmnForm tests shouldn't submit the formData 1`] = `
       </div>
       <div
         class="pf-c-form__group"
+        data-testid="wrapper-field"
       >
         <div
           class="pf-c-form__group-label"
@@ -856,6 +903,7 @@ exports[`DmnForm tests shouldn't submit the formData 1`] = `
             data-ouia-component-id="OUIA-Generated-TextInputBase-12"
             data-ouia-component-type="PF4/TextInput"
             data-ouia-safe="true"
+            data-testid="text-field"
             id="uniforms-0002-0007"
             name="yearsAndMonthDuration"
             placeholder=""
diff --git a/packages/form-generation-tool/package.json b/packages/form-generation-tool/package.json
index d61d7b11b6..bc640fd276 100644
--- a/packages/form-generation-tool/package.json
+++ b/packages/form-generation-tool/package.json
@@ -32,8 +32,8 @@
     "@kie-tools/uniforms-patternfly-codegen": "workspace:*",
     "inquirer": "^8.1.1",
     "lodash": "^4.17.21",
-    "uniforms": "^3.5.5",
-    "uniforms-bridge-json-schema": "^3.5.5"
+    "uniforms": "^3.10.2",
+    "uniforms-bridge-json-schema": "^3.10.2"
   },
   "devDependencies": {
     "@babel/core": "^7.16.0",
diff --git a/packages/form/package.json b/packages/form/package.json
index bbbf464777..57d5a6236e 100644
--- a/packages/form/package.json
+++ b/packages/form/package.json
@@ -27,6 +27,7 @@
     "@kie-tools-core/i18n": "workspace:*",
     "@kie-tools-core/notifications": "workspace:*",
     "@kie-tools/i18n-common-dictionary": "workspace:*",
+    "@kie-tools/uniforms-patternfly": "workspace:*",
     "@patternfly/react-core": "^4.157.3",
     "@patternfly/react-icons": "^4.11.17",
     "ajv": "^6.12.6",
@@ -35,9 +36,8 @@
     "lodash": "^4.17.21",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
-    "uniforms": "^3.5.5",
-    "uniforms-bridge-json-schema": "^3.5.5",
-    "uniforms-patternfly": "^4.7.9"
+    "uniforms": "^3.10.2",
+    "uniforms-bridge-json-schema": "^3.10.2"
   },
   "devDependencies": {
     "@babel/core": "^7.16.0",
diff --git a/packages/form/src/FormBase.tsx b/packages/form/src/FormBase.tsx
index f91d3aff19..02fe94c9ca 100644
--- a/packages/form/src/FormBase.tsx
+++ b/packages/form/src/FormBase.tsx
@@ -16,7 +16,7 @@
 
 import { AutoGenerationErrorFormStatus, EmptyFormStatus, FormStatus, ValidatorErrorFormStatus } from "./FormStatus";
 import { ErrorBoundary } from "./ErrorBoundary";
-import AutoForm from "uniforms-patternfly/dist/es6/AutoForm";
+import AutoForm from "@kie-tools/uniforms-patternfly/dist/esm/AutoForm";
 import * as React from "react";
 import { FormI18n } from "./i18n";
 import { FormJsonSchemaBridge } from "./uniforms/FormJsonSchemaBridge";
@@ -55,7 +55,7 @@ export function FormBase(props: React.PropsWithChildren<FormBaseProps>) {
       )}
       {props.formStatus === FormStatus.EMPTY && <EmptyFormStatus i18n={props.i18n} />}
       {props.formStatus === FormStatus.WITHOUT_ERROR && (
-        <div data-testid={"base-form"}>
+        <div data-testid={"form-base"}>
           <ErrorBoundary
             ref={props.errorBoundaryRef}
             setHasError={props.setFormError}
diff --git a/packages/kn-plugin-workflow/README.md b/packages/kn-plugin-workflow/README.md
index 21bc65ed5e..8ac70a2d76 100644
--- a/packages/kn-plugin-workflow/README.md
+++ b/packages/kn-plugin-workflow/README.md
@@ -10,7 +10,7 @@ All the commands in this section should be performed in the monorepo root.
 
 ### Prerequisites
 
-- Node `>= 16.13.2` _(To install, follow these instructions: https://nodejs.org/en/download/package-manager/)_
+- Node `>= 18.14.0` _(To install, follow these instructions: https://nodejs.org/en/download/package-manager/)_
 - pnpm `7.26.3` _(To install, follow these instructions: https://pnpm.io/installation)_
 - Go `1.19` _(To install, follow these instructions: https://go.dev/doc/install)_
 
diff --git a/packages/online-editor/src/devDeployments/DevDeploymentsDropdown.tsx b/packages/online-editor/src/devDeployments/DevDeploymentsDropdown.tsx
index 5b99e47485..1ce7260702 100644
--- a/packages/online-editor/src/devDeployments/DevDeploymentsDropdown.tsx
+++ b/packages/online-editor/src/devDeployments/DevDeploymentsDropdown.tsx
@@ -193,14 +193,14 @@ export function DevDeploymentsDropdown() {
       }
     } else {
       return [
-        <>
+        <div key={"empty-deployments"}>
           <EmptyState>
             <EmptyStateIcon icon={PficonSatelliteIcon} />
             <Title headingLevel="h4" size="md" style={{ color: "darkgray" }}>
               {`Choose a Cloud provider to see your Dev deployments.`}
             </Title>
           </EmptyState>
-        </>,
+        </div>,
       ];
     }
   }, [authSession, deleteAllDeployments, deployments.data, deployments.status, i18n]);
diff --git a/packages/online-editor/src/index.tsx b/packages/online-editor/src/index.tsx
index 6a601443be..706e521e06 100644
--- a/packages/online-editor/src/index.tsx
+++ b/packages/online-editor/src/index.tsx
@@ -20,15 +20,12 @@ import * as ReactDOM from "react-dom";
 import { App } from "./App";
 import "../static/resources/style.css";
 import * as incompatibleBrowser from "./workspace/startupBlockers/IncompatibleBrowser";
-import * as fsChanged from "./workspace/startupBlockers/FsChanged";
 
 async function main() {
   const appContainer = document.getElementById("app")!;
 
   if (await incompatibleBrowser.isTrue()) {
     ReactDOM.render(<incompatibleBrowser.Component />, appContainer);
-  } else if (await fsChanged.isTrue()) {
-    ReactDOM.render(<fsChanged.Component />, appContainer);
   } else {
     ReactDOM.render(<App />, appContainer);
   }
diff --git a/packages/online-editor/src/workspace/startupBlockers/FsChanged.tsx b/packages/online-editor/src/workspace/startupBlockers/FsChanged.tsx
deleted file mode 100644
index 980aac6d25..0000000000
--- a/packages/online-editor/src/workspace/startupBlockers/FsChanged.tsx
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
- *
- * Licensed 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.
- */
-
-import * as React from "react";
-import { useState } from "react";
-import { Modal } from "@patternfly/react-core/dist/js/components/Modal";
-import { ModalVariant } from "@patternfly/react-core/dist/esm/components/Modal";
-import { Button } from "@patternfly/react-core/dist/js/components/Button";
-import ExternalLinkSquareAltIcon from "@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon";
-import { Text, TextContent, TextVariants } from "@patternfly/react-core/dist/js/components/Text";
-import { StartupBlockerTemplate } from "./StartupBlockerTemplate";
-import { KieIcon } from "./KieIcon";
-import { LATEST_VERSION_COMPATIBLE_WITH_LFS } from "./LatestCompatibleVersion";
-
-const OLD_WORKSPACES_IDB_NAME = "workspaces";
-
-export async function isTrue() {
-  const dbs = await indexedDB.databases();
-  return dbs.map((db) => db.name).indexOf(OLD_WORKSPACES_IDB_NAME) > 0;
-}
-
-export function Component() {
-  const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
-  const [isDeleting, setDeleting] = useState(false);
-
-  return (
-    <StartupBlockerTemplate>
-      <Modal
-        variant={ModalVariant.small}
-        isOpen={isDeleteConfirmationModalOpen}
-        showClose={false}
-        titleIconVariant={KieIcon}
-        title={"Please confirm deleting everything"}
-        actions={[
-          <Button
-            key="delete-everything"
-            variant="primary"
-            isLarge={true}
-            style={{ width: "50%", backgroundColor: "var(--pf-c-button--m-danger--BackgroundColor)" }}
-            isLoading={isDeleting}
-            onClick={async () => {
-              setDeleting(true);
-              await Promise.all(
-                (await indexedDB.databases())
-                  .filter((db) => db.name)
-                  .map(async (db) => indexedDB.deleteDatabase(db.name!))
-              );
-              window.location.reload();
-            }}
-          >
-            Delete everything
-            <br />
-            and start fresh
-          </Button>,
-          <Button
-            key="latest-version"
-            variant="tertiary"
-            iconPosition="right"
-            style={{ width: "50%" }}
-            icon={<ExternalLinkSquareAltIcon />}
-            isLarge={true}
-            onClick={() => {
-              window.open(LATEST_VERSION_COMPATIBLE_WITH_LFS);
-            }}
-          >
-            Open latest
-            <br />
-            compatible version
-          </Button>,
-        ]}
-      >
-        <br />
-        <TextContent>
-          <Text component={TextVariants.h4}>This action cannot be undone.</Text>
-        </TextContent>
-        <br />
-        <hr />
-        <br />
-        Continuing will delete all models you have saved on this browser. Please certify that your work is already
-        persisted somewhere else.
-        <br />
-        <br />
-        {`If you're not sure, proceed to the latest compatible version to push or download your models.`}
-      </Modal>
-      <Modal
-        showClose={false}
-        isOpen={!isDeleteConfirmationModalOpen}
-        variant={ModalVariant.small}
-        title={"Oops!"}
-        titleIconVariant={KieIcon}
-        actions={[
-          <Button
-            key="delete-everything"
-            variant="primary"
-            isLarge={true}
-            style={{ width: "50%" }}
-            onClick={() => setDeleteConfirmationModalOpen(true)}
-          >
-            Delete everything
-            <br />
-            and start fresh
-          </Button>,
-          <Button
-            key="latest-version"
-            variant="secondary"
-            iconPosition="right"
-            style={{ width: "50%" }}
-            icon={<ExternalLinkSquareAltIcon />}
-            isLarge={true}
-            onClick={() => {
-              window.open(LATEST_VERSION_COMPATIBLE_WITH_LFS);
-            }}
-          >
-            Open latest
-            <br />
-            compatible version
-          </Button>,
-        ]}
-      >
-        <br />
-        <TextContent>
-          <Text component={TextVariants.h4}>KIE Sandbox has changed the way it saves your models.</Text>
-        </TextContent>
-        <br />
-        <hr />
-        <br />
-        {`If you have work that is not saved outside of this browser yet, proceed to the latest compatible version to push or download your models.`}
-        <br />
-        <br />
-        {`If your work is already safe somewhere else, start fresh by deleting the old models.`}
-        <br />
-      </Modal>
-    </StartupBlockerTemplate>
-  );
-}
diff --git a/packages/online-editor/src/workspace/startupBlockers/IncompatibleBrowser.tsx b/packages/online-editor/src/workspace/startupBlockers/IncompatibleBrowser.tsx
index 033afe714f..81d95665b3 100644
--- a/packages/online-editor/src/workspace/startupBlockers/IncompatibleBrowser.tsx
+++ b/packages/online-editor/src/workspace/startupBlockers/IncompatibleBrowser.tsx
@@ -22,7 +22,8 @@ import * as Bowser from "bowser";
 import { Text, TextContent, TextVariants } from "@patternfly/react-core/dist/js/components/Text";
 import { List, ListItem } from "@patternfly/react-core/dist/js/components/List";
 import { KieIcon } from "./KieIcon";
-import { LATEST_VERSION_COMPATIBLE_WITH_LFS } from "./LatestCompatibleVersion";
+
+export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.23.0";
 
 const hasNecessaryApis = window["SharedWorker"] && window["BroadcastChannel"];
 const isCompatibleBrowser = Bowser.getParser(window.navigator.userAgent).satisfies({
diff --git a/packages/serverless-logic-web-tools/src/index.tsx b/packages/serverless-logic-web-tools/src/index.tsx
index db8f193bdd..27441046d8 100644
--- a/packages/serverless-logic-web-tools/src/index.tsx
+++ b/packages/serverless-logic-web-tools/src/index.tsx
@@ -22,15 +22,12 @@ import { App } from "./App";
 import "../static/resources/style.css";
 import "../static/resources/application-services.css";
 import * as incompatibleBrowser from "./workspace/startupBlockers/IncompatibleBrowser";
-import * as fsChanged from "./workspace/startupBlockers/FsChanged";
 
 async function main() {
   const appContainer = document.getElementById("app")!;
 
   if (await incompatibleBrowser.isTrue()) {
     ReactDOM.render(<incompatibleBrowser.Component />, appContainer);
-  } else if (await fsChanged.isTrue()) {
-    ReactDOM.render(<fsChanged.Component />, appContainer);
   } else {
     ReactDOM.render(<App />, appContainer);
   }
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/FsChanged.tsx b/packages/serverless-logic-web-tools/src/workspace/startupBlockers/FsChanged.tsx
deleted file mode 100644
index 29200a6a01..0000000000
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/FsChanged.tsx
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
- *
- * Licensed 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.
- */
-
-import * as React from "react";
-import { useState } from "react";
-import { Modal } from "@patternfly/react-core/dist/js/components/Modal";
-import { ModalVariant } from "@patternfly/react-core/dist/esm/components/Modal";
-import { Button } from "@patternfly/react-core/dist/js/components/Button";
-import ExternalLinkSquareAltIcon from "@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon";
-import { Text, TextContent, TextVariants } from "@patternfly/react-core/dist/js/components/Text";
-import { StartupBlockerTemplate } from "./StartupBlockerTemplate";
-import { KieIcon } from "./KieIcon";
-import { LATEST_VERSION_COMPATIBLE_WITH_LFS } from "./LatestCompatibleVersion";
-import { APP_NAME } from "../../AppConstants";
-import Dexie from "dexie";
-
-const OLD_WORKSPACES_IDB_NAME = "workspaces";
-
-export async function isTrue() {
-  return await Dexie.exists(OLD_WORKSPACES_IDB_NAME);
-}
-
-export function Component() {
-  const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
-  const [isDeleting, setDeleting] = useState(false);
-
-  return (
-    <StartupBlockerTemplate>
-      <Modal
-        variant={ModalVariant.medium}
-        isOpen={isDeleteConfirmationModalOpen}
-        showClose={false}
-        titleIconVariant={KieIcon}
-        title={"Please confirm deleting everything"}
-        actions={[
-          <Button
-            key="delete-everything"
-            variant="primary"
-            isLarge={true}
-            style={{ width: "50%", backgroundColor: "var(--pf-c-button--m-danger--BackgroundColor)" }}
-            isLoading={isDeleting}
-            onClick={async () => {
-              setDeleting(true);
-              await Promise.all((await Dexie.getDatabaseNames()).map(async (dbName) => Dexie.delete(dbName)));
-              window.location.reload();
-            }}
-          >
-            Delete everything
-            <br />
-            and start fresh
-          </Button>,
-          <Button
-            key="latest-version"
-            variant="tertiary"
-            iconPosition="right"
-            style={{ width: "50%" }}
-            icon={<ExternalLinkSquareAltIcon />}
-            isLarge={true}
-            onClick={() => {
-              window.open(LATEST_VERSION_COMPATIBLE_WITH_LFS);
-            }}
-          >
-            Open latest
-            <br />
-            compatible version
-          </Button>,
-        ]}
-      >
-        <br />
-        <TextContent>
-          <Text component={TextVariants.h4}>This action cannot be undone.</Text>
-        </TextContent>
-        <br />
-        <hr />
-        <br />
-        Continuing will delete all models you have saved on this browser. Please certify that your work is already
-        persisted somewhere else.
-        <br />
-        <br />
-        {`If you're not sure, proceed to the latest compatible version to push or download your models.`}
-      </Modal>
-      <Modal
-        showClose={false}
-        isOpen={!isDeleteConfirmationModalOpen}
-        variant={ModalVariant.medium}
-        title={"Oops!"}
-        titleIconVariant={KieIcon}
-        actions={[
-          <Button
-            key="delete-everything"
-            variant="primary"
-            isLarge={true}
-            style={{ width: "50%" }}
-            onClick={() => setDeleteConfirmationModalOpen(true)}
-          >
-            Delete everything
-            <br />
-            and start fresh
-          </Button>,
-          <Button
-            key="latest-version"
-            variant="secondary"
-            iconPosition="right"
-            style={{ width: "50%" }}
-            icon={<ExternalLinkSquareAltIcon />}
-            isLarge={true}
-            onClick={() => {
-              window.open(LATEST_VERSION_COMPATIBLE_WITH_LFS);
-            }}
-          >
-            Open latest
-            <br />
-            compatible version
-          </Button>,
-        ]}
-      >
-        <br />
-        <TextContent>
-          <Text component={TextVariants.h4}>{`${APP_NAME} has changed the way it saves your models.`}</Text>
-        </TextContent>
-        <br />
-        <hr />
-        <br />
-        {`If you have work that is not saved outside of this browser yet, proceed to the latest compatible version to push or download your models.`}
-        <br />
-        <br />
-        {`If your work is already safe somewhere else, start fresh by deleting the old models.`}
-        <br />
-      </Modal>
-    </StartupBlockerTemplate>
-  );
-}
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/IncompatibleBrowser.tsx b/packages/serverless-logic-web-tools/src/workspace/startupBlockers/IncompatibleBrowser.tsx
index db9742e797..1f3af7c7bd 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/IncompatibleBrowser.tsx
+++ b/packages/serverless-logic-web-tools/src/workspace/startupBlockers/IncompatibleBrowser.tsx
@@ -21,10 +21,11 @@ import { StartupBlockerTemplate } from "./StartupBlockerTemplate";
 import { Text, TextContent, TextVariants } from "@patternfly/react-core/dist/js/components/Text";
 import { List, ListItem } from "@patternfly/react-core/dist/js/components/List";
 import { KieIcon } from "./KieIcon";
-import { LATEST_VERSION_COMPATIBLE_WITH_LFS } from "./LatestCompatibleVersion";
 import { APP_NAME } from "../../AppConstants";
 import { BROWSER_DETAILS, SUPPORTED_BROWSERS } from "./SupportedBrowsers";
 
+export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+
 export async function isTrue() {
   return !BROWSER_DETAILS.isCompatible;
 }
diff --git a/packages/serverless-workflow-diagram-editor/lienzo-core/src/main/java/com/ait/lienzo/client/core/shape/toolbox/items/tooltip/Tooltip.java b/packages/serverless-workflow-diagram-editor/lienzo-core/src/main/java/com/ait/lienzo/client/core/shape/toolbox/items/tooltip/Tooltip.java
index 9a46dc5221..9121cd0bbd 100644
--- a/packages/serverless-workflow-diagram-editor/lienzo-core/src/main/java/com/ait/lienzo/client/core/shape/toolbox/items/tooltip/Tooltip.java
+++ b/packages/serverless-workflow-diagram-editor/lienzo-core/src/main/java/com/ait/lienzo/client/core/shape/toolbox/items/tooltip/Tooltip.java
@@ -26,6 +26,7 @@ import com.ait.lienzo.client.core.shape.Group;
 import com.ait.lienzo.client.core.shape.IPrimitive;
 import com.ait.lienzo.client.core.shape.MultiPath;
 import com.ait.lienzo.client.core.shape.Text;
+import com.ait.lienzo.client.core.shape.TextLineBreakWrap;
 import com.ait.lienzo.client.core.types.BoundingBox;
 import com.ait.lienzo.client.core.types.Point2D;
 import com.ait.lienzo.shared.core.types.Direction;
@@ -53,6 +54,7 @@ public class Tooltip {
                 new Text("")
                         .setFillColor(TEXT_COLOR).
                         setStrokeWidth(TEXT_STROKE_WIDTH);
+        this.text.setWrapper(new TextLineBreakWrap(this.text));
         this.group =
                 new Group()
                         .setAlpha(0)
@@ -213,7 +215,7 @@ public class Tooltip {
             case WEST:
                 path.setRotationDegrees(180);
                 textLoc.setX(-hl - tpw - tw)
-                        .setY((th / 2) - tph);
+                        .setY(((th / 2) - tph) * -1);
                 break;
             case NORTH:
                 path.setRotationDegrees(270);
@@ -228,7 +230,7 @@ public class Tooltip {
             default:
                 path.setRotationDegrees(0);
                 textLoc.setX(hl + tpw)
-                        .setY((th / 2) - tph);
+                        .setY(((th / 2) - tph) * -1);
         }
 
         // Location.
diff --git a/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/BasicWiresExample.java b/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/BasicWiresExample.java
index 143538687c..776dc14a40 100644
--- a/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/BasicWiresExample.java
+++ b/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/BasicWiresExample.java
@@ -10,6 +10,8 @@ import com.ait.lienzo.client.core.shape.wires.WiresContainer;
 import com.ait.lienzo.client.core.shape.wires.WiresManager;
 import com.ait.lienzo.client.core.shape.wires.WiresShape;
 import com.ait.lienzo.client.core.types.Point2D;
+import com.ait.lienzo.shared.core.types.EventPropagationMode;
+import com.ait.lienzo.tools.client.event.HandlerRegistration;
 
 import static org.kie.lienzo.client.util.WiresUtils.connect;
 
@@ -19,12 +21,11 @@ public class BasicWiresExample extends BaseExample implements Example {
     public static final String BLUE_RECTANGLE = "blueRectangle";
     public static final String CIRCLE = "circle";
     public static final String PARENT = "parent";
-
+    private HandlerRegistration redRectangleMouseEnterHandler;
+    private HandlerRegistration redRectangleMouseExitHandler;
+    private HandlerRegistration parentMouseEnterHandler;
+    private HandlerRegistration parentMouseExitHandler;
     private WiresManager wiresManager;
-    private WiresShape shapeRedRectangle;
-    private WiresShape shapeCircle;
-    private WiresShape shapeBlueRectangle;
-    private WiresShape shapeParent;
 
     public BasicWiresExample(final String title) {
         super(title);
@@ -51,29 +52,44 @@ public class BasicWiresExample extends BaseExample implements Example {
         wiresManager.setLocationAcceptor(ILocationAcceptor.ALL);
         wiresManager.setControlPointsAcceptor(IControlPointsAcceptor.ALL);
 
-        shapeRedRectangle = createShape(RED_RECTANGLE,
-                                        new MultiPath().rect(0, 0, 100, 100)
-                                                .setStrokeColor("#FF0000")
-                                                .setFillColor("#FF0000"),
-                                        new Point2D(100, 50));
-
-        shapeCircle = createShape(CIRCLE,
-                                  new MultiPath().circle(50)
-                                          .setStrokeColor("#FF0000")
-                                          .setFillColor("#FF0000"),
-                                  new Point2D(400, 50));
-
-        shapeBlueRectangle = createShape(BLUE_RECTANGLE,
-                                         new MultiPath().rect(0, 0, 100, 100)
-                                                 .setStrokeColor("#0000FF")
-                                                 .setFillColor("#0000FF"),
-                                         new Point2D(650, 50));
-
-        shapeParent = createShape(PARENT,
-                                  new MultiPath().rect(0, 0, 600, 250)
-                                          .setStrokeColor("#000000")
-                                          .setFillColor("#FFFFFF"),
-                                  new Point2D(50, 300));
+        MultiPath redRectangle = new MultiPath().rect(0, 0, 100, 100)
+                .setStrokeColor("#FF0000")
+                .setFillColor("#FF0000");
+        redRectangleMouseEnterHandler = redRectangle.addNodeMouseEnterHandler(event -> {
+            console.log("red rectangle ENTER");
+        });
+        redRectangleMouseExitHandler = redRectangle.addNodeMouseExitHandler(event -> {
+            console.log("red rectangle EXIT");
+        });
+        redRectangle.setEventPropagationMode(EventPropagationMode.LAST_ANCESTOR);
+        WiresShape shapeRedRectangle = createShape(RED_RECTANGLE,
+                                                   redRectangle,
+                                                   new Point2D(100, 50));
+
+        WiresShape shapeCircle = createShape(CIRCLE,
+                                             new MultiPath().circle(50)
+                                                     .setStrokeColor("#FF0000")
+                                                     .setFillColor("#FF0000"),
+                                             new Point2D(400, 50));
+
+        WiresShape shapeBlueRectangle = createShape(BLUE_RECTANGLE,
+                                                    new MultiPath().rect(0, 0, 100, 100)
+                                                            .setStrokeColor("#0000FF")
+                                                            .setFillColor("#0000FF"),
+                                                    new Point2D(650, 50));
+
+        MultiPath parent = new MultiPath().rect(0, 0, 600, 250)
+                .setStrokeColor("#000000")
+                .setFillColor("#FFFFFF");
+        parentMouseEnterHandler = parent.addNodeMouseEnterHandler(event -> {
+            console.log("PARENT ENTER");
+        });
+        parentMouseExitHandler = parent.addNodeMouseExitHandler(event -> {
+            console.log("PARENT EXIT");
+        });
+        WiresShape shapeParent = createShape(PARENT,
+                                             parent,
+                                             new Point2D(50, 300));
 
         connect(shapeRedRectangle.getMagnets(),
                 3,
@@ -126,4 +142,13 @@ public class BasicWiresExample extends BaseExample implements Example {
         }
         return true;
     }
+
+    @Override
+    public void destroy() {
+        super.destroy();
+        redRectangleMouseEnterHandler.removeHandler();
+        redRectangleMouseExitHandler.removeHandler();
+        parentMouseEnterHandler.removeHandler();
+        parentMouseExitHandler.removeHandler();
+    }
 }
diff --git a/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/EventExample.java b/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/EventExample.java
index 2ca9b43891..b5bf359acf 100644
--- a/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/EventExample.java
+++ b/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/EventExample.java
@@ -31,10 +31,13 @@ import com.ait.lienzo.client.core.shape.Triangle;
 import com.ait.lienzo.client.core.types.Point2D;
 import com.ait.lienzo.gwtlienzo.event.shared.EventHandler;
 import com.ait.lienzo.shared.core.types.ColorName;
+import com.ait.lienzo.shared.core.types.EventPropagationMode;
 import org.kie.lienzo.client.util.Console;
 
 public class EventExample extends BaseExample implements Example {
 
+    MultiPath container;
+
     public EventExample(final String title) {
         super(title);
     }
@@ -56,16 +59,26 @@ public class EventExample extends BaseExample implements Example {
     }
 
     private void createDragTests() {
-        Rectangle rect = new Rectangle(500, 200);
-        rect.setX(50).setY(50);
-        rect.setFillColor(ColorName.PALEVIOLETRED);
-        rect.setStrokeColor(ColorName.PALEVIOLETRED);
-        rect.setDraggable(true);
-        layer.add(rect);
-
-        addHandlers(rect, NodeDragStartHandler.class, NodeDragMoveHandler.class, NodeDragEndHandler.class);
-
-        rect = new Rectangle(100, 100);
+        container = new MultiPath().rect(50, 50, 500, 200);
+        container.setID("Container");
+        container.setX(50).setY(50);
+        container.setFillColor(ColorName.PALEVIOLETRED);
+        container.setStrokeColor(ColorName.PALEVIOLETRED);
+        container.setEventPropagationMode(EventPropagationMode.NO_ANCESTORS);
+        container.setDraggable(true);
+        layer.add(container);
+
+        addHandlers(container,
+                    NodeDragStartHandler.class,
+                    NodeDragMoveHandler.class,
+                    NodeDragEndHandler.class,
+                    NodeMouseClickHandler.class,
+                    NodeMouseEnterHandler.class,
+                    NodeMouseExitHandler.class,
+                    NodeMouseOutHandler.class,
+                    NodeMouseOverHandler.class);
+
+        Rectangle rect = new Rectangle(100, 100);
         rect.setX(250).setY(100);
         rect.setFillColor(ColorName.DARKGRAY);
         rect.setStrokeColor(ColorName.DARKGRAY);
@@ -77,13 +90,15 @@ public class EventExample extends BaseExample implements Example {
 
     private void createMouseEnterExitTests() {
         Circle circ = new Circle(50);
+        circ.setID("YELLOWGREEN circle");
         circ.setX(150).setY(150);
         circ.setFillColor(ColorName.YELLOWGREEN);
         circ.setStrokeColor(ColorName.YELLOWGREEN);
-        circ.setDraggable(true);
+        circ.setEventPropagationMode(EventPropagationMode.FIRST_ANCESTOR);
+        circ.setDraggable(false);
         layer.add(circ);
 
-        addHandlers(circ, NodeMouseEnterHandler.class, NodeMouseExitHandler.class);
+        addHandlers(circ, NodeMouseEnterHandler.class, NodeMouseExitHandler.class, NodeMouseClickHandler.class);
 
         circ = new Circle(50);
         circ.setX(150).setY(350);
@@ -208,57 +223,57 @@ public class EventExample extends BaseExample implements Example {
 
         @Override
         public void onNodeDragStart(final NodeDragStartEvent event) {
-            console.log("drag start");
+            console.log(event.getSource().getID() + ": " + "drag start");
         }
 
         @Override
         public void onNodeDragMove(final NodeDragMoveEvent event) {
-            console.log("drag move");
+            console.log(event.getSource().getID() + ": " + "drag move");
         }
 
         @Override
         public void onNodeDragEnd(final NodeDragEndEvent event) {
-            console.log("drag end");
+            console.log(event.getSource().getID() + ": " + "drag end");
         }
 
         @Override
         public void onNodeMouseEnter(final NodeMouseEnterEvent event) {
-            console.log("mouse enter");
+            console.log(event.getSource().getID() + ": " + "mouse enter");
         }
 
         @Override
         public void onNodeMouseExit(final NodeMouseExitEvent event) {
-            console.log("mouse exit");
+            console.log(event.getSource().getID() + ": " + "mouse exit");
         }
 
         @Override
         public void onNodeMouseUp(final NodeMouseUpEvent event) {
-            console.log("mouse up");
+            console.log(event.getSource().getID() + ": " + "mouse up");
         }
 
         @Override
         public void onNodeMouseDown(final NodeMouseDownEvent event) {
-            console.log("mouse down");
+            console.log(event.getSource().getID() + ": " + "mouse down");
         }
 
         @Override
         public void onNodeMouseClick(final NodeMouseClickEvent event) {
-            console.log("mouse click");
+            console.log(event.getSource().getID() + ": " + "mouse click");
         }
 
         @Override
         public void onNodeMouseDoubleClick(final NodeMouseDoubleClickEvent event) {
-            console.log("mouse double click");
+            console.log(event.getSource().getID() + ": " + "mouse double click");
         }
 
         @Override
         public void onNodeMouseOver(final NodeMouseOverEvent event) {
-            console.log("mouse over");
+            console.log(event.getSource().getID() + ": " + "mouse over");
         }
 
         @Override
         public void onNodeMouseOut(final NodeMouseOutEvent event) {
-            console.log("mouse out");
+            console.log(event.getSource().getID() + ": " + "mouse out");
         }
     }
 }
diff --git a/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/WiresDockingExample.java b/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/WiresDockingExample.java
index 8301bada40..3757aa1336 100644
--- a/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/WiresDockingExample.java
+++ b/packages/serverless-workflow-diagram-editor/lienzo-webapp/src/main/java/org/kie/lienzo/client/WiresDockingExample.java
@@ -24,9 +24,6 @@ public class WiresDockingExample extends BaseExample implements Example {
 
     @Override
     public void run() {
-
-//        getToolBarContainer().add(m_label);
-
         final WiresManager wires_manager = WiresManager.get(layer);
 
         wires_manager.setContainmentAcceptor(IContainmentAcceptor.ALL);
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/ModelUtils.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/ModelUtils.java
index 0e6fc851f3..68808ec05b 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/ModelUtils.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/ModelUtils.java
@@ -79,7 +79,6 @@ public class ModelUtils {
             state.setName((String) Js.undefined());
             state.setType((String) Js.undefined());
             state.setTransition((String) Js.undefined());
-            state.setEventTimeout((String) Js.undefined());
             state.setCompensatedBy((String) Js.undefined());
             state.setEnd(false);
             if (null != state.getOnErrors()) {
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/ParallelStateBranch.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/ParallelStateBranch.java
index ed1c18ef02..90ecab16ad 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/ParallelStateBranch.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/ParallelStateBranch.java
@@ -16,11 +16,7 @@
 
 package org.kie.workbench.common.stunner.sw.definition;
 
-import jakarta.json.bind.annotation.JsonbTypeDeserializer;
-import jakarta.json.bind.annotation.JsonbTypeSerializer;
 import jsinterop.annotations.JsType;
-import org.kie.workbench.common.stunner.sw.definition.custom.WorkflowTimeoutsJsonDeserializer;
-import org.kie.workbench.common.stunner.sw.definition.custom.WorkflowTimeoutsJsonSerializer;
 
 @JsType
 public class ParallelStateBranch {
@@ -28,9 +24,7 @@ public class ParallelStateBranch {
     private String name;
     private ActionNode[] actions;
 
-    @JsonbTypeSerializer(WorkflowTimeoutsJsonSerializer.class)
-    @JsonbTypeDeserializer(WorkflowTimeoutsJsonDeserializer.class)
-    private Object timeouts;
+    private WorkflowTimeouts timeouts;
 
     public final String getName() {
         return name;
@@ -48,11 +42,11 @@ public class ParallelStateBranch {
         this.actions = actions;
     }
 
-    public final Object getTimeouts() {
+    public final WorkflowTimeouts getTimeouts() {
         return timeouts;
     }
 
-    public final void setTimeouts(Object timeouts) {
+    public final void setTimeouts(WorkflowTimeouts timeouts) {
         this.timeouts = timeouts;
     }
 }
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/State.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/State.java
index af2c38e2f3..15627ded35 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/State.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/State.java
@@ -35,13 +35,11 @@ import org.kie.workbench.common.stunner.sw.definition.custom.StateEndDefinitionJ
 import org.kie.workbench.common.stunner.sw.definition.custom.StateEndDefinitionJsonbTypeSerializer;
 import org.kie.workbench.common.stunner.sw.definition.custom.StateTransitionDefinitionJsonbTypeDeserializer;
 import org.kie.workbench.common.stunner.sw.definition.custom.StateTransitionDefinitionJsonbTypeSerializer;
-import org.kie.workbench.common.stunner.sw.definition.custom.WorkflowTimeoutsJsonDeserializer;
-import org.kie.workbench.common.stunner.sw.definition.custom.WorkflowTimeoutsJsonSerializer;
 
 /**
  * This class defines workflow states define building blocks of the workflow execution instructions.
  * They define the control flow logic instructions on what the workflow is supposed to do.
- *
+ * <p>
  * Type of the state is specified by its category, which is set in constructor for all its descendants.
  */
 @Bindable
@@ -94,11 +92,6 @@ public class State {
      */
     private ErrorTransition[] onErrors;
 
-    /**
-     * State specific timeouts.
-     */
-    private String eventTimeout;
-
     /**
      * Unique name of a workflow state which is responsible for compensation of this state.
      */
@@ -106,10 +99,7 @@ public class State {
 
     private StateDataFilter stateDataFilter;
 
-
-    @JsonbTypeSerializer(WorkflowTimeoutsJsonSerializer.class)
-    @JsonbTypeDeserializer(WorkflowTimeoutsJsonDeserializer.class)
-    private Object timeouts;
+    private WorkflowTimeouts timeouts;
 
     public State() {
         this.name = "State";
@@ -160,15 +150,6 @@ public class State {
         return this;
     }
 
-    public String getEventTimeout() {
-        return eventTimeout;
-    }
-
-    public State setEventTimeout(String eventTimeout) {
-        this.eventTimeout = eventTimeout;
-        return this;
-    }
-
     public String getCompensatedBy() {
         return compensatedBy;
     }
@@ -193,6 +174,7 @@ public class State {
     public void setMetadata(Metadata metadata) {
         this.metadata = metadata;
     }
+
     public StateDataFilter getStateDataFilter() {
         return stateDataFilter;
     }
@@ -201,11 +183,11 @@ public class State {
         this.stateDataFilter = stateDataFilter;
     }
 
-    public Object getTimeouts() {
+    public WorkflowTimeouts getTimeouts() {
         return timeouts;
     }
 
-    public void setTimeouts(Object timeouts) {
+    public void setTimeouts(WorkflowTimeouts timeouts) {
         this.timeouts = timeouts;
     }
 }
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/Timeout.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/Timeout.java
index b9a69ab74b..ce772a3105 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/Timeout.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/definition/Timeout.java
@@ -25,7 +25,6 @@ import org.kie.workbench.common.stunner.core.definition.annotation.definition.Ca
 import org.kie.workbench.common.stunner.core.definition.annotation.definition.Labels;
 import org.kie.workbench.common.stunner.core.definition.annotation.morph.MorphBase;
 
-// TODO: Timeouts are not being used for now, consider dropping all related classes and usages.
 @MorphBase(defaultType = EventTimeout.class)
 public class Timeout {
 
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/marshall/StateMarshalling.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/marshall/StateMarshalling.java
index cf89fee5a6..42241df6f3 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/marshall/StateMarshalling.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/main/java/org/kie/workbench/common/stunner/sw/marshall/StateMarshalling.java
@@ -34,7 +34,6 @@ import org.kie.workbench.common.stunner.sw.definition.ErrorTransition;
 import org.kie.workbench.common.stunner.sw.definition.EventConditionTransition;
 import org.kie.workbench.common.stunner.sw.definition.EventRef;
 import org.kie.workbench.common.stunner.sw.definition.EventState;
-import org.kie.workbench.common.stunner.sw.definition.EventTimeout;
 import org.kie.workbench.common.stunner.sw.definition.ForEachState;
 import org.kie.workbench.common.stunner.sw.definition.OnEvent;
 import org.kie.workbench.common.stunner.sw.definition.OperationState;
@@ -125,13 +124,6 @@ public interface StateMarshalling {
                 List<Edge> outEdges = stateNode.getOutEdges();
                 for (Edge edge : outEdges) {
                     if (edge.getContent() instanceof Dock) {
-                        Node timeoutNode = edge.getTargetNode();
-                        if (null != timeoutNode) {
-                            Object def = getElementDefinition(timeoutNode);
-                            if (def instanceof EventTimeout) {
-                                state.setEventTimeout(((EventTimeout) def).getEventTimeout());
-                            }
-                        }
                     } else {
                         Object def = getElementDefinition(edge);
                         if (def instanceof ErrorTransition) {
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/test/java/org/kie/workbench/common/stunner/sw/marshall/StateMarshallingTest.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/test/java/org/kie/workbench/common/stunner/sw/marshall/StateMarshallingTest.java
index 9bb96f9b7c..7eeb7b5a40 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/test/java/org/kie/workbench/common/stunner/sw/marshall/StateMarshallingTest.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-api/src/test/java/org/kie/workbench/common/stunner/sw/marshall/StateMarshallingTest.java
@@ -63,9 +63,8 @@ public class StateMarshallingTest extends BaseMarshallingTest {
         assertTrue(workflow.getStates()[0].getEnd() instanceof StateEnd);
         final StateEnd end = (StateEnd) workflow.getStates()[0].getEnd();
         assertTrue(end.getTerminate());
-        assertEquals("{}",end.getContinueAs());
+        assertEquals("{}", end.getContinueAs());
         assertFalse(end.getCompensate());
-        //assertTrue(end.get("produceEvents").equals("[]"));
     }
 
     @Test
@@ -123,7 +122,6 @@ public class StateMarshallingTest extends BaseMarshallingTest {
         assertNull(state1.getTransition());
         assertFalse(DefinitionTypeUtils.getEnd(state1.getEnd()));
         assertNull(state1.getCompensatedBy());
-        assertNull(state1.getEventTimeout());
         assertNull(state1.getOnErrors());
     }
 
@@ -144,7 +142,6 @@ public class StateMarshallingTest extends BaseMarshallingTest {
         assertNull(state1.getTransition());
         assertFalse(DefinitionTypeUtils.getEnd(state1.getEnd()));
         assertNull(state1.getCompensatedBy());
-        assertNull(state1.getEventTimeout());
         assertNotNull(state1.getOnErrors());
         assertEquals(1, state1.getOnErrors().length);
         ErrorTransition onError = state1.getOnErrors()[0];
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/ShapeFactory.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/ShapeFactory.java
index 592c452e14..f8527c2b1b 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/ShapeFactory.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/ShapeFactory.java
@@ -71,7 +71,7 @@ public class ShapeFactory
         } else if (instance instanceof End) {
             return new EndShape();
         } else if (instance instanceof State) {
-            return new StateShape((State) instance, resourceContentService);
+            return StateShape.create((State) instance, resourceContentService);
         } else if (TransitionShape.isTransition(instance)) {
             return TransitionShape.create(instance).setAppearance(instance);
         }
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/CallbackStateShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/CallbackStateShape.java
new file mode 100644
index 0000000000..9f4f21b87f
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/CallbackStateShape.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import org.appformer.kogito.bridge.client.resource.ResourceContentService;
+import org.kie.workbench.common.stunner.core.client.shape.MutationContext;
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.CornerIcon;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.IconPath;
+import org.kie.workbench.common.stunner.sw.definition.CallbackState;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public class CallbackStateShape extends StateShape {
+
+    public static final String ICON_COLOR = "#EC7A08";
+    public static final String ICON_SVG = "M63.97,36.13c0-2.13-1.64-3.95-3.76-4.07-2.31-.13-4.23,1.7-4.23,3.99,0,6.61-5.37,11.98-11.98,11.98H24.03v-4.99c0-1.18-.7-2.25-1.78-2.74-1.08-.47-2.34-.28-3.23,.52l-9.98,8.98c-.63,.56-.99,1.37-.99,2.22s.36,1.66,.99,2.23l9.98,8.98c.56,.51,1.28,.77,2,.77,.41,0,.83-.09,1.22-.26,1.08-.48,1.78-1.55,1.78-2.74v-4.99h19.97c10.98,0,19.92-8.91,19.97-19.89ZM20,16.08h19.85l.11,4.99c0,1.18,.7,2.25,1.78,2.74,.4,.17,.81,.26,1.11,.26,.73,0,1.44-.27,2.01-.77l9.98- [...]
+
+    public CallbackStateShape(State state, ResourceContentService resourceContentService) {
+        super(state, resourceContentService);
+    }
+
+    @Override
+    public void applyProperties(Node<View<State>, Edge> element, MutationContext mutationContext) {
+        super.applyProperties(element, mutationContext);
+        CallbackState state = (CallbackState) element.getContent().getDefinition();
+        if (state.getTimeouts() != null) {
+            getView().addChild(new CornerIcon(IconPath.CLOCK,
+                                              "EventTimeout: " + state.getTimeouts().getEventTimeout() + "\r\n"
+                                                      + "StateExecTimeout: " + state.getTimeouts().getStateExecTimeout() + "\r\n"
+                                                      + "ActionExecTimeout: " + state.getTimeouts().getActionExecTimeout()));
+        }
+    }
+
+    @Override
+    public String getIconColor() {
+        return ICON_COLOR;
+    }
+
+    @Override
+    public String getIconSvg() {
+        return ICON_SVG;
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EndShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EndShape.java
index 50d854263f..503a4404a2 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EndShape.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EndShape.java
@@ -16,11 +16,10 @@
 
 package org.kie.workbench.common.stunner.sw.client.shapes;
 
-import org.kie.workbench.common.stunner.core.client.shape.impl.NodeShapeImpl;
-
-public class EndShape extends NodeShapeImpl {
+public class EndShape extends ServerlessWorkflowShape<EndShapeView> {
 
     public EndShape() {
         super(new EndShapeView().asAbstractShape());
+        getShape().getShapeView().setController(this);
     }
 }
\ No newline at end of file
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EndShapeView.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EndShapeView.java
index 61be9546d7..841eafce69 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EndShapeView.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EndShapeView.java
@@ -18,7 +18,7 @@ package org.kie.workbench.common.stunner.sw.client.shapes;
 
 import com.ait.lienzo.client.core.shape.MultiPath;
 
-public class EndShapeView extends ServerlessWorkflowBasicShape<EndShapeView> {
+public class EndShapeView extends ServerlessWorkflowShapeView<EndShapeView> {
 
     private final static int END_SHAPE_SIZE = 46;
 
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EventStateShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EventStateShape.java
new file mode 100644
index 0000000000..6a23a4ad98
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/EventStateShape.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import org.appformer.kogito.bridge.client.resource.ResourceContentService;
+import org.kie.workbench.common.stunner.core.client.shape.MutationContext;
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.CornerIcon;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.IconPath;
+import org.kie.workbench.common.stunner.sw.definition.EventState;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public class EventStateShape extends StateShape {
+
+    public static final String ICON_COLOR = "#F4C145";
+    public static final String ICON_SVG = "M 45.254 45.197 L 36.216 45.197 L 42.206 31.792 C 42.613 30.98 42.307 29.965 41.597 29.355 C 40.887 28.746 39.768 28.849 39.06 29.457 L 19.561 45.705 C 18.953 46.215 18.646 47.128 18.953 47.939 C 19.258 48.754 19.968 49.262 20.883 49.262 L 29.921 49.262 L 23.928 62.667 C 23.521 63.478 23.825 64.493 24.538 65.103 C 24.943 65.51 25.247 65.609 25.756 65.609 C 26.263 65.609 26.67 65.406 27.076 65.103 L 46.573 48.854 C 47.183 48.347 47.488 47.431 47. [...]
+
+    public EventStateShape(State state, ResourceContentService resourceContentService) {
+        super(state, resourceContentService);
+    }
+
+    @Override
+    public void applyProperties(Node<View<State>, Edge> element, MutationContext mutationContext) {
+        super.applyProperties(element, mutationContext);
+        EventState state = (EventState) element.getContent().getDefinition();
+        if (state.getTimeouts() != null) {
+            getView().addChild(new CornerIcon(IconPath.CLOCK,
+                                              "EventTimeout: " + state.getTimeouts().getEventTimeout() + "\r\n"
+                                                      + "StateExecTimeout: " + state.getTimeouts().getStateExecTimeout() + "\r\n"
+                                                      + "ActionExecTimeout: " + state.getTimeouts().getActionExecTimeout()));
+        }
+    }
+
+    @Override
+    public String getIconColor() {
+        return ICON_COLOR;
+    }
+
+    @Override
+    public String getIconSvg() {
+        return ICON_SVG;
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ForEachStateShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ForEachStateShape.java
new file mode 100644
index 0000000000..05b0f81d14
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ForEachStateShape.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import org.appformer.kogito.bridge.client.resource.ResourceContentService;
+import org.kie.workbench.common.stunner.core.client.shape.MutationContext;
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.CornerIcon;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.IconPath;
+import org.kie.workbench.common.stunner.sw.definition.ForEachState;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public class ForEachStateShape extends StateShape {
+
+    public static final String ICON_COLOR = "#8F4700";
+    public static final String ICON_SVG = "M64,32.22v27.24c0,2.34-1.91,4.49-4.24,4.65-2.68,.19-4.9-1.92-4.9-4.56v-27.43c0-2.64,2.22-4.75,4.9-4.56,2.34,.16,4.24,2.31,4.24,4.65M45.71,59.56c0,2.64-2.22,4.75-4.9,4.56-2.34-.16-4.24-2.31-4.24-4.65v-27.24c0-2.34,1.91-4.49,4.24-4.65,2.68-.19,4.9,1.92,4.9,4.56v27.43M27.43,4.79v27.24c0,2.34-1.91,4.49-4.24,4.65-2.68,.19-4.9-1.92-4.9-4.56V4.7C18.29,2.07,20.51-.04,23.18,.14c2.34,.16,4.24,2.31,4.24,4.65M9.14,32.13c0,2.64-2.22,4.75-4.9,4.56C1.91,36.53, [...]
+
+    public ForEachStateShape(State state, ResourceContentService resourceContentService) {
+        super(state, resourceContentService);
+    }
+
+    @Override
+    public void applyProperties(Node<View<State>, Edge> element, MutationContext mutationContext) {
+        super.applyProperties(element, mutationContext);
+        ForEachState state = (ForEachState) element.getContent().getDefinition();
+        if (state.getTimeouts() != null) {
+            getView().addChild(new CornerIcon(IconPath.CLOCK,
+                                              "StateExecTimeout: " + state.getTimeouts().getStateExecTimeout() + "\r\n"
+                                                      + "ActionExecTimeout: " + state.getTimeouts().getActionExecTimeout()));
+        }
+    }
+
+    @Override
+    public String getIconColor() {
+        return ICON_COLOR;
+    }
+
+    @Override
+    public String getIconSvg() {
+        return ICON_SVG;
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/InjectStateShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/InjectStateShape.java
new file mode 100644
index 0000000000..f4620915d9
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/InjectStateShape.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import org.appformer.kogito.bridge.client.resource.ResourceContentService;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public class InjectStateShape extends StateShape {
+
+    public static final String ICON_COLOR = "#8BC1F7";
+    public static final String ICON_SVG = "M35.02,28.49s-.03-.03-.05-.04l-10.29-10.29c-1.78-1.78-4.68-1.79-6.46,0-1.79,1.79-1.78,4.68,0,6.46l2.81,2.81H4.57c-2.52,0-4.57,2.04-4.57,4.57s2.05,4.57,4.57,4.57H20.94l-2.75,2.75c-1.78,1.78-1.79,4.68,0,6.46s4.68,1.78,6.46,0l10.09-10.09c.28-.2,.54-.44,.77-.71,.5-.58,.82-1.27,.97-2,.37-1.59-.11-3.33-1.46-4.5M.01,4.23C.19,1.83,2.25,0,4.66,0H59.43c2.53,0,4.57,2.05,4.57,4.57s-2.04,4.57-4.57,4.57H4.57C1.93,9.14-.18,6.91,.01,4.23M46.09,18.29h13.24c2.34, [...]
+
+    public InjectStateShape(State state, ResourceContentService resourceContentService) {
+        super(state, resourceContentService);
+    }
+
+    @Override
+    public String getIconColor() {
+        return ICON_COLOR;
+    }
+
+    @Override
+    public String getIconSvg() {
+        return ICON_SVG;
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/OperationStateShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/OperationStateShape.java
new file mode 100644
index 0000000000..59d1377091
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/OperationStateShape.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import org.appformer.kogito.bridge.client.resource.ResourceContentService;
+import org.kie.workbench.common.stunner.core.client.shape.MutationContext;
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.CornerIcon;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.IconPath;
+import org.kie.workbench.common.stunner.sw.definition.OperationState;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public class OperationStateShape extends StateShape {
+
+    public static final String ICON_COLOR = "#0066CC";
+    public static final String ICON_SVG = "M61.99,20.83c.41,1.07,.06,2.29-.79,3.07l-5.41,4.93c.14,1.04,.21,2.1,.21,3.17s-.08,2.14-.21,3.17l5.41,4.92c.85,.79,1.2,2,.79,3.08-.55,1.49-1.21,2.92-1.96,4.29l-.59,1.01c-.83,1.38-1.75,2.68-2.76,3.91-.75,.89-1.96,1.2-3.06,.85l-6.96-2.22c-1.68,1.29-3.64,2.36-5.5,3.19l-1.56,7.14c-.25,1.12-1.12,1.92-2.28,2.22-1.72,.29-3.5,.44-5.42,.44-1.7,0-3.48-.15-5.2-.44-1.15-.3-2.02-1.1-2.27-2.22l-1.56-7.14c-1.98-.83-3.83-1.9-5.5-3.19l-6.96,2.22c-1.1,.35-2.32,.04 [...]
+
+    public OperationStateShape(State state, ResourceContentService resourceContentService) {
+        super(state, resourceContentService);
+    }
+
+    @Override
+    public void applyProperties(Node<View<State>, Edge> element, MutationContext mutationContext) {
+        super.applyProperties(element, mutationContext);
+        OperationState state = (OperationState) element.getContent().getDefinition();
+        if (state.getTimeouts() != null) {
+            getView().addChild(new CornerIcon(IconPath.CLOCK,
+                                              "StateExecTimeout: " + state.getTimeouts().getStateExecTimeout() + "\r\n"
+                                                      + "ActionExecTimeout: " + state.getTimeouts().getActionExecTimeout()));
+        }
+    }
+
+    @Override
+    public String getIconColor() {
+        return ICON_COLOR;
+    }
+
+    @Override
+    public String getIconSvg() {
+        return ICON_SVG;
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ParallelStateShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ParallelStateShape.java
new file mode 100644
index 0000000000..c8f8448d4e
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ParallelStateShape.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import org.appformer.kogito.bridge.client.resource.ResourceContentService;
+import org.kie.workbench.common.stunner.core.client.shape.MutationContext;
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.CornerIcon;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.IconPath;
+import org.kie.workbench.common.stunner.sw.definition.ParallelState;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public class ParallelStateShape extends StateShape {
+
+    public static final String ICON_COLOR = "#4CB140";
+    public static final String ICON_SVG = "M50.54,.01c2.34,.16,4.24,2.31,4.24,4.65V59.43c0,2.64-2.23,4.75-4.91,4.56-2.33-.17-4.23-2.31-4.23-4.65V4.57c0-2.63,2.22-4.75,4.9-4.56M13.46,63.99c-2.34-.16-4.24-2.31-4.24-4.65V4.57c0-2.52,2.04-4.57,4.57-4.57s4.57,2.05,4.57,4.57V59.43c0,2.64-2.22,4.75-4.9,4.56Z";
+
+    public ParallelStateShape(State state, ResourceContentService resourceContentService) {
+        super(state, resourceContentService);
+    }
+
+    @Override
+    public void applyProperties(Node<View<State>, Edge> element, MutationContext mutationContext) {
+        super.applyProperties(element, mutationContext);
+        ParallelState state = (ParallelState) element.getContent().getDefinition();
+        if (state.getTimeouts() != null) {
+            getView().addChild(new CornerIcon(IconPath.CLOCK,
+                                              "BranchExecTimeout: " + state.getTimeouts().getBranchExecTimeout() + "\r\n"
+                                                      + "StateExecTimeout: " + state.getTimeouts().getStateExecTimeout()));
+        }
+    }
+
+    @Override
+    public String getIconColor() {
+        return ICON_COLOR;
+    }
+
+    @Override
+    public String getIconSvg() {
+        return ICON_SVG;
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ServerlessWorkflowShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ServerlessWorkflowShape.java
new file mode 100644
index 0000000000..acd6097e67
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ServerlessWorkflowShape.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import com.ait.lienzo.client.core.event.NodeMouseEnterHandler;
+import com.ait.lienzo.client.core.event.NodeMouseExitHandler;
+import org.kie.workbench.common.stunner.core.client.shape.ShapeState;
+import org.kie.workbench.common.stunner.core.client.shape.impl.AbstractShape;
+import org.kie.workbench.common.stunner.core.client.shape.impl.NodeShapeImpl;
+import org.kie.workbench.common.stunner.core.client.shape.impl.ShapeStateHandler;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public abstract class ServerlessWorkflowShape<V extends ServerlessWorkflowShapeView> extends NodeShapeImpl<State, V> {
+
+    public ServerlessWorkflowShape(final AbstractShape shape) {
+        super(shape);
+    }
+
+    public NodeMouseExitHandler getExitHandler() {
+        return event -> {
+            if (getShapeStateHandler().getShapeState() == ShapeState.SELECTED) {
+                return;
+            }
+            getShapeStateHandler().applyState(ShapeState.NONE);
+            getShapeView().getShape().getLayer().batch();
+        };
+    }
+
+    public NodeMouseEnterHandler getEnterHandler() {
+        return event -> {
+            if (getShapeStateHandler().getShapeState() == ShapeState.SELECTED) {
+                return;
+            }
+            getShapeStateHandler().applyState(ShapeState.HIGHLIGHT);
+            getShapeView().getShape().getLayer().batch();
+        };
+    }
+
+    protected ShapeStateHandler getShapeStateHandler() {
+        return getShapeView().getShapeStateHandler();
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ServerlessWorkflowBasicShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ServerlessWorkflowShapeView.java
similarity index 66%
rename from packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ServerlessWorkflowBasicShape.java
rename to packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ServerlessWorkflowShapeView.java
index e173674522..d76deba61f 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ServerlessWorkflowBasicShape.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/ServerlessWorkflowShapeView.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
 
 import com.ait.lienzo.client.core.event.NodeMouseEnterHandler;
@@ -12,12 +27,11 @@ import com.ait.lienzo.tools.client.event.HandlerRegistration;
 import org.kie.workbench.common.stunner.client.lienzo.shape.impl.ShapeStateDefaultHandler;
 import org.kie.workbench.common.stunner.client.lienzo.shape.view.ViewEventHandlerManager;
 import org.kie.workbench.common.stunner.client.lienzo.shape.view.wires.ext.WiresShapeViewExt;
-import org.kie.workbench.common.stunner.core.client.shape.ShapeState;
 import org.kie.workbench.common.stunner.core.client.shape.impl.AbstractShape;
 import org.kie.workbench.common.stunner.core.client.shape.impl.ShapeStateHandler;
 import org.kie.workbench.common.stunner.core.client.shape.view.event.ShapeViewSupportedEvents;
 
-public abstract class ServerlessWorkflowBasicShape<T extends ServerlessWorkflowBasicShape> extends WiresShapeViewExt<T> {
+public abstract class ServerlessWorkflowShapeView<VIEW extends ServerlessWorkflowShapeView<VIEW>> extends WiresShapeViewExt<VIEW> {
 
     protected final static String SHAPE_STROKE_COLOR = "#ccc";
     protected final static String SHAPE_FILL_COLOR = "#fff";
@@ -26,14 +40,16 @@ public abstract class ServerlessWorkflowBasicShape<T extends ServerlessWorkflowB
     protected final static double SHAPE_TITLE_FONT_SIZE = 12.00;
     protected final static String SHAPE_TITLE_FONT_FAMILY = "Open Sans";
 
+    private ServerlessWorkflowShape<VIEW> controller;
+
     private ShapeStateDefaultHandler shapeStateHandler = new ShapeStateDefaultHandler().setBorderShape((() -> this))
             .setBackgroundShape(() -> this);
 
-    private final HandlerRegistration mouseEnterHandler;
+    private HandlerRegistration mouseEnterHandler;
 
-    private final HandlerRegistration mouseExitHandler;
+    private HandlerRegistration mouseExitHandler;
 
-    public ServerlessWorkflowBasicShape(MultiPath path, String title) {
+    public ServerlessWorkflowShapeView(MultiPath path, String title) {
         this(path);
         setTitle(title);
         setTitlePosition(VerticalAlignment.MIDDLE, HorizontalAlignment.CENTER, ReferencePosition.INSIDE, Orientation.HORIZONTAL);
@@ -45,7 +61,7 @@ public abstract class ServerlessWorkflowBasicShape<T extends ServerlessWorkflowB
         isTitleListening(false);
     }
 
-    public ServerlessWorkflowBasicShape(MultiPath path) {
+    public ServerlessWorkflowShapeView(MultiPath path) {
         super(path
                       .setAlpha(1.00)
                       .setDraggable(false)
@@ -57,48 +73,46 @@ public abstract class ServerlessWorkflowBasicShape<T extends ServerlessWorkflowB
         setEventHandlerManager(new ViewEventHandlerManager(getShape(), getShape(), ShapeViewSupportedEvents.ALL_DESKTOP_EVENT_TYPES));
 
         getShape().setEventPropagationMode(EventPropagationMode.NO_ANCESTORS);
+    }
 
-        NodeMouseEnterHandler enterEvent = event -> {
-            if (shapeStateHandler.getShapeState() == ShapeState.SELECTED) {
-                return;
-            }
-            shapeStateHandler.applyState(ShapeState.HIGHLIGHT);
-        };
-        mouseEnterHandler = getShape().addNodeMouseEnterHandler(enterEvent);
+    public void setController(ServerlessWorkflowShape<VIEW> controller) {
+        this.controller = controller;
+        mouseEnterHandler = getShape().addNodeMouseEnterHandler(getEnterHandler());
+        mouseExitHandler = getShape().addNodeMouseExitHandler(getExitHandler());
+    }
 
-        NodeMouseExitHandler exitEvent = event -> {
-            if (shapeStateHandler.getShapeState() == ShapeState.SELECTED) {
-                return;
-            }
-            shapeStateHandler.applyState(ShapeState.NONE);
-        };
-        mouseExitHandler = getShape().addNodeMouseExitHandler(exitEvent);
+    public NodeMouseExitHandler getExitHandler() {
+        return controller.getExitHandler();
+    }
+
+    public NodeMouseEnterHandler getEnterHandler() {
+        return controller.getEnterHandler();
     }
 
     public ShapeStateHandler getShapeStateHandler() {
         return shapeStateHandler;
     }
 
-    public AbstractShape<ServerlessWorkflowBasicShape> asAbstractShape() {
-        return new AbstractShape<ServerlessWorkflowBasicShape>() {
+    public AbstractShape<ServerlessWorkflowShapeView<VIEW>> asAbstractShape() {
+        return new AbstractShape<ServerlessWorkflowShapeView<VIEW>>() {
             @Override
             public ShapeStateHandler getShapeStateHandler() {
-                return ServerlessWorkflowBasicShape.this.getShapeStateHandler();
+                return ServerlessWorkflowShapeView.this.getShapeStateHandler();
             }
 
             @Override
             public void setUUID(String uuid) {
-                ServerlessWorkflowBasicShape.this.setUUID(uuid);
+                ServerlessWorkflowShapeView.this.setUUID(uuid);
             }
 
             @Override
             public String getUUID() {
-                return ServerlessWorkflowBasicShape.this.getUUID();
+                return ServerlessWorkflowShapeView.this.getUUID();
             }
 
             @Override
-            public ServerlessWorkflowBasicShape getShapeView() {
-                return ServerlessWorkflowBasicShape.this;
+            public ServerlessWorkflowShapeView<VIEW> getShapeView() {
+                return ServerlessWorkflowShapeView.this;
             }
         };
     }
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/SleepStateShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/SleepStateShape.java
new file mode 100644
index 0000000000..d0d3bc45dd
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/SleepStateShape.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import org.appformer.kogito.bridge.client.resource.ResourceContentService;
+import org.kie.workbench.common.stunner.core.client.shape.MutationContext;
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.CornerIcon;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.IconPath;
+import org.kie.workbench.common.stunner.sw.definition.SleepState;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public class SleepStateShape extends StateShape {
+
+    public static final String ICON_COLOR = "#5752D1";
+    public static final String ICON_SVG = "M54.24,53.11c3.71-4.77,5.92-10.77,5.92-17.28,0-15.56-12.61-28.17-28.17-28.17S3.83,20.27,3.83,35.83c0,6.49,2.19,12.46,5.87,17.22l-2.53,2.53c-1.63,1.63-1.8,4.46-.28,6.2,1.74,2,4.76,2.07,6.59,.24l2.8-2.8c4.49,3.02,9.9,4.79,15.71,4.79s11.17-1.75,15.65-4.74l2.75,2.75c1.63,1.63,4.46,1.8,6.2,.28,2-1.74,2.07-4.76,.24-6.59l-2.59-2.59Zm-13.09-8.11l-10.59-7.06c-.74-.4-1.18-1.22-1.18-2.11l-.1-15c0-1.47,1.28-2.65,2.65-2.65,1.57,0,2.75,1.18,2.75,2.65v13.58l9. [...]
+
+    public SleepStateShape(State state, ResourceContentService resourceContentService) {
+        super(state, resourceContentService);
+    }
+
+    @Override
+    public void applyProperties(Node<View<State>, Edge> element, MutationContext mutationContext) {
+        super.applyProperties(element, mutationContext);
+        SleepState state = (SleepState) element.getContent().getDefinition();
+        getView().addChild(new CornerIcon(IconPath.CLOCK, "Duration: " + state.getDuration()));
+    }
+
+    @Override
+    public String getIconColor() {
+        return ICON_COLOR;
+    }
+
+    @Override
+    public String getIconSvg() {
+        return ICON_SVG;
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java
index 06bac41e46..61972f5a82 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShape.java
@@ -16,11 +16,10 @@
 
 package org.kie.workbench.common.stunner.sw.client.shapes;
 
-import org.kie.workbench.common.stunner.core.client.shape.impl.NodeShapeImpl;
-
-public class StartShape extends NodeShapeImpl {
+public class StartShape extends ServerlessWorkflowShape<StartShapeView> {
 
     public StartShape() {
         super(new StartShapeView().asAbstractShape());
+        getShape().getShapeView().setController(this);
     }
 }
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShapeView.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShapeView.java
index 7924057f95..0c12529a83 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShapeView.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StartShapeView.java
@@ -18,7 +18,7 @@ package org.kie.workbench.common.stunner.sw.client.shapes;
 
 import com.ait.lienzo.client.core.shape.MultiPath;
 
-public class StartShapeView extends ServerlessWorkflowBasicShape<StartShapeView> {
+public class StartShapeView extends ServerlessWorkflowShapeView<StartShapeView> {
 
     private final static double START_SHAPE_RADIUS = 25;
 
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShape.java
index 9a3efd283b..2caf80386e 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShape.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShape.java
@@ -16,35 +16,77 @@
 
 package org.kie.workbench.common.stunner.sw.client.shapes;
 
+import com.ait.lienzo.client.core.event.NodeMouseExitHandler;
 import com.ait.lienzo.client.core.shape.Picture;
 import com.ait.lienzo.client.core.types.Transform;
 import org.appformer.kogito.bridge.client.resource.ResourceContentService;
 import org.appformer.kogito.bridge.client.resource.interop.ResourceContentOptions;
 import org.kie.workbench.common.stunner.core.client.shape.HasShapeState;
-import org.kie.workbench.common.stunner.core.client.shape.impl.NodeShapeImpl;
+import org.kie.workbench.common.stunner.core.client.shape.MutationContext;
+import org.kie.workbench.common.stunner.core.client.shape.ShapeState;
+import org.kie.workbench.common.stunner.core.client.shape.impl.ShapeStateHandler;
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
 import org.kie.workbench.common.stunner.core.util.StringUtils;
 import org.kie.workbench.common.stunner.sw.definition.State;
 
-public class StateShape extends NodeShapeImpl implements HasShapeState {
+import static java.lang.Math.floor;
+import static java.lang.Math.min;
+import static java.lang.Math.pow;
+import static java.lang.Math.sqrt;
+import static org.kie.workbench.common.stunner.sw.client.shapes.StateShapeView.STATE_CORNER_RADIUS;
+
+public class StateShape extends ServerlessWorkflowShape<StateShapeView> implements HasShapeState {
 
-    public static final String INJECT_STATE_ICON = "M35.02,28.49s-.03-.03-.05-.04l-10.29-10.29c-1.78-1.78-4.68-1.79-6.46,0-1.79,1.79-1.78,4.68,0,6.46l2.81,2.81H4.57c-2.52,0-4.57,2.04-4.57,4.57s2.05,4.57,4.57,4.57H20.94l-2.75,2.75c-1.78,1.78-1.79,4.68,0,6.46s4.68,1.78,6.46,0l10.09-10.09c.28-.2,.54-.44,.77-.71,.5-.58,.82-1.27,.97-2,.37-1.59-.11-3.33-1.46-4.5M.01,4.23C.19,1.83,2.25,0,4.66,0H59.43c2.53,0,4.57,2.05,4.57,4.57s-2.04,4.57-4.57,4.57H4.57C1.93,9.14-.18,6.91,.01,4.23M46.09,18.29h13 [...]
-    public static final String SWITCH_STATE_ICON = "M60.6,46.54h-6.53v-8.71c-.06-1.38-.72-2.69-1.83-3.52l-7.42-5.56h0l-8.41-6.36V4.4C36.4,1.87,34.26-.17,31.69,.01c-2.25,.16-4.09,2.22-4.09,4.48V22.41l-8.41,6.31h0l-7.42,5.55c-1.11,.83-1.76,2.14-1.83,3.52v8.71H3.4c-2.94-.03-4.42,3.53-2.34,5.61l11.01,10.89c1.29,1.29,3.38,1.29,4.68,0l11.01-10.89c2.08-1.96,.61-5.52-2.34-5.47h-6.67v-6.63s8.22-6.08,13.28-9.87c4.96,3.73,13.2,9.92,13.24,9.9v6.63h-6.67c-2.94-.05-4.42,3.51-2.34,5.47l11.01,10.89c1.29 [...]
-    public static final String OPERATION_STATE_ICON = "M61.99,20.83c.41,1.07,.06,2.29-.79,3.07l-5.41,4.93c.14,1.04,.21,2.1,.21,3.17s-.08,2.14-.21,3.17l5.41,4.92c.85,.79,1.2,2,.79,3.08-.55,1.49-1.21,2.92-1.96,4.29l-.59,1.01c-.83,1.38-1.75,2.68-2.76,3.91-.75,.89-1.96,1.2-3.06,.85l-6.96-2.22c-1.68,1.29-3.64,2.36-5.5,3.19l-1.56,7.14c-.25,1.12-1.12,1.92-2.28,2.22-1.72,.29-3.5,.44-5.42,.44-1.7,0-3.48-.15-5.2-.44-1.15-.3-2.02-1.1-2.27-2.22l-1.56-7.14c-1.98-.83-3.83-1.9-5.5-3.19l-6.96,2.22c-1.1, [...]
-    public static final String EVENT_STATE_ICON = "M 45.254 45.197 L 36.216 45.197 L 42.206 31.792 C 42.613 30.98 42.307 29.965 41.597 29.355 C 40.887 28.746 39.768 28.849 39.06 29.457 L 19.561 45.705 C 18.953 46.215 18.646 47.128 18.953 47.939 C 19.258 48.754 19.968 49.262 20.883 49.262 L 29.921 49.262 L 23.928 62.667 C 23.521 63.478 23.825 64.493 24.538 65.103 C 24.943 65.51 25.247 65.609 25.756 65.609 C 26.263 65.609 26.67 65.406 27.076 65.103 L 46.573 48.854 C 47.183 48.347 47.488 47 [...]
-    public static final String CALLBACK_STATE_ICON = "M63.97,36.13c0-2.13-1.64-3.95-3.76-4.07-2.31-.13-4.23,1.7-4.23,3.99,0,6.61-5.37,11.98-11.98,11.98H24.03v-4.99c0-1.18-.7-2.25-1.78-2.74-1.08-.47-2.34-.28-3.23,.52l-9.98,8.98c-.63,.56-.99,1.37-.99,2.22s.36,1.66,.99,2.23l9.98,8.98c.56,.51,1.28,.77,2,.77,.41,0,.83-.09,1.22-.26,1.08-.48,1.78-1.55,1.78-2.74v-4.99h19.97c10.98,0,19.92-8.91,19.97-19.89ZM20,16.08h19.85l.11,4.99c0,1.18,.7,2.25,1.78,2.74,.4,.17,.81,.26,1.11,.26,.73,0,1.44-.27,2.0 [...]
-    public static final String FOR_EACH_STATE_ICON = "M64,32.22v27.24c0,2.34-1.91,4.49-4.24,4.65-2.68,.19-4.9-1.92-4.9-4.56v-27.43c0-2.64,2.22-4.75,4.9-4.56,2.34,.16,4.24,2.31,4.24,4.65M45.71,59.56c0,2.64-2.22,4.75-4.9,4.56-2.34-.16-4.24-2.31-4.24-4.65v-27.24c0-2.34,1.91-4.49,4.24-4.65,2.68-.19,4.9,1.92,4.9,4.56v27.43M27.43,4.79v27.24c0,2.34-1.91,4.49-4.24,4.65-2.68,.19-4.9-1.92-4.9-4.56V4.7C18.29,2.07,20.51-.04,23.18,.14c2.34,.16,4.24,2.31,4.24,4.65M9.14,32.13c0,2.64-2.22,4.75-4.9,4.56C [...]
-    public static final String PARALLEL_STATE_ICON = "M50.54,.01c2.34,.16,4.24,2.31,4.24,4.65V59.43c0,2.64-2.23,4.75-4.91,4.56-2.33-.17-4.23-2.31-4.23-4.65V4.57c0-2.63,2.22-4.75,4.9-4.56M13.46,63.99c-2.34-.16-4.24-2.31-4.24-4.65V4.57c0-2.52,2.04-4.57,4.57-4.57s4.57,2.05,4.57,4.57V59.43c0,2.64-2.22,4.75-4.9,4.56Z";
-    public static final String SLEEP_STATE_ICON = "M54.24,53.11c3.71-4.77,5.92-10.77,5.92-17.28,0-15.56-12.61-28.17-28.17-28.17S3.83,20.27,3.83,35.83c0,6.49,2.19,12.46,5.87,17.22l-2.53,2.53c-1.63,1.63-1.8,4.46-.28,6.2,1.74,2,4.76,2.07,6.59,.24l2.8-2.8c4.49,3.02,9.9,4.79,15.71,4.79s11.17-1.75,15.65-4.74l2.75,2.75c1.63,1.63,4.46,1.8,6.2,.28,2-1.74,2.07-4.76,.24-6.59l-2.59-2.59Zm-13.09-8.11l-10.59-7.06c-.74-.4-1.18-1.22-1.18-2.11l-.1-15c0-1.47,1.28-2.65,2.65-2.65,1.57,0,2.75,1.18,2.75,2.65v [...]
     public static final String ANSIBLE_STATE_ICON = "M 31.047 11.68 L 47.108 46.485 L 22.848 29.706 L 31.047 11.68 Z M 59.577 54.501 L 34.874 2.301 C 34.167 0.796 32.757 0 31.047 0 C 29.331 0 27.819 0.796 27.114 2.301 L 0 59.559 L 9.274 59.559 L 20.008 35.95 L 52.04 58.671 C 53.326 59.586 54.256 60 55.466 60 C 57.885 60 60 58.407 60 56.109 C 60 55.734 59.85 55.14 59.577 54.501 Z";
     public static final String KAOTO_STATE_ICON = "M 13.524 8.426 L 4.212 16.823 L 21.506 16.823 C 35.471 16.823 38.797 16.463 38.797 15.142 C 38.797 13.823 24.387 -0.091 23.056 0.03 C 22.833 0.03 18.512 3.868 13.524 8.426M 44.007 0.87 C 43.564 1.23 43.23 5.067 43.23 9.264 C 43.23 15.743 43.564 16.823 45.227 16.823 C 46.336 16.823 47.776 17.421 48.552 18.262 C 49.661 19.461 51.654 17.901 57.751 11.185 C 62.297 6.387 65.401 2.069 64.958 1.349 C 64.071 -0.091 45.227 -0.569 44.007 0.87M 54. [...]
 
     private final StateShapeView shapeView;
+    private final ResourceContentService resourceContentService;
+
+    protected StateShape(State state, ResourceContentService resourceContentService) {
+        this(state.getName(), resourceContentService);
+    }
+
+    protected StateShape(String name, ResourceContentService resourceContentService) {
+        super(new StateShapeView(name).asAbstractShape());
 
-    public StateShape(State state, ResourceContentService resourceContentService) {
-        this(state.getName());
+        this.resourceContentService = resourceContentService;
+        this.shapeView = getShape().getShapeView();
+        this.shapeView.setController(this);
+    }
+
+    public static StateShape create(State state, ResourceContentService resourceContentService) {
+        switch (state.getType()) {
+            case "inject":
+                return new InjectStateShape(state, resourceContentService);
+            case "switch":
+                return new SwitchStateShape(state, resourceContentService);
+            case "operation":
+                return new OperationStateShape(state, resourceContentService);
+            case "event":
+                return new EventStateShape(state, resourceContentService);
+            case "callback":
+                return new CallbackStateShape(state, resourceContentService);
+            case "foreach":
+                return new ForEachStateShape(state, resourceContentService);
+            case "parallel":
+                return new ParallelStateShape(state, resourceContentService);
+            case "sleep":
+                return new SleepStateShape(state, resourceContentService);
+            default:
+                // If type is missing create Inject By default.
+                return new InjectStateShape(state, resourceContentService);
+        }
+    }
 
+    @Override
+    public void applyProperties(Node<View<State>, Edge> element, MutationContext mutationContext) {
+        super.applyProperties(element, mutationContext);
+        State state = element.getContent().getDefinition();
         if (state.metadata == null) {
-            setType(state.getType());
+            shapeView.setSvgIcon(getIconColor(), getIconSvg());
             return;
         }
 
@@ -62,20 +104,14 @@ public class StateShape extends NodeShapeImpl implements HasShapeState {
         }
 
         if (StringUtils.nonEmpty(state.metadata.type)) {
-            setType(state.metadata.type);
+            setPredefinedIcon(state.metadata.type);
         }
 
         if (isIconEmpty()) {
-            setType(state.getType());
+            shapeView.setSvgIcon(getIconColor(), getIconSvg());
         }
     }
 
-    public StateShape(String name) {
-        super(new StateShapeView(name).asAbstractShape());
-
-        shapeView = (StateShapeView) getShape().getShapeView();
-    }
-
     private void loadIconFromFile(State state, ResourceContentService resourceContentService) {
         resourceContentService
                 .get(state.metadata.icon, ResourceContentOptions.binary())
@@ -105,32 +141,8 @@ public class StateShape extends NodeShapeImpl implements HasShapeState {
         return iconData;
     }
 
-    public StateShape setType(String type) {
+    public StateShape setPredefinedIcon(String type) {
         switch (type) {
-            case "inject":
-                shapeView.setSvgIcon("#8BC1F7", INJECT_STATE_ICON);
-                break;
-            case "switch":
-                shapeView.setSvgIcon("#009596", SWITCH_STATE_ICON);
-                break;
-            case "operation":
-                shapeView.setSvgIcon("#0066CC", OPERATION_STATE_ICON);
-                break;
-            case "event":
-                shapeView.setSvgIcon("#F4C145", EVENT_STATE_ICON);
-                break;
-            case "callback":
-                shapeView.setSvgIcon("#EC7A08", CALLBACK_STATE_ICON);
-                break;
-            case "foreach":
-                shapeView.setSvgIcon("#8F4700", FOR_EACH_STATE_ICON);
-                break;
-            case "parallel":
-                shapeView.setSvgIcon("#4CB140", PARALLEL_STATE_ICON);
-                break;
-            case "sleep":
-                shapeView.setSvgIcon("#5752D1", SLEEP_STATE_ICON);
-                break;
             case "ansible":
                 shapeView.setSvgIcon("#BB271A", ANSIBLE_STATE_ICON);
                 break;
@@ -142,6 +154,89 @@ public class StateShape extends NodeShapeImpl implements HasShapeState {
         return this;
     }
 
+    public String getIconColor() {
+        return "";
+    }
+
+    public String getIconSvg() {
+        return "";
+    }
+
+    public NodeMouseExitHandler getExitHandler() {
+        return event -> {
+            if (getShapeStateHandler().getShapeState() == ShapeState.SELECTED) {
+                return;
+            }
+
+            if (isInsideOfTheState(floor(event.getX() - event.getSource().getAbsoluteLocation().getX()),
+                                   floor(event.getY() - event.getSource().getAbsoluteLocation().getY()))) {
+                return;
+            }
+
+            getShapeStateHandler().applyState(ShapeState.NONE);
+            shapeView.batch();
+        };
+    }
+
+    protected ShapeStateHandler getShapeStateHandler() {
+        return getView().getShapeStateHandler();
+    }
+
+    protected boolean isInsideOfTheState(double x, double y) {
+        double width = getActualWidth();
+        double height = getActualHeight();
+
+        if (isOutsideOfTheBox(x, y, width, height)) {
+            return false;
+        }
+
+        return !isOutsideOfCorners(x, y, width, height);
+    }
+
+    public boolean isOutsideOfTheBox(double x, double y, double width, double height) {
+        return x <= getX()
+                || x >= getX() + width
+                || y <= getY()
+                || y >= getY() + height;
+    }
+
+    public static boolean isOutsideOfCorners(double x, double y, double width, double height) {
+        double checkX = width - x < STATE_CORNER_RADIUS ? width - x : x; // Normalize location to the top left corner
+        double checkY = height - y < STATE_CORNER_RADIUS ? height - y : y; // Normalize location to the top left corner
+        return isOutsideOfCorner(checkX, checkY);
+    }
+
+    public static boolean isOutsideOfCorner(double xCursorPosition, double yCursorPosition) {
+        // If cursor position is more than corner radius it means position is not at the corner but over the icon
+        if (STATE_CORNER_RADIUS < xCursorPosition
+                || STATE_CORNER_RADIUS < yCursorPosition) {
+            return false;
+        }
+        return sqrt(pow(STATE_CORNER_RADIUS - xCursorPosition, 2)
+                            + pow(STATE_CORNER_RADIUS - yCursorPosition, 2))
+                >= STATE_CORNER_RADIUS;
+    }
+
+    protected double getActualWidth() {
+        return shapeView.getShape().getBoundingBox().getWidth()
+                * shapeView.getShape().asNode().getAbsoluteTransform().getScaleX()
+                - 2; // to compensate border
+    }
+
+    protected double getActualHeight() {
+        return shapeView.getShape().getBoundingBox().getHeight()
+                * shapeView.getShape().asNode().getAbsoluteTransform().getScaleY()
+                - 1; // to compensate border
+    }
+
+    protected double getX() {
+        return shapeView.getShape().getX();
+    }
+
+    protected double getY() {
+        return shapeView.getShape().getY();
+    }
+
     public boolean isIconEmpty() {
         return shapeView.isIconEmpty();
     }
@@ -172,7 +267,6 @@ public class StateShape extends NodeShapeImpl implements HasShapeState {
      */
     public static double calculateIconScale(int width, int height) {
         double size = StateShapeView.STATE_SHAPE_ICON_RADIUS * 2;
-        int min = Math.min(width, height);
-        return size / min;
+        return size / min(width, height);
     }
 }
\ No newline at end of file
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShapeView.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShapeView.java
index 63463ae68f..6449f263d6 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShapeView.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShapeView.java
@@ -29,19 +29,19 @@ import com.ait.lienzo.shared.core.types.EventPropagationMode;
 import com.ait.lienzo.shared.core.types.TextAlign;
 import com.ait.lienzo.shared.core.types.TextBaseLine;
 
-public class StateShapeView extends ServerlessWorkflowBasicShape<StateShapeView> {
+public class StateShapeView extends ServerlessWorkflowShapeView<StateShapeView> {
 
     private Group iconImage;
     private Circle backgroundCircle;
-
-    private final static double STATE_SHAPE_WIDTH = 250;
-    private final static double STATE_SHAPE_HEIGHT = 90;
+    protected final static double STATE_SHAPE_WIDTH = 250;
+    protected final static double STATE_SHAPE_HEIGHT = 90;
     public final static double STATE_SHAPE_ICON_RADIUS = 25;
+    public final static double STATE_CORNER_RADIUS = 5.00;
 
     public StateShapeView(String name) {
         super(new MultiPath()
                       .rect(0, 0, STATE_SHAPE_WIDTH, STATE_SHAPE_HEIGHT)
-                      .setCornerRadius(5.00));
+                      .setCornerRadius(STATE_CORNER_RADIUS));
         initialize(name);
     }
 
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/SwitchStateShape.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/SwitchStateShape.java
new file mode 100644
index 0000000000..c9114e5707
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/SwitchStateShape.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import org.appformer.kogito.bridge.client.resource.ResourceContentService;
+import org.kie.workbench.common.stunner.core.client.shape.MutationContext;
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.CornerIcon;
+import org.kie.workbench.common.stunner.sw.client.shapes.icons.IconPath;
+import org.kie.workbench.common.stunner.sw.definition.State;
+import org.kie.workbench.common.stunner.sw.definition.SwitchState;
+
+public class SwitchStateShape extends StateShape {
+
+    public static final String ICON_COLOR = "#009596";
+    public static final String ICON_SVG = "M60.6,46.54h-6.53v-8.71c-.06-1.38-.72-2.69-1.83-3.52l-7.42-5.56h0l-8.41-6.36V4.4C36.4,1.87,34.26-.17,31.69,.01c-2.25,.16-4.09,2.22-4.09,4.48V22.41l-8.41,6.31h0l-7.42,5.55c-1.11,.83-1.76,2.14-1.83,3.52v8.71H3.4c-2.94-.03-4.42,3.53-2.34,5.61l11.01,10.89c1.29,1.29,3.38,1.29,4.68,0l11.01-10.89c2.08-1.96,.61-5.52-2.34-5.47h-6.67v-6.63s8.22-6.08,13.28-9.87c4.96,3.73,13.2,9.92,13.24,9.9v6.63h-6.67c-2.94-.05-4.42,3.51-2.34,5.47l11.01,10.89c1.29,1.29,3.3 [...]
+
+    public SwitchStateShape(State state, ResourceContentService resourceContentService) {
+        super(state, resourceContentService);
+    }
+
+    @Override
+    public void applyProperties(Node<View<State>, Edge> element, MutationContext mutationContext) {
+        super.applyProperties(element, mutationContext);
+        SwitchState state = (SwitchState) element.getContent().getDefinition();
+        if (state.getTimeouts() != null) {
+            getView().addChild(new CornerIcon(IconPath.CLOCK,
+                                              "EventTimeout: " + state.getTimeouts().getEventTimeout() + "\r\n"
+                                                      + "StateExecTimeout: " + state.getTimeouts().getStateExecTimeout()));
+        }
+    }
+
+    @Override
+    public String getIconColor() {
+        return ICON_COLOR;
+    }
+
+    @Override
+    public String getIconSvg() {
+        return ICON_SVG;
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/TransitionView.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/TransitionView.java
index 526163ad27..ddc7f103d6 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/TransitionView.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/TransitionView.java
@@ -28,7 +28,7 @@ public class TransitionView extends WiresConnectorViewExt<TransitionView> {
     private static final double SELECTION_OFFSET = 30;
     private static final double DECORATOR_WIDTH = 8;
     private static final double DECORATOR_HEIGHT = 12;
-
+    private static final double OVERLAP_CORRECTION = 3;
     private static final double STROKE_WIDTH = 2;
 
     private static final double[] DEFAULT_POLYLINE_POINTS = {0, 0, 100, 100};
@@ -71,11 +71,11 @@ public class TransitionView extends WiresConnectorViewExt<TransitionView> {
     private static MultiPath getArrowMultiPath() {
         return new MultiPath()
                 .M(DECORATOR_WIDTH,
-                   DECORATOR_HEIGHT)
+                   DECORATOR_HEIGHT + OVERLAP_CORRECTION)
                 .L(0,
-                   DECORATOR_HEIGHT)
+                   DECORATOR_HEIGHT + OVERLAP_CORRECTION)
                 .L(DECORATOR_WIDTH / 2,
-                   0)
+                   OVERLAP_CORRECTION)
                 .Z();
     }
 }
\ No newline at end of file
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/icons/CornerIcon.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/icons/CornerIcon.java
new file mode 100644
index 0000000000..d46281c066
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/icons/CornerIcon.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes.icons;
+
+import com.ait.lienzo.client.core.shape.Group;
+import com.ait.lienzo.client.core.shape.MultiPath;
+import com.ait.lienzo.client.core.shape.Rectangle;
+import com.ait.lienzo.client.core.shape.toolbox.items.tooltip.PrimitiveTextTooltip;
+import com.ait.lienzo.shared.core.types.EventPropagationMode;
+import com.ait.lienzo.tools.client.event.HandlerRegistration;
+
+public class CornerIcon extends Group {
+
+    private final HandlerRegistration mouseEnterHandler;
+    private final HandlerRegistration mouseExitHandler;
+    private final String tooltipText;
+
+    private final Rectangle border = new Rectangle(20, 20)
+            .setFillColor("white")
+            .setFillAlpha(0.001)
+            .setStrokeAlpha(0.001)
+            .setStrokeColor("white")
+            .setCornerRadius(9)
+            .setEventPropagationMode(EventPropagationMode.NO_ANCESTORS)
+            .setListening(true);
+
+    public CornerIcon(String icon, String tooltip) {
+        setX(224);
+        setY(6);
+        setListening(true);
+        add(border);
+        this.tooltipText = tooltip;
+
+        PrimitiveTextTooltip tooltipElement = PrimitiveTextTooltip.Builder.build(tooltipText);
+        tooltipElement.withText(t -> {
+            t.setText(tooltipText);
+            t.setFontSize(12);
+        });
+        add(tooltipElement.asPrimitive());
+        tooltipElement.forComputedBoundingBox(border::getBoundingBox);
+
+        MultiPath clockIcon = new MultiPath(icon)
+                .setScale(2)
+                .setStrokeWidth(0)
+                .setFillColor("#CCC")
+                .setListening(false);
+        add(clockIcon);
+
+        mouseEnterHandler = border.addNodeMouseEnterHandler(event -> {
+            this.getParent().moveToTop();
+            tooltipElement.show();
+            clockIcon.setFillColor("#4F5255");
+        });
+        mouseExitHandler = border.addNodeMouseExitHandler(event -> {
+            tooltipElement.hide();
+            clockIcon.setFillColor("#CCC");
+            border.getLayer().batch();
+        });
+    }
+
+    @Override
+    public void destroy() {
+        super.destroy();
+        mouseEnterHandler.removeHandler();
+        mouseExitHandler.removeHandler();
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/icons/IconPath.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/icons/IconPath.java
new file mode 100644
index 0000000000..7a5ad766a0
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/main/java/org/kie/workbench/common/stunner/sw/client/shapes/icons/IconPath.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes.icons;
+
+public interface IconPath {
+
+    String CLOCK = "M 4.531 2.344 C 4.531 2.084 4.74 1.875 5 1.875 C 5.26 1.875 5.469 2.084 5.469 2.344 L 5.469 4.75 L 7.135 5.859 C 7.35 6.004 7.408 6.295 7.248 6.51 C 7.121 6.725 6.83 6.783 6.615 6.623 L 4.74 5.373 C 4.609 5.303 4.531 5.156 4.531 4.982 L 4.531 2.344 Z M 5 0 C 7.762 0 10 2.238 10 5 C 10 7.762 7.762 10 5 10 C 2.238 10 0 7.762 0 5 C 0 2.238 2.238 0 5 0 Z M 0.938 5 C 0.938 7.244 2.756 9.063 5 9.063 C 7.244 9.063 9.063 7.244 9.063 5 C 9.063 2.756 7.244 0.938 5 0.938 C 2.756 [...]
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateNode.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateNode.java
new file mode 100644
index 0000000000..5f20c32918
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateNode.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import java.util.List;
+import java.util.Set;
+
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public class StateNode implements Node<View<State>, Edge> {
+
+    private View<State> view;
+
+    public StateNode(View<State> view) {
+        this.view = view;
+    }
+
+    @Override
+    public List<Edge> getInEdges() {
+        return null;
+    }
+
+    @Override
+    public List<Edge> getOutEdges() {
+        return null;
+    }
+
+    @Override
+    public String getUUID() {
+        return null;
+    }
+
+    @Override
+    public Set<String> getLabels() {
+        return null;
+    }
+
+    @Override
+    public View<State> getContent() {
+        return view;
+    }
+
+    @Override
+    public void setContent(View<State> content) {
+        this.view = content;
+    }
+
+    @Override
+    public Node<View<State>, Edge> asNode() {
+        return null;
+    }
+
+    @Override
+    public Edge<View<State>, Node> asEdge() {
+        return null;
+    }
+}
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShapeTest.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShapeTest.java
index cd99e8da50..032145ca08 100644
--- a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShapeTest.java
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateShapeTest.java
@@ -13,22 +13,40 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.kie.workbench.common.stunner.sw.client.shapes;
 
+import com.ait.lienzo.client.core.event.NodeMouseExitEvent;
 import com.ait.lienzo.test.LienzoMockitoTestRunner;
 import elemental2.promise.Promise;
 import org.appformer.kogito.bridge.client.resource.ResourceContentService;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.kie.workbench.common.stunner.core.client.shape.ShapeState;
+import org.kie.workbench.common.stunner.core.client.shape.impl.ShapeStateHandler;
+import org.kie.workbench.common.stunner.core.graph.Edge;
+import org.kie.workbench.common.stunner.core.graph.Node;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.definition.CallbackState;
+import org.kie.workbench.common.stunner.sw.definition.EventState;
+import org.kie.workbench.common.stunner.sw.definition.ForEachState;
+import org.kie.workbench.common.stunner.sw.definition.InjectState;
 import org.kie.workbench.common.stunner.sw.definition.Metadata;
+import org.kie.workbench.common.stunner.sw.definition.OperationState;
+import org.kie.workbench.common.stunner.sw.definition.ParallelState;
+import org.kie.workbench.common.stunner.sw.definition.SleepState;
 import org.kie.workbench.common.stunner.sw.definition.State;
+import org.kie.workbench.common.stunner.sw.definition.SwitchState;
 import org.mockito.Mock;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.kie.workbench.common.stunner.sw.client.shapes.StateShapeView.STATE_SHAPE_HEIGHT;
+import static org.kie.workbench.common.stunner.sw.client.shapes.StateShapeView.STATE_SHAPE_WIDTH;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -60,7 +78,8 @@ public class StateShapeTest {
     private final String SLEEP_COLOR = "#5752D1";
     private final String ANSIBLE_COLOR = "#BB271A";
     private final String KAOTO_COLOR = "#332174";
-    private final String DEFAULT_EMPTY_COLOR = "#FFF";
+    private final int SHAPE_X = 50;
+    private final int SHAPE_Y = 50;
 
     @Mock
     ResourceContentService kogitoService;
@@ -115,35 +134,9 @@ public class StateShapeTest {
         customTypeStateIconTest(ANSIBLE, ANSIBLE, ANSIBLE_COLOR);
     }
 
-    public void simpleStateIconTest(String type, String color) {
-        State state = createState(type);
-
-        StateShape shape = new StateShape(state, kogitoService);
-        assertFalse(shape.getView().isIconEmpty());
-        assertEquals(color, shape.getView().getIconBackgroundColor());
-    }
-
-    private State createState(String type) {
-        State state = new State();
-        state.setName(NAME);
-        state.setType(type);
-        return state;
-    }
-
-    public void customTypeStateIconTest(String defaultType, String customType, String color) {
-        State state = createState(defaultType);
-        Metadata metadata = new Metadata();
-        metadata.setType(customType);
-        state.setMetadata(metadata);
-
-        StateShape shape = new StateShape(state, kogitoService);
-        assertFalse(shape.getView().isIconEmpty());
-        assertEquals(color, shape.getView().getIconBackgroundColor());
-    }
-
     @Test
     public void setNullPictureTest() {
-        StateShape shape = spy(new StateShape(createState(INJECT), kogitoService));
+        StateShape shape = spy(StateShape.create(createState(INJECT), kogitoService));
         shape.setIconPicture(null, "icon.png");
 
         verify(shape, never()).setIconPicture(any());
@@ -151,7 +144,7 @@ public class StateShapeTest {
 
     @Test
     public void setEmptyPictureTest() {
-        StateShape shape = spy(new StateShape(createState(INJECT), kogitoService));
+        StateShape shape = spy(StateShape.create(createState(INJECT), kogitoService));
         shape.setIconPicture("", "icon.png");
 
         verify(shape, never()).setIconPicture(any());
@@ -159,7 +152,7 @@ public class StateShapeTest {
 
     @Test
     public void setValidPictureTest() {
-        StateShape shape = spy(new StateShape(createState(INJECT), kogitoService));
+        StateShape shape = spy(StateShape.create(createState(INJECT), kogitoService));
         shape.setIconPicture("base64string", "icon.png");
 
         verify(shape, times(1)).setIconPicture(any());
@@ -172,7 +165,11 @@ public class StateShapeTest {
         metadata.setIcon("data://png..lalala");
         state.setMetadata(metadata);
 
-        StateShape shape = new StateShape(state, kogitoService);
+        StateShape shape = StateShape.create(state, kogitoService);
+        assertTrue(shape.getView().isIconEmpty());
+        assertNull(shape.getView().getIconBackgroundColor());
+
+        shape.applyProperties(createElement(state), null);
         assertFalse(shape.getView().isIconEmpty());
         assertEquals("#FFF", shape.getView().getIconBackgroundColor());
     }
@@ -187,7 +184,11 @@ public class StateShapeTest {
         when(kogitoService.get(eq("png..lalala"), any())).thenReturn(new Promise<>((resolve, reject) -> {
         }));
 
-        StateShape shape = new StateShape(state, kogitoService);
+        StateShape shape = StateShape.create(state, kogitoService);
+        assertTrue(shape.getView().isIconEmpty());
+        assertNull(shape.getView().getIconBackgroundColor());
+
+        shape.applyProperties(createElement(state), null);
         assertFalse(shape.getView().isIconEmpty());
         assertEquals(INJECT_COLOR, shape.getView().getIconBackgroundColor());
     }
@@ -195,33 +196,33 @@ public class StateShapeTest {
     @Test
     public void scaleLargeWidthSmallHeightTest() {
         assertEquals(2.0,
-                            StateShape.calculateIconScale((int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 4,
-                                                          (int) StateShapeView.STATE_SHAPE_ICON_RADIUS),
-                            0.0);
+                     StateShape.calculateIconScale((int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 4,
+                                                   (int) StateShapeView.STATE_SHAPE_ICON_RADIUS),
+                     0.0);
     }
 
     @Test
     public void scaleLargeHeightSmallWidthTest() {
         assertEquals(2.0,
-                            StateShape.calculateIconScale((int) StateShapeView.STATE_SHAPE_ICON_RADIUS,
-                                                          (int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 4),
-                            0.0);
+                     StateShape.calculateIconScale((int) StateShapeView.STATE_SHAPE_ICON_RADIUS,
+                                                   (int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 4),
+                     0.0);
     }
 
     @Test
     public void scaleLargeWidthLargerHeightTest() {
         assertEquals(0.5,
-                            StateShape.calculateIconScale((int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 4,
-                                                          (int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 8),
-                            0.0);
+                     StateShape.calculateIconScale((int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 4,
+                                                   (int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 8),
+                     0.0);
     }
 
     @Test
     public void scaleLargeHeightLargerWidthTest() {
         assertEquals(0.5,
-                            StateShape.calculateIconScale((int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 8,
-                                                          (int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 4),
-                            0.0);
+                     StateShape.calculateIconScale((int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 8,
+                                                   (int) StateShapeView.STATE_SHAPE_ICON_RADIUS * 4),
+                     0.0);
     }
 
     @Test
@@ -238,4 +239,210 @@ public class StateShapeTest {
     public void base64StringFromIncorrectPathGenerationTest() {
         assertEquals(IMAGE_DATA, StateShape.iconDataUri("imagejpeg", IMAGE_DATA));
     }
+
+    @Test
+    public void leftTopCornerOutsideTest() {
+        assertTrue(isCoordinateOutsideTest(0.5, 0.5));
+    }
+
+    @Test
+    public void leftTopCornerInsideTest() {
+        assertFalse(isCoordinateOutsideTest(6, 6));
+    }
+
+    @Test
+    public void rightTopCornerOutsideTest() {
+        assertTrue(isCoordinateOutsideTest(STATE_SHAPE_WIDTH - 0.5, 0.5));
+    }
+
+    @Test
+    public void rightTopCornerInsideTest() {
+        assertFalse(isCoordinateOutsideTest(STATE_SHAPE_WIDTH - 6, 6));
+    }
+
+    @Test
+    public void leftBottomCornerOutsideTest() {
+        assertTrue(isCoordinateOutsideTest(0.5, STATE_SHAPE_HEIGHT - 0.5));
+    }
+
+    @Test
+    public void leftBottomCornerInsideTest() {
+        assertFalse(isCoordinateOutsideTest(6, STATE_SHAPE_HEIGHT - 6));
+    }
+
+    @Test
+    public void rightBottomCornerOutsideTest() {
+        assertTrue(isCoordinateOutsideTest(STATE_SHAPE_WIDTH - 0.5, STATE_SHAPE_HEIGHT - 0.5));
+    }
+
+    @Test
+    public void rightBottomCornerInsideTest() {
+        assertFalse(isCoordinateOutsideTest(STATE_SHAPE_WIDTH - 6, STATE_SHAPE_HEIGHT - 6));
+    }
+
+    @Test
+    public void exitFromSelectedShapeTest() {
+        ShapeStateHandler stateHandler = mock(ShapeStateHandler.class);
+
+        StateShape shape = prepareShapeForExitTests(stateHandler, ShapeState.SELECTED);
+        NodeMouseExitEvent exitEvent = prepareMouseExitEvent(49, 49);
+
+        shape.getExitHandler().onNodeMouseExit(exitEvent);
+
+        verify(stateHandler, never()).applyState(any());
+    }
+
+    @Test
+    public void exitFromTheShapeToTopTest() {
+        ShapeStateHandler stateHandler = mock(ShapeStateHandler.class);
+
+        StateShape shape = prepareShapeForExitTests(stateHandler, ShapeState.HIGHLIGHT);
+        NodeMouseExitEvent exitEvent = prepareMouseExitEvent(55, 49);
+
+        shape.getExitHandler().onNodeMouseExit(exitEvent);
+
+        verify(stateHandler, times(1)).applyState(ShapeState.NONE);
+    }
+
+    @Test
+    public void exitFromTheLeftShapeTest() {
+        ShapeStateHandler stateHandler = mock(ShapeStateHandler.class);
+
+        StateShape shape = prepareShapeForExitTests(stateHandler, ShapeState.HIGHLIGHT);
+        NodeMouseExitEvent exitEvent = prepareMouseExitEvent(SHAPE_X - 5, SHAPE_Y + 5);
+
+        shape.getExitHandler().onNodeMouseExit(exitEvent);
+
+        verify(stateHandler, times(1)).applyState(ShapeState.NONE);
+    }
+
+    @Test
+    public void exitFromTheRightShapeTest() {
+        ShapeStateHandler stateHandler = mock(ShapeStateHandler.class);
+
+        StateShape shape = prepareShapeForExitTests(stateHandler, ShapeState.HIGHLIGHT);
+        NodeMouseExitEvent exitEvent = prepareMouseExitEvent((int) STATE_SHAPE_WIDTH + SHAPE_X + 5, SHAPE_Y + 5);
+
+        shape.getExitHandler().onNodeMouseExit(exitEvent);
+
+        verify(stateHandler, times(1)).applyState(ShapeState.NONE);
+    }
+
+    @Test
+    public void exitFromTheBottomShapeTest() {
+        ShapeStateHandler stateHandler = mock(ShapeStateHandler.class);
+
+        StateShape shape = prepareShapeForExitTests(stateHandler, ShapeState.HIGHLIGHT);
+        NodeMouseExitEvent exitEvent = prepareMouseExitEvent(SHAPE_X + 5, (int) STATE_SHAPE_HEIGHT + SHAPE_Y + 5);
+
+        shape.getExitHandler().onNodeMouseExit(exitEvent);
+
+        verify(stateHandler, times(1)).applyState(ShapeState.NONE);
+    }
+
+    @Test
+    public void raisedExitInsideOfTheShapeTest() {
+        ShapeStateHandler stateHandler = mock(ShapeStateHandler.class);
+
+        StateShape shape = prepareShapeForExitTests(stateHandler, ShapeState.HIGHLIGHT);
+        NodeMouseExitEvent exitEvent = prepareMouseExitEvent(SHAPE_X + 5, SHAPE_Y + 5);
+
+        shape.getExitHandler().onNodeMouseExit(exitEvent);
+
+        verify(stateHandler, never()).applyState(ShapeState.NONE);
+    }
+
+    @SuppressWarnings("rawtypes")
+    private NodeMouseExitEvent prepareMouseExitEvent(int mouseX, int mouseY) {
+        NodeMouseExitEvent exitEvent = mock(NodeMouseExitEvent.class);
+        when(exitEvent.getX()).thenReturn(mouseX);
+        when(exitEvent.getY()).thenReturn(mouseY);
+        com.ait.lienzo.client.core.shape.Node node = mock(com.ait.lienzo.client.core.shape.Node.class);
+        when(exitEvent.getSource()).thenReturn(node);
+        com.ait.lienzo.client.core.types.Point2D point = new com.ait.lienzo.client.core.types.Point2D(SHAPE_X, SHAPE_Y);
+        when(node.getAbsoluteLocation()).thenReturn(point);
+        return exitEvent;
+    }
+
+    private StateShape prepareShapeForExitTests(ShapeStateHandler stateHandler, ShapeState currentState) {
+        State state = createState(INJECT);
+        StateShape shape = spy(StateShape.create(state, kogitoService));
+        shape.applyProperties(createElement(state), null);
+
+        when(stateHandler.getShapeState()).thenReturn(currentState);
+        when(shape.getShapeStateHandler()).thenReturn(stateHandler);
+
+        return shape;
+    }
+
+    private void simpleStateIconTest(String type, String color) {
+        State state = createState(type);
+
+        StateShape shape = StateShape.create(state, kogitoService);
+        assertTrue(shape.getView().isIconEmpty());
+        assertNull(shape.getView().getIconBackgroundColor());
+
+        shape.applyProperties(createElement(state), null);
+        assertFalse(shape.getView().isIconEmpty());
+        assertEquals(color, shape.getView().getIconBackgroundColor());
+    }
+
+    private State createState(String type) {
+        State state;
+        switch (type) {
+            case "inject":
+                state = new InjectState();
+                break;
+            case "switch":
+                state = new SwitchState();
+                break;
+            case "operation":
+                state = new OperationState();
+                break;
+            case "event":
+                state = new EventState();
+                break;
+            case "callback":
+                state = new CallbackState();
+                break;
+            case "foreach":
+                state = new ForEachState();
+                break;
+            case "parallel":
+                state = new ParallelState();
+                break;
+            case "sleep":
+                state = new SleepState();
+                break;
+            default:
+                state = new State();
+        }
+        state.setName(NAME);
+        state.setType(type);
+        return state;
+    }
+
+    private void customTypeStateIconTest(String defaultType, String customType, String color) {
+        State state = createState(defaultType);
+        Metadata metadata = new Metadata();
+        metadata.setType(customType);
+        state.setMetadata(metadata);
+
+        StateShape shape = StateShape.create(state, kogitoService);
+        assertTrue(shape.getView().isIconEmpty());
+        assertNull(shape.getView().getIconBackgroundColor());
+
+        shape.applyProperties(createElement(state), null);
+        assertFalse(shape.getView().isIconEmpty());
+        assertEquals(color, shape.getView().getIconBackgroundColor());
+    }
+
+    private Node<View<State>, Edge> createElement(State state) {
+        StateView view = new StateView(state);
+        return new StateNode(view);
+    }
+
+    private boolean isCoordinateOutsideTest(double x, double y) {
+        return StateShape.isOutsideOfCorners(x, y, STATE_SHAPE_WIDTH, STATE_SHAPE_HEIGHT);
+    }
 }
diff --git a/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateView.java b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateView.java
new file mode 100644
index 0000000000..d170bb52d8
--- /dev/null
+++ b/packages/serverless-workflow-diagram-editor/sw-editor/sw-editor-client/src/test/java/org/kie/workbench/common/stunner/sw/client/shapes/StateView.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.workbench.common.stunner.sw.client.shapes;
+
+import org.kie.workbench.common.stunner.core.graph.content.Bounds;
+import org.kie.workbench.common.stunner.core.graph.content.view.View;
+import org.kie.workbench.common.stunner.sw.definition.State;
+
+public class StateView implements View<State> {
+
+    private State state;
+
+    public StateView(State state) {
+        this.state = state;
+    }
+
+    @Override
+    public Bounds getBounds() {
+        return null;
+    }
+
+    @Override
+    public void setBounds(Bounds bounds) {
+
+    }
+
+    @Override
+    public State getDefinition() {
+        return state;
+    }
+
+    @Override
+    public void setDefinition(State definition) {
+        this.state = definition;
+    }
+}
diff --git a/packages/stunner-editors/kie-wb-common-dmn/kie-dmn-feel-gwt/src/main/java/org/kie/dmn/feel/super/org/kie/dmn/feel/lang/ast/visitor/ASTHeuristicCheckerVisitor.java b/packages/stunner-editors/kie-wb-common-dmn/kie-dmn-feel-gwt/src/main/java/org/kie/dmn/feel/super/org/kie/dmn/feel/lang/ast/visitor/ASTHeuristicCheckerVisitor.java
new file mode 100644
index 0000000000..969c131a6c
--- /dev/null
+++ b/packages/stunner-editors/kie-wb-common-dmn/kie-dmn-feel-gwt/src/main/java/org/kie/dmn/feel/super/org/kie/dmn/feel/lang/ast/visitor/ASTHeuristicCheckerVisitor.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.kie.dmn.feel.lang.ast.visitor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.kie.dmn.api.feel.runtime.events.FEELEvent;
+import org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity;
+import org.kie.dmn.feel.lang.ast.ASTNode;
+import org.kie.dmn.feel.lang.ast.InfixOpNode;
+import org.kie.dmn.feel.lang.ast.NullNode;
+import org.kie.dmn.feel.lang.ast.RangeNode;
+import org.kie.dmn.feel.lang.ast.UnaryTestNode;
+import org.kie.dmn.feel.runtime.events.ASTHeuristicCheckEvent;
+import org.kie.dmn.feel.util.Msg;
+
+public class ASTHeuristicCheckerVisitor extends DefaultedVisitor<List<FEELEvent>> {
+
+    @Override
+    public List<FEELEvent> defaultVisit(ASTNode n) {
+        List<FEELEvent> result = new ArrayList<>();
+        for (ASTNode node : n.getChildrenNode()) {
+            if (node != null) {
+                result.addAll(node.accept(this));
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public List<FEELEvent> visit(InfixOpNode n) {
+        if (!(n.getLeft() instanceof UnaryTestNode || n.getLeft() instanceof RangeNode) && (n.getRight() instanceof UnaryTestNode || n.getRight() instanceof RangeNode)) {
+            return Arrays.asList(new ASTHeuristicCheckEvent(Severity.WARN, Msg.createMessage(Msg.COMPARING_TO_UT, n.getOperator().symbol + " (" + n.getRight().getText()) + ")", n));
+        }
+        return defaultVisit(n);
+    }
+
+    @Override
+    public List<FEELEvent> visit(UnaryTestNode n) {
+        if (n.getValue() instanceof UnaryTestNode || n.getValue() instanceof RangeNode) {
+            return Arrays.asList(new ASTHeuristicCheckEvent(Severity.WARN, Msg.createMessage(Msg.UT_OF_UT, n.getOperator().symbol + " (" + n.getValue().getText()) + ")", n));
+        }
+        return defaultVisit(n);
+    }
+
+    @Override
+    public List<FEELEvent> visit(RangeNode n) {
+        if ((n.getStart() instanceof NullNode && n.getEnd() instanceof RangeNode)
+                || (n.getStart() instanceof RangeNode && n.getEnd() instanceof NullNode)) {
+            return Arrays.asList(new ASTHeuristicCheckEvent(Severity.WARN, Msg.createMessage(Msg.UT_OF_UT, n.getText()), n));
+        }
+        return defaultVisit(n);
+    }
+}
diff --git a/packages/stunner-editors/kie-wb-common-dmn/kie-wb-common-dmn-client/pom.xml b/packages/stunner-editors/kie-wb-common-dmn/kie-wb-common-dmn-client/pom.xml
index f31910aa5f..ae6befcae7 100644
--- a/packages/stunner-editors/kie-wb-common-dmn/kie-wb-common-dmn-client/pom.xml
+++ b/packages/stunner-editors/kie-wb-common-dmn/kie-wb-common-dmn-client/pom.xml
@@ -27,19 +27,7 @@
   <artifactId>kie-wb-common-dmn-client</artifactId>
   <name>Kie Workbench - Common - DMN - Client</name>
   <description>Kie Workbench - Common - DMN - Client</description>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <configuration>
-                    <source>11</source>
-                    <target>11</target>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-    <packaging>jar</packaging>
+  <packaging>jar</packaging>
 
   <properties>
     <java.module.name>org.kie.wb.common.dmn.client</java.module.name>
diff --git a/packages/stunner-editors/kie-wb-common-dmn/kie-wb-common-dmn-client/src/main/java/org/kie/workbench/common/dmn/client/editors/expressions/types/dtable/DecisionTableEditorDefinitionEnricher.java b/packages/stunner-editors/kie-wb-common-dmn/kie-wb-common-dmn-client/src/main/java/org/kie/workbench/common/dmn/client/editors/expressions/types/dtable/DecisionTableEditorDefinitionEnricher.java
index 3d84b8383f..d03841ea69 100644
--- a/packages/stunner-editors/kie-wb-common-dmn/kie-wb-common-dmn-client/src/main/java/org/kie/workbench/common/dmn/client/editors/expressions/types/dtable/DecisionTableEditorDefinitionEnricher.java
+++ b/packages/stunner-editors/kie-wb-common-dmn/kie-wb-common-dmn-client/src/main/java/org/kie/workbench/common/dmn/client/editors/expressions/types/dtable/DecisionTableEditorDefinitionEnricher.java
@@ -417,7 +417,7 @@ public class DecisionTableEditorDefinitionEnricher implements ExpressionEditorMo
     }
 
     private QName getQName(final ItemDefinition itemDefinition) {
-        if (!itemDefinitionUtils.findByName(itemDefinition.getName().getValue()).isEmpty()) {
+        if (itemDefinitionUtils.findByName(itemDefinition.getName().getValue()).isPresent()) {
             return getQNameFromItemDefinitionName(itemDefinition);
         } else {
             return itemDefinition.getTypeRef();
diff --git a/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-client/kie-wb-common-stunner-lienzo/src/main/java/org/kie/workbench/common/stunner/client/lienzo/components/toolbox/actions/AbstractActionsToolboxView.java b/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-client/kie-wb-common-stunner-lienzo/src/main/java/org/kie/workbench/common/stunner/client/lienzo/components/toolbox/actions/AbstractActionsToolboxView.java
index 4ef4afaa50..e5983b7825 100644
--- a/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-client/kie-wb-common-stunner-lienzo/src/main/java/org/kie/workbench/common/stunner/client/lienzo/components/toolbox/actions/AbstractActionsToolboxView.java
+++ b/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-client/kie-wb-common-stunner-lienzo/src/main/java/org/kie/workbench/common/stunner/client/lienzo/components/toolbox/actions/AbstractActionsToolboxView.java
@@ -172,6 +172,7 @@ public abstract class AbstractActionsToolboxView<V extends AbstractActionsToolbo
                                      final IsToolboxActionDraggable toolboxAction,
                                      final ButtonItem button,
                                      final NodeMouseMoveEvent event) {
+        button.disable();
         toolboxAction.onMoveStart(toolbox.getCanvasHandler(),
                                   toolbox.getElementUUID(),
                                   new MouseMoveEvent(event.getX(),
diff --git a/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-sets/kie-wb-common-stunner-bpmn/kie-wb-common-stunner-bpmn-marshalling/src/main/java/org/kie/workbench/common/stunner/bpmn/client/marshall/converters/customproperties/AssociationList.java b/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-sets/kie-wb-common-stunner-bpmn/kie-wb-common-stunner-bpmn-marshalling/src/main/java/org/kie/workbench/common/stunner/bpmn/client/marshall/converters/cu [...]
index 794bcbc6d5..8b0a8c9a0f 100644
--- a/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-sets/kie-wb-common-stunner-bpmn/kie-wb-common-stunner-bpmn-marshalling/src/main/java/org/kie/workbench/common/stunner/bpmn/client/marshall/converters/customproperties/AssociationList.java
+++ b/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-sets/kie-wb-common-stunner-bpmn/kie-wb-common-stunner-bpmn-marshalling/src/main/java/org/kie/workbench/common/stunner/bpmn/client/marshall/converters/customproperties/AssociationList.java
@@ -19,11 +19,9 @@ package org.kie.workbench.common.stunner.bpmn.client.marshall.converters.customp
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import org.kie.workbench.common.stunner.core.util.StringUtils;
 import org.uberfire.commons.UUID;
 
 import static java.util.stream.Collectors.toList;
@@ -60,14 +58,17 @@ public class AssociationList {
     }
 
     public static AssociationList fromString(String encoded) {
-        return Optional.ofNullable(encoded)
-                .filter(StringUtils::nonEmpty)
-                .map(s -> new AssociationList(
-                        Arrays.stream(s.replaceAll(REGEX_DELIMITER, REPLACE_DELIMITER_AVOID_CONFLICTS)
-                                              .split(REPLACED_DELIMITER))
-                                .map(AssociationDeclaration::fromString)
-                                .collect(toList())))
-                .orElse(new AssociationList());
+        // The change below is to fix SDM issue on Mac OS
+        if (encoded == null || encoded.isEmpty()) {
+            return new AssociationList();
+        } else {
+            return new AssociationList(
+                    Arrays.stream(encoded.replaceAll(REGEX_DELIMITER, REPLACE_DELIMITER_AVOID_CONFLICTS)
+                                    .split(REPLACED_DELIMITER))
+                            .map(AssociationDeclaration::fromString)
+                            .collect(toList()));
+
+        }
     }
 
     public List<AssociationDeclaration> getInputs() {
diff --git a/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-sets/kie-wb-common-stunner-bpmn/kie-wb-common-stunner-bpmn-marshalling/src/main/java/org/kie/workbench/common/stunner/bpmn/client/marshall/converters/tostunner/processes/ProcessConverterDelegate.java b/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-sets/kie-wb-common-stunner-bpmn/kie-wb-common-stunner-bpmn-marshalling/src/main/java/org/kie/workbench/common/stunner/bpmn/client/marshall/c [...]
index 8479455034..3409dcf939 100644
--- a/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-sets/kie-wb-common-stunner-bpmn/kie-wb-common-stunner-bpmn-marshalling/src/main/java/org/kie/workbench/common/stunner/bpmn/client/marshall/converters/tostunner/processes/ProcessConverterDelegate.java
+++ b/packages/stunner-editors/kie-wb-common-stunner/kie-wb-common-stunner-sets/kie-wb-common-stunner-bpmn/kie-wb-common-stunner-bpmn-marshalling/src/main/java/org/kie/workbench/common/stunner/bpmn/client/marshall/converters/tostunner/processes/ProcessConverterDelegate.java
@@ -16,7 +16,9 @@
 
 package org.kie.workbench.common.stunner.bpmn.client.marshall.converters.tostunner.processes;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -25,6 +27,7 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import elemental2.dom.DomGlobal;
 import org.eclipse.bpmn2.BaseElement;
 import org.eclipse.bpmn2.FlowElement;
 import org.eclipse.bpmn2.Lane;
@@ -112,19 +115,37 @@ final class ProcessConverterDelegate {
     }
 
     private Result<Map<String, BpmnNode>> convertFlowElements(List<FlowElement> flowElements) {
-        final List<Result<BpmnNode>> results = flowElements
-                .stream()
-                .map(converterFactory.flowElementConverter()::convertNode)
-                .collect(Collectors.toList());
-
-        final Map<String, BpmnNode> resultMap = results.stream()
-                .map(Result::value)
-                .filter(Objects::nonNull)
-                .collect(Collectors.toMap(n -> n.value().getUUID(), n -> n));
-
-        return ResultComposer.composeResults(resultMap, results);
+        final List<Result<BpmnNode>> results = new ArrayList<>();
+        final Map<String, BpmnNode> resultMap = new HashMap<>();
+        for (FlowElement element : flowElements) {
+            try {
+                logMessage("Converting  " + element);
+                Result<BpmnNode> result = converterFactory.flowElementConverter().convertNode(element);
+                results.add(result);
+                logMessage("Mapping  " + result);
+                if (result.value() != null) {
+                    BpmnNode n = result.value();
+                    resultMap.put(n.value().getUUID(), n);
+                }
+            } catch (Throwable t) {
+                logMessage("Failed to convert/map " + element + t);
+            }
+        }
+        Result toReturn = null;
+        try {
+            logMessage("Composing result");
+            toReturn = ResultComposer.composeResults(resultMap, results);
+        }  catch (Throwable t) {
+            logMessage("Failed to compose result " + t);
+        }
+        return toReturn;
     }
 
+    private void logMessage(String message) {
+        if (DomGlobal.console != null) { //Safe for Unit Test
+            DomGlobal.console.log(message);
+        }
+    }
     private Result<BpmnNode>[] convertLane(Lane lane, List<Lane> parents, Map<String, BpmnNode> freeFloatingNodes, BpmnNode firstDiagramNode) {
         if (lane.getChildLaneSet() != null) {
             parents.add(lane);
diff --git a/packages/uniforms-bootstrap4-codegen/package.json b/packages/uniforms-bootstrap4-codegen/package.json
index a5db7597e4..62861e3196 100644
--- a/packages/uniforms-bootstrap4-codegen/package.json
+++ b/packages/uniforms-bootstrap4-codegen/package.json
@@ -27,8 +27,8 @@
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
     "underscore": "^1.13.1",
-    "uniforms": "^3.5.5",
-    "uniforms-bridge-simple-schema-2": "^3.5.5"
+    "uniforms": "^3.10.2",
+    "uniforms-bridge-simple-schema-2": "^3.10.2"
   },
   "devDependencies": {
     "@babel/core": "^7.16.0",
diff --git a/packages/uniforms-bootstrap4-codegen/src/__tests__/_createContext.ts b/packages/uniforms-bootstrap4-codegen/src/__tests__/_createContext.ts
index d56d008b2f..382b7a6095 100644
--- a/packages/uniforms-bootstrap4-codegen/src/__tests__/_createContext.ts
+++ b/packages/uniforms-bootstrap4-codegen/src/__tests__/_createContext.ts
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import { Context, randomIds } from "uniforms/cjs";
+import { Context, BaseForm, randomIds } from "uniforms/cjs";
 
 import createSchema from "./_createSchema";
 
@@ -32,6 +32,16 @@ export default function createContext(schema?: {}, context?: Partial<Context<any
     randomId,
     submitting: false,
     validating: false,
+    formRef: new BaseForm<any>({
+      autosave: false,
+      autosaveDelay: 0,
+      error: false,
+      label: true,
+      model: {},
+      noValidate: false,
+      onSubmit: (event) => {},
+      schema: createSchema(schema),
+    }),
     ...context,
     schema: createSchema(schema),
     submitted: false,
diff --git a/packages/uniforms-bootstrap4-codegen/src/uniforms/BootstrapCodeGenContext.tsx b/packages/uniforms-bootstrap4-codegen/src/uniforms/BootstrapCodeGenContext.tsx
index 9c2cd9b55b..79a3060b60 100644
--- a/packages/uniforms-bootstrap4-codegen/src/uniforms/BootstrapCodeGenContext.tsx
+++ b/packages/uniforms-bootstrap4-codegen/src/uniforms/BootstrapCodeGenContext.tsx
@@ -16,7 +16,7 @@
 
 import React, { createContext, useContext } from "react";
 
-import { Bridge, context, Context, randomIds } from "uniforms/cjs";
+import { Bridge, BaseForm, context, Context, randomIds } from "uniforms/cjs";
 
 import { FormElement } from "../api";
 
@@ -56,6 +56,16 @@ export const CodeGenContextProvider: React.FC<ProviderProps> = (props) => {
     onSubmit: (event) => {},
     randomId: randomIds(),
     schema: props.schema,
+    formRef: new BaseForm<any>({
+      autosave: false,
+      autosaveDelay: 0,
+      error: false,
+      label: true,
+      model: {},
+      noValidate: false,
+      onSubmit: (event) => {},
+      schema: props.schema,
+    }),
     state: {
       readOnly: false,
       disabled: false,
diff --git a/packages/uniforms-patternfly-codegen/package.json b/packages/uniforms-patternfly-codegen/package.json
index aad9d79292..5f091ee6ee 100644
--- a/packages/uniforms-patternfly-codegen/package.json
+++ b/packages/uniforms-patternfly-codegen/package.json
@@ -26,8 +26,8 @@
     "prettier": "^2.2.1",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
-    "uniforms": "^3.5.5",
-    "uniforms-bridge-simple-schema-2": "^3.5.5"
+    "uniforms": "^3.10.2",
+    "uniforms-bridge-simple-schema-2": "^3.10.2"
   },
   "devDependencies": {
     "@babel/core": "^7.16.0",
diff --git a/packages/uniforms-patternfly-codegen/src/__tests__/_createContext.ts b/packages/uniforms-patternfly-codegen/src/__tests__/_createContext.ts
index d56d008b2f..382b7a6095 100644
--- a/packages/uniforms-patternfly-codegen/src/__tests__/_createContext.ts
+++ b/packages/uniforms-patternfly-codegen/src/__tests__/_createContext.ts
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import { Context, randomIds } from "uniforms/cjs";
+import { Context, BaseForm, randomIds } from "uniforms/cjs";
 
 import createSchema from "./_createSchema";
 
@@ -32,6 +32,16 @@ export default function createContext(schema?: {}, context?: Partial<Context<any
     randomId,
     submitting: false,
     validating: false,
+    formRef: new BaseForm<any>({
+      autosave: false,
+      autosaveDelay: 0,
+      error: false,
+      label: true,
+      model: {},
+      noValidate: false,
+      onSubmit: (event) => {},
+      schema: createSchema(schema),
+    }),
     ...context,
     schema: createSchema(schema),
     submitted: false,
diff --git a/packages/uniforms-patternfly-codegen/src/uniforms/CodeGenContext.tsx b/packages/uniforms-patternfly-codegen/src/uniforms/CodeGenContext.tsx
index b7057cf8a6..a65e4e605c 100644
--- a/packages/uniforms-patternfly-codegen/src/uniforms/CodeGenContext.tsx
+++ b/packages/uniforms-patternfly-codegen/src/uniforms/CodeGenContext.tsx
@@ -16,7 +16,7 @@
 
 import React, { createContext, useContext } from "react";
 
-import { Bridge, context, Context, randomIds } from "uniforms/cjs";
+import { Bridge, BaseForm, context, Context, randomIds } from "uniforms/cjs";
 
 import { FormElement } from "../api";
 
@@ -56,6 +56,16 @@ export const CodeGenContextProvider: React.FC<ProviderProps> = (props) => {
     onSubmit: (event) => {},
     randomId: randomIds(),
     schema: props.schema,
+    formRef: new BaseForm<any>({
+      autosave: false,
+      autosaveDelay: 0,
+      error: false,
+      label: true,
+      model: {},
+      noValidate: false,
+      onSubmit: (event) => {},
+      schema: props.schema,
+    }),
     state: {
       readOnly: false,
       disabled: false,
diff --git a/packages/uniforms-patternfly/.vscode/launch.json b/packages/uniforms-patternfly/.vscode/launch.json
new file mode 100644
index 0000000000..edebfa0ad2
--- /dev/null
+++ b/packages/uniforms-patternfly/.vscode/launch.json
@@ -0,0 +1,29 @@
+{
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "name": "Current TS File",
+      "type": "node",
+      "request": "launch",
+      "env": {},
+      "args": ["${relativeFile}"],
+      "runtimeArgs": ["-r", "ts-node/register"],
+      "cwd": "${workspaceRoot}",
+      "protocol": "inspector",
+      "internalConsoleOptions": "openOnSessionStart"
+    },
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "Jest Current File",
+      "program": "${workspaceFolder}/node_modules/.bin/jest",
+      "args": ["${fileBasenameNoExtension}", "--config", "jest.config.js"],
+      "console": "integratedTerminal",
+      "internalConsoleOptions": "neverOpen",
+      "disableOptimisticBPs": true,
+      "windows": {
+        "program": "${workspaceFolder}/node_modules/jest/bin/jest"
+      }
+    }
+  ]
+}
diff --git a/packages/uniforms-patternfly/README.md b/packages/uniforms-patternfly/README.md
new file mode 100644
index 0000000000..3d4f5ad446
--- /dev/null
+++ b/packages/uniforms-patternfly/README.md
@@ -0,0 +1,97 @@
+# Basic usage
+
+`uniforms` is a plugin for React to be able to create dynamic forms with built-in state management and form validation.
+`uniforms` provides you with simple re-usable form components which allows for rapid prototyping and cleaner React components.
+
+This package extends uniforms to provide [Patternfly React](https://www.patternfly.org/v4/) components inside your forms.
+For more information about `uniforms` please go to https://uniforms.tools/
+
+Looking for building mobile enabled forms? Check [Uniforms-ionic](https://github.com/aerogear/uniforms-ionic) package that provides Ionic extensions
+
+### 1. Install the required packages
+
+To start using uniforms, we have to install three independent packages:
+
+1. Core
+2. Bridge
+3. Theme
+
+In this example, we will use the JSON Schema to describe our desired data format and style our form using the Pattenfly UI theme.
+
+```shell
+npm install uniforms@^3.10.2
+npm install uniforms-bridge-json-schema@^3.10.2
+npm install @kie-tools/uniforms-patternfly
+npm install @patternfly/react-core @patternfly/react-icons
+```
+
+Don't forget that it's necessary to correctly load the styles from Patternfly. To do it, we recommend taking a look into the
+[Patternfly React Seed](https://github.com/patternfly/patternfly-react-seed), or you can simply load the styles directly into
+your `index.html` like in the example app of this repo.
+
+Obs: If you use a previous version of the `tslib` indirectly (version 1), it should be necessary to add this dependency as well.
+
+```shell
+npm install tslib@^2.3.1
+```
+
+### 2. Start by defining a schema
+
+After we've installed required packages, it's time to define our schema. We can do it in a plain JSON, which is a valid JSON Schema instance:
+
+```js
+const schema = {
+  type: "object",
+  properties: {
+    foo: {
+      type: "string",
+    },
+  },
+};
+```
+
+### 3. Then create the bridge
+
+Now that we have the schema, we can create the uniforms bridge of it, by using the corresponding uniforms bridge package.
+Creating the bridge instance is necessary - without it, uniforms would not be able to process form generation and validation.
+As we are using the JSON Schema, we have to import the `uniforms-bridge-json-schema` package. Also, because we're doing an
+example of a JSON Schema, it's necessary to use a JSON Schema validation library, and in this example we'll be using the AJV.
+
+```js
+import { JSONSchemaBridge } from "uniforms-bridge-json-schema";
+import AJV from "ajv";
+
+const ajv = new Ajv({ allErrors: true, useDefaults: true });
+
+function createValidator(schema) {
+  const validator = ajv.compile(schema);
+
+  return (model) => {
+    validator(model);
+    return validator.errors?.length ? { details: validator.errors } : null;
+  };
+}
+
+const bridge = new JSONSchemaBridge(schema, createValidator(schema));
+```
+
+### 4. Finally, use it in a form! 🎉
+
+Uniforms theme packages provide the `AutoForm` component, which is able to generate the form based on the given schema.
+All we have to do now is to pass the previously created Bridge to the `AutoForm`:
+
+```js
+import * as React from "react";
+import { AutoForm } from "@kie-tools/uniforms-patternfly/dist/esm";
+
+import schema from "./schema";
+
+export default function MyForm() {
+  return <AutoForm schema={bridge} onSubmit={console.log} />;
+}
+```
+
+And that's it! `AutoForm` will generate a complete form with labeled fields, errors list (if any) and a submit button.
+
+Also, it will take care of validation and handle model changes. In case you need more advanced feature, take a deeper look
+into the Uniforms docs.
diff --git a/packages/uniforms-patternfly/__mocks__/styleMock.js b/packages/uniforms-patternfly/__mocks__/styleMock.js
new file mode 100644
index 0000000000..f053ebf797
--- /dev/null
+++ b/packages/uniforms-patternfly/__mocks__/styleMock.js
@@ -0,0 +1 @@
+module.exports = {};
diff --git a/packages/online-editor/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/env/index.js
similarity index 61%
copy from packages/online-editor/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to packages/uniforms-patternfly/env/index.js
index 38ca944907..ac71eb1337 100644
--- a/packages/online-editor/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/env/index.js
@@ -1,11 +1,11 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed 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
+ *        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,
@@ -14,4 +14,11 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.23.0";
+const { varsWithName, composeEnv } = require("@kie-tools-scripts/build-env");
+
+module.exports = composeEnv([require("@kie-tools/root-env/env")], {
+  vars: varsWithName({}),
+  get env() {
+    return {};
+  },
+});
diff --git a/packages/uniforms-patternfly/jest.config.js b/packages/uniforms-patternfly/jest.config.js
new file mode 100644
index 0000000000..076ed79f77
--- /dev/null
+++ b/packages/uniforms-patternfly/jest.config.js
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+module.exports = {
+  globals: {
+    "ts-jest": {
+      tsconfig: "<rootDir>/tsconfig.esm.json",
+    },
+  },
+  reporters: ["default", ["jest-junit", { outputFile: "./dist-tests/junit-report.xml" }]],
+  setupFilesAfterEnv: ["./src/__tests__/jest.setup.ts"],
+  moduleDirectories: ["node_modules"],
+  testRegex: "src/__tests__/.*\\.test\\.(jsx?|tsx?)$",
+  transform: {
+    "^.+\\.jsx?$": ["babel-jest", { presets: [["@babel/env", { targets: { node: "current" } }], "@babel/react"] }],
+    "^.+\\.tsx?$": "ts-jest",
+  },
+  moduleNameMapper: {
+    "\\.(css|less|sass|scss)$": "<rootDir>/__mocks__/styleMock.js",
+  },
+};
diff --git a/packages/uniforms-patternfly-codegen/package.json b/packages/uniforms-patternfly/package.json
similarity index 60%
copy from packages/uniforms-patternfly-codegen/package.json
copy to packages/uniforms-patternfly/package.json
index aad9d79292..4b1db7a9d2 100644
--- a/packages/uniforms-patternfly-codegen/package.json
+++ b/packages/uniforms-patternfly/package.json
@@ -1,8 +1,12 @@
 {
-  "name": "@kie-tools/uniforms-patternfly-codegen",
+  "name": "@kie-tools/uniforms-patternfly",
   "version": "0.0.0",
-  "description": "",
+  "description": "Patternfly forms for uniforms",
   "license": "Apache-2.0",
+  "author": {
+    "name": "Gianluca",
+    "email": "gzuccare@redhat.com"
+  },
   "keywords": [],
   "homepage": "https://github.com/kiegroup/kie-tools",
   "repository": {
@@ -16,49 +20,48 @@
     "dist"
   ],
   "scripts": {
-    "build:dev": "rimraf dist && webpack --env dev",
-    "build:prod": "pnpm lint && pnpm test && rimraf dist && webpack",
+    "build:dev": "rimraf dist && tsc --build tsconfig.cjs.json && tsc --build tsconfig.esm.json",
+    "build:prod": "rimraf dist && pnpm lint && pnpm test && rimraf dist && tsc --build tsconfig.cjs.json && tsc --build tsconfig.esm.json",
     "lint": "run-script-if --bool \"$(build-env linters.run)\" --then \"kie-tools--eslint ./src\"",
-    "test": "run-script-if --ignore-errors \"$(build-env tests.ignoreFailures)\" --bool \"$(build-env tests.run)\" --then \"jest --silent --verbose --passWithNoTests\""
+    "test": "run-script-if --ignore-errors \"$(build-env tests.ignoreFailures)\" --bool \"$(build-env tests.run)\" --then \"jest --verbose --passWithNoTests\""
   },
   "dependencies": {
+    "@types/invariant": "^2.2.35",
+    "invariant": "^2.2.4",
     "lodash": "^4.17.21",
-    "prettier": "^2.2.1",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
-    "uniforms": "^3.5.5",
-    "uniforms-bridge-simple-schema-2": "^3.5.5"
+    "uniforms": "^3.10.2"
   },
   "devDependencies": {
-    "@babel/core": "^7.16.0",
     "@babel/preset-env": "^7.16.0",
     "@babel/preset-react": "^7.16.0",
-    "@kie-tools-core/webpack-base": "workspace:*",
+    "@kie-tools-scripts/build-env": "workspace:*",
     "@kie-tools/eslint": "workspace:*",
     "@kie-tools/root-env": "workspace:*",
     "@kie-tools/tsconfig": "workspace:*",
+    "@patternfly/react-core": "^4.157.3",
+    "@patternfly/react-icons": "^4.11.17",
+    "@testing-library/jest-dom": "^5.16.1",
     "@testing-library/react": "^11.2.6",
     "@types/jest": "^26.0.23",
-    "@types/jest-when": "^2.7.4",
     "@types/lodash": "^4.14.168",
-    "@types/prettier": "^2.2.1",
     "@types/react": "^17.0.6",
     "@types/react-dom": "^17.0.5",
     "@types/simpl-schema": "^1.12.0",
+    "@types/testing-library__jest-dom": "^5.9.1",
     "@types/testing-library__react": "^9.1.2",
     "copy-webpack-plugin": "^11.0.0",
     "jest": "^26.6.3",
-    "jest-junit": "^14.0.0",
-    "jest-raw-loader": "^1.0.1",
-    "jest-when": "^3.5.0",
-    "raw-loader": "^4.0.2",
     "rimraf": "^3.0.2",
+    "simpl-schema": "^1.12.0",
     "ts-jest": "^26.5.6",
+    "ts-node": "^10.9.1",
     "typescript": "^4.4.2",
+    "uniforms-bridge-simple-schema-2": "^3.10.2",
     "webpack": "^5.36.2",
     "webpack-cli": "^4.10.0",
     "webpack-dev-server": "^4.7.3",
-    "webpack-merge": "^5.8.0",
-    "webpack-node-externals": "^3.0.0"
+    "webpack-merge": "^5.8.0"
   }
 }
\ No newline at end of file
diff --git a/packages/unitables/src/uniforms/AutoField.tsx b/packages/uniforms-patternfly/src/AutoField.tsx
similarity index 59%
copy from packages/unitables/src/uniforms/AutoField.tsx
copy to packages/uniforms-patternfly/src/AutoField.tsx
index 2fed18b7e6..2182ec1228 100644
--- a/packages/unitables/src/uniforms/AutoField.tsx
+++ b/packages/uniforms-patternfly/src/AutoField.tsx
@@ -1,11 +1,11 @@
 /*
- * Copyright 2021 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed 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
+ *       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,
@@ -14,19 +14,20 @@
  * limitations under the License.
  */
 
-import {
-  BoolField,
-  DateField,
-  ListField,
-  NestField,
-  NumField,
-  RadioField,
-  SelectField,
-  TextField,
-} from "uniforms-patternfly";
 import { createAutoField } from "uniforms";
+import invariant from "invariant";
+import BoolField from "./BoolField";
+import DateField from "./DateField";
+import ListField from "./ListField";
+import NestField from "./NestField";
+import NumField from "./NumField";
+import RadioField from "./RadioField";
+import SelectField from "./SelectField";
+import TextField from "./TextField";
 
-export const AutoField = createAutoField((props) => {
+export type AutoFieldProps = Parameters<typeof AutoField>[0];
+
+const AutoField = createAutoField((props) => {
   if (props.allowedValues) {
     return props.checkboxes && props.fieldType !== Array ? RadioField : SelectField;
   }
@@ -43,7 +44,10 @@ export const AutoField = createAutoField((props) => {
     case Object:
       return NestField;
     case String:
-    default:
       return TextField;
   }
+
+  return invariant(false, "Unsupported field type: %s", props.fieldType);
 });
+
+export default AutoField;
diff --git a/packages/uniforms-patternfly/src/AutoFields.tsx b/packages/uniforms-patternfly/src/AutoFields.tsx
new file mode 100644
index 0000000000..09b779fff5
--- /dev/null
+++ b/packages/uniforms-patternfly/src/AutoFields.tsx
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import { ComponentType, createElement } from "react";
+import { useForm } from "uniforms";
+import AutoField from "./AutoField";
+
+export type AutoFieldsProps = {
+  autoField?: ComponentType<{ name: string }>;
+  element?: ComponentType | string;
+  fields?: string[];
+  omitFields?: string[];
+};
+
+function AutoFields({ autoField = AutoField, element = "div", fields, omitFields = [], ...props }: AutoFieldsProps) {
+  const { schema } = useForm();
+
+  return createElement(
+    element!,
+    props,
+    (fields ?? schema.getSubfields())
+      .filter((field) => !omitFields!.includes(field))
+      .map((field) => createElement(autoField!, { key: field, name: field }))
+  );
+}
+
+export default AutoFields;
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/src/AutoForm.tsx
similarity index 64%
copy from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to packages/uniforms-patternfly/src/AutoForm.tsx
index 7f7980f259..f8a58b03e1 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/src/AutoForm.tsx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,4 +14,15 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+import { AutoForm } from "uniforms";
+import ValidatedQuickForm from "./ValidatedQuickForm";
+
+function Auto(parent: any): any {
+  class _ extends AutoForm.Auto(parent) {
+    static Auto = Auto;
+  }
+
+  return _;
+}
+
+export default Auto(ValidatedQuickForm);
diff --git a/packages/uniforms-patternfly/src/BaseForm.tsx b/packages/uniforms-patternfly/src/BaseForm.tsx
new file mode 100644
index 0000000000..4e0d65e544
--- /dev/null
+++ b/packages/uniforms-patternfly/src/BaseForm.tsx
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { Form } from "@patternfly/react-core";
+import { BaseForm, context } from "uniforms";
+
+function Patternfly(parent: any): any {
+  class _ extends parent {
+    static Patternfly = Patternfly;
+
+    static displayName = `Patternfly${parent.displayName}`;
+
+    render() {
+      return (
+        <context.Provider value={this.getContext()}>
+          <Form data-testid="base-form" {...this.getNativeFormProps()} />
+        </context.Provider>
+      );
+    }
+  }
+
+  return _;
+}
+
+export default Patternfly(BaseForm);
diff --git a/packages/uniforms-patternfly/src/BoolField.tsx b/packages/uniforms-patternfly/src/BoolField.tsx
new file mode 100644
index 0000000000..c0565d0bdd
--- /dev/null
+++ b/packages/uniforms-patternfly/src/BoolField.tsx
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { Checkbox, CheckboxProps } from "@patternfly/react-core/dist/js/components/Checkbox";
+import { Switch, SwitchProps } from "@patternfly/react-core/dist/js/components/Switch";
+import { connectField, FieldProps } from "uniforms";
+import wrapField from "./wrapField";
+
+enum ComponentType {
+  checkbox = "checkbox",
+  switch = "switch",
+}
+
+export type BoolFieldProps = FieldProps<
+  boolean,
+  CheckboxProps & SwitchProps,
+  {
+    appearance?: ComponentType;
+    inputRef?: React.RefObject<Switch | Checkbox> & React.RefObject<HTMLInputElement>;
+  }
+>;
+
+function BoolField({ appearance, disabled, id, inputRef, label, name, onChange, value, ...props }: BoolFieldProps) {
+  const Component = appearance === ComponentType.switch ? Switch : Checkbox;
+  return wrapField(
+    { id, ...props },
+    <Component
+      data-testid={"bool-field"}
+      isChecked={value ?? false}
+      isDisabled={disabled}
+      id={id}
+      name={name}
+      onChange={() => disabled || onChange(!value)}
+      ref={inputRef}
+      label={label}
+    />
+  );
+}
+
+BoolField.defaultProps = { appearance: ComponentType.checkbox };
+
+export default connectField(BoolField);
diff --git a/packages/uniforms-patternfly/src/DateField.tsx b/packages/uniforms-patternfly/src/DateField.tsx
new file mode 100644
index 0000000000..e98a8f562e
--- /dev/null
+++ b/packages/uniforms-patternfly/src/DateField.tsx
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { useCallback, useMemo } from "react";
+import { connectField, FieldProps } from "uniforms";
+import { DatePicker } from "@patternfly/react-core/dist/js/components/DatePicker";
+import { InputGroup } from "@patternfly/react-core/dist/js/components/InputGroup";
+import { TimePicker } from "@patternfly/react-core/dist/js/components/TimePicker";
+import { TextInputProps } from "@patternfly/react-core/dist/js/components/TextInput";
+import { Flex, FlexItem } from "@patternfly/react-core/dist/js/layouts/Flex";
+import wrapField from "./wrapField";
+
+export type DateFieldProps = FieldProps<
+  string,
+  TextInputProps,
+  {
+    inputRef?: React.RefObject<HTMLInputElement>;
+    max?: string;
+    min?: string;
+  }
+>;
+
+const DateConstructor = (typeof global === "object" ? global : window).Date;
+
+function DateField({ onChange, ...props }: DateFieldProps) {
+  const dateValue = useCallback(
+    (newDate?: Date) => {
+      if (newDate) {
+        return new DateConstructor(newDate);
+      }
+      if (!props.value) {
+        return undefined;
+      }
+      return new DateConstructor(props.value);
+    },
+    [props.value]
+  );
+
+  const parseDate = useMemo(() => {
+    const date = dateValue();
+    if (!date) {
+      return "";
+    }
+    return date.toISOString().slice(0, -14);
+  }, [dateValue]);
+
+  const parseTime = useMemo(() => {
+    const date = dateValue();
+    if (!date) {
+      return "";
+    }
+    return `${date.getUTCHours()}:${date.getUTCMinutes()}`;
+  }, [dateValue]);
+
+  const onDateChange = useCallback(
+    (value: string, date?: Date) => {
+      if (!date) {
+        onChange(date);
+      } else {
+        const newDate = dateValue(date);
+        const time = parseTime;
+        if (time !== "") {
+          newDate?.setUTCHours(parseInt(time?.split(":")[0]));
+          newDate?.setUTCMinutes(parseInt(time?.split(":")[1]?.split(" ")[0]));
+        } else {
+          newDate?.setUTCHours(0);
+          newDate?.setUTCMinutes(0);
+        }
+        onChange(newDate?.toISOString());
+      }
+    },
+    [onChange, parseTime, dateValue]
+  );
+
+  const isInvalid = useMemo(() => {
+    const date = dateValue();
+    if (date) {
+      if (props.min) {
+        const minDate = new Date(props.min);
+        if (minDate.toString() === "Invalid Date") {
+          return false;
+        } else if (date < minDate) {
+          return `Should be after ${minDate.toISOString()}`;
+        }
+      }
+      if (props.max) {
+        const maxDate = new Date(props.max);
+        if (maxDate.toString() === "Invalid Date") {
+          return false;
+        } else if (date > maxDate) {
+          return `Should be before ${maxDate.toISOString()}`;
+        }
+      }
+    }
+    return false;
+  }, [dateValue, props.min, props.max]);
+
+  const onTimeChange = useCallback(
+    (time: string, hours?: number, minutes?: number) => {
+      const newDate = dateValue();
+      if (newDate) {
+        if (hours && minutes) {
+          newDate.setUTCHours(hours);
+          newDate.setUTCMinutes(minutes);
+        } else if (time !== "") {
+          const localeHours = parseInt(time?.split(":")[0]);
+          const localeMinutes = parseInt(time?.split(":")[1]?.split(" ")[0]);
+          if (!isNaN(localeHours) && !isNaN(localeMinutes)) {
+            newDate.setUTCHours(localeHours);
+            newDate.setUTCMinutes(localeMinutes);
+          }
+        } else {
+          newDate.setUTCHours(0);
+          newDate.setUTCMinutes(0);
+        }
+        onChange(newDate.toISOString());
+      }
+    },
+    [onChange, dateValue]
+  );
+
+  return wrapField(
+    props as any,
+    <Flex
+      data-testid={"date-field"}
+      style={{ margin: 0 }}
+      direction={{ default: "column" }}
+      id={props.id}
+      name={props.name}
+      ref={props.inputRef}
+    >
+      <FlexItem style={{ margin: 0 }}>
+        <InputGroup style={{ background: "transparent" }}>
+          <DatePicker
+            id={`date-picker-${props.id}`}
+            data-testid={`date-picker`}
+            isDisabled={props.disabled}
+            name={props.name}
+            onChange={onDateChange}
+            value={parseDate}
+          />
+          <TimePicker
+            id={`time-picker-${props.id}`}
+            data-testid={`time-picker`}
+            isDisabled={props.disabled || !props.value}
+            name={props.name}
+            onChange={onTimeChange}
+            style={{ width: "120px" }}
+            value={parseTime}
+            is24Hour
+          />
+        </InputGroup>
+      </FlexItem>
+      {isInvalid && (
+        <div
+          id={`${props.id}-invalid-date-time`}
+          style={{
+            fontSize: "0.875rem",
+            color: "#c9190b",
+            marginTop: "0.25rem",
+          }}
+        >
+          {isInvalid}
+        </div>
+      )}
+    </Flex>
+  );
+}
+
+export default connectField(DateField);
diff --git a/packages/uniforms-patternfly/src/ErrorField.tsx b/packages/uniforms-patternfly/src/ErrorField.tsx
new file mode 100644
index 0000000000..fff76f5c7f
--- /dev/null
+++ b/packages/uniforms-patternfly/src/ErrorField.tsx
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { HTMLProps } from "react";
+import { connectField, filterDOMProps, Override } from "uniforms";
+
+export type ErrorFieldProps = Override<
+  HTMLProps<HTMLDivElement>,
+  {
+    error?: boolean;
+    errorMessage?: string;
+  }
+>;
+
+function ErrorField({ children, error, errorMessage, ...props }: ErrorFieldProps) {
+  return !error ? null : (
+    <div data-testid={"error-field"} {...filterDOMProps(props)}>
+      {children ? children : <div style={{ margin: "3px" }}>{errorMessage}</div>}
+    </div>
+  );
+}
+
+ErrorField.defaultProps = {
+  style: {
+    backgroundColor: "rgba(255, 85, 0, 0.2)",
+    border: "1px solid rgb(255, 85, 0)",
+    borderRadius: "7px",
+    margin: "20px 0px",
+    padding: "10px",
+  },
+};
+
+export default connectField<ErrorFieldProps>(ErrorField, { initialValue: false });
diff --git a/packages/uniforms-patternfly/src/ErrorsField.tsx b/packages/uniforms-patternfly/src/ErrorsField.tsx
new file mode 100644
index 0000000000..b62f66027a
--- /dev/null
+++ b/packages/uniforms-patternfly/src/ErrorsField.tsx
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { HTMLProps } from "react";
+import { useForm, filterDOMProps } from "uniforms";
+
+export type ErrorsFieldProps = HTMLProps<HTMLDivElement>;
+
+function ErrorsField({ children, ...props }: ErrorsFieldProps) {
+  const { error, schema } = useForm();
+
+  return !error && !children ? null : (
+    <div data-testid={"errors-field"} {...filterDOMProps(props)}>
+      {children}
+      <ul>
+        {schema.getErrorMessages(error).map((message, index) => (
+          <li key={index} style={{ margin: "3px" }}>
+            {message}
+          </li>
+        ))}
+      </ul>
+    </div>
+  );
+}
+
+ErrorsField.defaultProps = {
+  style: {
+    backgroundColor: "rgba(255, 85, 0, 0.2)",
+    border: "1px solid rgb(255, 85, 0)",
+    borderRadius: "7px",
+    margin: "20px 0px",
+    padding: "10px",
+  },
+};
+
+export default ErrorsField;
diff --git a/packages/uniforms-patternfly/src/HiddenField.tsx b/packages/uniforms-patternfly/src/HiddenField.tsx
new file mode 100644
index 0000000000..633dc3b892
--- /dev/null
+++ b/packages/uniforms-patternfly/src/HiddenField.tsx
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { HTMLProps, Ref, useEffect } from "react";
+import { useField, filterDOMProps } from "uniforms";
+
+export type HiddenFieldProps = {
+  inputRef?: Ref<HTMLInputElement>;
+  name: string;
+  noDOM?: boolean;
+  value?: any;
+} & HTMLProps<HTMLInputElement>;
+
+export default function HiddenField({ value, ...rawProps }: HiddenFieldProps) {
+  const props = useField(rawProps.name, rawProps, { initialValue: false })[0];
+
+  useEffect(() => {
+    if (value !== undefined && value !== props.value) props.onChange(value);
+  });
+
+  return props.noDOM ? null : (
+    <input
+      data-testid="hidden-field"
+      disabled={props.disabled}
+      name={props.name}
+      ref={props.inputRef}
+      type="hidden"
+      value={value ?? props.value ?? ""}
+      {...filterDOMProps(props)}
+    />
+  );
+}
diff --git a/packages/uniforms-patternfly/src/ListAddField.tsx b/packages/uniforms-patternfly/src/ListAddField.tsx
new file mode 100644
index 0000000000..923f3c6eb8
--- /dev/null
+++ b/packages/uniforms-patternfly/src/ListAddField.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import cloneDeep from "lodash/cloneDeep";
+import { Button, ButtonProps } from "@patternfly/react-core/dist/js/components/Button";
+import { PlusCircleIcon } from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
+import { connectField, FieldProps, filterDOMProps, joinName, useField } from "uniforms";
+
+export type ListAddFieldProps = FieldProps<
+  unknown,
+  ButtonProps,
+  {
+    initialCount?: number;
+    name: string;
+    disabled?: boolean;
+    value?: unknown;
+  }
+>;
+
+function ListAddField({ disabled = false, name, value, ...props }: ListAddFieldProps) {
+  const nameParts = joinName(null, name);
+  const parentName = joinName(nameParts.slice(0, -1));
+  const parent = useField<{ maxCount?: number }, unknown[]>(parentName, {}, { absoluteName: true })[0];
+
+  const limitNotReached = !disabled && !(parent.maxCount! <= parent.value!.length);
+
+  return (
+    <Button
+      data-testid={"list-add-field"}
+      variant="plain"
+      style={{ paddingLeft: "0", paddingRight: "0" }}
+      disabled={!limitNotReached}
+      onClick={() => {
+        !disabled && limitNotReached && parent.onChange(parent.value!.concat([cloneDeep(value)]));
+      }}
+      {...filterDOMProps(props)}
+    >
+      <PlusCircleIcon color="#0088ce" />
+    </Button>
+  );
+}
+
+export default connectField<ListAddFieldProps>(ListAddField, {
+  initialValue: false,
+  kind: "leaf",
+});
diff --git a/packages/uniforms-patternfly/src/ListDelField.tsx b/packages/uniforms-patternfly/src/ListDelField.tsx
new file mode 100644
index 0000000000..cfe69d527d
--- /dev/null
+++ b/packages/uniforms-patternfly/src/ListDelField.tsx
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { ReactNode } from "react";
+import { Button, ButtonProps } from "@patternfly/react-core/dist/js/components/Button";
+import { MinusCircleIcon } from "@patternfly/react-icons/dist/js/icons/minus-circle-icon";
+import { connectField, FieldProps, filterDOMProps, joinName, useField } from "uniforms";
+
+export type ListDelFieldProps = FieldProps<unknown, ButtonProps, { icon?: ReactNode }>;
+
+function ListDelField({ name, disabled, ...props }: ListDelFieldProps) {
+  const nameParts = joinName(null, name);
+  const nameIndex = +nameParts[nameParts.length - 1];
+  const parentName = joinName(nameParts.slice(0, -1));
+  const parent = useField<{ minCount?: number }, unknown[]>(parentName, {}, { absoluteName: true })[0];
+
+  const limitNotReached = !disabled && !(parent.minCount! >= parent.value!.length);
+
+  return (
+    <Button
+      data-testid={"list-del-field"}
+      disabled={!limitNotReached || disabled}
+      variant="plain"
+      style={{ paddingLeft: "0", paddingRight: "0" }}
+      onClick={() => {
+        const value = parent.value!.slice();
+        value.splice(nameIndex, 1);
+        !disabled && limitNotReached && parent.onChange(value);
+      }}
+      {...filterDOMProps(props)}
+    >
+      <MinusCircleIcon color="#cc0000" />
+    </Button>
+  );
+}
+
+export default connectField<ListDelFieldProps>(ListDelField, {
+  initialValue: false,
+  kind: "leaf",
+});
diff --git a/packages/uniforms-patternfly/src/ListField.tsx b/packages/uniforms-patternfly/src/ListField.tsx
new file mode 100644
index 0000000000..f6c6c2ba75
--- /dev/null
+++ b/packages/uniforms-patternfly/src/ListField.tsx
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { Children, cloneElement, isValidElement, ReactNode } from "react";
+import { Tooltip } from "@patternfly/react-core/dist/js/components/Tooltip";
+import { Split, SplitItem } from "@patternfly/react-core/dist/js/layouts/Split";
+import { OutlinedQuestionCircleIcon } from "@patternfly/react-icons/dist/js/icons/outlined-question-circle-icon";
+import { connectField, filterDOMProps, HTMLFieldProps } from "uniforms";
+import ListItemField from "./ListItemField";
+import ListAddField from "./ListAddField";
+
+export type ListFieldProps = HTMLFieldProps<
+  unknown[],
+  HTMLDivElement,
+  {
+    children?: ReactNode;
+    info?: string;
+    error?: boolean;
+    initialCount?: number;
+    itemProps?: object;
+    showInlineError?: boolean;
+  }
+>;
+
+declare module "uniforms" {
+  interface FilterDOMProps {
+    wrapperCol: never;
+    labelCol: never;
+  }
+}
+
+filterDOMProps.register("minCount", "wrapperCol", "labelCol");
+
+function ListField({
+  children = <ListItemField name="$" />,
+  error,
+  errorMessage,
+  info,
+  initialCount,
+  itemProps,
+  label,
+  name,
+  value,
+  showInlineError,
+  ...props
+}: ListFieldProps) {
+  return (
+    <div data-testid={"list-field"} {...filterDOMProps(props)}>
+      <Split hasGutter>
+        <SplitItem>
+          {label && (
+            <label>
+              {label}
+              {!!info && (
+                <span>
+                  &nbsp;
+                  <Tooltip content={info}>
+                    <OutlinedQuestionCircleIcon />
+                  </Tooltip>
+                </span>
+              )}
+            </label>
+          )}
+        </SplitItem>
+        <SplitItem isFilled />
+        <SplitItem>
+          <ListAddField name={"$"} initialCount={initialCount} />
+        </SplitItem>
+      </Split>
+
+      <div>
+        {value?.map((item, itemIndex) =>
+          Children.map(children, (child, childIndex) =>
+            isValidElement(child)
+              ? cloneElement(child as React.ReactElement<{ name: string }, string>, {
+                  key: `${itemIndex}-${childIndex}`,
+                  name: child.props.name
+                    ?.split(/\$(.*)/s)
+                    .slice(0, -1)
+                    .join(`${itemIndex}`),
+                  ...itemProps,
+                })
+              : child
+          )
+        )}
+      </div>
+    </div>
+  );
+}
+
+export default connectField(ListField);
diff --git a/packages/uniforms-patternfly/src/ListItemField.tsx b/packages/uniforms-patternfly/src/ListItemField.tsx
new file mode 100644
index 0000000000..8152e2e96b
--- /dev/null
+++ b/packages/uniforms-patternfly/src/ListItemField.tsx
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { ReactNode } from "react";
+import { connectField } from "uniforms";
+import AutoField from "./AutoField";
+import ListDelField from "./ListDelField";
+
+export type ListItemFieldProps = {
+  children?: ReactNode;
+  value?: unknown;
+};
+
+function ListItemField({ children = <AutoField label={null} name={""} /> }: ListItemFieldProps) {
+  return (
+    <div
+      data-testid={"list-item-field"}
+      style={{
+        marginBottom: "1rem",
+        display: "flex",
+        justifyContent: "space-between",
+      }}
+    >
+      <div style={{ width: "100%", marginRight: "10px" }}>{children}</div>
+      <div>
+        <ListDelField name={""} />
+      </div>
+    </div>
+  );
+}
+
+export default connectField<ListItemFieldProps>(ListItemField, {
+  initialValue: false,
+});
diff --git a/packages/uniforms-patternfly/src/LongTextField.tsx b/packages/uniforms-patternfly/src/LongTextField.tsx
new file mode 100644
index 0000000000..aa3d7bb272
--- /dev/null
+++ b/packages/uniforms-patternfly/src/LongTextField.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { TextArea } from "@patternfly/react-core/dist/js/components/TextArea";
+import { connectField, filterDOMProps, HTMLFieldProps } from "uniforms";
+
+export type LongTextFieldProps = HTMLFieldProps<
+  string,
+  HTMLDivElement,
+  {
+    inputRef?: React.RefObject<HTMLInputElement>;
+    onChange: (value: string, event: React.ChangeEvent<HTMLTextAreaElement>) => void;
+    value?: string;
+    prefix?: string;
+  }
+>;
+
+function LongTextField({
+  disabled,
+  id,
+  inputRef,
+  label,
+  name,
+  onChange,
+  placeholder,
+  value,
+  ...props
+}: LongTextFieldProps) {
+  return (
+    <div data-testid={"long-text-field"} {...filterDOMProps(props)}>
+      {label && <label htmlFor={id}>{label}</label>}
+      <TextArea
+        id={id}
+        disabled={disabled}
+        name={name}
+        aria-label={name}
+        onChange={(value, event) => onChange(event.target.value)}
+        placeholder={placeholder}
+        ref={inputRef}
+        value={value ?? ""}
+      />
+    </div>
+  );
+}
+
+export default connectField(LongTextField);
diff --git a/packages/uniforms-patternfly/src/NestField.tsx b/packages/uniforms-patternfly/src/NestField.tsx
new file mode 100644
index 0000000000..f9d527babf
--- /dev/null
+++ b/packages/uniforms-patternfly/src/NestField.tsx
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { connectField, filterDOMProps, HTMLFieldProps } from "uniforms";
+import { Card, CardBody } from "@patternfly/react-core/dist/js/components/Card";
+import AutoField from "./AutoField";
+
+export type NestFieldProps = HTMLFieldProps<object, HTMLDivElement, { helperText?: string; itemProps?: object }>;
+
+function NestField({
+  children,
+  error,
+  errorMessage,
+  fields,
+  itemProps,
+  label,
+  name,
+  showInlineError,
+  disabled,
+  ...props
+}: NestFieldProps) {
+  return (
+    <Card data-testid={"nest-field"} {...filterDOMProps(props)}>
+      <CardBody className="pf-c-form">
+        {label && (
+          <label>
+            <b>{label}</b>
+          </label>
+        )}
+        {children || fields?.map((field) => <AutoField key={field} disabled={disabled} name={field} {...itemProps} />)}
+      </CardBody>
+    </Card>
+  );
+}
+
+export default connectField(NestField);
diff --git a/packages/uniforms-patternfly/src/NumField.tsx b/packages/uniforms-patternfly/src/NumField.tsx
new file mode 100644
index 0000000000..b778e1bcb2
--- /dev/null
+++ b/packages/uniforms-patternfly/src/NumField.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { Ref } from "react";
+import { TextInput, TextInputProps } from "@patternfly/react-core/dist/js/components/TextInput";
+import { connectField } from "uniforms";
+import wrapField from "./wrapField";
+
+export type NumFieldProps = {
+  id: string;
+  decimal?: boolean;
+  inputRef?: Ref<HTMLInputElement>;
+  onChange: (value?: number) => void;
+  disabled?: boolean;
+  value?: number;
+  error?: boolean;
+} & Omit<TextInputProps, "isDisabled">;
+
+function NumField(props: NumFieldProps) {
+  const onChange = (value: string, event: React.FormEvent<HTMLInputElement>) => {
+    const parse = props.decimal ? parseFloat : parseInt;
+    const v = parse((event.target as any)?.value);
+    props.onChange(isNaN(v) ? undefined : v);
+  };
+
+  return wrapField(
+    props,
+    <TextInput
+      data-testid={"num-field"}
+      name={props.name}
+      isDisabled={props.disabled}
+      id={props.id}
+      max={props.max}
+      min={props.min}
+      onChange={onChange}
+      placeholder={props.placeholder}
+      ref={props.inputRef}
+      step={props.decimal ? 0.01 : 1}
+      type="number"
+      value={props.value ?? ""}
+      validated={props.error ? "error" : "default"}
+    />
+  );
+}
+
+export default connectField(NumField);
diff --git a/packages/uniforms-patternfly/src/QuickForm.tsx b/packages/uniforms-patternfly/src/QuickForm.tsx
new file mode 100644
index 0000000000..e9ce315645
--- /dev/null
+++ b/packages/uniforms-patternfly/src/QuickForm.tsx
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import { QuickForm } from "uniforms";
+import AutoField from "./AutoField";
+import BaseForm from "./BaseForm";
+import ErrorsField from "./ErrorsField";
+import SubmitField from "./SubmitField";
+
+function Quick(parent: any): any {
+  class _ extends QuickForm.Quick(parent) {
+    static Quick = Quick;
+
+    getAutoField() {
+      return AutoField;
+    }
+
+    getErrorsField() {
+      return ErrorsField;
+    }
+
+    getSubmitField() {
+      return SubmitField;
+    }
+  }
+
+  return _;
+}
+
+export default Quick(BaseForm);
diff --git a/packages/uniforms-patternfly/src/RadioField.tsx b/packages/uniforms-patternfly/src/RadioField.tsx
new file mode 100644
index 0000000000..8ed5cfc86d
--- /dev/null
+++ b/packages/uniforms-patternfly/src/RadioField.tsx
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { Radio } from "@patternfly/react-core/dist/js/components/Radio";
+import { connectField, filterDOMProps, HTMLFieldProps } from "uniforms";
+import wrapField from "./wrapField";
+
+export type RadioFieldProps = HTMLFieldProps<
+  string,
+  HTMLDivElement,
+  {
+    transform?: (string?: string) => string;
+    allowedValues: string[];
+    onChange: (value: string) => void;
+    value?: string;
+    disabled?: boolean;
+  }
+>;
+
+function RadioField(props: RadioFieldProps) {
+  filterDOMProps.register("checkboxes", "decimal");
+  return wrapField(
+    props,
+    <div data-testid={"radio-field"} {...filterDOMProps(props)}>
+      {props.allowedValues?.map((item) => (
+        <React.Fragment key={item}>
+          <Radio
+            isChecked={item === props.value}
+            isDisabled={props.disabled}
+            id={`${props.id}`}
+            name={props.name}
+            label={props.transform ? props.transform(item) : item}
+            aria-label={props.name}
+            onChange={() => props.onChange(item)}
+          />
+        </React.Fragment>
+      ))}
+    </div>
+  );
+}
+
+export default connectField(RadioField);
diff --git a/packages/uniforms-patternfly/src/SelectCheckboxField.tsx b/packages/uniforms-patternfly/src/SelectCheckboxField.tsx
new file mode 100644
index 0000000000..1852633542
--- /dev/null
+++ b/packages/uniforms-patternfly/src/SelectCheckboxField.tsx
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { useMemo } from "react";
+import { Checkbox } from "@patternfly/react-core/dist/js/components/Checkbox";
+import { Radio } from "@patternfly/react-core/dist/js/components/Radio";
+import { filterDOMProps } from "uniforms";
+import { SelectCheckboxProps } from "./SelectField";
+
+function xor<T>(item: T, array: T[]) {
+  const index = array.indexOf(item);
+  if (index === -1) {
+    return array.concat([item]);
+  }
+
+  return array.slice(0, index).concat(array.slice(index + 1));
+}
+
+filterDOMProps.register("autoValue");
+
+function SelectCheckboxField(props: SelectCheckboxProps) {
+  const Group = useMemo(() => (props.fieldType === Array ? Checkbox : Radio), [props]);
+
+  return (
+    <div data-testid={"select-checkbox-field"} {...filterDOMProps(props)}>
+      {props.label && <label>{props.label}</label>}
+      {props.allowedValues!.map((item: string, index: number) => {
+        return (
+          <React.Fragment key={index}>
+            <label htmlFor={props.id}>{props.transform ? props.transform(item) : item}</label>
+            <Group
+              id={`${props.id}-${item}`}
+              isDisabled={props.disabled}
+              name={props.name}
+              aria-label={props.name}
+              value={props.value}
+              isChecked={
+                props.fieldType === Array && Array.isArray(props.value)
+                  ? props.value!.includes(item)
+                  : props.value === item
+              }
+              onChange={() => {
+                props.onChange(props.fieldType === Array && Array.isArray(props.value) ? xor(item, props.value) : item);
+              }}
+            />
+          </React.Fragment>
+        );
+      })}
+    </div>
+  );
+}
+
+export default SelectCheckboxField;
diff --git a/packages/uniforms-patternfly/src/SelectField.tsx b/packages/uniforms-patternfly/src/SelectField.tsx
new file mode 100644
index 0000000000..3992324cf5
--- /dev/null
+++ b/packages/uniforms-patternfly/src/SelectField.tsx
@@ -0,0 +1,53 @@
+import * as React from "react";
+import { SelectDirection, SelectProps } from "@patternfly/react-core/dist/js/components/Select";
+import { connectField, FieldProps } from "uniforms";
+import SelectCheckboxField from "./SelectCheckboxField";
+import SelectInputsField from "./SelectInputsField";
+import { CheckboxProps } from "@patternfly/react-core/dist/js/components/Checkbox";
+import { RadioProps } from "@patternfly/react-core/dist/js/components/Radio";
+
+export type SelectCheckboxProps = FieldProps<
+  string | string[],
+  CheckboxProps | RadioProps,
+  {
+    onChange: (value?: string | string[]) => void;
+    transform?: (value?: string) => string;
+    allowedValues: string[];
+    id?: string;
+    fieldType?: typeof Array;
+    disabled?: boolean;
+  }
+>;
+
+export type SelectInputProps = FieldProps<
+  string | string[],
+  SelectProps,
+  {
+    checkboxes?: boolean;
+    required?: boolean;
+    fieldType?: typeof Array;
+    onChange: (value?: string | string[] | number | number[]) => void;
+    placeholder?: string;
+    allowedValues?: (string | number)[];
+    disabled?: boolean;
+    error?: boolean;
+    transform?: (value?: string | number) => string | number;
+    direction?: SelectDirection;
+    menuAppendTo?: HTMLElement;
+  }
+>;
+
+type SelectFieldProps = SelectCheckboxProps | SelectInputProps;
+
+function isSelectCheckboxProps(toBeDetermined: SelectFieldProps): toBeDetermined is SelectCheckboxProps {
+  return (toBeDetermined as SelectInputProps).checkboxes === true;
+}
+
+function SelectField(props: SelectFieldProps) {
+  if (isSelectCheckboxProps(props)) {
+    return <SelectCheckboxField {...props} />;
+  }
+  return <SelectInputsField {...props} />;
+}
+
+export default connectField<SelectFieldProps>(SelectField);
diff --git a/packages/uniforms-patternfly/src/SelectInputsField.tsx b/packages/uniforms-patternfly/src/SelectInputsField.tsx
new file mode 100644
index 0000000000..d0fa71f4e0
--- /dev/null
+++ b/packages/uniforms-patternfly/src/SelectInputsField.tsx
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { useCallback, useMemo, useState } from "react";
+import {
+  Select,
+  SelectOption,
+  SelectOptionObject,
+  SelectVariant,
+} from "@patternfly/react-core/dist/js/components/Select";
+import wrapField from "./wrapField";
+import { SelectInputProps } from "./SelectField";
+
+type SelectFieldValue = string | string[] | number | number[];
+
+function isSelectOptionObject(
+  toBeDetermined: string | number | SelectOptionObject
+): toBeDetermined is SelectOptionObject {
+  return typeof toBeDetermined === "object" && !Array.isArray(toBeDetermined) && toBeDetermined !== null;
+}
+
+function isSelectOptionString(toBeDetermined: string[] | number[]): toBeDetermined is string[] {
+  return (toBeDetermined.length > 0 && typeof toBeDetermined[0] === "string") || toBeDetermined.length === 0;
+}
+
+function SelectInputsField(props: SelectInputProps) {
+  const [expanded, setExpanded] = useState<boolean>(false);
+  const [selected, setSelected] = useState<SelectFieldValue | undefined>(() => {
+    if (!props.value) {
+      return [];
+    }
+    if (Array.isArray(props.value)) {
+      return [...props.value];
+    }
+    return props.value;
+  });
+
+  const parseInput = useCallback(
+    (selection: string | number | SelectOptionObject, fieldType: typeof Array): SelectFieldValue => {
+      const parsedSelection = isSelectOptionObject(selection) ? selection.toString() : selection;
+
+      if (fieldType !== Array) {
+        return parsedSelection !== "" ? parsedSelection : "";
+      }
+
+      if (Array.isArray(selected)) {
+        if (isSelectOptionString(selected) && typeof parsedSelection === "string") {
+          if (selected.includes(parsedSelection)) {
+            return selected.filter((s) => s !== parsedSelection);
+          }
+          return [parsedSelection, ...selected];
+        } else if (!isSelectOptionString(selected) && typeof parsedSelection === "number") {
+          if (selected.includes(parsedSelection)) {
+            return selected.filter((s) => s !== parsedSelection);
+          }
+          return [parsedSelection, ...selected];
+        }
+      }
+      return [];
+    },
+    [selected]
+  );
+
+  const handleSelect = useCallback(
+    (event: React.MouseEvent | React.ChangeEvent, selection: string | SelectOptionObject) => {
+      if (selection === props.placeholder) {
+        props.onChange(undefined);
+        setSelected([]);
+      } else {
+        const items = parseInput(selection, props.fieldType);
+        props.onChange(items);
+        setSelected(items);
+      }
+      setExpanded(false);
+    },
+    [parseInput, props]
+  );
+
+  const selectOptions = useMemo(() => {
+    const options: JSX.Element[] = [];
+    if (props.placeholder) {
+      options.push(
+        <SelectOption key={`placeholder ${props.allowedValues!.length}`} isPlaceholder value={props.placeholder} />
+      );
+    }
+    props.allowedValues?.forEach((value) =>
+      options.push(
+        <SelectOption key={value} value={value}>
+          {props.transform ? props.transform(value) : value}
+        </SelectOption>
+      )
+    );
+    return options;
+  }, [props]);
+
+  return wrapField(
+    props as any,
+    <div data-testid={"select-inputs-field"} id={props.id}>
+      <Select
+        isDisabled={props.disabled}
+        id={props.id}
+        variant={props.fieldType === Array ? SelectVariant.typeaheadMulti : SelectVariant.single}
+        name={props.name}
+        placeholderText={props.placeholder}
+        isOpen={expanded}
+        selections={selected}
+        onToggle={(isExpanded) => setExpanded(isExpanded)}
+        onSelect={handleSelect}
+        value={props.value || (props.fieldType === Array ? [] : undefined)}
+        menuAppendTo={props.menuAppendTo}
+        direction={props.direction}
+      >
+        {selectOptions}
+      </Select>
+    </div>
+  );
+}
+
+export default SelectInputsField;
diff --git a/packages/uniforms-patternfly/src/SubmitField.tsx b/packages/uniforms-patternfly/src/SubmitField.tsx
new file mode 100644
index 0000000000..a404a18c49
--- /dev/null
+++ b/packages/uniforms-patternfly/src/SubmitField.tsx
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { Button, ButtonProps } from "@patternfly/react-core/dist/js/components/Button";
+import { Override, useForm } from "uniforms";
+
+export type SubmitFieldProps = Override<ButtonProps, { inputRef?: React.RefObject<HTMLButtonElement> }>;
+
+function SubmitField({ disabled, inputRef, value, ...props }: SubmitFieldProps) {
+  const { error, state } = useForm();
+
+  return (
+    <Button
+      data-testid={"submit-field"}
+      isDisabled={disabled === undefined ? !!(error || state.disabled) : disabled}
+      type="submit"
+      ref={inputRef}
+      variant="primary"
+    >
+      {value}
+    </Button>
+  );
+}
+
+SubmitField.defaultProps = { value: "Submit" };
+
+export default SubmitField;
diff --git a/packages/uniforms-patternfly/src/TextField.tsx b/packages/uniforms-patternfly/src/TextField.tsx
new file mode 100644
index 0000000000..92d5274302
--- /dev/null
+++ b/packages/uniforms-patternfly/src/TextField.tsx
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { Ref, useCallback, useMemo } from "react";
+import { DatePicker } from "@patternfly/react-core/dist/js/components/DatePicker";
+import { TextInput, TextInputProps } from "@patternfly/react-core/dist/js/components/TextInput";
+import { TimePicker } from "@patternfly/react-core/dist/js/components/TimePicker";
+import { connectField, filterDOMProps } from "uniforms";
+import wrapField from "./wrapField";
+
+export type TextFieldProps = {
+  id: string;
+  decimal?: boolean;
+  inputRef?: Ref<HTMLInputElement>;
+  onChange: (value?: string) => void;
+  value?: string;
+  disabled?: boolean;
+  error?: boolean;
+  errorMessage?: string;
+  helperText?: string;
+  field?: { format: string };
+} & Omit<TextInputProps, "isDisabled">;
+
+const timeRgx = /^([0-1]?[0-9]|2[0-3]):([0-5][0-9])(:[0-5][0-9])?/;
+
+function TextField({ onChange, ...props }: TextFieldProps) {
+  const isDateInvalid = useMemo(() => {
+    if (typeof props.value === "string" && (props.type === "date" || props.field?.format === "date")) {
+      const date = new Date(props.value);
+      if (typeof props.min === "string") {
+        const minDate = new Date(props.min);
+        if (minDate.toString() === "Invalid Date") {
+          return false;
+        } else if (date.toISOString() < minDate.toISOString()) {
+          return props.errorMessage && props.errorMessage.trim().length > 0
+            ? props.errorMessage
+            : `Should be after ${props.min}`;
+        }
+      }
+      if (typeof props.max === "string") {
+        const maxDate = new Date(props.max);
+        if (maxDate.toString() === "Invalid Date") {
+          return false;
+        } else if (date.toISOString() > maxDate.toISOString()) {
+          return props.errorMessage && props.errorMessage.trim().length > 0
+            ? props.errorMessage
+            : `Should be before ${props.max}`;
+        }
+      }
+    }
+    return false;
+  }, [props.value, props.max, props.min, props.errorMessage, props.type, props.field]);
+
+  const parseTime = useCallback((time: string) => {
+    const parsedTime = timeRgx.exec(time);
+    const date = new Date();
+    if (!parsedTime) {
+      return undefined;
+    }
+    date.setUTCHours(Number(parsedTime[1]), Number(parsedTime[2]!));
+    return date;
+  }, []);
+
+  const isTimeInvalid = useMemo(() => {
+    if (typeof props.value === "string" && (props.type === "time" || props.field?.format === "time")) {
+      const parsedTime = parseTime(props.value);
+      if (parsedTime && typeof props.min === "string" && timeRgx.exec(props.min)) {
+        const parsedMin = parseTime(props.min)!;
+        if (parsedTime < parsedMin) {
+          if (parsedMin.getUTCMinutes() < 10) {
+            return `Should be after ${parsedMin.getUTCHours()}:0${parsedMin.getUTCMinutes()}`;
+          }
+          return `Should be after ${parsedMin.getUTCHours()}:${parsedMin.getUTCMinutes()}`;
+        }
+      }
+      if (parsedTime && typeof props.max === "string" && timeRgx.exec(props.max)) {
+        const parsedMax = parseTime(props.max)!;
+        if (parsedTime > parsedMax) {
+          if (parsedMax.getUTCMinutes() < 10) {
+            return `Should be before ${parsedMax.getUTCHours()}:0${parsedMax.getUTCMinutes()}`;
+          }
+          return `Should be before ${parsedMax.getUTCHours()}:${parsedMax.getUTCMinutes()}`;
+        }
+      }
+    }
+    return false;
+  }, [props.type, props.field, props.value, props.max, props.min, parseTime]);
+
+  const onDateChange = useCallback(
+    (value: string) => {
+      onChange(value);
+    },
+    [onChange]
+  );
+
+  const onTimeChange = useCallback(
+    (time: string) => {
+      const parsedTime = time.split(":");
+      if (parsedTime.length === 2) {
+        onChange([...parsedTime, "00"].join(":"));
+      } else {
+        onChange(time);
+      }
+    },
+    [onChange]
+  );
+
+  const { helperText, ...withoutHelperTextProps } = props;
+
+  return wrapField(
+    props,
+    props.type === "date" || props.field?.format === "date" ? (
+      <>
+        <DatePicker
+          data-testid={"text-field-date-picker"}
+          name={props.name}
+          isDisabled={props.disabled}
+          onChange={onDateChange}
+          value={props.value ?? ""}
+          {...filterDOMProps(props)}
+        />
+        {isDateInvalid && (
+          <div
+            style={{
+              fontSize: "0.875rem",
+              color: "#c9190b",
+              marginTop: "0.25rem",
+            }}
+          >
+            {isDateInvalid}
+          </div>
+        )}
+      </>
+    ) : props.type === "time" || props.field?.format === "time" ? (
+      <>
+        <TimePicker
+          data-testid={"text-field-time-picker"}
+          name={props.name}
+          isDisabled={props.disabled}
+          onChange={onTimeChange}
+          is24Hour
+          value={props.value ?? ""}
+        />
+        {isTimeInvalid && (
+          <div
+            style={{
+              fontSize: "0.875rem",
+              color: "#c9190b",
+              marginTop: "0.25rem",
+            }}
+          >
+            {isTimeInvalid}
+          </div>
+        )}
+      </>
+    ) : (
+      <TextInput
+        data-testid={"text-field"}
+        name={props.name}
+        isDisabled={props.disabled}
+        validated={props.error ? "error" : "default"}
+        onChange={(value, event) => onChange((event.target as any)?.value)}
+        placeholder={props.placeholder}
+        ref={props.inputRef}
+        type={props.type ?? "text"}
+        value={props.value ?? ""}
+        {...filterDOMProps(withoutHelperTextProps)}
+      />
+    )
+  );
+}
+
+export default connectField(TextField);
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/src/ValidatedForm.tsx
similarity index 64%
copy from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to packages/uniforms-patternfly/src/ValidatedForm.tsx
index 7f7980f259..67e88ce074 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/src/ValidatedForm.tsx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,4 +14,15 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+import { ValidatedForm } from "uniforms";
+import BaseForm from "./BaseForm";
+
+function Validated(parent: any): any {
+  class _ extends ValidatedForm.Validated(parent) {
+    static Validated = Validated;
+  }
+
+  return _;
+}
+
+export default Validated(BaseForm);
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/src/ValidatedQuickForm.tsx
similarity index 70%
copy from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to packages/uniforms-patternfly/src/ValidatedQuickForm.tsx
index 7f7980f259..a794533c0e 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/src/ValidatedQuickForm.tsx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,4 +14,8 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+import BaseForm from "./BaseForm";
+import QuickForm from "./QuickForm";
+import ValidatedForm from "./ValidatedForm";
+
+export default ValidatedForm.Validated(QuickForm.Quick(BaseForm));
diff --git a/packages/uniforms-patternfly/src/__tests__/AutoField.test.tsx b/packages/uniforms-patternfly/src/__tests__/AutoField.test.tsx
new file mode 100644
index 0000000000..ac55835414
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/AutoField.test.tsx
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { AutoField } from "..";
+import { usingUniformsContext } from "./test-utils";
+import { render, screen } from "@testing-library/react";
+
+test("<AutoField> - works - default field", () => {
+  render(usingUniformsContext(<AutoField name="x" />, { x: { type: String } }));
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders RadioField", () => {
+  render(
+    usingUniformsContext(<AutoField name="x" />, {
+      x: {
+        type: String,
+        allowedValues: ["x", "y"],
+        uniforms: { checkboxes: true },
+      },
+    })
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders SelectField - input", () => {
+  render(
+    usingUniformsContext(<AutoField name="x" />, {
+      x: { type: Array, allowedValues: ["x", "y"] },
+      "x.$": { type: String },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders SelectField - checkbox", () => {
+  render(
+    usingUniformsContext(
+      <AutoField name="x" checkboxes={true} />,
+
+      {
+        x: { type: Array, allowedValues: ["x", "y"] },
+        "x.$": { type: String },
+      }
+    )
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders DateField", () => {
+  render(usingUniformsContext(<AutoField name="x" />, { x: { type: Date } }));
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders ListField", () => {
+  render(usingUniformsContext(<AutoField name="x" />, { x: { type: Array }, "x.$": { type: String } }));
+  expect(screen.getByTestId("list-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders NumField", () => {
+  render(usingUniformsContext(<AutoField name="x" />, { x: { type: Number } }));
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders NestField", () => {
+  render(usingUniformsContext(<AutoField name="x" />, { x: { type: Object } }));
+  expect(screen.getByTestId("nest-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders TextField", () => {
+  render(usingUniformsContext(<AutoField name="x" />, { x: { type: String } }));
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders BoolField", () => {
+  render(usingUniformsContext(<AutoField name="x" />, { x: { type: Boolean } }));
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+});
+
+test("<AutoField> - renders Component (model)", () => {
+  const Component = jest.fn(() => null);
+  render(usingUniformsContext(<AutoField name="x" />, { x: { type: String, uniforms: { component: Component } } }));
+  expect(Component).toHaveBeenCalledTimes(1);
+});
+
+test("<AutoField> - renders Component (specified)", () => {
+  const Component = jest.fn(() => null);
+  render(usingUniformsContext(<AutoField name="x" component={Component} />, { x: { type: String } }));
+  expect(Component).toHaveBeenCalledTimes(1);
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/AutoFields.test.tsx b/packages/uniforms-patternfly/src/__tests__/AutoFields.test.tsx
new file mode 100644
index 0000000000..421d64b822
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/AutoFields.test.tsx
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { AutoFields } from "..";
+import { usingUniformsContext } from "./test-utils";
+import { render, screen } from "@testing-library/react";
+
+test("<AutoFields> - works", () => {
+  render(usingUniformsContext(<AutoFields />, { x: { type: String } }));
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+});
+
+test("<AutoFields> - render all fields by default", () => {
+  render(
+    usingUniformsContext(<AutoFields />, {
+      x: { type: String },
+      y: { type: String },
+      z: { type: String },
+    })
+  );
+
+  expect(screen.getAllByTestId("text-field")).toHaveLength(3);
+});
+
+test("<AutoFields> - renders only specified fields", () => {
+  render(
+    usingUniformsContext(
+      <AutoFields fields={["x", "y"]} />,
+
+      {
+        x: { type: String },
+        y: { type: String },
+        z: { type: String },
+      }
+    )
+  );
+
+  expect(screen.getAllByTestId("text-field")).toHaveLength(2);
+  expect(screen.queryByLabelText("z")).toBeNull();
+});
+
+test("<AutoFields> - does not render ommited fields", () => {
+  render(
+    usingUniformsContext(
+      <AutoFields omitFields={["x"]} />,
+
+      {
+        x: { type: String },
+        y: { type: String },
+        z: { type: String },
+      }
+    )
+  );
+
+  expect(screen.getAllByTestId("text-field")).toHaveLength(2);
+  expect(screen.queryByLabelText("x")).toBeNull();
+});
+
+test("<AutoFields> - works with custom component", () => {
+  const Component = jest.fn(() => null);
+
+  render(
+    usingUniformsContext(
+      <AutoFields autoField={Component} />,
+
+      {
+        x: { type: String },
+        y: { type: String },
+        z: { type: String },
+      }
+    )
+  );
+
+  expect(Component).toHaveBeenCalledTimes(3);
+});
+
+test("<AutoFields> - wraps fields in specified element", () => {
+  render(
+    usingUniformsContext(
+      <AutoFields element="section" />,
+
+      {
+        x: { type: String },
+        y: { type: String },
+        z: { type: String },
+      }
+    )
+  );
+
+  expect(screen.getAllByTestId("text-field")).toHaveLength(3);
+});
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/src/__tests__/AutoForm.test.tsx
similarity index 57%
copy from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to packages/uniforms-patternfly/src/__tests__/AutoForm.test.tsx
index 7f7980f259..81eacdf661 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/src/__tests__/AutoForm.test.tsx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,4 +14,12 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+import * as React from "react";
+import { AutoForm } from "../";
+import { createSimpleSchema, usingUniformsContext } from "./test-utils";
+import { render, screen } from "@testing-library/react";
+
+test("<AutoForm> - works", () => {
+  render(usingUniformsContext(<AutoForm schema={createSimpleSchema()} />));
+  expect(screen.getByTestId("base-form")).toBeInTheDocument();
+});
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/src/__tests__/BaseForm.test.tsx
similarity index 57%
copy from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to packages/uniforms-patternfly/src/__tests__/BaseForm.test.tsx
index 7f7980f259..65d5244541 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/src/__tests__/BaseForm.test.tsx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,4 +14,12 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+import * as React from "react";
+import { BaseForm } from "../";
+import { usingUniformsContext, createSimpleSchema } from "./test-utils";
+import { render, screen } from "@testing-library/react";
+
+test("<BaseForm> - works", () => {
+  render(usingUniformsContext(<BaseForm schema={createSimpleSchema()} />));
+  expect(screen.getByTestId("base-form")).toBeInTheDocument();
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/BoolField.test.tsx b/packages/uniforms-patternfly/src/__tests__/BoolField.test.tsx
new file mode 100644
index 0000000000..208df40ba0
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/BoolField.test.tsx
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { BoolField } from "..";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<BoolField> - renders an input", () => {
+  render(usingUniformsContext(<BoolField name="x" />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+});
+
+test("<BoolField> - renders a input with correct id (inherited)", () => {
+  render(usingUniformsContext(<BoolField name="x" />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByTestId("bool-field").getAttribute("id")).toEqual("uniforms-0000-0001");
+});
+
+test("<BoolField> - renders a input with correct id (specified)", () => {
+  render(usingUniformsContext(<BoolField name="x" id="y" />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByTestId("bool-field").getAttribute("id")).toBe("y");
+});
+
+test("<BoolField> - renders a input with correct name", () => {
+  render(usingUniformsContext(<BoolField name="x" />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByTestId("bool-field").getAttribute("name")).toBe("x");
+});
+
+test("<BoolField> - renders an input with correct type", () => {
+  render(usingUniformsContext(<BoolField name="x" />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByTestId("bool-field").getAttribute("type")).toBe("checkbox");
+});
+
+test("<BoolField> - renders an input with correct disabled state", () => {
+  render(usingUniformsContext(<BoolField name="x" disabled={true} />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByTestId("bool-field")).toBeDisabled();
+});
+
+test("<BoolField> - renders a input with correct label (specified)", () => {
+  render(usingUniformsContext(<BoolField name="x" label="BoolFieldLabel" />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByText("BoolFieldLabel")).toBeInTheDocument();
+});
+
+test("<BoolField> - renders a input with correct value (default)", () => {
+  render(usingUniformsContext(<BoolField name="x" />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByTestId("bool-field")).not.toHaveAttribute("checked");
+});
+
+test("<BoolField> - renders a input with correct value (model)", () => {
+  render(usingUniformsContext(<BoolField name="x" />, { x: { type: Boolean } }, { model: { x: true } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByTestId("bool-field")).toHaveAttribute("checked");
+});
+
+test("<BoolField> - renders a input with correct value (specified)", () => {
+  render(usingUniformsContext(<BoolField name="x" value />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByTestId("bool-field")).toHaveAttribute("checked");
+});
+
+test("<BoolField> - renders a input which correctly reacts on change", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<BoolField name="x" />, { x: { type: Boolean } }, { onChange }));
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+
+  fireEvent.click(screen.getByTestId("bool-field"));
+  expect(onChange).toHaveBeenLastCalledWith("x", true);
+});
+
+test("<BoolField> - renders a wrapper with unknown props", () => {
+  render(usingUniformsContext(<BoolField name="x" data-x="x" data-y="y" data-z="z" />, { x: { type: Boolean } }));
+
+  expect(screen.getByTestId("bool-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getAttribute("data-x")).toBe("x");
+  expect(screen.getByTestId("wrapper-field").getAttribute("data-y")).toBe("y");
+  expect(screen.getByTestId("wrapper-field").getAttribute("data-z")).toBe("z");
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/DateField.test.tsx b/packages/uniforms-patternfly/src/__tests__/DateField.test.tsx
new file mode 100644
index 0000000000..9df35ac27f
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/DateField.test.tsx
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { DateField } from "../";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<DateField> - renders an input", () => {
+  render(usingUniformsContext(<DateField name="x" />, { x: { type: Date } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+});
+
+test("<DateField> - renders a input with correct id (inherited)", () => {
+  render(usingUniformsContext(<DateField name="x" />, { x: { type: Date } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+  expect(screen.getByTestId("date-picker")).toBeInTheDocument();
+  expect(screen.getByTestId("time-picker")).toBeInTheDocument();
+});
+
+test("<DateField> - renders a input with correct id (specified)", () => {
+  render(usingUniformsContext(<DateField name="x" id="y" />, { x: { type: Date } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+  expect(screen.getByTestId("date-picker").getAttribute("id")).toBe("date-picker-y");
+  // TimePicker component id is in an inside div
+  expect(screen.getByTestId("time-picker").getElementsByClassName("pf-c-select")[0].getAttribute("id")).toBe(
+    "time-picker-y"
+  );
+});
+
+test("<DateField> - renders a input with correct name", () => {
+  render(usingUniformsContext(<DateField name="x" />, { x: { type: Date } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+  expect(screen.getByTestId("date-field").getAttribute("name")).toBe("x");
+});
+
+test("<DateField> - renders an input with correct disabled state", () => {
+  render(usingUniformsContext(<DateField name="x" disabled />, { x: { type: Date } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+
+  const inputs = screen.getByTestId("date-field").getElementsByTagName("input");
+  expect(inputs[0]).toBeDisabled();
+  expect(inputs[1]).toBeDisabled();
+});
+
+test("<DateField> - renders a input with correct label (specified)", () => {
+  render(usingUniformsContext(<DateField required={false} name="x" label="DateFieldLabel" />, { x: { type: Date } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+  expect(screen.getByText("DateFieldLabel")).toBeInTheDocument();
+});
+
+test("<DateField> - renders a input with correct label (specified)", () => {
+  render(usingUniformsContext(<DateField required={true} name="x" label="DateFieldLabel" />, { x: { type: Date } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+  expect(screen.getByText("DateFieldLabel")).toBeInTheDocument();
+  expect(screen.getByText("*")).toBeInTheDocument();
+});
+
+test("<DateField> - renders a input with correct value (default)", () => {
+  render(usingUniformsContext(<DateField name="x" />, { x: { type: Date } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+
+  const inputs = screen.getByTestId("date-field").getElementsByTagName("input");
+  expect(inputs[0].getAttribute("value")).toBe("");
+  expect(inputs[1].getAttribute("value")).toBe("");
+});
+
+test("<DateField> - renders a input with correct value (model)", () => {
+  const now = new Date();
+  render(usingUniformsContext(<DateField name="x" />, { x: { type: Date } }, { model: { x: now } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+  expect(screen.getByTestId("time-picker").getAttribute("value")).toEqual(
+    `${now.getUTCHours()}:${now.getUTCMinutes()}`
+  );
+});
+
+test("<DateField> - renders a input with correct value (specified)", () => {
+  const now = new Date();
+  render(usingUniformsContext(<DateField name="x" value={now.toISOString()} />, { x: { type: Date } }));
+
+  expect(screen.getByTestId("date-field")).toBeInTheDocument();
+
+  const inputs = screen.getByTestId("date-field").getElementsByTagName("input");
+  expect(inputs[0].getAttribute("value")).toEqual(now.toISOString().slice(0, -14));
+  expect(inputs[1].getAttribute("value")).toBe("");
+});
+
+test("<DateField> - renders a input which correctly reacts on change (DatePicker)", () => {
+  const onChange = jest.fn();
+
+  const now = "2000-04-04";
+  render(usingUniformsContext(<DateField name="x" />, { x: { type: Date } }, { onChange }));
+
+  const input = screen.getByTestId("date-picker").getElementsByTagName("input")[0];
+  fireEvent.change(input, { target: { value: now } });
+
+  expect(onChange).toHaveBeenLastCalledWith("x", `${now}T00:00:00.000Z`);
+});
+
+test("<DateField> - renders a input which correctly reacts on change (DatePicker - empty)", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<DateField name="x" onChange={onChange} />, { x: { type: Date } }));
+
+  const input = screen.getByTestId("date-picker").getElementsByTagName("input")[0];
+  fireEvent.change(input, { target: { value: "" } });
+
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<DateField> - renders a input which correctly reacts on change (TimePicker - invalid)", () => {
+  const onChange = jest.fn();
+
+  const now = "10:00";
+  render(usingUniformsContext(<DateField name="x" />, { x: { type: Date } }, { onChange }));
+
+  const input = screen.getByTestId("time-picker").getElementsByTagName("input")[0];
+  fireEvent.change(input, { target: { value: now } });
+
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<DateField> - renders a input which correctly reacts on change (TimePicker - valid)", () => {
+  const onChange = jest.fn();
+
+  const date = "2000-04-04";
+  const time = "10:30";
+  render(usingUniformsContext(<DateField name="x" value={`${date}T00:00:00Z`} />, { x: { type: Date } }, { onChange }));
+
+  const input = screen.getByTestId("time-picker").getElementsByTagName("input")[0];
+  fireEvent.change(input, { target: { value: time } });
+
+  expect(onChange).toHaveBeenLastCalledWith("x", `${date}T${time}:00.000Z`);
+});
+
+test("<DateField> - test max property - valid", () => {
+  const date = "1998-12-31";
+  const max = "1999-01-01T00:00:00Z";
+  render(usingUniformsContext(<DateField name="x" max={max} value={`${date}T00:00:00Z`} />, { x: { type: Date } }));
+
+  expect(screen.queryByTestId("Should be before")).toBeNull();
+});
+
+test("<DateField> - test max property - invalid", () => {
+  const date = "1999-01-02";
+  const max = "1999-01-01T00:00:00.000Z";
+  render(usingUniformsContext(<DateField name="x" max={max} value={`${date}T00:00:00Z`} />, { x: { type: Date } }));
+
+  expect(screen.getByText(`Should be before ${max}`)).toBeInTheDocument();
+});
+
+test("<DateField> - test min property - valid", () => {
+  const date = "1999-01-02";
+  const min = "1999-01-01T00:00:00Z";
+  render(usingUniformsContext(<DateField name="x" min={min} value={`${date}T00:00:00Z`} />, { x: { type: Date } }));
+
+  expect(screen.queryByTestId("Should be after")).toBeNull();
+});
+
+test("<DateField> - test min property - invalid", () => {
+  const date = "1998-12-31";
+  const min = "1999-01-01T00:00:00.000Z";
+  render(usingUniformsContext(<DateField name="x" min={min} value={`${date}T00:00:00Z`} />, { x: { type: Date } }));
+
+  expect(screen.getByText(`Should be after ${min}`)).toBeInTheDocument();
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/ErrorField.test.tsx b/packages/uniforms-patternfly/src/__tests__/ErrorField.test.tsx
new file mode 100644
index 0000000000..496539759c
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/ErrorField.test.tsx
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { ErrorField } from "..";
+import { render, screen } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+const error = {
+  error: "validation-error",
+  reason: "X is required",
+  details: [{ name: "x", type: "required", details: { value: null } }],
+  message: "X is required [validation-error]",
+};
+
+test("<ErrorField> - works", () => {
+  render(usingUniformsContext(<ErrorField name="x" error={true} />, { x: { type: String } }));
+
+  expect(screen.getByTestId("error-field")).toBeInTheDocument();
+});
+
+test("<ErrorField> - renders correct error message (context)", () => {
+  render(usingUniformsContext(<ErrorField name="x" />, { x: { type: String } }, { error }));
+
+  expect(screen.getByTestId("error-field")).toBeInTheDocument();
+  expect(screen.getByText(error.reason)).toBeInTheDocument();
+});
+
+test("<ErrorField> - renders correct error message (specified)", () => {
+  render(
+    usingUniformsContext(<ErrorField name="x" error={error.details[0]} errorMessage={error.reason} />, {
+      x: { type: String },
+    })
+  );
+
+  expect(screen.getByTestId("error-field")).toBeInTheDocument();
+  expect(screen.getByText(error.reason)).toBeInTheDocument();
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/ErrorsField.test.tsx b/packages/uniforms-patternfly/src/__tests__/ErrorsField.test.tsx
new file mode 100644
index 0000000000..0b5fd0230d
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/ErrorsField.test.tsx
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { ErrorsField } from "..";
+import { render, screen } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+const error = {
+  error: "validation-error",
+  reason: "X is required",
+  details: [
+    { name: "x", type: "required", details: { value: null } },
+    { name: "y", type: "required", details: { value: null } },
+    { name: "z", type: "required", details: { value: null } },
+  ],
+  message: "X is required [validation-error]",
+};
+
+test("<ErrorsField> - renders list of correct error messages (context)", () => {
+  render(
+    usingUniformsContext(
+      <ErrorsField name="x" />,
+      { x: { type: String }, y: { type: String }, z: { type: String } },
+      { error }
+    )
+  );
+
+  expect(screen.getByTestId("errors-field")).toBeInTheDocument();
+  expect(screen.getByText("X is required")).toBeInTheDocument();
+  expect(screen.getByText("Y is required")).toBeInTheDocument();
+  expect(screen.getByText("Z is required")).toBeInTheDocument();
+});
+
+test("<ErrorsField> - renders children (specified)", () => {
+  render(
+    usingUniformsContext(
+      <ErrorsField name="x">Error message list</ErrorsField>,
+      { x: { type: String }, y: { type: String }, z: { type: String } },
+      { error }
+    )
+  );
+
+  expect(screen.getByText("Error message list")).toBeInTheDocument();
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/HiddenField.test.tsx b/packages/uniforms-patternfly/src/__tests__/HiddenField.test.tsx
new file mode 100644
index 0000000000..3855356b5c
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/HiddenField.test.tsx
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { HiddenField } from "..";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<HiddenField> - renders an input", () => {
+  render(usingUniformsContext(<HiddenField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("hidden-field")).toBeInTheDocument();
+});
+
+test("<HiddenField> - renders an input with correct id (inherited)", () => {
+  render(usingUniformsContext(<HiddenField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("hidden-field")).toBeInTheDocument();
+  expect(screen.getByTestId("hidden-field").getAttribute("id")).toBeTruthy();
+});
+
+test("<HiddenField> - renders an input with correct id (specified)", () => {
+  render(usingUniformsContext(<HiddenField name="x" id="y" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("hidden-field")).toBeInTheDocument();
+  expect(screen.getByTestId("hidden-field").getAttribute("id")).toBe("y");
+});
+
+test("<HiddenField> - renders an input with correct name", () => {
+  render(usingUniformsContext(<HiddenField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("hidden-field")).toBeInTheDocument();
+  expect(screen.getByTestId("hidden-field").getAttribute("name")).toBe("x");
+});
+
+test("<HiddenField> - renders an input with correct type", () => {
+  render(usingUniformsContext(<HiddenField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("hidden-field")).toBeInTheDocument();
+  expect(screen.getByTestId("hidden-field").getAttribute("type")).toBe("hidden");
+});
+
+test("<HiddenField> - renders an input with correct value (default)", () => {
+  render(usingUniformsContext(<HiddenField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("hidden-field")).toBeInTheDocument();
+  expect(screen.getByTestId("hidden-field").getAttribute("value")).toBe("");
+});
+
+test("<HiddenField> - renders an input with correct value (model)", () => {
+  render(usingUniformsContext(<HiddenField name="x" />, { x: { type: String } }, { model: { x: "y" } }));
+
+  expect(screen.getByTestId("hidden-field")).toBeInTheDocument();
+  expect(screen.getByTestId("hidden-field").getAttribute("value")).toBe("y");
+});
+
+test("<HiddenField> - renders an input which correctly reacts on model change", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<HiddenField name="x" />, { x: { type: String } }, { onChange }));
+
+  fireEvent.change(screen.getByTestId("hidden-field"), { target: { value: "y" } });
+  expect(screen.getByTestId("hidden-field").getAttribute("value")).toBe("y");
+});
+
+test("<HiddenField> - renders an input which correctly reacts on model change (empty)", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<HiddenField name="x" />, { x: { type: String } }, { onChange }));
+
+  fireEvent.change(screen.getByTestId("hidden-field"), { target: { value: undefined } });
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<HiddenField> - renders an input which correctly reacts on model change (same value)", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<HiddenField name="x" />, { x: { type: String } }, { model: { x: "y" }, onChange }));
+
+  fireEvent.change(screen.getByTestId("hidden-field"), { target: { value: "y" } });
+
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<HiddenField noDOM> - renders nothing", () => {
+  render(usingUniformsContext(<HiddenField noDOM name="x" />, { x: { type: String } }));
+
+  expect(screen.queryByTestId("hidden-field")).toBeNull();
+});
+
+test("<HiddenField noDOM> - renders nothing which correctly reacts on model change", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<HiddenField noDOM name="x" />, { x: { type: String } }, { onChange }));
+
+  expect(screen.queryByTestId("hidden-field")).toBeNull();
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<HiddenField noDOM> - renders nothing which correctly reacts on model change (empty)", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<HiddenField noDOM name="x" />, { x: { type: String } }, { onChange }));
+
+  expect(screen.queryByTestId("hidden-field")).toBeNull();
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<HiddenField noDOM> - renders nothing which correctly reacts on model change (same value)", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(<HiddenField noDOM name="x" />, { x: { type: String } }, { model: { x: "y" }, onChange })
+  );
+
+  expect(screen.queryByTestId("hidden-field")).toBeNull();
+  expect(onChange).not.toHaveBeenCalled();
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/ListAddField.test.tsx b/packages/uniforms-patternfly/src/__tests__/ListAddField.test.tsx
new file mode 100644
index 0000000000..868dbbe9cf
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/ListAddField.test.tsx
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { ListAddField } from "../";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+const onChange = jest.fn();
+
+const context = { onChange, model: { x: [] } };
+const schema = { x: { type: Array, maxCount: 3 }, "x.$": String };
+
+beforeEach(() => {
+  onChange.mockClear();
+});
+
+test("<ListAddField> - works", () => {
+  render(usingUniformsContext(<ListAddField name="x.$" />, schema, context));
+
+  expect(screen.getByTestId("list-add-field")).toBeInTheDocument();
+});
+
+test("<ListAddField> - prevents onClick when disabled", () => {
+  render(usingUniformsContext(<ListAddField name="x.1" disabled />, schema, context));
+
+  fireEvent.click(screen.getByTestId("list-add-field"));
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<ListAddField> - prevents onClick when limit reached", () => {
+  render(usingUniformsContext(<ListAddField name="x.1" />, { ...schema, x: { ...schema.x, maxCount: 0 } }, context));
+
+  fireEvent.click(screen.getByTestId("list-add-field"));
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<ListAddField> - correctly reacts on click", () => {
+  render(usingUniformsContext(<ListAddField name="x.1" value="y" />, schema, context));
+
+  fireEvent.click(screen.getByTestId("list-add-field"));
+  expect(onChange).toHaveBeenLastCalledWith("x", ["y"]);
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/ListDelField.test.tsx b/packages/uniforms-patternfly/src/__tests__/ListDelField.test.tsx
new file mode 100644
index 0000000000..cdf5710e7e
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/ListDelField.test.tsx
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { ListDelField } from "../";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+const onChange = jest.fn();
+
+const context = { onChange, model: { x: ["x", "y", "z"] } };
+const schema = { x: { type: Array, maxCount: 3 }, "x.$": String };
+
+beforeEach(() => {
+  onChange.mockClear();
+});
+
+test("<ListDelField> - works", () => {
+  render(usingUniformsContext(<ListDelField name="x.1" />, schema, context));
+
+  expect(screen.getByTestId("list-del-field")).toBeInTheDocument();
+});
+
+test("<ListDelField> - prevents onClick when disabled", () => {
+  render(usingUniformsContext(<ListDelField name="x.1" disabled />, schema, context));
+
+  fireEvent.click(screen.getByTestId("list-del-field"));
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<ListDelField> - prevents onClick when limit reached", () => {
+  render(usingUniformsContext(<ListDelField name="x.1" />, { ...schema, x: { ...schema.x, minCount: 3 } }, context));
+
+  fireEvent.click(screen.getByTestId("list-del-field"));
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<ListDelField> - correctly reacts on click", () => {
+  render(usingUniformsContext(<ListDelField name="x.1" />, schema, context));
+
+  fireEvent.click(screen.getByTestId("list-del-field"));
+  expect(onChange).toHaveBeenLastCalledWith("x", ["x", "z"]);
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/ListField.test.tsx b/packages/uniforms-patternfly/src/__tests__/ListField.test.tsx
new file mode 100644
index 0000000000..8501938884
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/ListField.test.tsx
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { ListField } from "../";
+import { render, screen } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<ListField> - works", () => {
+  render(usingUniformsContext(<ListField name="x" />, { x: { type: Array }, "x.$": { type: String } }));
+
+  expect(screen.getByTestId("list-field")).toBeInTheDocument();
+});
+
+test("<ListField> - renders ListAddField", () => {
+  render(
+    usingUniformsContext(<ListField name="x" label="ListFieldLabel" />, { x: { type: Array }, "x.$": { type: String } })
+  );
+
+  expect(screen.getByTestId("list-add-field")).toBeInTheDocument();
+  expect(screen.getByText("ListFieldLabel")).toBeInTheDocument();
+});
+
+test("<ListField> - renders correct label (specified)", () => {
+  render(
+    usingUniformsContext(<ListField name="x" label="ListFieldLabel" />, { x: { type: Array }, "x.$": { type: String } })
+  );
+
+  expect(screen.getByText("ListFieldLabel")).toBeInTheDocument();
+});
+
+test("<ListField> - renders correct numer of items with initialCount (specified)", () => {
+  render(
+    usingUniformsContext(<ListField name="x" initialCount={3} />, { x: { type: Array }, "x.$": { type: String } })
+  );
+
+  expect(screen.getAllByTestId("text-field")).toHaveLength(3);
+});
+
+test("<ListField> - renders children (specified)", () => {
+  const Child: () => null = jest.fn(() => null);
+
+  render(
+    usingUniformsContext(
+      <ListField name="x" initialCount={2}>
+        <Child />
+      </ListField>,
+      { x: { type: Array }, "x.$": { type: String } }
+    )
+  );
+
+  expect(Child).toHaveBeenCalledTimes(2);
+});
+
+test("<ListField> - renders children with correct name (children)", () => {
+  const Child: any = jest.fn((props: any) => <div {...props} data-testid={"child-div"} />);
+
+  render(
+    usingUniformsContext(
+      <ListField name="x" initialCount={2}>
+        <Child name="$" />
+      </ListField>,
+      { x: { type: Array }, "x.$": { type: String } }
+    )
+  );
+
+  const childDivs = screen.getAllByTestId("child-div");
+  expect(childDivs[0].getAttribute("name")).toBe("0");
+  expect(childDivs[1].getAttribute("name")).toBe("1");
+});
+
+test("<ListField> - renders children with correct name (value)", () => {
+  render(
+    usingUniformsContext(<ListField name="x" initialCount={2} />, { x: { type: Array }, "x.$": { type: String } })
+  );
+
+  const textFields = screen.getAllByTestId("text-field");
+  expect(textFields[0].getAttribute("name")).toBe("x.0");
+  expect(textFields[1].getAttribute("name")).toBe("x.1");
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/ListItemField.test.tsx b/packages/uniforms-patternfly/src/__tests__/ListItemField.test.tsx
new file mode 100644
index 0000000000..086f75f283
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/ListItemField.test.tsx
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { ListItemField } from "../";
+import { render, screen } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<ListItemField> - works", () => {
+  render(usingUniformsContext(<ListItemField name="x.1" />, { x: { type: Array }, "x.$": { type: String } }));
+
+  expect(screen.getByTestId("list-item-field")).toBeInTheDocument();
+});
+
+test("<ListItemField> - renders AutoField", () => {
+  const element = <ListItemField name="x.1" />;
+  render(usingUniformsContext(element, { x: { type: Array }, "x.$": { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+});
+
+test("<ListItemField> - renders children if specified", () => {
+  const Child: () => null = jest.fn(() => null);
+
+  render(
+    usingUniformsContext(
+      <ListItemField name="x.1">
+        <Child />
+      </ListItemField>,
+      { x: { type: Array }, "x.$": { type: String } }
+    )
+  );
+
+  expect(Child).toHaveBeenCalledTimes(1);
+});
+
+test("<ListItemField> - renders ListDelField", () => {
+  render(usingUniformsContext(<ListItemField name="x.1" />, { x: { type: Array }, "x.$": { type: String } }));
+
+  expect(screen.getByTestId("list-del-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field").getAttribute("name")).toBe("x.1");
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/LongTextField.test.tsx b/packages/uniforms-patternfly/src/__tests__/LongTextField.test.tsx
new file mode 100644
index 0000000000..4dcdcb0878
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/LongTextField.test.tsx
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { LongTextField } from "../";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<LongTextField> - renders a textarea", () => {
+  render(usingUniformsContext(<LongTextField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+});
+
+test("<LongTextField> - renders a textarea with correct disabled state", () => {
+  render(usingUniformsContext(<LongTextField name="x" disabled />, { x: { type: String } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  const textarea = screen.getByTestId("long-text-field").getElementsByTagName("textarea")[0];
+  expect(textarea).toBeDisabled();
+});
+
+test("<LongTextField> - renders a textarea with correct id (inherited)", () => {
+  render(usingUniformsContext(<LongTextField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  const textarea = screen.getByTestId("long-text-field").getElementsByTagName("textarea")[0];
+  expect(textarea.getAttribute("id")).toBeTruthy();
+});
+
+test("<LongTextField> - renders a textarea with correct id (specified)", () => {
+  render(usingUniformsContext(<LongTextField name="x" id="y" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  const textarea = screen.getByTestId("long-text-field").getElementsByTagName("textarea")[0];
+  expect(textarea.getAttribute("id")).toBe("y");
+});
+
+test("<LongTextField> - renders a textarea with correct name", () => {
+  render(usingUniformsContext(<LongTextField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  const textarea = screen.getByTestId("long-text-field").getElementsByTagName("textarea")[0];
+  expect(textarea.getAttribute("name")).toBe("x");
+});
+
+test("<LongTextField> - renders a textarea with correct placeholder", () => {
+  render(usingUniformsContext(<LongTextField name="x" placeholder="y" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  const textarea = screen.getByTestId("long-text-field").getElementsByTagName("textarea")[0];
+  expect(textarea.getAttribute("placeholder")).toBe("y");
+});
+
+test("<LongTextField> - renders a textarea with correct value (default)", () => {
+  render(usingUniformsContext(<LongTextField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  const textarea = screen.getByTestId("long-text-field").getElementsByTagName("textarea")[0];
+  expect(textarea.getAttribute("value")).toBe(null);
+});
+
+test("<LongTextField> - renders a textarea with correct value (model)", () => {
+  render(usingUniformsContext(<LongTextField name="x" />, { x: { type: String } }, { model: { x: "y" } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  expect(screen.getByText("y")).toBeInTheDocument();
+});
+
+test("<LongTextField> - renders a textarea with correct value (specified)", () => {
+  render(usingUniformsContext(<LongTextField name="x" value="y" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  expect(screen.getByText("y")).toBeInTheDocument();
+});
+
+test("<LongTextField> - renders a textarea which correctly reacts on change", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<LongTextField name="x" />, { x: { type: String } }, { onChange }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  const textarea = screen.getByTestId("long-text-field").getElementsByTagName("textarea")[0];
+  fireEvent.change(textarea, { target: { value: "y" } });
+  expect(onChange).toHaveBeenLastCalledWith("x", "y");
+});
+
+test("<LongTextField> - renders a textarea which correctly reacts on change (empty)", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<LongTextField name="x" />, { x: { type: String } }, { onChange }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  const textarea = screen.getByTestId("long-text-field").getElementsByTagName("textarea")[0];
+  fireEvent.change(textarea, { target: { value: "" } });
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<LongTextField> - renders a textarea which correctly reacts on change (same value)", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<LongTextField name="x" />, { x: { type: String } }, { model: { x: "y" }, onChange }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  const textarea = screen.getByTestId("long-text-field").getElementsByTagName("textarea")[0];
+  fireEvent.change(textarea, { target: { value: "y" } });
+  expect(screen.getByText("y")).toBeInTheDocument();
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<LongTextField> - renders a label", () => {
+  render(usingUniformsContext(<LongTextField name="x" label="y" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("long-text-field")).toBeInTheDocument();
+  expect(screen.getByText("y")).toBeInTheDocument();
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/NestField.test.tsx b/packages/uniforms-patternfly/src/__tests__/NestField.test.tsx
new file mode 100644
index 0000000000..930fcd4923
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/NestField.test.tsx
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { NestField } from "..";
+import { render, screen } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<NestField> - renders an <AutoField> for each field", () => {
+  render(
+    usingUniformsContext(<NestField name="x" />, {
+      x: { type: Object },
+      "x.a": { type: String },
+      "x.b": { type: Number },
+    })
+  );
+
+  expect(screen.getByTestId("nest-field")).toBeInTheDocument();
+  expect(screen.getByTestId("nest-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("nest-field").getElementsByTagName("input")[0].getAttribute("name")).toBe("x.a");
+  expect(screen.getByTestId("nest-field").getElementsByTagName("input")[1].getAttribute("name")).toBe("x.b");
+});
+
+test("<NestField> - renders custom content if given", () => {
+  render(
+    usingUniformsContext(
+      <NestField name="x">
+        <article data-test="content" />
+      </NestField>,
+      {
+        x: { type: Object },
+        "x.a": { type: String },
+        "x.b": { type: Number },
+      }
+    )
+  );
+
+  expect(screen.getByTestId("nest-field")).toBeInTheDocument();
+  expect(screen.getByTestId("nest-field").getElementsByTagName("article")).toHaveLength(1);
+  expect(screen.getByTestId("nest-field").getElementsByTagName("article")[0].getAttribute("data-test")).toBe("content");
+});
+
+test("<NestField> - renders a label", () => {
+  render(
+    usingUniformsContext(<NestField name="x" label="y" />, {
+      x: { type: Object },
+      "x.a": { type: String },
+      "x.b": { type: Number },
+    })
+  );
+
+  expect(screen.getByTestId("nest-field")).toBeInTheDocument();
+  expect(screen.getByTestId("nest-field").getElementsByTagName("label")).toHaveLength(3);
+  expect(screen.getByTestId("nest-field").getElementsByTagName("label")[0].textContent).toBe("y");
+  expect(screen.getByTestId("nest-field").getElementsByTagName("label")[1].textContent).toBe("A *");
+  expect(screen.getByTestId("nest-field").getElementsByTagName("label")[2].textContent).toBe("B *");
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/NumField.test.tsx b/packages/uniforms-patternfly/src/__tests__/NumField.test.tsx
new file mode 100644
index 0000000000..f083604b77
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/NumField.test.tsx
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { NumField } from "..";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<NumField> - renders an input", () => {
+  render(usingUniformsContext(<NumField name="x" />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+});
+
+test("<NumField> - renders an input with correct disabled state", () => {
+  render(usingUniformsContext(<NumField name="x" disabled />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field")).toBeDisabled();
+});
+
+test("<NumField> - renders an input with correct id (inherited)", () => {
+  render(usingUniformsContext(<NumField name="x" />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("id")).toBeTruthy();
+});
+
+test("<NumField> - renders an input with correct id (specified)", () => {
+  render(usingUniformsContext(<NumField name="x" id="y" />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("id")).toBe("y");
+});
+
+test("<NumField> - renders an input with correct max", () => {
+  render(usingUniformsContext(<NumField name="x" max={10} />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("max")).toBe("10");
+});
+
+test("<NumField> - renders an input with correct min", () => {
+  render(usingUniformsContext(<NumField name="x" min={10} />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("min")).toBe("10");
+});
+
+test("<NumField> - renders an input with correct name", () => {
+  render(usingUniformsContext(<NumField name="x" />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("name")).toBe("x");
+});
+
+test("<NumField> - renders an input with correct placeholder", () => {
+  render(usingUniformsContext(<NumField name="x" placeholder="y" />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("placeholder")).toBe("y");
+});
+
+test("<NumField> - renders an input with correct step (decimal)", () => {
+  render(usingUniformsContext(<NumField name="x" decimal />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("step")).toBe("0.01");
+});
+
+test("<NumField> - renders an input with correct step (integer)", () => {
+  render(usingUniformsContext(<NumField name="x" decimal={false} />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("step")).toBe("1");
+});
+
+test("<NumField> - renders an input with correct type", () => {
+  render(usingUniformsContext(<NumField name="x" />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("type")).toBe("number");
+});
+
+test("<NumField> - renders an input with correct value (default)", () => {
+  render(usingUniformsContext(<NumField name="x" />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("value")).toBe("");
+});
+
+test("<NumField> - renders an input with correct value (decimal)", () => {
+  const onChange = jest.fn();
+  render(usingUniformsContext(<NumField name="x" decimal={true} />, { x: { type: Number } }, { onChange }));
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+
+  // NOTE: All following tests are here to cover hacky NumField implementation.
+  const spy = jest.spyOn(global.console, "error").mockImplementation(() => {});
+
+  [
+    { value: "0.1", expected: 0.1 },
+    { value: "2", expected: 2 },
+    { value: "-1", expected: -1 },
+  ].forEach(({ value, expected }) => {
+    fireEvent.change(screen.getByTestId("num-field"), { target: { value } });
+    expect(onChange).toHaveBeenLastCalledWith("x", expected);
+  });
+
+  spy.mockRestore();
+});
+
+test("<NumField> - renders an input with correct value (not decimal)", () => {
+  const onChange = jest.fn();
+  render(usingUniformsContext(<NumField name="x" decimal={false} />, { x: { type: Number } }, { onChange }));
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+
+  // NOTE: All following tests are here to cover hacky NumField implementation.
+  const spy = jest.spyOn(global.console, "error").mockImplementation(() => {});
+
+  [
+    { value: "0.1", expected: 0 },
+    { value: "2", expected: 2 },
+    { value: "-1", expected: -1 },
+  ].forEach(({ value, expected }) => {
+    fireEvent.change(screen.getByTestId("num-field"), { target: { value } });
+    expect(onChange).toHaveBeenLastCalledWith("x", expected);
+  });
+
+  spy.mockRestore();
+});
+
+test("<NumField> - renders an input with correct value (specified)", () => {
+  render(usingUniformsContext(<NumField name="x" value={2} />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  expect(screen.getByTestId("num-field").getAttribute("value")).toBe("2");
+});
+
+test("<NumField> - renders an input which correctly reacts on change", () => {
+  const onChange = jest.fn();
+  const value = "1";
+
+  render(usingUniformsContext(<NumField name="x" />, { x: { type: Number } }, { onChange }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  fireEvent.change(screen.getByTestId("num-field"), { target: { value } });
+
+  expect(onChange).toHaveBeenLastCalledWith("x", 1);
+});
+
+test("<NumField> - renders an input which correctly reacts on change (decimal on decimal)", () => {
+  const onChange = jest.fn();
+  const value = "2.5";
+
+  render(usingUniformsContext(<NumField name="x" decimal />, { x: { type: Number } }, { onChange }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  fireEvent.change(screen.getByTestId("num-field"), { target: { value } });
+
+  expect(onChange).toHaveBeenLastCalledWith("x", 2.5);
+});
+
+test("<NumField> - renders an input which correctly reacts on change (decimal on integer)", () => {
+  const onChange = jest.fn();
+  const value = "2.5";
+
+  render(usingUniformsContext(<NumField name="x" decimal={false} />, { x: { type: Number } }, { onChange }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  fireEvent.change(screen.getByTestId("num-field"), { target: { value } });
+
+  expect(onChange).toHaveBeenLastCalledWith("x", 2);
+});
+
+test("<NumField> - renders an input which correctly reacts on change (empty)", () => {
+  const onChange = jest.fn();
+  const value = "";
+
+  render(usingUniformsContext(<NumField name="x" />, { x: { type: Number } }, { onChange }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  fireEvent.change(screen.getByTestId("num-field"), { target: { value } });
+
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<NumField> - renders an input which correctly reacts on change (same value)", () => {
+  const onChange = jest.fn();
+  const value = "1";
+
+  render(usingUniformsContext(<NumField name="x" />, { x: { type: Number } }, { model: { x: 1 }, onChange }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  fireEvent.change(screen.getByTestId("num-field"), { target: { value } });
+
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<NumField> - renders an input which correctly reacts on change (zero)", () => {
+  const onChange = jest.fn();
+  const value = "0";
+
+  render(usingUniformsContext(<NumField name="x" />, { x: { type: Number } }, { onChange }));
+
+  expect(screen.getByTestId("num-field")).toBeInTheDocument();
+  fireEvent.change(screen.getByTestId("num-field"), { target: { value } });
+
+  expect(onChange).toHaveBeenLastCalledWith("x", 0);
+});
+
+test("<NumField> - renders a label", () => {
+  render(usingUniformsContext(<NumField required={false} name="x" label="y" />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("wrapper-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y");
+  const id = screen.getByTestId("num-field").getAttribute("id");
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].getAttribute("for")).toBe(id);
+});
+
+test("<NumField> - renders a label", () => {
+  render(usingUniformsContext(<NumField required={true} name="x" label="y" />, { x: { type: Number } }));
+
+  expect(screen.getByTestId("wrapper-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y *");
+  const id = screen.getByTestId("num-field").getAttribute("id");
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].getAttribute("for")).toBe(id);
+});
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/src/__tests__/QuickForm.test.tsx
similarity index 56%
copy from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to packages/uniforms-patternfly/src/__tests__/QuickForm.test.tsx
index 7f7980f259..db096667b1 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/src/__tests__/QuickForm.test.tsx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,4 +14,12 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+import * as React from "react";
+import { QuickForm } from "..";
+import { usingUniformsContext, createSimpleSchema } from "./test-utils";
+import { render, screen } from "@testing-library/react";
+
+test("<QuickForm> - works", () => {
+  render(usingUniformsContext(<QuickForm schema={createSimpleSchema()} />));
+  expect(screen.getByTestId("base-form")).toBeInTheDocument();
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/RadioField.test.tsx b/packages/uniforms-patternfly/src/__tests__/RadioField.test.tsx
new file mode 100644
index 0000000000..b1f6f51aa7
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/RadioField.test.tsx
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { RadioField } from "..";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<RadioField> - renders a set of checkboxes", () => {
+  render(usingUniformsContext(<RadioField name="x" allowedValues={[]} />, { x: { type: String, allowedValues: [] } }));
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")).toHaveLength(0);
+});
+
+test("<RadioField> - renders a set of checkboxes", () => {
+  render(
+    usingUniformsContext(<RadioField name="x" allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")).toHaveLength(2);
+});
+
+test("<RadioField> - renders a set of checkboxes with correct disabled state", () => {
+  render(
+    usingUniformsContext(<RadioField name="x" disabled allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[0]).toBeDisabled();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[1]).toBeDisabled();
+});
+
+test("<RadioField> - renders a set of checkboxes with correct id (inherited)", () => {
+  render(
+    usingUniformsContext(<RadioField name="x" allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[0].getAttribute("id")).toBeTruthy();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[1].getAttribute("id")).toBeTruthy();
+});
+
+test("<RadioField> - renders a set of checkboxes with correct id (specified)", () => {
+  render(
+    usingUniformsContext(<RadioField name="x" id="y" allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[0].getAttribute("id")).toBe("y");
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[1].getAttribute("id")).toBe("y");
+});
+
+test("<RadioField> - renders a set of checkboxes with correct name", () => {
+  render(
+    usingUniformsContext(<RadioField name="x" allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[0].getAttribute("name")).toBe("x");
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[1].getAttribute("name")).toBe("x");
+});
+
+test("<RadioField> - renders a set of checkboxes with correct options", () => {
+  render(
+    usingUniformsContext(<RadioField name="x" allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("wrapper-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")).toHaveLength(2);
+  expect(screen.getByTestId("radio-field").getElementsByTagName("label")[0].textContent).toBe("a");
+  expect(screen.getByTestId("radio-field").getElementsByTagName("label")[1].textContent).toBe("b");
+});
+
+test("<RadioField> - renders a set of checkboxes with correct options (transform)", () => {
+  render(
+    usingUniformsContext(
+      <RadioField name="x" transform={(x: string) => x.toUpperCase()} allowedValues={["a", "b"]} />,
+      { x: { type: String, allowedValues: ["a", "b"] } }
+    )
+  );
+
+  expect(screen.getByTestId("wrapper-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")).toHaveLength(2);
+  expect(screen.getByTestId("radio-field").getElementsByTagName("label")[0].textContent).toBe("A");
+  expect(screen.getByTestId("radio-field").getElementsByTagName("label")[1].textContent).toBe("B");
+});
+
+test("<RadioField> - renders a set of checkboxes with correct value (default)", () => {
+  render(
+    usingUniformsContext(<RadioField name="x" allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[0]).not.toBeChecked();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[1]).not.toBeChecked();
+});
+
+test("<RadioField> - renders a set of checkboxes with correct value (model)", () => {
+  render(
+    usingUniformsContext(
+      <RadioField name="x" allowedValues={["a", "b"]} />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { model: { x: "b" } }
+    )
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[0]).not.toBeChecked();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[1]).toBeChecked();
+});
+
+test("<RadioField> - renders a set of checkboxes with correct value (specified)", () => {
+  render(
+    usingUniformsContext(<RadioField name="x" value="b" allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[0]).not.toBeChecked();
+  expect(screen.getByTestId("radio-field").getElementsByTagName("input")[1]).toBeChecked();
+});
+
+test("<RadioField> - renders a set of checkboxes which correctly reacts on change", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(
+      <RadioField name="x" allowedValues={["a", "b"]} />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { onChange }
+    )
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  const inputs = screen.getByTestId("radio-field").getElementsByTagName("input");
+  expect(inputs).toHaveLength(2);
+  fireEvent.click(inputs[1]);
+  expect(onChange).toHaveBeenLastCalledWith("x", "b");
+});
+
+test("<RadioField> - renders a set of checkboxes which correctly reacts on change (same value)", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(
+      <RadioField name="x" allowedValues={["a", "b"]} />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { model: { x: "b" }, onChange }
+    )
+  );
+
+  expect(screen.getByTestId("radio-field")).toBeInTheDocument();
+  const inputs = screen.getByTestId("radio-field").getElementsByTagName("input");
+  expect(inputs).toHaveLength(2);
+  fireEvent.click(inputs[0]);
+  expect(onChange).toHaveBeenLastCalledWith("x", "a");
+});
+
+test("<RadioField> - renders a label", () => {
+  render(
+    usingUniformsContext(<RadioField required={false} name="x" label="y" allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("wrapper-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")).toHaveLength(3);
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y");
+});
+
+test("<RadioField> - renders a label (required)", () => {
+  render(
+    usingUniformsContext(<RadioField required={true} name="x" label="y" allowedValues={["a", "b"]} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("wrapper-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")).toHaveLength(3);
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y *");
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/SelectField.test.tsx b/packages/uniforms-patternfly/src/__tests__/SelectField.test.tsx
new file mode 100644
index 0000000000..74d2e4c6b3
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/SelectField.test.tsx
@@ -0,0 +1,498 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { SelectField } from "..";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<SelectField checkboxes> - renders a set of checkboxes", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" checkboxes={true} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes with correct disabled state", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" disabled checkboxes={true} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[0]).toBeDisabled();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1]).toBeDisabled();
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes with correct id (inherited)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" checkboxes={true} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[0].getAttribute("id")).toBeTruthy();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1].getAttribute("id")).toBeTruthy();
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes with correct id (specified)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" id="y" checkboxes={true} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[0].getAttribute("id")).toBe("y-a");
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1].getAttribute("id")).toBe("y-b");
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes with correct name", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" checkboxes={true} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[0].getAttribute("name")).toBe("x");
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1].getAttribute("name")).toBe("x");
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes with correct options", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" checkboxes={true} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("label")).toHaveLength(2);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("label")[0].textContent).toBe("a");
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("label")[1].textContent).toBe("b");
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes with correct options (transform)", () => {
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" transform={(x: string) => x.toUpperCase()} checkboxes={true} />,
+      { x: { type: String, allowedValues: ["a", "b"] } }
+    )
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("label")).toHaveLength(2);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("label")[0].textContent).toBe("A");
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("label")[1].textContent).toBe("B");
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes with correct value (default)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" checkboxes={true} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[0]).not.toBeChecked();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1]).not.toBeChecked();
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes with correct value (model)", () => {
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" checkboxes={true} />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { model: { x: "b" } }
+    )
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[0]).not.toBeChecked();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1]).toBeChecked();
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes with correct value (specified)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" value="b" checkboxes={true} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[0]).not.toBeChecked();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1]).toBeChecked();
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes which correctly reacts on change", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" checkboxes={true} />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { onChange }
+    )
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+
+  fireEvent.click(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1]);
+  expect(onChange).toHaveBeenLastCalledWith("x", "b");
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes which correctly reacts on change (array check)", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" checkboxes={true} />,
+      {
+        x: { type: Array },
+        "x.$": { type: String, allowedValues: ["a", "b"] },
+      },
+      { onChange }
+    )
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+
+  fireEvent.click(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1]);
+  expect(onChange).toHaveBeenLastCalledWith("x", ["b"]);
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes which correctly reacts on change (array uncheck)", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" value={["b"]} checkboxes={true} />,
+      {
+        x: { type: Array },
+        "x.$": { type: String, allowedValues: ["a", "b"] },
+      },
+      { onChange }
+    )
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+
+  fireEvent.click(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[1]);
+  expect(onChange).toHaveBeenLastCalledWith("x", []);
+});
+
+test("<SelectField checkboxes> - renders a set of checkboxes which correctly reacts on change (same value)", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" checkboxes={true} />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { model: { x: "b" }, onChange }
+    )
+  );
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")).toHaveLength(2);
+
+  fireEvent.click(screen.getByTestId("select-checkbox-field").getElementsByTagName("input")[0]);
+  expect(onChange).toHaveBeenLastCalledWith("x", "a");
+});
+
+test("<SelectField checkboxes> - renders a label", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" label="y" checkboxes={true} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-checkbox-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("label")).toHaveLength(3);
+  expect(screen.getByTestId("select-checkbox-field").getElementsByTagName("label")[1].textContent).toBe("a");
+});
+
+test("<SelectField> - renders a select", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+});
+
+test("<SelectField> - renders a select with correct disabled state", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" disabled />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[0]).toBeDisabled();
+});
+
+test("<SelectField> - renders a select with correct id (specified)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" id="y" />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  expect(screen.getByTestId("select-inputs-field").getAttribute("id")).toBe("y");
+});
+
+test("<SelectField> - renders a select with correct name", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  // patternfly don't pass down the name property
+  // expect(screen.getByTestId("select-inputs-field").getAttribute("name")).toBe("x");
+});
+
+test("<SelectField> - renders a select with correct options", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[0]);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(3);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[1].textContent).toBe("a");
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[2].textContent).toBe("b");
+});
+
+test("<SelectField> - renders a select with correct options (transform)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" transform={(x: string) => x.toUpperCase()} />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[0]);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(3);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[1].textContent).toBe("A");
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[2].textContent).toBe("B");
+});
+
+test("<SelectField> - renders a select with correct options (numbers)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" />, { x: { type: String, allowedValues: [1, 2] } })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[0]);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(3);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[1].textContent).toBe("1");
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[2].textContent).toBe("2");
+});
+
+test("<SelectField> - renders a select with correct placeholder (implicit)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" placeholder="y" />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("span")[0].textContent).toBe("y");
+  expect(screen.getByTestId("select-inputs-field").getAttribute("value")).toBe(null);
+});
+
+test("<SelectField> - renders a select with correct value (default)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  expect(screen.getByTestId("select-inputs-field").getAttribute("value")).toBe(null);
+});
+
+test("<SelectField> - renders a select with correct value (model)", () => {
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { model: { x: "b" } }
+    )
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  expect(screen.getByTestId("select-inputs-field").textContent).toBe("b");
+});
+
+test("<SelectField> - renders a select with correct value (specified)", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} name="x" value="b" />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("span")[0].textContent).toBe("b");
+});
+
+test("<SelectField> - renders a select which correctly reacts on change", () => {
+  const onChange = jest.fn();
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { onChange }
+    )
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[0]);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(3);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[2]);
+  expect(onChange).toHaveBeenLastCalledWith("x", "b");
+});
+
+test("<SelectField> - renders a select which correctly reacts on change (array)", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" value={undefined} />,
+      {
+        x: { type: Array },
+        "x.$": { type: String, allowedValues: ["a", "b"] },
+      },
+      { onChange }
+    )
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[0]);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(3);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[2]);
+  expect(onChange).toHaveBeenLastCalledWith("x", ["b"]);
+});
+
+test("<SelectField> - renders a select which correctly reacts on change (placeholder)", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" placeholder={"test"} />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { onChange }
+    )
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[0]);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(4);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[1]);
+  expect(onChange).toHaveBeenCalled();
+});
+
+test("<SelectField> - renders a select which correctly reacts on change (same value)", () => {
+  const onChange = jest.fn();
+
+  render(
+    usingUniformsContext(
+      <SelectField onToggle={() => {}} name="x" />,
+      { x: { type: String, allowedValues: ["a", "b"] } },
+      { model: { x: "b" }, onChange }
+    )
+  );
+
+  expect(screen.getByTestId("select-inputs-field")).toBeInTheDocument();
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(1);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[0]);
+  expect(screen.getByTestId("select-inputs-field").getElementsByTagName("button")).toHaveLength(3);
+  fireEvent.click(screen.getByTestId("select-inputs-field").getElementsByTagName("button")[2]);
+  expect(onChange).toHaveBeenLastCalledWith("x", "b");
+});
+
+test("<SelectField> - renders a label", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} required={false} name="x" label="y" />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("wrapper-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")).toHaveLength(1);
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y");
+});
+
+test("<SelectField> - renders a label", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} required={true} name="x" label="y" />, {
+      x: { type: String, allowedValues: ["a", "b"] },
+    })
+  );
+
+  expect(screen.getByTestId("wrapper-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")).toHaveLength(1);
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y *");
+});
+
+test("<SelectField> - renders a number label", () => {
+  render(
+    usingUniformsContext(<SelectField onToggle={() => {}} required={true} name="x" label={1} />, {
+      x: { type: Number, allowedValues: [1, 2] },
+    })
+  );
+
+  expect(screen.getByTestId("wrapper-field")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")).toHaveLength(1);
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("1 *");
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/SubmitField.test.tsx b/packages/uniforms-patternfly/src/__tests__/SubmitField.test.tsx
new file mode 100644
index 0000000000..3bb1c07a54
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/SubmitField.test.tsx
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { SubmitField } from "..";
+import { render, screen } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<SubmitField> - renders", () => {
+  render(usingUniformsContext(<SubmitField />));
+
+  expect(screen.getByTestId("submit-field")).toBeInTheDocument();
+});
+
+test("<SubmitField> - renders disabled if error", () => {
+  render(usingUniformsContext(<SubmitField />, undefined, { error: {} }));
+
+  expect(screen.getByTestId("submit-field")).toBeInTheDocument();
+  expect(screen.getByTestId("submit-field")).toBeDisabled();
+});
+
+test("<SubmitField> - renders enabled if error and enabled", () => {
+  render(usingUniformsContext(<SubmitField disabled={false} />, undefined, { error: {} }));
+
+  expect(screen.getByTestId("submit-field")).toBeInTheDocument();
+  expect(screen.getByTestId("submit-field")).toBeEnabled();
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/TextField.test.tsx b/packages/uniforms-patternfly/src/__tests__/TextField.test.tsx
new file mode 100644
index 0000000000..4f2e0d4682
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/TextField.test.tsx
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { TextField } from "../";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { usingUniformsContext } from "./test-utils";
+
+test("<TextField> - renders an input", () => {
+  render(usingUniformsContext(<TextField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+});
+
+test("<TextField> - renders an input with correct disabled state", () => {
+  render(usingUniformsContext(<TextField name="x" disabled />, { x: { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field")).toBeDisabled();
+});
+
+test("<TextField> - renders an input with correct id (inherited)", () => {
+  render(usingUniformsContext(<TextField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field").getAttribute("id")).toBeTruthy();
+});
+
+test("<TextField> - renders an input with correct id (specified)", () => {
+  render(usingUniformsContext(<TextField name="x" id="y" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field").getAttribute("id")).toBe("y");
+});
+
+test("<TextField> - renders an input with correct name", () => {
+  render(usingUniformsContext(<TextField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field").getAttribute("name")).toBe("x");
+});
+
+test("<TextField> - renders an input with correct placeholder", () => {
+  render(usingUniformsContext(<TextField name="x" placeholder="y" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field").getAttribute("placeholder")).toBe("y");
+});
+
+test("<TextField> - renders an input with correct type", () => {
+  render(usingUniformsContext(<TextField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field").getAttribute("type")).toBe("text");
+});
+
+test("<TextField> - renders an input with correct value (default)", () => {
+  render(usingUniformsContext(<TextField name="x" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field").getAttribute("value")).toBe("");
+});
+
+test("<TextField> - renders an input with correct value (model)", () => {
+  render(usingUniformsContext(<TextField name="x" />, { x: { type: String } }, { model: { x: "y" } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field").getAttribute("value")).toBe("y");
+});
+
+test("<TextField> - renders an input with correct value (specified)", () => {
+  render(usingUniformsContext(<TextField name="x" value="y" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field").getAttribute("value")).toBe("y");
+});
+
+test("<TextField> - renders an input which correctly reacts on change", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<TextField name="x" />, { x: { type: String } }, { onChange }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  fireEvent.change(screen.getByTestId("text-field"), { target: { value: "y" } });
+  expect(onChange).toHaveBeenLastCalledWith("x", "y");
+});
+
+test("<TextField> - renders an input which correctly reacts on change (empty)", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<TextField name="x" />, { x: { type: String } }, { onChange }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  fireEvent.change(screen.getByTestId("text-field"), { target: { value: "" } });
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<TextField> - renders an input which correctly reacts on change (same value)", () => {
+  const onChange = jest.fn();
+
+  render(usingUniformsContext(<TextField name="x" />, { x: { type: String } }, { model: { x: "y" }, onChange }));
+
+  expect(screen.getByTestId("text-field")).toBeInTheDocument();
+  fireEvent.change(screen.getByTestId("text-field"), { target: { value: "y" } });
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<TextField> - renders a label", () => {
+  render(usingUniformsContext(<TextField required={false} name="x" label="y" />, { x: { type: String } }));
+
+  expect(screen.getByText("y")).toBeInTheDocument();
+});
+
+test("<TextField> - renders a label", () => {
+  render(usingUniformsContext(<TextField required={true} name="x" label="y" />, { x: { type: String } }));
+
+  expect(screen.getByText("y")).toBeInTheDocument();
+  expect(screen.getByText("*")).toBeInTheDocument();
+});
+
+test("<TextField> - renders a help", () => {
+  render(usingUniformsContext(<TextField required={false} name="x" helperText="y" />, { x: { type: String } }));
+
+  expect(screen.getByTestId("wrapper-field").querySelector(".pf-c-form__helper-text")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").querySelector(".pf-c-form__helper-text")?.textContent).toBe("y");
+});
+
+test("<TextField> - renders a initial value on date field (DatePicker)", () => {
+  const date = "2000-04-04";
+  render(
+    usingUniformsContext(
+      <TextField required={true} name="x" label="y" type={"date"} />,
+      { x: { type: String } },
+      { model: { x: date } }
+    )
+  );
+
+  expect(screen.getByTestId("text-field-date-picker")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field-date-picker").getElementsByTagName("input")[0].value).toBe(date);
+});
+
+test("<TextField> - renders a disabled date field (DatePicker)", () => {
+  render(
+    usingUniformsContext(<TextField required={true} name="x" label="y" type={"date"} disabled={true} />, {
+      x: { type: String },
+    })
+  );
+
+  expect(screen.getByTestId("text-field-date-picker")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field-date-picker").getElementsByTagName("input")[0]).toBeDisabled();
+});
+
+test("<TextField> - renders a input which correctly reacts on change (DatePicker)", () => {
+  const onChange = jest.fn();
+  const date = "2000-04-04";
+
+  render(
+    usingUniformsContext(
+      <TextField required={true} name="x" label="y" type={"date"} />,
+      { x: { type: String } },
+      { onChange }
+    )
+  );
+
+  const input = screen.getByTestId("text-field-date-picker").getElementsByTagName("input")[0];
+  fireEvent.change(input, { target: { value: date } });
+
+  expect(screen.getByTestId("text-field-date-picker")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y *");
+  expect(onChange).toHaveBeenLastCalledWith("x", date);
+});
+
+test("<TextField> - renders a input which correctly reacts on change (DatePicker - empty)", () => {
+  const onChange = jest.fn();
+  const date = "";
+
+  render(
+    usingUniformsContext(
+      <TextField required={true} name="x" label="y" type={"date"} />,
+      { x: { type: String } },
+      { onChange }
+    )
+  );
+
+  const input = screen.getByTestId("text-field-date-picker").getElementsByTagName("input")[0];
+  fireEvent.change(input, { target: { value: date } });
+
+  expect(screen.getByTestId("text-field-date-picker")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y *");
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<TextField> - renders a initial value on time field (TimePicker)", () => {
+  const time = "10:00";
+  render(
+    usingUniformsContext(
+      <TextField required={true} name="x" label="y" type={"time"} />,
+      { x: { type: String } },
+      { model: { x: time } }
+    )
+  );
+
+  expect(screen.getByTestId("text-field-time-picker")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field-time-picker").getAttribute("value")).toBe(time);
+});
+
+test("<TextField> - renders a disabled date field (TimePicker)", () => {
+  render(
+    usingUniformsContext(<TextField required={true} name="x" label="y" type={"time"} disabled={true} />, {
+      x: { type: String },
+    })
+  );
+
+  expect(screen.getByTestId("text-field-time-picker")).toBeInTheDocument();
+  expect(screen.getByTestId("text-field-time-picker").getElementsByTagName("input")[0]).toBeDisabled();
+});
+
+test("<TextField> - renders a input which correctly reacts on change (TimePicker)", () => {
+  const onChange = jest.fn();
+  const time = "10:10";
+
+  render(
+    usingUniformsContext(
+      <TextField required={true} name="x" label="y" type={"time"} />,
+      { x: { type: String } },
+      { onChange }
+    )
+  );
+
+  const input = screen.getByTestId("text-field-time-picker").getElementsByTagName("input")[0];
+  fireEvent.change(input, { target: { value: time } });
+
+  expect(screen.getByTestId("text-field-time-picker")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y *");
+  expect(onChange).toHaveBeenLastCalledWith("x", "10:10:00");
+});
+
+test("<TextField> - renders a input which correctly reacts on change (TimePicker - empty)", () => {
+  const onChange = jest.fn();
+  const time = "";
+
+  render(
+    usingUniformsContext(
+      <TextField required={true} name="x" label="y" type={"time"} />,
+      { x: { type: String } },
+      { onChange }
+    )
+  );
+
+  const input = screen.getByTestId("text-field-time-picker").getElementsByTagName("input")[0];
+  fireEvent.change(input, { target: { value: time } });
+
+  expect(screen.getByTestId("text-field-time-picker")).toBeInTheDocument();
+  expect(screen.getByTestId("wrapper-field").getElementsByTagName("label")[0].textContent).toBe("y *");
+  expect(onChange).not.toHaveBeenCalled();
+});
+
+test("<TextField> - test max property (TimePicker - valid)", () => {
+  const time = "10:00";
+  const max = "12:00";
+  render(
+    usingUniformsContext(<TextField name="x" label="y" max={max} type={"time"} value={time} />, { x: { type: String } })
+  );
+
+  expect(screen.getByTestId("text-field-time-picker")).toBeInTheDocument();
+  expect(screen.queryByText("Should be before")).not.toBeInTheDocument();
+});
+
+test("<TextField> - test max property (TimePicker - invalid)", () => {
+  const time = "13:00";
+  const max = "12:00";
+  render(
+    usingUniformsContext(<TextField name="x" label="y" max={max} type={"time"} value={time} />, { x: { type: String } })
+  );
+
+  expect(screen.getByTestId("text-field-time-picker")).toBeInTheDocument();
+  expect(screen.queryByText("Should be before 12:00")).toBeInTheDocument();
+});
+
+test("<TextField> - test min property (TimePicker - valid)", () => {
+  const time = "13:00";
+  const min = "12:00";
+  render(
+    usingUniformsContext(<TextField name="x" label="y" min={min} type={"time"} value={time} />, { x: { type: String } })
+  );
+
+  expect(screen.getByTestId("text-field-time-picker")).toBeInTheDocument();
+  expect(screen.queryByText("Should be after")).not.toBeInTheDocument();
+});
+
+test("<TextField> - test min property (TimePicker - invalid)", () => {
+  const time = "10:00";
+  const min = "12:00";
+  render(
+    usingUniformsContext(<TextField name="x" label="y" min={min} type={"time"} value={time} />, { x: { type: String } })
+  );
+
+  expect(screen.getByTestId("text-field-time-picker")).toBeInTheDocument();
+  expect(screen.queryByText("Should be after 12:00")).toBeInTheDocument();
+});
+
+test("<TextField> - test max property (DatePicker - valid)", () => {
+  const date = "2000-01-01";
+  const max = "2000-01-02";
+  render(
+    usingUniformsContext(<TextField name="x" label="y" max={max} type={"date"} value={date} />, { x: { type: String } })
+  );
+
+  expect(screen.getByTestId("text-field-date-picker")).toBeInTheDocument();
+  expect(screen.queryByText("Should be before")).not.toBeInTheDocument();
+});
+
+test("<TextField> - test max property (DatePicker - invalid)", () => {
+  const date = "2000-01-02";
+  const max = "2000-01-01";
+  render(
+    usingUniformsContext(<TextField name="x" label="y" max={max} type={"date"} value={date} />, { x: { type: String } })
+  );
+
+  expect(screen.getByTestId("text-field-date-picker")).toBeInTheDocument();
+  expect(screen.queryByText("Should be before 2000-01-01")).toBeInTheDocument();
+});
+
+test("<TextField> - test min property (DatePicker - valid)", () => {
+  const date = "2000-01-02";
+  const min = "2000-01-01";
+  render(
+    usingUniformsContext(<TextField name="x" label="y" min={min} type={"date"} value={date} />, { x: { type: String } })
+  );
+
+  expect(screen.getByTestId("text-field-date-picker")).toBeInTheDocument();
+  expect(screen.queryByText("Should be after")).not.toBeInTheDocument();
+});
+
+test("<TextField> - test min property (DatePicker - invalid)", () => {
+  const date = "2000-01-01";
+  const min = "2000-01-02";
+  render(
+    usingUniformsContext(<TextField name="x" label="y" min={min} type={"date"} value={date} />, { x: { type: String } })
+  );
+
+  expect(screen.getByTestId("text-field-date-picker")).toBeInTheDocument();
+  expect(screen.queryByText("Should be after 2000-01-02")).toBeInTheDocument();
+});
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/src/__tests__/ValidateQuickForm.test.tsx
similarity index 55%
copy from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
copy to packages/uniforms-patternfly/src/__tests__/ValidateQuickForm.test.tsx
index 7f7980f259..f412b22d69 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/src/__tests__/ValidateQuickForm.test.tsx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,4 +14,12 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+import * as React from "react";
+import { ValidatedQuickForm } from "..";
+import { render, screen } from "@testing-library/react";
+import { createSimpleSchema, usingUniformsContext } from "./test-utils";
+
+test("<ValidatedQuickForm> - works", () => {
+  render(usingUniformsContext(<ValidatedQuickForm schema={createSimpleSchema()} />));
+  expect(screen.getByTestId("base-form")).toBeInTheDocument();
+});
diff --git a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/src/__tests__/ValidatedForm.test.tsx
similarity index 56%
rename from packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
rename to packages/uniforms-patternfly/src/__tests__/ValidatedForm.test.tsx
index 7f7980f259..41d09b69e3 100644
--- a/packages/serverless-logic-web-tools/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/src/__tests__/ValidatedForm.test.tsx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,4 +14,12 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.25.0";
+import * as React from "react";
+import { ValidatedForm } from "..";
+import { createSimpleSchema, usingUniformsContext } from "./test-utils";
+import { render, screen } from "@testing-library/react";
+
+test("<ValidatedForm> - works", () => {
+  render(usingUniformsContext(<ValidatedForm schema={createSimpleSchema()} />));
+  expect(screen.getByTestId("base-form")).toBeInTheDocument();
+});
diff --git a/packages/uniforms-patternfly/src/__tests__/index.test.ts b/packages/uniforms-patternfly/src/__tests__/index.test.ts
new file mode 100644
index 0000000000..a893f84235
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/index.test.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as unstyled from "..";
+
+it("exports everything", () => {
+  expect(unstyled).toEqual({
+    AutoFields: expect.any(Function),
+    AutoField: expect.any(Function),
+    AutoForm: expect.any(Function),
+    BaseForm: expect.any(Function),
+    BoolField: expect.any(Function),
+    DateField: expect.any(Function),
+    ErrorField: expect.any(Function),
+    ErrorsField: expect.any(Function),
+    HiddenField: expect.any(Function),
+    ListAddField: expect.any(Function),
+    ListDelField: expect.any(Function),
+    ListField: expect.any(Function),
+    ListItemField: expect.any(Function),
+    LongTextField: expect.any(Function),
+    NestField: expect.any(Function),
+    NumField: expect.any(Function),
+    QuickForm: expect.any(Function),
+    RadioField: expect.any(Function),
+    SelectField: expect.any(Function),
+    SubmitField: expect.any(Function),
+    TextField: expect.any(Function),
+    ValidatedForm: expect.any(Function),
+    ValidatedQuickForm: expect.any(Function),
+  });
+});
diff --git a/packages/online-editor/src/workspace/startupBlockers/LatestCompatibleVersion.ts b/packages/uniforms-patternfly/src/__tests__/jest.setup.ts
similarity index 75%
rename from packages/online-editor/src/workspace/startupBlockers/LatestCompatibleVersion.ts
rename to packages/uniforms-patternfly/src/__tests__/jest.setup.ts
index 38ca944907..b8ba348a89 100644
--- a/packages/online-editor/src/workspace/startupBlockers/LatestCompatibleVersion.ts
+++ b/packages/uniforms-patternfly/src/__tests__/jest.setup.ts
@@ -1,11 +1,11 @@
 /*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed 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
+ *     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,
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-export const LATEST_VERSION_COMPATIBLE_WITH_LFS = "0.23.0";
+import "@testing-library/jest-dom";
diff --git a/packages/uniforms-patternfly/src/__tests__/test-utils.tsx b/packages/uniforms-patternfly/src/__tests__/test-utils.tsx
new file mode 100644
index 0000000000..a3848ffe86
--- /dev/null
+++ b/packages/uniforms-patternfly/src/__tests__/test-utils.tsx
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { Context, BaseForm, context, randomIds } from "uniforms";
+import SimpleSchema from "simpl-schema";
+import { SimpleSchema2Bridge } from "uniforms-bridge-simple-schema-2";
+
+const randomId = randomIds();
+
+export function createSimpleSchema(schema = {}) {
+  return new SimpleSchema2Bridge(new SimpleSchema(schema));
+}
+
+export function usingUniformsContext(children: React.ReactElement, schema = {}, ctx?: Partial<Context<any>>) {
+  const partialCtx = {
+    changed: false,
+    changedMap: {},
+    error: null,
+    model: {},
+    name: [],
+    onChange: () => {},
+    onSubmit: () => {},
+    randomId,
+    submitting: false,
+    validating: false,
+    submitted: false,
+    schema: createSimpleSchema(schema),
+    formRef: new BaseForm<any>({
+      autosave: false,
+      autosaveDelay: 0,
+      error: false,
+      label: true,
+      model: {},
+      noValidate: false,
+      onSubmit: (event) => {},
+      schema: createSimpleSchema(schema),
+    }),
+    ...ctx,
+    state: {
+      disabled: false,
+      label: false,
+      placeholder: false,
+      showInlineError: false,
+      readOnly: false,
+    },
+  };
+  return <context.Provider value={partialCtx}>{children}</context.Provider>;
+}
diff --git a/packages/uniforms-patternfly/src/index.ts b/packages/uniforms-patternfly/src/index.ts
new file mode 100644
index 0000000000..0300e9e461
--- /dev/null
+++ b/packages/uniforms-patternfly/src/index.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+export { default as AutoFields } from "./AutoFields";
+export { default as AutoField } from "./AutoField";
+export { default as AutoForm } from "./AutoForm";
+export { default as BaseForm } from "./BaseForm";
+export { default as BoolField } from "./BoolField";
+export { default as DateField } from "./DateField";
+export { default as ErrorField } from "./ErrorField";
+export { default as ErrorsField } from "./ErrorsField";
+export { default as HiddenField } from "./HiddenField";
+export { default as ListAddField } from "./ListAddField";
+export { default as ListDelField } from "./ListDelField";
+export { default as ListField } from "./ListField";
+export { default as ListItemField } from "./ListItemField";
+export { default as LongTextField } from "./LongTextField";
+export { default as NestField } from "./NestField";
+export { default as NumField } from "./NumField";
+export { default as QuickForm } from "./QuickForm";
+export { default as RadioField } from "./RadioField";
+export { default as SelectField } from "./SelectField";
+export { default as SubmitField } from "./SubmitField";
+export { default as TextField } from "./TextField";
+export { default as ValidatedForm } from "./ValidatedForm";
+export { default as ValidatedQuickForm } from "./ValidatedQuickForm";
diff --git a/packages/uniforms-patternfly/src/wrapField.tsx b/packages/uniforms-patternfly/src/wrapField.tsx
new file mode 100644
index 0000000000..f0204b6a2c
--- /dev/null
+++ b/packages/uniforms-patternfly/src/wrapField.tsx
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+import * as React from "react";
+import { FormGroup, FormGroupProps } from "@patternfly/react-core/dist/js/components/Form";
+import { filterDOMProps } from "uniforms";
+
+declare module "uniforms" {
+  interface FilterDOMProps {
+    decimal: never;
+    minCount: never;
+    autoValue: never;
+    isDisabled: never;
+    checkboxes: never;
+    exclusiveMaximum: never;
+    exclusiveMinimum: never;
+    menuAppendTo: never;
+  }
+}
+
+filterDOMProps.register(
+  "decimal",
+  "minCount",
+  "autoValue",
+  "isDisabled",
+  "exclusiveMaximum",
+  "exclusiveMinimum",
+  "menuAppendTo"
+);
+
+type WrapperProps = {
+  id: string;
+  error?: boolean;
+  errorMessage?: string;
+  help?: string;
+  showInlineError?: boolean;
+} & Omit<FormGroupProps, "onChange" | "fieldId">;
+
+export default function wrapField(
+  { id, label, type, disabled, error, errorMessage, showInlineError, help, required, ...props }: WrapperProps,
+  children: React.ReactNode
+) {
+  return (
+    <FormGroup
+      data-testid={"wrapper-field"}
+      fieldId={id}
+      label={label}
+      isRequired={required}
+      validated={error ? "error" : "default"}
+      type={type}
+      helperText={help}
+      helperTextInvalid={errorMessage}
+      {...filterDOMProps(props)}
+    >
+      {children}
+    </FormGroup>
+  );
+}
diff --git a/packages/uniforms-patternfly/tsconfig.cjs.json b/packages/uniforms-patternfly/tsconfig.cjs.json
new file mode 100644
index 0000000000..5197e7369d
--- /dev/null
+++ b/packages/uniforms-patternfly/tsconfig.cjs.json
@@ -0,0 +1,13 @@
+{
+  "extends": "@kie-tools/tsconfig/tsconfig.json",
+  "compilerOptions": {
+    "baseUrl": "src",
+    "outDir": "dist/cjs",
+    "rootDir": "src",
+    "module": "commonjs",
+    "target": "es6",
+    "esModuleInterop": true,
+    "moduleResolution": "node"
+  },
+  "include": ["src"]
+}
diff --git a/packages/uniforms-patternfly/tsconfig.esm.json b/packages/uniforms-patternfly/tsconfig.esm.json
new file mode 100644
index 0000000000..ebfe721109
--- /dev/null
+++ b/packages/uniforms-patternfly/tsconfig.esm.json
@@ -0,0 +1,13 @@
+{
+  "extends": "@kie-tools/tsconfig/tsconfig.json",
+  "compilerOptions": {
+    "baseUrl": "src",
+    "outDir": "dist/esm",
+    "rootDir": "src",
+    "module": "es6",
+    "target": "es6",
+    "esModuleInterop": true,
+    "moduleResolution": "node"
+  },
+  "include": ["src"]
+}
diff --git a/packages/unitables-dmn/package.json b/packages/unitables-dmn/package.json
index ce07fabd6a..57800c96da 100644
--- a/packages/unitables-dmn/package.json
+++ b/packages/unitables-dmn/package.json
@@ -44,9 +44,8 @@
     "react-dom": "^17.0.2",
     "react-id-generator": "^3.0.1",
     "react-table": "^7.6.2",
-    "uniforms": "^3.5.5",
-    "uniforms-bridge-json-schema": "^3.5.5",
-    "uniforms-patternfly": "^4.7.9"
+    "uniforms": "^3.10.2",
+    "uniforms-bridge-json-schema": "^3.10.2"
   },
   "devDependencies": {
     "@babel/core": "^7.16.0",
diff --git a/packages/unitables/package.json b/packages/unitables/package.json
index f3b3bab2e6..7e0dfee795 100644
--- a/packages/unitables/package.json
+++ b/packages/unitables/package.json
@@ -31,6 +31,7 @@
     "@kie-tools/boxed-expression-component": "workspace:*",
     "@kie-tools/form": "workspace:*",
     "@kie-tools/i18n-common-dictionary": "workspace:*",
+    "@kie-tools/uniforms-patternfly": "workspace:*",
     "@patternfly/react-core": "^4.157.3",
     "@patternfly/react-icons": "^4.11.17",
     "@types/lodash": "^4.14.168",
@@ -43,9 +44,8 @@
     "react-dom": "^17.0.2",
     "react-id-generator": "^3.0.1",
     "react-table": "^7.6.2",
-    "uniforms": "^3.5.5",
-    "uniforms-bridge-json-schema": "^3.5.5",
-    "uniforms-patternfly": "^4.7.9"
+    "uniforms": "^3.10.2",
+    "uniforms-bridge-json-schema": "^3.10.2"
   },
   "devDependencies": {
     "@babel/core": "^7.16.0",
diff --git a/packages/unitables/src/uniforms/AutoField.tsx b/packages/unitables/src/uniforms/AutoField.tsx
index 2fed18b7e6..f52a468f1e 100644
--- a/packages/unitables/src/uniforms/AutoField.tsx
+++ b/packages/unitables/src/uniforms/AutoField.tsx
@@ -23,7 +23,7 @@ import {
   RadioField,
   SelectField,
   TextField,
-} from "uniforms-patternfly";
+} from "@kie-tools/uniforms-patternfly/dist/esm";
 import { createAutoField } from "uniforms";
 
 export const AutoField = createAutoField((props) => {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c5cf573b2d..960d5da083 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -28,8 +28,8 @@ importers:
         specifier: ^0.6.1
         version: 0.6.1(prettier@2.3.0)
       "@types/node":
-        specifier: ^15.0.2
-        version: 15.6.0
+        specifier: ^18.13.0
+        version: 18.13.0
       filemanager-webpack-plugin:
         specifier: ^7.0.0
         version: 7.0.0
@@ -450,6 +450,94 @@ importers:
         specifier: ^5.8.0
         version: 5.8.0
 
+  examples/uniforms-patternfly:
+    dependencies:
+      "@kie-tools-core/patternfly-base":
+        specifier: workspace:*
+        version: link:../../packages/patternfly-base
+      "@kie-tools/uniforms-patternfly":
+        specifier: workspace:*
+        version: link:../../packages/uniforms-patternfly
+      "@patternfly/react-core":
+        specifier: ^4.157.3
+        version: 4.168.8(react-dom@17.0.2)(react@17.0.2)
+      "@patternfly/react-icons":
+        specifier: ^4.11.17
+        version: 4.19.8(react-dom@17.0.2)(react@17.0.2)
+      ajv:
+        specifier: ^6.12.6
+        version: 6.12.6
+      react:
+        specifier: ^17.0.2
+        version: 17.0.2
+      react-dom:
+        specifier: ^17.0.2
+        version: 17.0.2(react@17.0.2)
+      simpl-schema:
+        specifier: ^1.12.0
+        version: 1.12.0
+      uniforms:
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
+      uniforms-bridge-json-schema:
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
+      uniforms-bridge-simple-schema:
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
+      uniforms-bridge-simple-schema-2:
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
+    devDependencies:
+      "@kie-tools-core/webpack-base":
+        specifier: workspace:*
+        version: link:../../packages/webpack-base
+      "@kie-tools/root-env":
+        specifier: workspace:*
+        version: link:../../packages/root-env
+      "@kie-tools/tsconfig":
+        specifier: workspace:*
+        version: link:../../packages/tsconfig
+      "@types/react":
+        specifier: ^17.0.6
+        version: 17.0.21
+      "@types/react-dom":
+        specifier: ^17.0.5
+        version: 17.0.8
+      "@types/react-router":
+        specifier: ^5.1.14
+        version: 5.1.14
+      "@types/react-router-dom":
+        specifier: ^5.1.7
+        version: 5.1.7
+      "@types/simpl-schema":
+        specifier: ^1.12.0
+        version: 1.12.0
+      copy-webpack-plugin:
+        specifier: ^11.0.0
+        version: 11.0.0(webpack@5.70.0)
+      react-router-dom:
+        specifier: ^5.2.1
+        version: 5.3.0(react@17.0.2)
+      rimraf:
+        specifier: ^3.0.2
+        version: 3.0.2
+      typescript:
+        specifier: ^4.4.2
+        version: 4.8.4
+      webpack:
+        specifier: ^5.36.2
+        version: 5.70.0(webpack-cli@4.10.0)
+      webpack-cli:
+        specifier: ^4.10.0
+        version: 4.10.0(webpack-dev-server@4.7.3)(webpack@5.70.0)
+      webpack-dev-server:
+        specifier: ^4.7.3
+        version: 4.7.3(webpack-cli@4.10.0)(webpack@5.70.0)
+      webpack-merge:
+        specifier: ^5.8.0
+        version: 5.8.0
+
   examples/webapp:
     dependencies:
       "@kie-tools-core/editor":
@@ -1943,6 +2031,9 @@ importers:
       "@kie-tools/dashbuilder-component-api":
         specifier: workspace:*
         version: link:../dashbuilder-component-api
+      "@kie-tools/uniforms-patternfly":
+        specifier: workspace:*
+        version: link:../uniforms-patternfly
       "@patternfly/react-core":
         specifier: ^4.157.3
         version: 4.168.8(react-dom@17.0.2)(react@17.0.2)
@@ -1962,14 +2053,11 @@ importers:
         specifier: ^17.0.2
         version: 17.0.2(react@17.0.2)
       uniforms:
-        specifier: ^3.5.5
-        version: 3.6.2(react@17.0.2)
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
       uniforms-bridge-json-schema:
-        specifier: ^3.5.5
-        version: 3.6.2(react@17.0.2)
-      uniforms-patternfly:
-        specifier: ^4.7.9
-        version: 4.7.9(@patternfly/react-core@4.168.8)(react@17.0.2)
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
     devDependencies:
       "@babel/core":
         specifier: ^7.16.0
@@ -3283,6 +3371,9 @@ importers:
       "@kie-tools/i18n-common-dictionary":
         specifier: workspace:*
         version: link:../i18n-common-dictionary
+      "@kie-tools/uniforms-patternfly":
+        specifier: workspace:*
+        version: link:../uniforms-patternfly
       "@patternfly/react-core":
         specifier: ^4.157.3
         version: 4.168.8(react-dom@17.0.2)(react@17.0.2)
@@ -3308,14 +3399,11 @@ importers:
         specifier: ^17.0.2
         version: 17.0.2(react@17.0.2)
       uniforms:
-        specifier: ^3.5.5
-        version: 3.6.2(react@17.0.2)
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
       uniforms-bridge-json-schema:
-        specifier: ^3.5.5
-        version: 3.6.2(react@17.0.2)
-      uniforms-patternfly:
-        specifier: ^4.7.9
-        version: 4.7.9(@patternfly/react-core@4.168.8)(@patternfly/react-icons@4.19.8)(react@17.0.2)
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
     devDependencies:
       "@babel/core":
         specifier: ^7.16.0
@@ -3392,6 +3480,9 @@ importers:
       "@kie-tools/i18n-common-dictionary":
         specifier: workspace:*
         version: link:../i18n-common-dictionary
+      "@kie-tools/uniforms-patternfly":
+        specifier: workspace:*
+        version: link:../uniforms-patternfly
       "@patternfly/react-core":
         specifier: ^4.157.3
         version: 4.168.8(react-dom@17.0.2)(react@17.0.2)
@@ -3417,14 +3508,11 @@ importers:
         specifier: ^17.0.2
         version: 17.0.2(react@17.0.2)
       uniforms:
-        specifier: ^3.5.5
-        version: 3.6.2(react@17.0.2)
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
       uniforms-bridge-json-schema:
-        specifier: ^3.5.5
-        version: 3.6.2(react@17.0.2)
-      uniforms-patternfly:
-        specifier: ^4.7.9
-        version: 4.7.9(@patternfly/react-core@4.168.8)(@patternfly/react-icons@4.19.8)(react@17.0.2)
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
     devDependencies:
       "@babel/core":
         specifier: ^7.16.0
@@ -3511,11 +3599,11 @@ importers:
         specifier: ^4.17.21
         version: 4.17.21
       uniforms:
-        specifier: ^3.5.5
-        version: 3.6.2
+        specifier: ^3.10.2
+        version: 3.10.2
       uniforms-bridge-json-schema:
-        specifier: ^3.5.5
-        version: 3.6.2
+        specifier: ^3.10.2
+        version: 3.10.2
     devDependencies:
       "@babel/core":
         specifier: ^7.16.0
@@ -6763,11 +6851,11 @@ importers:
         specifier: ^1.13.1
         version: 1.13.1
       uniforms:
-        specifier: ^3.5.5
-        version: 3.6.2(react@17.0.2)
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
       uniforms-bridge-simple-schema-2:
-        specifier: ^3.5.5
-        version: 3.6.2(react@17.0.2)
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
     devDependencies:
       "@babel/core":
         specifier: ^7.16.0
@@ -6860,6 +6948,115 @@ importers:
         specifier: ^3.0.0
         version: 3.0.0
 
+  packages/uniforms-patternfly:
+    dependencies:
+      "@types/invariant":
+        specifier: ^2.2.35
+        version: 2.2.35
+      invariant:
+        specifier: ^2.2.4
+        version: 2.2.4
+      lodash:
+        specifier: ^4.17.21
+        version: 4.17.21
+      react:
+        specifier: ^17.0.2
+        version: 17.0.2
+      react-dom:
+        specifier: ^17.0.2
+        version: 17.0.2(react@17.0.2)
+      uniforms:
+        specifier: ^3.10.2
+        version: 3.10.2(react@17.0.2)
+    devDependencies:
+      "@babel/preset-env":
+        specifier: ^7.16.0
+        version: 7.16.11
+      "@babel/preset-react":
+        specifier: ^7.16.0
+        version: 7.16.0
+      "@kie-tools-scripts/build-env":
+        specifier: workspace:*
+        version: link:../../scripts/build-env
+      "@kie-tools/eslint":
+        specifier: workspace:*
+        version: link:../eslint
+      "@kie-tools/root-env":
+        specifier: workspace:*
+        version: link:../root-env
+      "@kie-tools/tsconfig":
+        specifier: workspace:*
+        version: link:../tsconfig
+      "@patternfly/react-core":
+        specifier: ^4.157.3
+        version: 4.168.8(react-dom@17.0.2)(react@17.0.2)
... 2579 lines suppressed ...


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org