diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectType.jsx b/openbis_ng_ui/src/js/components/types/objectType/ObjectType.jsx
index 679e93880a8be4ab65df2dbd17f0574f7a832c4c..71bcb46cee38a068c7bafb5c41c439818c53c873 100644
--- a/openbis_ng_ui/src/js/components/types/objectType/ObjectType.jsx
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectType.jsx
@@ -140,14 +140,14 @@ class ObjectType extends React.PureComponent {
     new ObjectTypeHandlerRemove(
       this.state,
       this.setState.bind(this)
-    ).executeRemoveConfirm()
+    ).executeRemove(true)
   }
 
   handleRemoveCancel() {
     new ObjectTypeHandlerRemove(
       this.state,
       this.setState.bind(this)
-    ).executeRemoveCancel()
+    ).executeCancel()
   }
 
   handleSave() {
diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerRemove.js b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerRemove.js
index 3ab191f52e6e6568794475f8c4dda78e3419b15c..4a7be869ab2ff2018e922c7c26b5a903ba66a016 100644
--- a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerRemove.js
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerRemove.js
@@ -6,37 +6,39 @@ export default class ObjectTypeHandlerRemove {
     this.setState = setState
   }
 
-  executeRemove() {
+  executeRemove(confirmed = false) {
     const { selection } = this.state
     if (selection.type === 'section') {
-      this.handleRemoveSection(selection.params.id)
+      this.handleRemoveSection(selection.params.id, confirmed)
     } else if (selection.type === 'property') {
-      this.handleRemoveProperty(selection.params.id)
+      this.handleRemoveProperty(selection.params.id, confirmed)
     }
   }
 
-  executeRemoveConfirm() {
-    this.setState({
-      removeSectionDialogOpen: false,
-      removePropertyDialogOpen: false
-    })
-    this.executeRemove()
-  }
-
-  executeRemoveCancel() {
-    this.setState({
-      removeSectionDialogOpen: false,
-      removePropertyDialogOpen: false
-    })
+  executeCancel() {
+    const { selection } = this.state
+    if (selection.type === 'section') {
+      this.setState({
+        removeSectionDialogOpen: false
+      })
+    } else if (selection.type === 'property') {
+      this.setState({
+        removePropertyDialogOpen: false
+      })
+    }
   }
 
-  handleRemoveSection(sectionId) {
-    const { sections, properties, removeSectionDialogOpen } = this.state
+  handleRemoveSection(sectionId, confirmed) {
+    const { sections, properties } = this.state
 
     const sectionIndex = sections.findIndex(section => section.id === sectionId)
     const section = sections[sectionIndex]
 
-    if (this.isSectionUsed(section) && !removeSectionDialogOpen) {
+    if (confirmed) {
+      this.setState({
+        removeSectionDialogOpen: false
+      })
+    } else if (this.isSectionUsed(section)) {
       this.setState({
         removeSectionDialogOpen: true
       })
@@ -60,15 +62,19 @@ export default class ObjectTypeHandlerRemove {
     }))
   }
 
-  handleRemoveProperty(propertyId) {
-    const { sections, properties, removePropertyDialogOpen } = this.state
+  handleRemoveProperty(propertyId, confirmed) {
+    const { sections, properties } = this.state
 
     const propertyIndex = properties.findIndex(
       property => property.id === propertyId
     )
     const property = properties[propertyIndex]
 
-    if (this.isPropertyUsed(property) && !removePropertyDialogOpen) {
+    if (confirmed) {
+      this.setState({
+        removePropertyDialogOpen: false
+      })
+    } else if (this.isPropertyUsed(property)) {
       this.setState({
         removePropertyDialogOpen: true
       })
@@ -114,6 +120,6 @@ export default class ObjectTypeHandlerRemove {
   }
 
   isPropertyUsed(property) {
-    return property.usages !== 0
+    return _.isFinite(property.usages) && property.usages !== 0
   }
 }
diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerValidate.js b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerValidate.js
index 4a2ad1c417289bbee32b1a3f07013d002ed56ab6..1be4cfa73b8d41146bd68225a30d89dabe2fd6b8 100644
--- a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerValidate.js
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerValidate.js
@@ -24,7 +24,7 @@ export default class ObjectTypeHandlerValidate {
     const { validate, type, properties } = this.getState()
 
     if (!validate) {
-      return
+      return true
     }
 
     const [typeErrors, typeErrorsMap] = this.validateType(type)
@@ -66,11 +66,16 @@ export default class ObjectTypeHandlerValidate {
 
       return {
         type: newType,
-        properties: newProperties,
-        selection: errorSelection ? errorSelection : state.selection
+        properties: newProperties
       }
     })
 
+    if (errorSelection) {
+      this.setState({
+        selection: errorSelection
+      })
+    }
+
     return _.isEmpty(typeErrors) && _.isEmpty(propertiesErrors)
   }
 
diff --git a/openbis_ng_ui/srcTest/js/common/ComponentState.js b/openbis_ng_ui/srcTest/js/common/ComponentState.js
index 1996729c2449cfab294b380a62a42ed8081034d3..c4f443c6c9ec0fa1864362794f3f4e1e9e3f1dff 100644
--- a/openbis_ng_ui/srcTest/js/common/ComponentState.js
+++ b/openbis_ng_ui/srcTest/js/common/ComponentState.js
@@ -11,8 +11,14 @@ class ComponentState {
     return this.state
   }
 
+  getGetState() {
+    return () => {
+      return this.getState()
+    }
+  }
+
   getSetState() {
-    return newStateOrFunction => {
+    return (newStateOrFunction, callback) => {
       let changes
 
       if (_.isFunction(newStateOrFunction)) {
@@ -22,6 +28,10 @@ class ComponentState {
       }
 
       this.state = { ...this.state, ...changes }
+
+      if (callback) {
+        callback()
+      }
     }
   }
 
diff --git a/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerLoad.test.js b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerLoad.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..20961dd41fbfcdb187221873b57a7feb0720be6b
--- /dev/null
+++ b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerLoad.test.js
@@ -0,0 +1,225 @@
+import ObjectTypeHandlerLoad from '../../../../../src/js/components/types/objectType/ObjectTypeHandlerLoad.js'
+import ComponentState from '../../../common/ComponentState.js'
+
+describe('ObjectTypeHandlerLoadTest', () => {
+  test('load success', done => {
+    const componentState = new ComponentState({})
+    const facade = {
+      loadType: jest.fn(),
+      loadUsages: jest.fn()
+    }
+
+    facade.loadType.mockReturnValueOnce(
+      Promise.resolve({
+        code: 'TYPE_CODE',
+        description: 'TYPE_DESCRIPTION',
+        listable: true,
+        showContainer: true,
+        showParents: true,
+        showParentMetadata: true,
+        autoGeneratedCode: true,
+        generatedCodePrefix: 'TYPE_CODE_PREFIX',
+        subcodeUnique: true,
+        validationPlugin: { name: 'TYPE_VALIDATION_PLUGIN' },
+        propertyAssignments: [
+          {
+            propertyType: {
+              code: 'PROPERTY_0_CODE',
+              label: 'PROPERTY_0_LABEL',
+              description: 'PROPERTY_0_DESCRIPTION',
+              dataType: 'VARCHAR',
+              schema: null,
+              transformation: null
+            },
+            plugin: { name: 'PROPERTY_0_PLUGIN' },
+            mandatory: true,
+            section: 'SECTION_0',
+            showInEditView: true,
+            showRawValueInForms: true
+          },
+          {
+            propertyType: {
+              code: 'PROPERTY_1_CODE',
+              label: 'PROPERTY_1_LABEL',
+              description: 'PROPERTY_1_DESCRIPTION',
+              dataType: 'CONTROLLED_VOCABULARY',
+              vocabulary: {
+                code: 'PROPERTY_1_VOCABULARY_CODE'
+              },
+              schema: null,
+              transformation: null
+            },
+            plugin: null,
+            mandatory: false,
+            section: 'SECTION_0',
+            showInEditView: false,
+            showRawValueInForms: false
+          },
+          {
+            propertyType: {
+              code: 'PROPERTY_2_CODE',
+              label: 'PROPERTY_2_LABEL',
+              description: 'PROPERTY_2_DESCRIPTION',
+              dataType: 'MATERIAL',
+              materialType: {
+                code: 'PROPERTY_2_MATERIAL_TYPE_CODE'
+              },
+              schema: null,
+              transformation: null
+            },
+            plugin: null,
+            mandatory: false,
+            section: null,
+            showInEditView: false,
+            showRawValueInForms: false
+          }
+        ]
+      })
+    )
+    facade.loadUsages.mockReturnValueOnce(
+      Promise.resolve({
+        type: 3,
+        property: {
+          PROPERTY_0_CODE: 2,
+          PROPERTY_1_CODE: 1
+        }
+      })
+    )
+
+    execute(123, componentState, facade).finally(() => {
+      expect(facade.loadType).toBeCalledWith(123)
+      expect(facade.loadUsages).toBeCalledWith(123)
+
+      const state = componentState.getState()
+
+      delete state.type.original
+      state.properties.forEach(property => {
+        delete property.original
+      })
+
+      componentState.assertState({
+        loading: false,
+        selection: null,
+        sections: [
+          {
+            id: 'section-0',
+            name: 'SECTION_0',
+            properties: ['property-0', 'property-1']
+          },
+          {
+            id: 'section-1',
+            name: null,
+            properties: ['property-2']
+          }
+        ],
+        properties: [
+          {
+            code: 'PROPERTY_0_CODE',
+            dataType: 'VARCHAR',
+            description: 'PROPERTY_0_DESCRIPTION',
+            errors: {},
+            id: 'property-0',
+            label: 'PROPERTY_0_LABEL',
+            mandatory: true,
+            materialType: null,
+            plugin: 'PROPERTY_0_PLUGIN',
+            schema: null,
+            section: 'section-0',
+            showInEditView: true,
+            showRawValueInForms: true,
+            transformation: null,
+            usages: 2,
+            vocabulary: null
+          },
+          {
+            code: 'PROPERTY_1_CODE',
+            dataType: 'CONTROLLED_VOCABULARY',
+            description: 'PROPERTY_1_DESCRIPTION',
+            errors: {},
+            id: 'property-1',
+            label: 'PROPERTY_1_LABEL',
+            mandatory: false,
+            materialType: null,
+            plugin: null,
+            schema: null,
+            section: 'section-0',
+            showInEditView: false,
+            showRawValueInForms: false,
+            transformation: null,
+            usages: 1,
+            vocabulary: 'PROPERTY_1_VOCABULARY_CODE'
+          },
+          {
+            code: 'PROPERTY_2_CODE',
+            dataType: 'MATERIAL',
+            description: 'PROPERTY_2_DESCRIPTION',
+            errors: {},
+            id: 'property-2',
+            label: 'PROPERTY_2_LABEL',
+            mandatory: false,
+            materialType: 'PROPERTY_2_MATERIAL_TYPE_CODE',
+            plugin: null,
+            schema: null,
+            section: 'section-1',
+            showInEditView: false,
+            showRawValueInForms: false,
+            transformation: null,
+            usages: 0,
+            vocabulary: null
+          }
+        ],
+        type: {
+          autoGeneratedCode: true,
+          code: 'TYPE_CODE',
+          description: 'TYPE_DESCRIPTION',
+          errors: {},
+          generatedCodePrefix: 'TYPE_CODE_PREFIX',
+          listable: true,
+          showContainer: true,
+          showParentMetadata: true,
+          showParents: true,
+          subcodeUnique: true,
+          usages: 3,
+          validationPlugin: 'TYPE_VALIDATION_PLUGIN'
+        },
+        propertiesCounter: 3,
+        sectionsCounter: 2,
+        removePropertyDialogOpen: false,
+        removeSectionDialogOpen: false
+      })
+
+      done()
+    })
+  })
+
+  test('load failure', done => {
+    const componentState = new ComponentState({})
+    const facade = {
+      loadType: jest.fn(),
+      loadUsages: jest.fn(),
+      catch: jest.fn()
+    }
+
+    facade.loadType.mockReturnValueOnce(Promise.reject('Server unavailable'))
+    facade.loadUsages.mockReturnValueOnce(Promise.resolve({}))
+
+    execute(123, componentState, facade).finally(() => {
+      expect(facade.loadType).toBeCalledWith(123)
+      expect(facade.loadUsages).toBeCalledWith(123)
+      expect(facade.catch).toBeCalledWith('Server unavailable')
+
+      componentState.assertState({ loading: false })
+
+      done()
+    })
+  })
+})
+
+const execute = (objectId, componentState, facade) => {
+  return new ObjectTypeHandlerLoad(
+    objectId,
+    componentState.getState(),
+    componentState.getSetState(),
+    facade
+  ).execute()
+}
diff --git a/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerOrderChange.test.js b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerOrderChange.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..b20e2b7f39b9331e8ef3d0b83367c8c0b540d48a
--- /dev/null
+++ b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerOrderChange.test.js
@@ -0,0 +1,134 @@
+import ObjectTypeHandlerOrderChange from '../../../../../src/js/components/types/objectType/ObjectTypeHandlerOrderChange.js'
+import ComponentState from '../../../common/ComponentState.js'
+
+describe('ObjectTypeHandlerOrderChangeTest', () => {
+  test('section', () => {
+    const componentState = new ComponentState({
+      sections: [
+        {
+          id: 'section-0'
+        },
+        {
+          id: 'section-1'
+        },
+        {
+          id: 'section-2'
+        }
+      ]
+    })
+
+    execute(componentState, 'section', {
+      fromIndex: 2,
+      toIndex: 0
+    })
+
+    componentState.assertState({
+      sections: [
+        {
+          id: 'section-2'
+        },
+        {
+          id: 'section-0'
+        },
+        {
+          id: 'section-1'
+        }
+      ]
+    })
+  })
+
+  test('property', () => {
+    const componentState = new ComponentState({
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0', 'property-1']
+        },
+        {
+          id: 'section-1',
+          properties: []
+        },
+        {
+          id: 'section-2',
+          properties: ['property-2', 'property-3', 'property-4']
+        }
+      ],
+      properties: [
+        { id: 'property-0', section: 'section-0' },
+        { id: 'property-1', section: 'section-0' },
+        { id: 'property-2', section: 'section-2' },
+        { id: 'property-3', section: 'section-2' },
+        { id: 'property-4', section: 'section-2' }
+      ]
+    })
+
+    execute(componentState, 'property', {
+      fromSectionId: 'section-2',
+      toSectionId: 'section-2',
+      fromIndex: 0,
+      toIndex: 1
+    })
+
+    componentState.assertState({
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0', 'property-1']
+        },
+        {
+          id: 'section-1',
+          properties: []
+        },
+        {
+          id: 'section-2',
+          properties: ['property-3', 'property-2', 'property-4']
+        }
+      ],
+      properties: [
+        { id: 'property-0', section: 'section-0' },
+        { id: 'property-1', section: 'section-0' },
+        { id: 'property-2', section: 'section-2' },
+        { id: 'property-3', section: 'section-2' },
+        { id: 'property-4', section: 'section-2' }
+      ]
+    })
+
+    execute(componentState, 'property', {
+      fromSectionId: 'section-2',
+      toSectionId: 'section-0',
+      fromIndex: 1,
+      toIndex: 1
+    })
+
+    componentState.assertState({
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0', 'property-2', 'property-1']
+        },
+        {
+          id: 'section-1',
+          properties: []
+        },
+        {
+          id: 'section-2',
+          properties: ['property-3', 'property-4']
+        }
+      ],
+      properties: [
+        { id: 'property-0', section: 'section-0' },
+        { id: 'property-1', section: 'section-0' },
+        { id: 'property-2', section: 'section-0' },
+        { id: 'property-3', section: 'section-2' },
+        { id: 'property-4', section: 'section-2' }
+      ]
+    })
+  })
+})
+
+const execute = (componentState, type, params) => {
+  new ObjectTypeHandlerOrderChange(
+    componentState.getState(),
+    componentState.getSetState()
+  ).execute(type, params)
+}
diff --git a/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerRemove.test.js b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerRemove.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..8b8622681f6ddcf549350e555d93b6addba43157
--- /dev/null
+++ b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerRemove.test.js
@@ -0,0 +1,326 @@
+import ObjectTypeHandlerRemove from '../../../../../src/js/components/types/objectType/ObjectTypeHandlerRemove.js'
+import ComponentState from '../../../common/ComponentState.js'
+
+describe('ObjectTypeHandlerRemoveTest', () => {
+  test('section not used', () => {
+    const componentState = new ComponentState({
+      selection: {
+        type: 'section',
+        params: {
+          id: 'section-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0', 'property-1']
+        },
+        {
+          id: 'section-1',
+          properties: ['property-2', 'property-3', 'property-4']
+        }
+      ],
+      properties: [
+        { id: 'property-0', section: 'section-0' },
+        { id: 'property-1', section: 'section-0' },
+        { id: 'property-2', section: 'section-1' },
+        { id: 'property-3', section: 'section-1' },
+        { id: 'property-4', section: 'section-1' }
+      ]
+    })
+
+    executeRemove(componentState)
+
+    componentState.assertState({
+      selection: null,
+      sections: [
+        {
+          id: 'section-1',
+          properties: ['property-2', 'property-3', 'property-4']
+        }
+      ],
+      properties: [
+        { id: 'property-2', section: 'section-1' },
+        { id: 'property-3', section: 'section-1' },
+        { id: 'property-4', section: 'section-1' }
+      ]
+    })
+  })
+
+  test('section used and confirmed', () => {
+    const componentState = new ComponentState({
+      selection: {
+        type: 'section',
+        params: {
+          id: 'section-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+
+    executeRemove(componentState)
+    executeRemove(componentState)
+
+    componentState.assertState({
+      removeSectionDialogOpen: true,
+      selection: {
+        type: 'section',
+        params: {
+          id: 'section-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+
+    executeRemove(componentState, true)
+
+    componentState.assertState({
+      removeSectionDialogOpen: false,
+      selection: null,
+      sections: [],
+      properties: []
+    })
+  })
+
+  test('section used and cancelled', () => {
+    const componentState = new ComponentState({
+      selection: {
+        type: 'section',
+        params: {
+          id: 'section-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+
+    executeRemove(componentState)
+    executeRemove(componentState)
+
+    componentState.assertState({
+      removeSectionDialogOpen: true,
+      selection: {
+        type: 'section',
+        params: {
+          id: 'section-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+
+    executeCancel(componentState)
+
+    componentState.assertState({
+      removeSectionDialogOpen: false,
+      selection: {
+        type: 'section',
+        params: {
+          id: 'section-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+  })
+
+  test('property not used', () => {
+    const componentState = new ComponentState({
+      selection: {
+        type: 'property',
+        params: {
+          id: 'property-3'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0', 'property-1']
+        },
+        {
+          id: 'section-1',
+          properties: ['property-2', 'property-3', 'property-4']
+        }
+      ],
+      properties: [
+        { id: 'property-0', section: 'section-0' },
+        { id: 'property-1', section: 'section-0' },
+        { id: 'property-2', section: 'section-1' },
+        { id: 'property-3', section: 'section-1' },
+        { id: 'property-4', section: 'section-1' }
+      ]
+    })
+
+    executeRemove(componentState)
+
+    componentState.assertState({
+      selection: null,
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0', 'property-1']
+        },
+        {
+          id: 'section-1',
+          properties: ['property-2', 'property-4']
+        }
+      ],
+      properties: [
+        { id: 'property-0', section: 'section-0' },
+        { id: 'property-1', section: 'section-0' },
+        { id: 'property-2', section: 'section-1' },
+        { id: 'property-4', section: 'section-1' }
+      ]
+    })
+  })
+
+  test('property used and confirmed', () => {
+    const componentState = new ComponentState({
+      selection: {
+        type: 'property',
+        params: {
+          id: 'property-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+
+    executeRemove(componentState)
+    executeRemove(componentState)
+
+    componentState.assertState({
+      removePropertyDialogOpen: true,
+      selection: {
+        type: 'property',
+        params: {
+          id: 'property-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+
+    executeRemove(componentState, true)
+
+    componentState.assertState({
+      removePropertyDialogOpen: false,
+      selection: null,
+      sections: [
+        {
+          id: 'section-0',
+          properties: []
+        }
+      ],
+      properties: []
+    })
+  })
+
+  test('property used and cancelled', () => {
+    const componentState = new ComponentState({
+      selection: {
+        type: 'property',
+        params: {
+          id: 'property-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+
+    executeRemove(componentState)
+    executeRemove(componentState)
+
+    componentState.assertState({
+      removePropertyDialogOpen: true,
+      selection: {
+        type: 'property',
+        params: {
+          id: 'property-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+
+    executeCancel(componentState)
+
+    componentState.assertState({
+      removePropertyDialogOpen: false,
+      selection: {
+        type: 'property',
+        params: {
+          id: 'property-0'
+        }
+      },
+      sections: [
+        {
+          id: 'section-0',
+          properties: ['property-0']
+        }
+      ],
+      properties: [{ id: 'property-0', section: 'section-0', usages: 1 }]
+    })
+  })
+})
+
+const executeRemove = (componentState, confirmed) => {
+  new ObjectTypeHandlerRemove(
+    componentState.getState(),
+    componentState.getSetState()
+  ).executeRemove(confirmed)
+}
+
+const executeCancel = componentState => {
+  new ObjectTypeHandlerRemove(
+    componentState.getState(),
+    componentState.getSetState()
+  ).executeCancel()
+}
diff --git a/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerSelectionChange.test.js b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerSelectionChange.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..834e128f3bb7227a5ad17e2e5d61c69a45f1224a
--- /dev/null
+++ b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerSelectionChange.test.js
@@ -0,0 +1,49 @@
+import ObjectTypeHandlerSelectionChange from '../../../../../src/js/components/types/objectType/ObjectTypeHandlerSelectionChange.js'
+import ComponentState from '../../../common/ComponentState.js'
+
+describe('ObjectTypeHandlerSelectionChangeTest', () => {
+  test('section', () => {
+    const componentState = new ComponentState({
+      selection: null
+    })
+
+    execute(componentState, 'section', {
+      id: 'section-0'
+    })
+
+    componentState.assertState({
+      selection: {
+        type: 'section',
+        params: {
+          id: 'section-0'
+        }
+      }
+    })
+  })
+
+  test('property', () => {
+    const componentState = new ComponentState({
+      selection: null
+    })
+
+    execute(componentState, 'property', {
+      id: 'property-0'
+    })
+
+    componentState.assertState({
+      selection: {
+        type: 'property',
+        params: {
+          id: 'property-0'
+        }
+      }
+    })
+  })
+})
+
+const execute = (componentState, type, params) => {
+  new ObjectTypeHandlerSelectionChange(
+    componentState.getState(),
+    componentState.getSetState()
+  ).execute(type, params)
+}
diff --git a/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerValidate.test.js b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerValidate.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..4e1d143faf95b2e3f9ba5bfc0825332494e822f0
--- /dev/null
+++ b/openbis_ng_ui/srcTest/js/components/types/objectType/ObjectTypeHandlerValidate.test.js
@@ -0,0 +1,255 @@
+import ObjectTypeHandlerValidate from '../../../../../src/js/components/types/objectType/ObjectTypeHandlerValidate.js'
+import ComponentState from '../../../common/ComponentState.js'
+import { dto } from '../../../../../src/js/services/openbis.js'
+
+jest.mock('../../../../../src/js/services/openbis.js')
+
+beforeEach(() => {
+  jest.resetAllMocks()
+  dto.DataType.CONTROLLEDVOCABULARY = 'CONTROLLEDVOCABULARY'
+  dto.DataType.MATERIAL = 'MATERIAL'
+})
+
+describe('ObjectTypeHandlerValidateTest', () => {
+  test('validation enabled with autofocus fails', done => {
+    const componentState = new ComponentState({
+      validate: false,
+      selection: {
+        type: 'testtype'
+      },
+      type: { usages: 1 },
+      properties: [
+        { id: 'property-0' },
+        { id: 'property-1', dataType: dto.DataType.CONTROLLEDVOCABULARY },
+        { id: 'property-2', dataType: dto.DataType.MATERIAL },
+        { id: 'property-3', mandatory: true }
+      ]
+    })
+
+    execute(componentState, true, true).then(result => {
+      componentState.assertState({
+        validate: true,
+        selection: {
+          type: 'type',
+          params: {
+            part: 'code'
+          }
+        },
+        type: {
+          usages: 1,
+          errors: {
+            code: 'Code cannot be empty',
+            generatedCodePrefix: 'Generated code prefix cannot be empty'
+          }
+        },
+        properties: [
+          {
+            id: 'property-0',
+            errors: {
+              code: 'Code cannot be empty',
+              dataType: 'Data Type cannot be empty',
+              description: 'Description cannot be empty',
+              label: 'Label cannot be empty'
+            }
+          },
+          {
+            id: 'property-1',
+            dataType: dto.DataType.CONTROLLEDVOCABULARY,
+            errors: {
+              code: 'Code cannot be empty',
+              description: 'Description cannot be empty',
+              label: 'Label cannot be empty',
+              vocabulary: 'Vocabulary cannot be empty'
+            }
+          },
+          {
+            id: 'property-2',
+            dataType: dto.DataType.MATERIAL,
+            errors: {
+              code: 'Code cannot be empty',
+              description: 'Description cannot be empty',
+              label: 'Label cannot be empty',
+              materialType: 'Material Type cannot be empty'
+            }
+          },
+          {
+            id: 'property-3',
+            mandatory: true,
+            errors: {
+              code: 'Code cannot be empty',
+              dataType: 'Data Type cannot be empty',
+              description: 'Description cannot be empty',
+              label: 'Label cannot be empty',
+              initialValueForExistingEntities: 'Initial Value cannot be empty'
+            }
+          }
+        ]
+      })
+      expect(result).toBe(false)
+      done()
+    })
+  })
+
+  test('validation enabled without autofocus fails', done => {
+    const componentState = new ComponentState({
+      validate: false,
+      selection: {
+        type: 'testtype'
+      },
+      type: {},
+      properties: [{ id: 'property-0' }]
+    })
+
+    execute(componentState, true, false).then(result => {
+      componentState.assertState({
+        validate: true,
+        selection: {
+          type: 'testtype'
+        },
+        type: {
+          errors: {
+            code: 'Code cannot be empty',
+            generatedCodePrefix: 'Generated code prefix cannot be empty'
+          }
+        },
+        properties: [
+          {
+            id: 'property-0',
+            errors: {
+              code: 'Code cannot be empty',
+              dataType: 'Data Type cannot be empty',
+              description: 'Description cannot be empty',
+              label: 'Label cannot be empty'
+            }
+          }
+        ]
+      })
+      expect(result).toBe(false)
+      done()
+    })
+  })
+
+  test('validation enabled with autofocus succeeds', done => {
+    const componentState = new ComponentState({
+      validate: false,
+      selection: {
+        type: 'testtype'
+      },
+      type: { code: 'TYPE_CODE', generatedCodePrefix: 'TYPE_CODE_PREFIX' },
+      properties: [
+        {
+          id: 'property-0',
+          code: 'PROPERTY_CODE',
+          dataType: 'PROPERTY_DATA_TYPE',
+          description: 'PROPERTY_DESCRIPTION',
+          label: 'PROPERTY_LABEL'
+        }
+      ]
+    })
+
+    execute(componentState, true, true).then(result => {
+      componentState.assertState({
+        validate: true,
+        selection: {
+          type: 'testtype'
+        },
+        type: {
+          code: 'TYPE_CODE',
+          generatedCodePrefix: 'TYPE_CODE_PREFIX',
+          errors: {}
+        },
+        properties: [
+          {
+            id: 'property-0',
+            code: 'PROPERTY_CODE',
+            dataType: 'PROPERTY_DATA_TYPE',
+            description: 'PROPERTY_DESCRIPTION',
+            label: 'PROPERTY_LABEL',
+            errors: {}
+          }
+        ]
+      })
+      expect(result).toBe(true)
+      done()
+    })
+  })
+
+  test('validation enabled without autofocus succeeds', done => {
+    const componentState = new ComponentState({
+      validate: false,
+      selection: {
+        type: 'testtype'
+      },
+      type: { code: 'TYPE_CODE', generatedCodePrefix: 'TYPE_CODE_PREFIX' },
+      properties: [
+        {
+          id: 'property-0',
+          code: 'PROPERTY_CODE',
+          dataType: 'PROPERTY_DATA_TYPE',
+          description: 'PROPERTY_DESCRIPTION',
+          label: 'PROPERTY_LABEL'
+        }
+      ]
+    })
+
+    execute(componentState, true, false).then(result => {
+      componentState.assertState({
+        validate: true,
+        selection: {
+          type: 'testtype'
+        },
+        type: {
+          code: 'TYPE_CODE',
+          generatedCodePrefix: 'TYPE_CODE_PREFIX',
+          errors: {}
+        },
+        properties: [
+          {
+            id: 'property-0',
+            code: 'PROPERTY_CODE',
+            dataType: 'PROPERTY_DATA_TYPE',
+            description: 'PROPERTY_DESCRIPTION',
+            label: 'PROPERTY_LABEL',
+            errors: {}
+          }
+        ]
+      })
+      expect(result).toBe(true)
+      done()
+    })
+  })
+
+  test('validation disabled', done => {
+    const componentState = new ComponentState({
+      validate: false,
+      selection: {
+        type: 'testtype'
+      },
+      type: {},
+      properties: [{ id: 'property-0' }]
+    })
+
+    execute(componentState, false, true).then(result => {
+      componentState.assertState({
+        validate: false,
+        selection: {
+          type: 'testtype'
+        },
+        type: {},
+        properties: [{ id: 'property-0' }]
+      })
+      expect(result).toBe(true)
+      done()
+    })
+  })
+})
+
+const execute = (componentState, enabled, autofocus) => {
+  const handler = new ObjectTypeHandlerValidate(
+    componentState.getGetState(),
+    componentState.getSetState()
+  )
+  return handler.setEnabled(enabled).then(() => {
+    return handler.execute(autofocus)
+  })
+}