diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php
index f913779fc526e9b8e45e592a4e1a8834d0ff7f03..2070a64884b1172f5e00e6e950981d10c5ddadb3 100644
--- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php
@@ -8,6 +8,7 @@ namespace Magento\Catalog\Test\Unit\Ui\DataProvider\Product\Form\Modifier;
 use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Categories;
 use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
 use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
+use Magento\Framework\App\CacheInterface;
 use Magento\Framework\DB\Helper as DbHelper;
 use Magento\Framework\UrlInterface;
 use Magento\Store\Model\Store;
@@ -112,4 +113,38 @@ class CategoriesTest extends AbstractModifierTest
 
         $this->assertArrayHasKey($groupCode, $this->getModel()->modifyMeta($meta));
     }
+
+    public function testModifyMetaWithCaching()
+    {
+        $this->arrayManagerMock->expects($this->exactly(2))
+            ->method('findPath')
+            ->willReturn(true);
+        $cacheManager = $this->getMockBuilder(CacheInterface::class)
+            ->getMockForAbstractClass();
+        $cacheManager->expects($this->once())
+            ->method('load')
+            ->with(Categories::CATEGORY_TREE_ID . '_');
+        $cacheManager->expects($this->once())
+            ->method('save');
+        
+        $modifier = $this->createModel();
+        $cacheContextProperty = new \ReflectionProperty(
+            Categories::class,
+            'cacheManager'
+        );
+        $cacheContextProperty->setAccessible(true);
+        $cacheContextProperty->setValue($modifier, $cacheManager);
+
+        $groupCode = 'test_group_code';
+        $meta = [
+            $groupCode => [
+                'children' => [
+                    'category_ids' => [
+                        'sortOrder' => 10,
+                    ],
+                ],
+            ],
+        ];
+        $modifier->modifyMeta($meta);
+    }
 }
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php
index b0e663532e8690ab75c8a0580155d3629009f86e..0e46b5899851f477abaae8a0591c4e95e872ef3e 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php
@@ -7,6 +7,8 @@ namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier;
 
 use Magento\Catalog\Model\Locator\LocatorInterface;
 use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\App\CacheInterface;
 use Magento\Framework\DB\Helper as DbHelper;
 use Magento\Catalog\Model\Category as CategoryModel;
 use Magento\Framework\UrlInterface;
@@ -14,9 +16,16 @@ use Magento\Framework\Stdlib\ArrayManager;
 
 /**
  * Data provider for categories field of product page
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class Categories extends AbstractModifier
 {
+    /**#@+
+     * Category tree cache id
+     */
+    const CATEGORY_TREE_ID = 'CATALOG_PRODUCT_CATEGORY_TREE';
+    /**#@-*/
+
     /**
      * @var CategoryCollectionFactory
      */
@@ -29,6 +38,7 @@ class Categories extends AbstractModifier
 
     /**
      * @var array
+     * @deprecated
      */
     protected $categoriesTrees = [];
 
@@ -47,6 +57,11 @@ class Categories extends AbstractModifier
      */
     protected $arrayManager;
 
+    /**
+     * @var CacheInterface
+     */
+    private $cacheManager;
+
     /**
      * @param LocatorInterface $locator
      * @param CategoryCollectionFactory $categoryCollectionFactory
@@ -68,6 +83,21 @@ class Categories extends AbstractModifier
         $this->arrayManager = $arrayManager;
     }
 
+    /**
+     * Retrieve cache interface
+     *
+     * @return CacheInterface
+     * @deprecated
+     */
+    private function getCacheManager()
+    {
+        if (!$this->cacheManager) {
+            $this->cacheManager = ObjectManager::getInstance()
+                ->get(CacheInterface::class);
+        }
+        return $this->cacheManager;
+    }
+
     /**
      * {@inheritdoc}
      */
@@ -254,8 +284,9 @@ class Categories extends AbstractModifier
      */
     protected function getCategoriesTree($filter = null)
     {
-        if (isset($this->categoriesTrees[$filter])) {
-            return $this->categoriesTrees[$filter];
+        $categoryTree = $this->getCacheManager()->load(self::CATEGORY_TREE_ID . '_' . $filter);
+        if ($categoryTree) {
+            return unserialize($categoryTree);
         }
 
         $storeId = $this->locator->getStore()->getId();
@@ -307,9 +338,16 @@ class Categories extends AbstractModifier
             $categoryById[$category->getId()]['label'] = $category->getName();
             $categoryById[$category->getParentId()]['optgroup'][] = &$categoryById[$category->getId()];
         }
+        
+        $this->getCacheManager()->save(
+            serialize($categoryById[CategoryModel::TREE_ROOT_ID]['optgroup']),
+            self::CATEGORY_TREE_ID . '_' . $filter,
+            [
+                \Magento\Catalog\Model\Category::CACHE_TAG,
+                \Magento\Framework\App\Cache\Type\Block::CACHE_TAG
+            ]
+        );
 
-        $this->categoriesTrees[$filter] = $categoryById[CategoryModel::TREE_ROOT_ID]['optgroup'];
-
-        return $this->categoriesTrees[$filter];
+        return $categoryById[CategoryModel::TREE_ROOT_ID]['optgroup'];
     }
 }
diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js
index b7031f1df85c24edc1c68761bb370682eb5bfcd0..d0799aad750d9f776b495717c832c6b1cb89d754 100644
--- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js
+++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js
@@ -19,6 +19,9 @@ define([
             level: 0,
             visible: true,
             disabled: false,
+            listens: {
+                'opened': 'onVisibilityChange'
+            },
             additionalClasses: {}
         },
 
@@ -30,7 +33,19 @@ define([
             _.bindAll(this, 'onChildrenUpdate', 'onChildrenError', 'onContentLoading');
 
             return this._super()
-                       ._setClasses();
+                ._setClasses();
+        },
+
+        /**
+         * Initializes components' configuration.
+         *
+         * @returns {Fieldset} Chainable.
+         */
+        initConfig: function () {
+            this._super();
+            this._wasOpened = this.opened || !this.collapsible;
+
+            return this;
         },
 
         /**
@@ -117,6 +132,17 @@ define([
             return this;
         },
 
+        /**
+         * Handler of the "opened" property changes.
+         *
+         * @param {Boolean} isOpened
+         */
+        onVisibilityChange: function (isOpened) {
+            if (!this._wasOpened) {
+                this._wasOpened = isOpened;
+            }
+        },
+
         /**
          * Is being invoked on children validation error.
          * Sets error property to one incoming.
diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js
index 393b0f04e512cb9df9dd509044b26c599fd170c0..be312c71f1fb20a1a2140765d598d8567bfaf648 100644
--- a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js
+++ b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js
@@ -9,7 +9,8 @@ define([
     'Magento_Ui/js/lib/key-codes',
     'mage/translate',
     'ko',
-    'jquery'
+    'jquery',
+    'Magento_Ui/js/lib/view/utils/async'
 ], function (_, Abstract, keyCodes, $t, ko, $) {
     'use strict';
 
@@ -148,9 +149,14 @@ define([
             showPath: true,
             labelsDecoration: false,
             disableLabel: false,
+            filterRateLimit: 500,
             closeBtnLabel: $t('Done'),
             optgroupTmpl: 'ui/grid/filters/elements/ui-select-optgroup',
             quantityPlaceholder: $t('options'),
+            hoverClass: '_hover',
+            rootListSelector: 'ul.admin__action-multiselect-menu-inner._root',
+            visibleOptionSelector: 'li.admin__action-multiselect-menu-inner-item:visible',
+            actionTargetSelector: '.action-menu-item',
             selectedPlaceholders: {
                 defaultPlaceholder: $t('Select...'),
                 lotPlaceholders: $t('Selected')
@@ -179,6 +185,23 @@ define([
             }
         },
 
+        /**
+         * Initializes UISelect component.
+         *
+         * @returns {UISelect} Chainable.
+         */
+        initialize: function () {
+            this._super();
+
+            $.async(
+                this.rootListSelector,
+                this,
+                this.onRootListRender.bind(this)
+            );
+
+            return this;
+        },
+
         /**
          * Parses options and merges the result with instance
          * Set defaults according to mode and levels configuration
@@ -281,6 +304,8 @@ define([
                 'filterOptionsFocus'
             ]);
 
+            this.filterInputValue.extend({rateLimit: this.filterRateLimit});
+
             return this;
         },
 
@@ -307,17 +332,22 @@ define([
          * @returns {Boolean} level visibility.
          */
         showLevels: function (data) {
-            var curLevel = ++data.level;
+            var curLevel = ++data.level,
+                isVisible;
 
-            if (!data.visible) {
-                data.visible = ko.observable(!!data.hasOwnProperty(this.separator) &&
+            if (data.visible) {
+                isVisible = data.visible();
+            } else {
+                isVisible = !!data.hasOwnProperty(this.separator) &&
                     _.isBoolean(this.levelsVisibility) &&
                     this.levelsVisibility ||
-                    data.hasOwnProperty(this.separator) && parseInt(this.levelsVisibility, 10) >= curLevel);
+                    data.hasOwnProperty(this.separator) && parseInt(this.levelsVisibility, 10) >= curLevel;
 
+                data.visible = ko.observable(isVisible);
+                data.isVisited = isVisible;
             }
 
-            return data.visible();
+            return isVisible;
         },
 
         /**
@@ -398,7 +428,13 @@ define([
             var value = this.filterInputValue().trim().toLowerCase(),
                 array = [];
 
-            if (value === '') {
+            if (value && value.length < 2) {
+                return false;
+            }
+
+            this.cleanHoveredElement();
+
+            if (!value) {
                 this.renderPath = false;
                 this.options(this.cacheOptions.tree);
                 this._setItemsQuantity(false);
@@ -421,7 +457,6 @@ define([
                     this.options(array);
                     this._setItemsQuantity(array.length);
                 }
-                this.cleanHoveredElement();
 
                 return false;
             }
@@ -509,8 +544,12 @@ define([
          * @returns {Object} Chainable
          */
         cleanHoveredElement: function () {
-            if (!_.isNull(this.hoverElIndex())) {
-                this.hoverElIndex(null);
+            if (this.hoveredElement) {
+                $(this.hoveredElement)
+                    .children(this.actionTargetSelector)
+                    .removeClass(this.hoverClass);
+
+                this.hoveredElement = null;
             }
 
             return this;
@@ -543,14 +582,16 @@ define([
          * @return {Boolean}
          */
         isHovered: function (data) {
-            var index = this.getOptionIndex(data),
-                status = this.hoverElIndex() === index;
+            var element = this.hoveredElement,
+                elementData;
 
-            if (this.selectType === 'optgroup' && data.hasOwnProperty(this.separator)) {
+            if (!element) {
                 return false;
             }
 
-            return status;
+            elementData = ko.dataFor(this.hoveredElement);
+
+            return data.value === elementData.value;
         },
 
         /**
@@ -612,10 +653,10 @@ define([
          * Change visibility to child level
          *
          * @param {Object} data - element data
-         * @param {Object} elem - element
          */
-        openChildLevel: function (data, elem) {
-            var contextElement;
+        openChildLevel: function (data) {
+            var contextElement = data,
+                isVisible;
 
             if (
                 this.openLevelsAction &&
@@ -623,8 +664,13 @@ define([
                 this.openLevelsAction &&
                 data.hasOwnProperty(this.separator) && parseInt(this.levelsVisibility, 10) <= data.level
             ) {
-                contextElement = ko.contextFor($(elem).parents('li').children('ul')[0]).$data.current;
-                contextElement.visible(!contextElement.visible());
+                isVisible = !contextElement.visible();
+
+                if (isVisible && !contextElement.isVisited) {
+                    contextElement.isVisited = true;
+                }
+
+                contextElement.visible(isVisible);
             }
         },
 
@@ -642,25 +688,23 @@ define([
         },
 
         /**
-         * Add hover to some list element and clears element ID to variable
+         * @deprecated
+         */
+        onMousemove: function () {},
+
+        /**
+         * Handles hover on list items.
          *
-         * @param {Object} data - object with data about this element
-         * @param {Number} index - element index
          * @param {Object} event - mousemove event
          */
-        onMousemove: function (data, index, event) {
-            var id,
-                context = ko.contextFor(event.target);
-
-            if (this.isCursorPositionChange(event)) {
-                return false;
-            }
+        onDelegatedMouseMouve: function (event) {
+            var target = $(event.currentTarget).closest(this.visibleOptionSelector)[0];
 
-            if (typeof context.$data === 'object') {
-                id = this.getOptionIndex(context.$data);
+            if (this.isCursorPositionChange(event) || this.hoveredElement === target) {
+                return;
             }
 
-            id !== this.hoverElIndex() ? this.hoverElIndex(id) : false;
+            this._hoverTo(target);
             this.setCursorPosition(event);
         },
 
@@ -736,8 +780,8 @@ define([
             }
 
             if (this.listVisible()) {
-                if (!_.isNull(this.hoverElIndex())) {
-                    this.toggleOptionSelected(this.cacheOptions.plain[this.hoverElIndex()]);
+                if (this.hoveredElement) {
+                    this.toggleOptionSelected(ko.dataFor(this.hoveredElement));
                 }
             } else {
                 this.setListVisible(true);
@@ -756,35 +800,7 @@ define([
          * selected first option in list
          */
         pageDownKeyHandler: function () {
-            var el,
-                nextEl,
-                nextData,
-                nextIndex;
-
-            if (!this.listVisible()) {
-                return false;
-            }
-
-            if (this.filterInputValue()) {
-                el = !_.isNull(this.hoverElIndex()) ?
-                    this._getElemByData(this.cacheOptions.plain[this.hoverElIndex()]) : false;
-                nextEl = el ? el.next() : $(this.cacheUiSelect).find('li:visible').eq(0);
-                nextIndex = nextEl.length ? nextEl.index() : 0;
-                nextData = this.options()[nextIndex];
-                this.hoverElIndex(this.getOptionIndex(nextData));
-
-                return false;
-            }
-
-            if (!_.isNull(this.hoverElIndex()) && this.hoverElIndex() !== this.cacheOptions.plain.length - 1) {
-                this._setHoverToElement(1);
-                this._scrollTo(this.hoverElIndex());
-
-                return false;
-            }
-
-            this._setHoverToElement(1, -1);
-            this._scrollTo(this.hoverElIndex());
+            this._setHoverToElement(1);
         },
 
         /**
@@ -813,28 +829,19 @@ define([
          * Set hover to visible element
          *
          * @param {Number} direction - iterator
-         * @param {Number} index - current hovered element
-         * @param {Array} list - collection items
          */
-        _setHoverToElement: function (direction, index, list) {
-            var modifiedIndex,
-                curData,
-                canBeHovered = true;
-
-            list = list || $(this.cacheUiSelect).find('li');
-            index = index || _.isNumber(index) ? index : this.hoverElIndex();
-            modifiedIndex = index + direction;
-            modifiedIndex < 0 ? modifiedIndex = this.cacheOptions.plain.length - 1 : false;
-            curData = this.cacheOptions.plain[modifiedIndex];
+        _setHoverToElement: function (direction) {
+            var element;
 
-            if (this.selectType === 'optgroup' && !_.findWhere(this.cacheOptions.lastOptions, {value: curData.value})) {
-                canBeHovered = false;
+            if (direction ===  1) {
+                element = this._getNextElement();
+            } else if (direction === -1) {
+                element = this._getPreviousElement();
             }
 
-            if (list.eq(modifiedIndex).is(':visible') && canBeHovered) {
-                this.hoverElIndex(modifiedIndex);
-            } else {
-                this._setHoverToElement(direction, modifiedIndex, list);
+            if (element) {
+                this._hoverTo(element);
+                this._scrollTo(element);
             }
         },
 
@@ -844,18 +851,15 @@ define([
          *
          * @param {Number} index - element index
          */
-        _scrollTo: function (index) {
-            var curEl,
-                parents,
-                wrapper,
+        _scrollTo: function (element) {
+            var curEl = $(element).children(this.actionTargetSelector),
+                wrapper = $(this.rootList),
                 curElPos = {},
                 wrapperPos = {};
 
-            curEl = $(this.cacheUiSelect).find('li').eq(index);
-            parents = curEl.parents('ul');
-            wrapper = parents.eq(parents.length - 1);
             curElPos.start = curEl.offset().top;
-            curElPos.end = curElPos.start + curEl.height();
+            curElPos.end = curElPos.start + curEl.outerHeight();
+
             wrapperPos.start = wrapper.offset().top;
             wrapperPos.end = wrapperPos.start + wrapper.height();
 
@@ -871,46 +875,7 @@ define([
          * selected last option in list
          */
         pageUpKeyHandler: function () {
-            var el,
-                nextEl,
-                nextIndex,
-                nextData;
-
-            if (!this.listVisible()) {
-                return false;
-            }
-
-            if (this.filterInputValue()) {
-                el = !_.isNull(this.hoverElIndex()) ?
-                    this._getElemByData(this.cacheOptions.plain[this.hoverElIndex()]) : false;
-                nextEl = el ? el.prev() : $(this.cacheUiSelect).find('li:visible').eq(this.options().length-1);
-                nextIndex = nextEl.length ? nextEl.index() : this.options().length-1;
-                nextData = this.options()[nextIndex];
-                this.hoverElIndex(this.getOptionIndex(nextData));
-
-                return false;
-            }
-
-
-            if (this.filterInputValue()) {
-                el = !_.isNull(this.hoverElIndex()) ?
-                    this._getElemByData(this.cacheOptions.plain[this.hoverElIndex()]) : false;
-                nextEl = el ? el.next() : $(this.cacheUiSelect).find('li:visible').eq(0);
-                nextIndex = nextEl.length ? nextEl.index() : 0;
-                nextData = this.options()[nextIndex];
-                this.hoverElIndex(this.getOptionIndex(nextData));
-
-                return false;
-            }
-
-            if (this.hoverElIndex()) {
-                this._setHoverToElement(-1);
-                this._scrollTo(this.hoverElIndex());
-
-                return false;
-            }
-            this._setHoverToElement(-1, this.cacheOptions.plain.length);
-            this._scrollTo(this.hoverElIndex());
+            this._setHoverToElement(-1);
         },
 
         /**
@@ -990,6 +955,129 @@ define([
             return selected.map(function (option) {
                 return option.label;
             }).join(', ');
+        },
+
+        /**
+         * Defines previous option element to
+         * the one that is currently hovered.
+         *
+         * @returns {Element}
+         */
+        _getPreviousElement: function () {
+            var currentElement = this.hoveredElement,
+                lastElement    = this._getLastIn(this.rootList),
+                previousElement;
+
+            if (!currentElement) {
+                return lastElement;
+            }
+
+            previousElement = $(currentElement).prev()[0];
+
+            return (
+                this._getLastIn(previousElement) ||
+                previousElement ||
+                this._getFirstParentOf(currentElement) ||
+                lastElement
+            );
+        },
+
+        /**
+         * Defines next option element to
+         * the one that is currently hovered.
+         *
+         * @returns {Element}
+         */
+        _getNextElement: function () {
+            var currentElement = this.hoveredElement,
+                firstElement   = this._getFirstIn(this.rootList);
+
+            if (!currentElement) {
+                return firstElement;
+            }
+
+            return (
+                this._getFirstIn(currentElement) ||
+                $(currentElement).next()[0] ||
+                this._getParentsOf(currentElement).next()[0] ||
+                firstElement
+            );
+        },
+
+        /**
+         * Returns first option element in provided scope.
+         *
+         * @param {Element} scope
+         * @returns {Element}
+         */
+        _getFirstIn: function (scope) {
+            return $(scope).find(this.visibleOptionSelector)[0];
+        },
+
+        /**
+         * Returns last descendant option element in provided scope.
+         *
+         * @param {Element} scope
+         * @returns {Element}
+         */
+        _getLastIn: function (scope) {
+            return $(scope).find(this.visibleOptionSelector).last()[0];
+        },
+
+        /**
+         * Returns a collection of parent option elements.
+         *
+         * @param {Element} scope
+         * @returns {jQueryCollection}
+         */
+        _getParentsOf: function (scope) {
+            return $(scope).parents(this.visibleOptionSelector);
+        },
+
+        /**
+         * Returns first parent option element.
+         *
+         * @param {Element} scope
+         * @returns {Element}
+         */
+        _getFirstParentOf: function (scope) {
+            return this._getParentsOf(scope)[0];
+        },
+
+        /**
+         * Sets hover class to provided option element.
+         *
+         * @param {Element} element
+         */
+        _hoverTo: function(element) {
+            if (this.hoveredElement) {
+                $(this.hoveredElement)
+                    .children(this.actionTargetSelector)
+                    .removeClass(this.hoverClass);
+            }
+
+            $(element)
+                .children(this.actionTargetSelector)
+                .addClass(this.hoverClass);
+
+            this.hoveredElement = element;
+        },
+
+        /**
+         * Callback which fires when root list element is rendered.
+         *
+         * @param {Element} element
+         */
+        onRootListRender: function (element) {
+            var targetSelector = 'li > ' + this.actionTargetSelector;
+
+            this.rootList = element;
+
+            $(this.rootList).on(
+                'mousemove',
+                targetSelector,
+                this.onDelegatedMouseMouve.bind(this)
+            );
         }
     });
 });
diff --git a/app/code/Magento/Ui/view/base/web/templates/form/fieldset.html b/app/code/Magento/Ui/view/base/web/templates/form/fieldset.html
index b45e5385d037ac9b4cecca6888a7318e6ec8e384..6cb7fb62b3a911309797333c12f33435850243cb 100644
--- a/app/code/Magento/Ui/view/base/web/templates/form/fieldset.html
+++ b/app/code/Magento/Ui/view/base/web/templates/form/fieldset.html
@@ -9,7 +9,8 @@
      attr="'data-level': $data.level, 'data-index': index"
      data-bind="visible: $data.visible === undefined ? true: $data.visible">
     <div class="fieldset-wrapper-title"
-         attr="tabindex: !collapsible ? -1 : 0"
+         attr="tabindex: !collapsible ? -1 : 0,
+               'data-state-collapsible': collapsible ? opened() ? 'open' : 'closed' : null"
          click="toggleOpened"
          keyboard="13: toggleOpened"
          if="label">
@@ -30,7 +31,7 @@
                 <span class="admin__page-nav-item-message _error">
                     <span class="admin__page-nav-item-message-icon"></span>
                     <span class="admin__page-nav-item-message-tooltip"
-                            data-bind="i18n: 'This tab contains invalid data. Please resolve this before saving.'">
+                          data-bind="i18n: 'This tab contains invalid data. Please resolve this before saving.'">
                     </span>
                 </span>
                 <span class="admin__page-nav-item-message-loader">
@@ -43,7 +44,10 @@
     </div>
 
     <div class="admin__fieldset-wrapper-content"
-         css="'admin__collapsible-content': collapsible, '_show': opened, '_hide': !opened">
-        <fieldset class="admin__fieldset" each="data: elems, as: 'element'" render=""/>
+         css="'admin__collapsible-content': collapsible, '_show': opened, '_hide': !opened()">
+        <fieldset
+                if="opened() || _wasOpened"
+                class="admin__fieldset"
+                each="data: elems, as: 'element'" render=""/>
     </div>
 </div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select-optgroup.html b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select-optgroup.html
index 506aa4381c9b816bb4608802732bb7ffed3c3a46..5f938b6e343128e4d3700d605234eec16a216f47 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select-optgroup.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select-optgroup.html
@@ -10,6 +10,7 @@
     attr: {
         'data-level': $data.current.level++
     }">
+    <!-- ko if: $data.current.visible() || $data.current.isVisited  -->
     <!-- ko foreach: { data: $data.current.optgroup, as: 'option'}  -->
     <li class="admin__action-multiselect-menu-inner-item"
         data-bind="css: { _parent: $data.optgroup }">
@@ -56,4 +57,5 @@
         <!-- /ko-->
     </li>
     <!-- /ko -->
+    <!-- /ko -->
 </ul>
\ No newline at end of file
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html
index a913d27c8aa5aeba6968ab6283dc4717e65e3a9d..fae89160e138e8d56ba6a99a34f5957d36f6a35b 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html
@@ -178,4 +178,4 @@
         </div>
         <!-- /ko -->
     </div>
-</div>
+</div>
\ No newline at end of file
diff --git a/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html b/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html
index c856f9fd68d077b3df7b9d3692a5406915c1e41e..8b45955b5a480363ac05fb5fee3cd4e50632a446 100644
--- a/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html
+++ b/app/code/Magento/Ui/view/base/web/templates/modal/modal-component.html
@@ -4,9 +4,6 @@
  * See COPYING.txt for license details.
  */
 -->
-
-<div data-bind="css: modalClass, hasFocus: focused">
-    <!-- ko foreach: { data: elems, as: 'element' } -->
-        <!-- ko template: element.getTemplate() --><!-- /ko -->
-    <!-- /ko -->
+<div css="modalClass" hasFocus="focused">
+    <each if="state() || $data.modal" args="data: elems, as: 'element'" render=""/>
 </div>
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml
index 42f490d76191a524b12c7837ab5958907199fece..63535106514f2c893b0f822daeb7350fd7d892a3 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml
@@ -8,7 +8,7 @@
 <sections>
     <general_information>
         <class>\Magento\Ui\Test\Block\Adminhtml\Section</class>
-        <selector>div[class=fieldset-wrapper]</selector>
+        <selector>[data-index="general"]</selector>
         <strategy>css selector</strategy>
         <fields>
             <is_active>
@@ -31,8 +31,8 @@
     </general_information>
     <content>
         <class>\Magento\Ui\Test\Block\Adminhtml\Section</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::select[@name='landing_page']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="content"]</selector>
+        <strategy>css selector</strategy>
         <fields>
             <description>
                 <input>textarea</input>
@@ -45,8 +45,8 @@
     </content>
     <display_setting>
         <class>\Magento\Ui\Test\Block\Adminhtml\Section</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::input[@name='is_anchor']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="display_settings"]</selector>
+        <strategy>css selector</strategy>
         <fields>
             <display_mode>
                 <input>select</input>
@@ -83,8 +83,8 @@
     </display_setting>
     <seo>
         <class>\Magento\Ui\Test\Block\Adminhtml\Section</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::input[@name='meta_title']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="search_engine_optimization"]</selector>
+        <strategy>css selector</strategy>
         <fields>
             <url_key>
                 <input>input</input>
@@ -98,13 +98,13 @@
     </seo>
     <category_products>
         <class>\Magento\Catalog\Test\Block\Adminhtml\Category\Edit\Section\Products</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::div[@id='catalog_category_products']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="assign_products"]</selector>
+        <strategy>css selector</strategy>
     </category_products>
     <design>
         <class>\Magento\Ui\Test\Block\Adminhtml\Section</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::select[@name='page_layout']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="design"]</selector>
+        <strategy>css selector</strategy>
         <fields>
             <use_parent_category_settings>
                 <input>checkbox</input>
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertAddedProductAttributeOnProductForm.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertAddedProductAttributeOnProductForm.php
index 78e28abebe44bfd8f009fa8aee49f0d77e6c974a..54bf06f70b5f1c0f192b79bda3ade75a85cd37f7 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertAddedProductAttributeOnProductForm.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertAddedProductAttributeOnProductForm.php
@@ -92,9 +92,8 @@ class AssertAddedProductAttributeOnProductForm extends AbstractConstraint
         $catalogProductAttribute = ($productAttributeOriginal !== null)
             ? array_merge($productAttributeOriginal->getData(), $attribute->getData())
             : $attribute->getData();
-        if ($catalogProductEdit->getProductForm()->isSectionVisible(self::ATTRIBUTES)) {
-            $catalogProductEdit->getProductForm()->openSection(self::ATTRIBUTES);
-        }
+        $catalogProductEdit->getProductForm()->openSection(self::ATTRIBUTES);
+
         \PHPUnit_Framework_Assert::assertTrue(
             $catalogProductEdit->getProductForm()->checkAttributeLabel($catalogProductAttribute),
             "Product Attribute is absent on Product form."
diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Block/Adminhtml/Promo/Catalog/Edit/PromoForm.xml b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Block/Adminhtml/Promo/Catalog/Edit/PromoForm.xml
index 25e8617faf11292f03e40461bb146cbd6a1779c1..4474370eebbc75d27383be14700bdbb6f47d90c4 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Block/Adminhtml/Promo/Catalog/Edit/PromoForm.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Block/Adminhtml/Promo/Catalog/Edit/PromoForm.xml
@@ -8,8 +8,8 @@
 <sections>
     <rule_information>
         <class>\Magento\CatalogRule\Test\Block\Adminhtml\Promo\Catalog\Edit\Section\RuleInformation</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::input[@name='name']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="rule_information"]</selector>
+        <strategy>css selector</strategy>
         <fields>
             <is_active>
                 <input>select</input>
@@ -26,19 +26,20 @@
     </rule_information>
     <conditions>
         <class>\Magento\CatalogRule\Test\Block\Adminhtml\Promo\Catalog\Edit\Section\Conditions</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::div[@class='rule-tree']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="block_promo_catalog_edit_tab_conditions"]</selector>
+        <strategy>css selector</strategy>
         <fields>
             <conditions>
                 <selector>[id^="catalog_rule_formrule_conditions_fieldset_"]</selector>
+                <strategy>css selector</strategy>
                 <input>conditions</input>
             </conditions>
         </fields>
     </conditions>
     <actions>
         <class>\Magento\Ui\Test\Block\Adminhtml\Section</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::select[@name='simple_action']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="actions"]</selector>
+        <strategy>css selector</strategy>
         <fields>
             <simple_action>
                 <input>select</input>
diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml
index cc48c0fc1cfb54ee51bc4e8ddffed12df210348e..6dcb041efa0703e86dcb2de2fbf38e1456600775 100644
--- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml
+++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml
@@ -40,8 +40,8 @@
     </rule_information>
     <conditions>
         <class>\Magento\Ui\Test\Block\Adminhtml\Section</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::div[@class='rule-tree']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="conditions"]</selector>
+        <strategy>css selector</strategy>
         <fields>
             <conditions_serialized>
                 <selector>.fieldset[id^="sales_rule_formrule_conditions_fieldset_"]</selector>
@@ -51,8 +51,8 @@
     </conditions>
     <actions>
         <class>\Magento\Ui\Test\Block\Adminhtml\Section</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::select[@name='simple_action']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="actions"]</selector>
+        <strategy>css selector</strategy>
         <fields>
             <simple_action>
                 <input>select</input>
@@ -77,7 +77,7 @@
     </actions>
     <labels>
         <class>\Magento\SalesRule\Test\Block\Adminhtml\Promo\Quote\Edit\Section\Labels</class>
-        <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::input[@name='store_labels[0]']]</selector>
-        <strategy>xpath</strategy>
+        <selector>[data-index="labels"]</selector>
+        <strategy>css selector</strategy>
     </labels>
 </sections>
diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/FormSections.php b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/FormSections.php
index 6a5919b8cb1cfcfc2c8a666009e977ab0baafe0b..b5edbc6c791939cbf8f7d2e3b54ded1b22add3d7 100644
--- a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/FormSections.php
+++ b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/FormSections.php
@@ -6,50 +6,26 @@
 
 namespace Magento\Ui\Test\Block\Adminhtml;
 
-use Magento\Mtf\Client\Locator;
-use Magento\Mtf\Client\ElementInterface;
 use Magento\Mtf\Fixture\InjectableFixture;
 
 /**
- * Is used to represent a new unified form with collapsible sections on the page.
+ * Is used to represent a new unified form with collapsible sections inside.
  */
 class FormSections extends AbstractFormContainers
 {
     /**
-     * CSS locator of the section collapsible title
+     * CSS locator of collapsed section.
      *
      * @var string
      */
-    protected $sectionTitle = '.fieldset-wrapper-title';
+    protected $collapsedSection = '[data-state-collapsible="closed"]';
 
     /**
-     * CSS locator of the section content
+     * CSS locator of expanded section.
      *
      * @var string
      */
-    protected $content = '.admin__fieldset-wrapper-content';
-
-    /**
-     * XPath locator of the collapsible fieldset
-     *
-     * @var string
-     */
-    protected $collapsible =
-        'div[contains(@class,"fieldset-wrapper")][contains(@class,"admin__collapsible-block-wrapper")]';
-
-    /**
-     * Locator for opened collapsible tab.
-     *
-     * @var string
-     */
-    protected $opened = '._show';
-
-    /**
-     * Locator for error messages.
-     *
-     * @var string
-     */
-    protected $errorMessages = '[data-ui-id="messages-message-error"]';
+    protected $expandedSection = '[data-state-collapsible="open"]';
 
     /**
      * Get Section class.
@@ -72,58 +48,39 @@ class FormSections extends AbstractFormContainers
     }
 
     /**
-     * Get the section title element
-     *
-     * @param string $sectionName
-     * @return ElementInterface
-     */
-    protected function getSectionTitleElement($sectionName)
-    {
-        $container = $this->getContainerElement($sectionName);
-        return $container->find($this->sectionTitle);
-    }
-
-    /**
-     * Opens the section.
+     * Expand section by its name.
      *
      * @param string $sectionName
      * @return $this
      */
     public function openSection($sectionName)
     {
-        if ($this->isCollapsible($sectionName) && !$this->isCollapsed($sectionName)) {
-            $this->getSectionTitleElement($sectionName)->click();
-        } else {
-            //Scroll to the top of the page so that the page actions header does not overlap any controls
-            $this->browser->find($this->header)->hover();
+        $section = $this->getContainerElement($sectionName)->find($this->collapsedSection);
+        if ($section->isVisible()) {
+            $section->click();
         }
+
         return $this;
     }
 
     /**
-     * Checks if the section is collapsible on the form.
+     * Check if section is collapsible.
      *
+     * @deprecated
      * @param string $sectionName
      * @return bool
      */
     public function isCollapsible($sectionName)
     {
-        $title = $this->getSectionTitleElement($sectionName);
-        if (!$title->isVisible()) {
-            return false;
-        };
-        return $title->find('parent::' . $this->collapsible, Locator::SELECTOR_XPATH)->isVisible();
-    }
+        $section = $this->getContainerElement($sectionName);
 
-    /**
-     * Check if collapsible section is opened.
-     *
-     * @param string $sectionName
-     * @return bool
-     */
-    private function isCollapsed($sectionName)
-    {
-        return $this->getContainerElement($sectionName)->find($this->opened)->isVisible();
+        if ($section->find($this->collapsedSection)->isVisible()) {
+            return true;
+        } elseif ($section->find($this->expandedSection)->isVisible()) {
+            return true;
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -151,11 +108,12 @@ class FormSections extends AbstractFormContainers
     /**
      * Check if section is visible.
      *
+     * @deprecated
      * @param string $sectionName
      * @return bool
      */
     public function isSectionVisible($sectionName)
     {
-        return ($this->isCollapsible($sectionName) && !$this->isCollapsed($sectionName));
+        return !$this->getContainerElement($sectionName)->find($this->collapsedSection)->isVisible();
     }
 }