diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_listing.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_listing.xml
index 3984099e1e81c1bf7faac1fc4f395dc8e5af650e..342d4d0a4b83efe2dc56ce1f29586bffcdf603ab 100644
--- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_listing.xml
+++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_listing.xml
@@ -43,6 +43,7 @@
         <argument name="data" xsi:type="array">
             <item name="config" xsi:type="array">
                 <item name="template" xsi:type="string">ui/grid/toolbar</item>
+                <item name="stickyTmpl" xsi:type="string">ui/grid/sticky/toolbar</item>
             </item>
         </argument>
         <bookmark name="bookmarks">
@@ -692,4 +693,13 @@
             </argument>
         </column>
     </columns>
+    <container name="sticky">
+        <argument name="data" xsi:type="array">
+            <item name="config" xsi:type="array">
+                <item name="component" xsi:type="string">Magento_Ui/js/grid/sticky/sticky</item>
+                <item name="toolbarProvider" xsi:type="string">cms_page_listing.cms_page_listing.listing_top</item>
+                <item name="listingProvider" xsi:type="string">cms_page_listing.cms_page_listing.cms_page_columns</item>
+            </item>
+        </argument>
+    </container>
 </listing>
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js
index f37832bc8972eddc3bf8e31b4a8d095e7bcdcff0..091cd9ee2ea570a238e72a1a27cb9eb36d579322 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js
@@ -2,6 +2,7 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
+
 define([
     'underscore',
     'mageUtils',
@@ -35,7 +36,7 @@ define([
          */
         initObservable: function () {
             this._super()
-                .observe('actions opened');
+                .observe('actions');
 
             return this;
         },
@@ -253,40 +254,6 @@ define([
             var action = this.getAction(rowIndex, actionIndex);
 
             return _.isObject(action.callback) || action.confirm || !action.href;
-        },
-
-        /**
-         * Opens or closes specific actions list.
-         *
-         * @param {Number} rowIndex - Index of a row,
-         *      where actions are displayed.
-         * @returns {ActionsColumn} Chainable.
-         */
-        toggleList: function (rowIndex) {
-            var state = false;
-
-            if (rowIndex !== this.opened()) {
-                state = rowIndex;
-            }
-
-            this.opened(state);
-
-            return this;
-        },
-
-        /**
-         * Closes actions list.
-         *
-         * @param {Number} rowIndex - Index of a row,
-         *      where actions are displayed.
-         * @returns {ActionsColumn}
-         */
-        closeList: function (rowIndex) {
-            if (this.opened() === rowIndex) {
-                this.opened(false);
-            }
-
-            return this;
         }
     });
 });
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js
index 905cd7d44ebf43246d26e2327392adb0fee0bc26..b48f1c9ed8a35adc457b2f03b0190a2cb4d7c3d2 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js
@@ -60,7 +60,6 @@ define([
             this._super()
                 .observe([
                     'disabled',
-                    'menuVisible',
                     'selected',
                     'excluded',
                     'excludeMode',
@@ -74,28 +73,6 @@ define([
             return this;
         },
 
-        /**
-         * Toggles menu with a list of select actions.
-         *
-         * @returns {Multiselect} Chainable.
-         */
-        toggleMenu: function () {
-            this.menuVisible(!this.menuVisible());
-
-            return this;
-        },
-
-        /**
-         * Hides menu with a list of select actions.
-         *
-         * @returns {Multiselect} Chainable.
-         */
-        hideMenu: function () {
-            this.menuVisible(false);
-
-            return this;
-        },
-
         /**
          * Selects specified record.
          *
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/controls/bookmarks/bookmarks.js b/app/code/Magento/Ui/view/base/web/js/grid/controls/bookmarks/bookmarks.js
index a684f5eece069704ff3361ef02eb43c7d98f6a04..8bd34bb57f6a29a6413a6405275506ca6fee1db8 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/controls/bookmarks/bookmarks.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/controls/bookmarks/bookmarks.js
@@ -5,10 +5,9 @@
 define([
     'underscore',
     'mageUtils',
-    'uiRegistry',
     'uiLayout',
-    'Magento_Ui/js/lib/collapsible'
-], function (_, utils, registry, layout, Collapsible) {
+    'uiComponent'
+], function (_, utils, layout, Component) {
     'use strict';
 
     /**
@@ -27,7 +26,7 @@ define([
         return path.join('.');
     }
 
-    return Collapsible.extend({
+    return Component.extend({
         defaults: {
             template: 'ui/grid/controls/bookmarks/bookmarks',
             defaultIndex: 'default',
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/controls/columns.js b/app/code/Magento/Ui/view/base/web/js/grid/controls/columns.js
index 765e437334285090378e1db15d8ac33753da9ab8..4673bedd251f7a8b3ac778b40985bff8ed7a5eb0 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/controls/columns.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/controls/columns.js
@@ -6,11 +6,11 @@ define([
     'underscore',
     'mageUtils',
     'mage/translate',
-    'Magento_Ui/js/lib/collapsible'
-], function (_, utils, $t, Collapsible) {
+    'uiComponent'
+], function (_, utils, $t, Component) {
     'use strict';
 
-    return Collapsible.extend({
+    return Component.extend({
         defaults: {
             template: 'ui/grid/controls/columns',
             minVisible: 1,
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/dnd.js b/app/code/Magento/Ui/view/base/web/js/grid/dnd.js
index 3fa06634bd42c2a69c9001ec5b559dbdc1317c16..fbcffc466f1d01583e1b470799550a8afa78e7fe 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/dnd.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/dnd.js
@@ -78,6 +78,7 @@ define([
             y >= area.top && y <= area.bottom
         );
     }
+
     /*eslint-enable no-extra-parens*/
 
     /**
@@ -109,10 +110,22 @@ define([
         return ko.dataFor(elem);
     }
 
+    /**
+     * Checks whether cols are identical
+     *
+     * @param {HTMLElement} c1
+     * @param {HTMLElement} c2
+     * @returns {Boolean}
+     */
+    function compareCols(c1, c2) {
+        return c1.cellIndex === c2.cellIndex;
+    }
+
     return Class.extend({
         defaults: {
             rootSelector: '${ $.columnsProvider }:.admin__data-grid-wrap',
             tableSelector: '${ $.rootSelector } -> table.data-grid',
+            mainTableSelector: '[data-role="grid"]',
             columnSelector: '${ $.tableSelector } thead tr th',
             noSelectClass: '_no-select',
             hiddenClass: '_hidden',
@@ -178,7 +191,7 @@ define([
          * @returns {Dnd} Chainable.
          */
         initTable: function (table) {
-            this.table = table;
+            this.table =  $(table).is(this.mainTableSelector) ?  table : this.table;
 
             $(table).addClass('data-grid-draggable');
 
@@ -263,12 +276,15 @@ define([
                 rect;
 
             this.coords = this.columns.map(function (column) {
-                var data;
+                var data,
+                    colIndex = _.findIndex(cells, function (cell) {
+                        return compareCols(cell, column);
+                    });
 
                 rect = column.getBoundingClientRect();
 
                 data = {
-                    index: cells.indexOf(column),
+                    index: colIndex,
                     target: column,
                     orig: rect,
                     left: rect.left - bodyRect.left,
@@ -372,7 +388,7 @@ define([
                 this.dragleave(leavedArea);
             }
 
-            if (area && area.target !== this.dragArea.target) {
+            if (area && !compareCols(area.target, this.dragArea.target)) {
                 this.dragenter(area);
             }
         },
@@ -479,7 +495,7 @@ define([
 
             getModel(dragElem).dragging(false);
 
-            if (dropArea && dropArea.target !== dragElem) {
+            if (dropArea && !compareCols(dropArea.target, dragElem)) {
                 this.drop(dropArea, dragArea);
             }
         },
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/editing/editor-view.js b/app/code/Magento/Ui/view/base/web/js/grid/editing/editor-view.js
index aab27ca0645e1f2246e5fdc07bf49096d9c846f5..9bd9d5318fef5db2c341e98c18abd6ad7bf211bc 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/editing/editor-view.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/editing/editor-view.js
@@ -96,8 +96,10 @@ define([
          * @returns {View} Chainable.
          */
         initBulk: function (table) {
+            var tableBody = $('tbody', table)[0];
+
             $(this.bulkTmpl)
-                .prependTo('tbody', table)
+                .prependTo(tableBody)
                 .applyBindings(this.model);
 
             return this;
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/export.js b/app/code/Magento/Ui/view/base/web/js/grid/export.js
index 0350d156f2656ec8316d7152437a48ca93729dc5..032f5fcc36717432c9a8c0f08a41d7f9c42f5d13 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/export.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/export.js
@@ -5,12 +5,11 @@
 define([
     'jquery',
     'underscore',
-    'ko',
-    'Magento_Ui/js/lib/collapsible'
-], function ($, _, ko, Collapsible) {
+    'uiComponent'
+], function ($, _, Component) {
     'use strict';
 
-    return Collapsible.extend({
+    return Component.extend({
 
         defaults: {
             template: 'ui/grid/exportButton',
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/chips.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/chips.js
index 5af83c6ddf76f96ee751b7895c787a763ecdb588..a31b1fd2d50ad971f39ad8f4644d7bdb361cd095 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/filters/chips.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/chips.js
@@ -9,7 +9,8 @@ define([
 
     return Component.extend({
         defaults: {
-            template: 'ui/grid/filters/chips'
+            template: 'ui/grid/filters/chips',
+            stickyTmpl: 'ui/grid/sticky/chips'
         },
 
         /**
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js
index b0eca130b00dfef94b2da7f276724771b61d01a0..e90dfd5984189c933635261bcfbf9d17da9dcbe8 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js
@@ -6,8 +6,8 @@ define([
     'underscore',
     'mageUtils',
     'uiLayout',
-    'Magento_Ui/js/lib/collapsible'
-], function (_, utils, layout, Collapsible) {
+    'uiComponent'
+], function (_, utils, layout, Component) {
     'use strict';
 
     /**
@@ -34,9 +34,10 @@ define([
         return utils.mapRecursive(data, utils.removeEmptyValues.bind(utils));
     }
 
-    return Collapsible.extend({
+    return Component.extend({
         defaults: {
             template: 'ui/grid/filters/filters',
+            stickyTmpl: 'ui/grid/sticky/filters',
             applied: {
                 placeholder: true
             },
@@ -157,15 +158,6 @@ define([
             return this;
         },
 
-        /**
-         * Tells wether filters pannel should be opened.
-         *
-         * @returns {Boolean}
-         */
-        isOpened: function () {
-            return this.opened() && this.hasVisible();
-        },
-
         /**
          * Tells wether specified filter should be visible.
          *
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/listing.js b/app/code/Magento/Ui/view/base/web/js/grid/listing.js
index 2713816a1686ac77edcca7a407fa61ff4a3a5841..7ec985fcf76a53dc540a4cd020b5a339b8e9065e 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/listing.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/listing.js
@@ -2,6 +2,7 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
+
 define([
     'underscore',
     'Magento_Ui/js/lib/spinner',
@@ -13,6 +14,7 @@ define([
     return Component.extend({
         defaults: {
             template: 'ui/grid/listing',
+            stickyTmpl: 'ui/grid/sticky/listing',
             positions: false,
             storageConfig: {
                 positions: '${ $.storageConfig.path }.positions'
@@ -103,6 +105,11 @@ define([
             return this;
         },
 
+        /**
+         * Creates resize widget instance.
+         *
+         * @returns {Listing} Chainable.
+         */
         initResize: function () {
             layout([this.resizeConfig]);
 
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/massactions.js b/app/code/Magento/Ui/view/base/web/js/grid/massactions.js
index 871bbfbffc1624b312d6006ccf4b38599c5abb04..2e9a39b2bddd261c83c889ff1f31de99836b9544 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/massactions.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/massactions.js
@@ -2,6 +2,7 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
+
 define([
     'underscore',
     'uiRegistry',
@@ -16,6 +17,8 @@ define([
     return Collapsible.extend({
         defaults: {
             template: 'ui/grid/actions',
+            stickyTmpl: 'ui/grid/sticky/actions',
+            componentType: 'massaction',
             selectProvider: '',
             actions: [],
             noItemsMsg: $t('You haven\'t selected any items!'),
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js
index d1d61eca2b76bf231a0667d88da125f153977851..3f8c6cd8a658c3d9f660897434d1ca026033247a 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js
@@ -2,6 +2,7 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
+
 define([
     'ko',
     'underscore',
@@ -14,9 +15,11 @@ define([
     return Component.extend({
         defaults: {
             template: 'ui/grid/paging/paging',
+            totalTmpl: 'ui/grid/paging-total',
             pageSize: 20,
             current: 1,
             selectProvider: '',
+            componentType: 'paging',
 
             sizesConfig: {
                 component: 'Magento_Ui/js/grid/paging/sizes',
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/sizes.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/sizes.js
index 177d1c6afe0a3b8e96f71c4e2339b27ca8f7df8e..62908da372266e90bba65a6f5c8dd368d42129c9 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/paging/sizes.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/sizes.js
@@ -6,11 +6,11 @@ define([
     'ko',
     'underscore',
     'mageUtils',
-    'Magento_Ui/js/lib/collapsible'
-], function (ko, _, utils, Collapsible) {
+    'uiComponent'
+], function (ko, _, utils, Component) {
     'use strict';
 
-    return Collapsible.extend({
+    return Component.extend({
         defaults: {
             template: 'ui/grid/paging/sizes',
             value: 20,
@@ -392,8 +392,8 @@ define([
          * Listener of the 'value' property changes.
          */
         onValueChange: function () {
-            this.close()
-                .discardAll();
+            this.discardAll()
+                .trigger('close');
         },
 
         /**
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/resize.js b/app/code/Magento/Ui/view/base/web/js/grid/resize.js
index a98dd05073ba2303d37f67167d3b2e1d37baa623..cef9cb707cf7d064cea50b21c89142009fb0c087 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/resize.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/resize.js
@@ -18,6 +18,7 @@ define([
         defaults: {
             rootSelector: '${ $.columnsProvider }:.admin__data-grid-wrap',
             tableSelector: '${ $.rootSelector } -> table.data-grid',
+            mainTableSelector: '[data-role="grid"]',
             columnSelector: '${ $.tableSelector } thead tr th',
             fieldSelector: '${ $.tableSelector } tbody tr td',
 
@@ -84,10 +85,15 @@ define([
          * @returns {Object} Chainable
          */
         initTable: function (table) {
-            this.table = table;
-            this.tableWidth = $(table).outerWidth();
+            if ($(table).is(this.mainTableSelector))
+            {
+                this.table = table;
+                this.tableWidth = $(table).outerWidth();
+                $(window).resize(this.checkAfterResize);
+            }
+
             $(table).addClass(this.fixedLayoutClass);
-            $(window).resize(this.checkAfterResize);
+
             return this;
         },
 
@@ -202,15 +208,17 @@ define([
          */
         initColumn: function (column) {
             var model = ko.dataFor(column),
+                ctxIndex = this.getCtxIndex(ko.contextFor(column)),
                 table = this.table;
 
             model.width = this.getDefaultWidth(column);
 
-            if (!this.hasColumn(model, false)) {
+            if (!this.hasColumn(model, ctxIndex, false)) {
+                this.columnsElements[model.index] = this.columnsElements[model.index] || {};
+                this.columnsElements[model.index][ctxIndex] = column;
                 this.initResizableElement(column);
-                this.columnsElements[model.index] = column;
-                $(column).outerWidth(model.width);
                 this.setStopPropagationHandler(column);
+                $(column).outerWidth(model.width);
             }
 
             this.refreshLastColumn(column);
@@ -383,7 +391,7 @@ define([
         _canResize: function (column) {
             if (
                 $(column).hasClass(this.visibleClass) ||
-                !$(this.resizeConfig.depResizeElem.elem).find('.' + this.resizableElementClass).length
+                !$(this.resizeConfig.depResizeElem.elems[0]).find('.' + this.resizableElementClass).length
             ) {
                 return false;
             }
@@ -406,11 +414,11 @@ define([
             event.stopImmediatePropagation();
             cfg.curResizeElem.model = ko.dataFor(column);
             cfg.curResizeElem.ctx = ko.contextFor(column);
-            cfg.curResizeElem.elem = this.hasColumn(cfg.curResizeElem.model, true);
+            cfg.curResizeElem.elems = this.hasColumn(cfg.curResizeElem.model, false, true);
             cfg.curResizeElem.position = event.pageX;
-            cfg.depResizeElem.elem = this.getNextElement(cfg.curResizeElem.elem);
-            cfg.depResizeElem.model = ko.dataFor(cfg.depResizeElem.elem);
-            cfg.depResizeElem.ctx = ko.contextFor(cfg.depResizeElem.elem);
+            cfg.depResizeElem.elems = this.getNextElements(cfg.curResizeElem.elems[0]);
+            cfg.depResizeElem.model = ko.dataFor(cfg.depResizeElem.elems[0]);
+            cfg.depResizeElem.ctx = ko.contextFor(cfg.depResizeElem.elems[0]);
 
             this._setResizeClass();
 
@@ -420,8 +428,8 @@ define([
 
             event.stopPropagation();
             this.resizable = true;
-            cfg.curResizeElem.model.width = $(cfg.curResizeElem.elem).outerWidth();
-            cfg.depResizeElem.model.width = $(cfg.depResizeElem.elem).outerWidth();
+            cfg.curResizeElem.model.width = $(cfg.curResizeElem.elems[0]).outerWidth();
+            cfg.depResizeElem.model.width = $(cfg.depResizeElem.elems[0]).outerWidth();
             body.addClass(this.inResizeClass);
             body.bind('mousemove', this.mousemoveHandler);
             $(window).bind('mouseup', this.mouseupHandler);
@@ -435,7 +443,8 @@ define([
          */
         mousemoveHandler: function (event) {
             var cfg = this.resizeConfig,
-                width = event.pageX - cfg.curResizeElem.position;
+                width = event.pageX - cfg.curResizeElem.position,
+                self = this;
 
             event.stopPropagation();
             event.preventDefault();
@@ -448,26 +457,40 @@ define([
             ) {
                 cfg.curResizeElem.model.width += width;
                 cfg.depResizeElem.model.width -= width;
-                $(cfg.curResizeElem.elem).outerWidth(cfg.curResizeElem.model.width);
-                $(cfg.depResizeElem.elem).outerWidth(cfg.depResizeElem.model.width);
+
+                cfg.curResizeElem.elems.forEach(function (el) {
+                    $(el).outerWidth(cfg.curResizeElem.model.width);
+                });
+                cfg.depResizeElem.elems.forEach(function (el) {
+                    $(el).outerWidth(cfg.depResizeElem.model.width);
+                });
+
                 cfg.previousWidth = width;
                 cfg.curResizeElem.position = event.pageX;
             } else if (width <= -(cfg.curResizeElem.model.width - this.minColumnWidth)) {
-                $(cfg.curResizeElem.elem).outerWidth(this.minColumnWidth);
 
-                $(cfg.depResizeElem.elem).outerWidth(
+                cfg.curResizeElem.elems.forEach(function (el) {
+                    $(el).outerWidth(self.minColumnWidth);
+                });
+                cfg.depResizeElem.elems.forEach(function (el) {
+                    $(el).outerWidth(
                     cfg.depResizeElem.model.width +
                     cfg.curResizeElem.model.width -
-                    this.minColumnWidth
-                );
+                    self.minColumnWidth);
+                });
+
             } else if (width >= cfg.depResizeElem.model.width - this.minColumnWidth) {
 
-                $(cfg.depResizeElem.elem).outerWidth(this.minColumnWidth);
-                $(cfg.curResizeElem.elem).outerWidth(
-                    cfg.curResizeElem.model.width +
-                    cfg.depResizeElem.model.width -
-                    this.minColumnWidth
-                );
+                cfg.depResizeElem.elems.forEach(function (el) {
+                    $(el).outerWidth(self.minColumnWidth);
+                });
+                cfg.curResizeElem.elems.forEach(function (el) {
+                    $(el).outerWidth(
+                        cfg.curResizeElem.model.width +
+                        cfg.depResizeElem.model.width -
+                        self.minColumnWidth
+                    );
+                });
             }
         },
 
@@ -502,17 +525,17 @@ define([
          * @param {Object} element - current element
          * @returns {Object} next element data
          */
-        getNextElement: function (element) {
+        getNextElements: function (element) {
             var nextElem = $(element).next()[0],
                 nextElemModel = ko.dataFor(nextElem),
-                nextElemData = this.hasColumn(nextElemModel, true);
+                nextElemData = this.hasColumn(nextElemModel, false, true);
 
             if (nextElemData) {
                 if (nextElemModel.visible()) {
                     return nextElemData;
                 }
 
-                return this.getNextElement(nextElem);
+                return this.getNextElements(nextElem);
             }
         },
 
@@ -540,14 +563,20 @@ define([
          * Check column is render or not
          *
          * @param {Object} model - cur column model
+         * @param {String|Boolean} ctxIndex - index of context, or false, if want to get cols from all ctx
          * @param {Boolean} returned - need return column object or not
-         * @return {Boolean} if returned param is false, returned boolean falue, else return current object data
+         * @return {Boolean} if returned param is false, returned boolean value, else return current object data
          */
-        hasColumn: function (model, returned) {
-            if (this.columnsElements.hasOwnProperty(model.index)) {
+        hasColumn: function (model, ctxIndex, returned) {
+            var colElem = this.columnsElements[model.index] || {},
+                getFromAllCtx = ctxIndex === false;
+
+            if (colElem && (getFromAllCtx || colElem.hasOwnProperty(ctxIndex))) {
 
                 if (returned) {
-                    return this.columnsElements[model.index];
+                    return getFromAllCtx ?
+                        _.values(colElem) :
+                        colElem[ctxIndex];
                 }
 
                 return true;
@@ -581,6 +610,19 @@ define([
             }
 
             return false;
+        },
+
+        /**
+         * Generate index that will indentify context
+         *
+         * @param {Object} ctx
+         * @return {String}
+         */
+        getCtxIndex: function (ctx)
+        {
+            return ctx ? ctx.$parents.reduce(function (pv, cv) {
+                return (pv.index || pv) + (cv || {}).index;
+            }) : ctx;
         }
     });
 });
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/sticky/sticky.js b/app/code/Magento/Ui/view/base/web/js/grid/sticky/sticky.js
new file mode 100644
index 0000000000000000000000000000000000000000..e779851a9315625ebe3b60f5c09c8769098139d1
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/js/grid/sticky/sticky.js
@@ -0,0 +1,577 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+    'Magento_Ui/js/lib/view/utils/async',
+    'underscore',
+    'uiComponent',
+    'Magento_Ui/js/lib/view/utils/raf'
+], function ($, _, Component, raf) {
+    'use strict';
+
+    return Component.extend({
+        defaults: {
+            listingSelector: '${ $.listingProvider }::not([data-role = "sticky-el-root"])',
+            toolbarSelector: '${ $.toolbarProvider }::not([data-role = "sticky-el-root"])',
+            bulkRowSelector: '[data-role = "data-grid-bulk-row"]',
+            bulkRowHeaderSelector: '.data-grid-info-panel:visible',
+            tableSelector: 'table',
+            columnSelector: 'thead tr th',
+            rowSelector: 'tbody tr',
+            toolbarCollapsiblesSelector: '[data-role="toolbar-menu-item"]',
+            toolbarCollapsiblesActiveClass: '_active',
+            template: 'ui/grid/sticky/sticky',
+            stickyContainerSelector: '.sticky-header',
+            stickyElementSelector: '[data-role = "sticky-el-root"]',
+            leftDataGridCapSelector: '.data-grid-cap-left',
+            rightDataGridCapSelector: '.data-grid-cap-right',
+            visible: false,
+            enableToolbar: true,
+            enableHeader: true,
+            modules: {
+                toolbar: '${ $.toolbarProvider }',
+                listing: '${ $.listingProvider }'
+            },
+            otherStickyElsSize: 77,
+            containerNode: null,
+            listingNode: null,
+            toolbarNode: null,
+            stickyListingNode: null,
+            stickyToolbarNode: null,
+            storedOriginalToolbarElements: [],
+            cache: {},
+            flags: {},
+            dirtyFlag: 'dirty'
+        },
+
+        /**
+         * Initializes Sticky component.
+         *
+         * @returns {Object} Chainable.
+         */
+        initialize: function () {
+            this._super();
+            _.bindAll(this,
+                'adjustStickyElems',
+                'initListingNode',
+                'initToolbarNode',
+                'initContainerNode',
+                'initStickyListingNode',
+                'initStickyToolbarNode',
+                'initLeftDataGridCap',
+                'initRightDataGridCap'
+            );
+
+            $.async(this.listingSelector,
+                this.initListingNode);
+            $.async(this.toolbarSelector,
+                this.initToolbarNode);
+
+            $.async(this.stickyContainerSelector,
+                this,
+                this.initContainerNode);
+            $.async(this.stickyElementSelector,
+                this.listing(),
+                this.initStickyListingNode);
+            $.async(this.stickyElementSelector,
+                this.toolbar(),
+                this.initStickyToolbarNode);
+
+            return this;
+        },
+
+        /**
+         * Init observables
+         *
+         * @returns {Object} Chainable.
+         */
+        initObservable: function () {
+            this._super()
+                .observe(['visible']);
+
+            return this;
+        },
+
+        /**
+         * Init original listing node
+         *
+         * @param {HTMLElement} node
+         */
+        initListingNode: function (node) {
+            if ($(node).is(this.stickyElementSelector)) {
+                return;
+            }
+            this.listingNode = $(node);
+            this.columns = this.listingNode.find(this.columnSelector);
+        },
+
+        /**
+         * Init original toolbar node
+         *
+         * @param {HTMLElement} node
+         */
+        initToolbarNode: function (node) {
+            if ($(node).is(this.stickyElementSelector)) {
+                return;
+            }
+            this.toolbarNode = $(node);
+        },
+
+        /**
+         * Init sticky listing node
+         *
+         * @param {HTMLElement} node
+         */
+        initStickyListingNode: function (node) {
+            this.stickyListingNode = $(node);
+            this.checkPos();
+            this.initListeners();
+        },
+
+        /**
+         * Init sticky toolbar node
+         *
+         * @param {HTMLElement} node
+         */
+        initStickyToolbarNode: function (node) {
+            this.stickyToolbarNode = $(node);
+        },
+
+        /**
+         * Init sticky header container node
+         *
+         * @param {HTMLElement} node
+         */
+        initContainerNode: function (node) {
+            this.containerNode = $(node);
+
+            $.async(this.leftDataGridCapSelector,
+                node,
+                this.initLeftDataGridCap);
+            $.async(this.rightDataGridCapSelector,
+                node,
+                this.initRightDataGridCap);
+        },
+
+        /**
+         * Init left DataGridCap
+         *
+         * @param {HTMLElement} node
+         */
+        initLeftDataGridCap: function (node) {
+            this.leftDataGridCap = $(node);
+        },
+
+        /**
+         * Init right DataGridCap
+         *
+         * @param {HTMLElement} node
+         */
+        initRightDataGridCap: function (node) {
+            this.rightDataGridCap = $(node);
+        },
+
+        /**
+         * Init listeners
+         *
+         * @returns {Object} Chainable.
+         */
+        initListeners: function () {
+            this.adjustStickyElems();
+            this.initOnResize()
+                .initOnScroll()
+                .initOnListingScroll();
+
+            return this;
+        },
+
+        /**
+         * Start to listen to window scroll event
+         *
+         * @returns {Object} Chainable.
+         */
+        initOnScroll: function () {
+            this.lastHorizontalScrollPos = $(window).scrollLeft();
+            document.addEventListener('scroll', function () {
+                this.flags.scrolled = true;
+            }.bind(this));
+
+            return this;
+        },
+
+        /**
+         * Start to listen to original listing scroll event
+         *
+         * @returns {Object} Chainable.
+         */
+        initOnListingScroll: function () {
+            $(this.listingNode).scroll(function (e) {
+                this.flags.listingScrolled = true;
+                this.flags.listingScrolledValue = $(e.target).scrollLeft();
+            }.bind(this));
+
+            return this;
+        },
+
+        /**
+         * Start to listen to window resize event
+         *
+         * @returns {Object} Chainable.
+         */
+        initOnResize: function () {
+            $(window).resize(function () {
+                this.flags.resized = true;
+            }.bind(this));
+
+            return this;
+        },
+
+        /**
+         * Adjust sticky header elements according to flags of the events that have happened in the endless RAF loop
+         */
+        adjustStickyElems: function () {
+            if (this.flags.resized ||
+                this.flags.scrolled) {
+                this.checkPos();
+            }
+
+            if (this.visible()) {
+                this.checkTableElemsWidth();
+
+                if (this.flags.originalWidthChanged) {
+                    this.adjustContainerElemsWidth();
+                }
+
+                if (this.flags.resized) {
+                    this.onResize();
+                }
+
+                if (this.flags.scrolled) {
+                    this.onWindowScroll();
+                }
+
+                if (this.flags.listingScrolled) {
+                    this.onListingScroll(this.flags.listingScrolledValue);
+                }
+            }
+            _.each(this.flags, function (val, key) {
+                if (val === this.dirtyFlag) {
+                    this.flags[key] = false;
+                } else if (val) {
+                    this.flags[key] = this.dirtyFlag;
+                }
+            }, this);
+
+            raf(this.adjustStickyElems);
+        },
+
+        /**
+         * Handles window scroll
+         */
+        onWindowScroll: function () {
+            var scrolled = $(window).scrollLeft(),
+                horizontal = this.lastHorizontalScrollPos !== scrolled;
+
+            if (horizontal) {
+                this.adjustOffset()
+                    .adjustDataGridCapPositions();
+                this.lastHorizontalScrollPos = scrolled;
+            } else {
+                this.checkPos();
+            }
+        },
+
+        /**
+         * Handles original listing scroll
+         *
+         * @param {Number} scrolled
+         */
+        onListingScroll: function (scrolled) {
+            this.adjustOffset(scrolled);
+        },
+
+        /**
+         * Handles window resize
+         */
+        onResize: function () {
+            this.checkPos();
+            this.adjustContainerElemsWidth()
+                .adjustDataGridCapPositions();
+        },
+
+        /**
+         * Check if original table or columns change it dimensions and sets appropriate flag
+         */
+        checkTableElemsWidth: function () {
+            var newWidth = this.getTableWidth();
+
+            if (this.cache.tableWidth !== newWidth) {
+                this.cache.tableWidth = newWidth;
+                this.flags.originalWidthChanged = true;
+            } else if (this.cache.colChecksum !== this.getColsChecksum()) {
+                this.cache.colChecksum = this.getColsChecksum();
+                this.flags.originalWidthChanged = true;
+            }
+        },
+
+        /**
+         * Get the checksum of original columns width
+         *
+         * @returns {Number}.
+         */
+        getColsChecksum: function () {
+            return _.reduce(this.columns,
+            function (pv, cv) {
+                return ($(pv).width() || pv) + '' + $(cv).width();
+            });
+        },
+
+        /**
+         * Get the width of the sticky table wrapper
+         *
+         * @returns {Number}.
+         */
+        getListingWidth: function () {
+            return this.listingNode.width();
+        },
+
+        /**
+         * Get the width of the original table
+         *
+         * @returns {Number}.
+         */
+        getTableWidth: function () {
+            return this.listingNode.find(this.tableSelector).width();
+        },
+
+        /**
+         * Get the top elem: header or toolbar
+         *
+         * @returns {HTMLElement}.
+         */
+        getTopElement: function () {
+            return this.toolbarNode || this.listingNode;
+        },
+
+        /**
+         * Get the height of the other sticky elem (Page header)
+         *
+         * @returns {Number}.
+         */
+        getOtherStickyElementsSize: function () {
+            return this.otherStickyElsSize;
+        },
+
+        /**
+         * Get top Y coord of the sticky header
+         *
+         * @returns {Number}.
+         */
+        getListingTopYCoord: function () {
+            var bulkRowHeight = (this.listingNode.find(this.bulkRowSelector) || {}).height();
+
+            return this.listingNode.find('tbody').offset().top -
+                this.containerNode.height() -
+                $(window).scrollTop() +
+                bulkRowHeight;
+        },
+
+        /**
+         * Check if sticky header must be visible
+         *
+         * @returns {Boolean}.
+         */
+        getMustBeSticky: function () {
+            var stickyTopCondition = this.getListingTopYCoord() - this.getOtherStickyElementsSize(),
+                stickyBottomCondition = this.listingNode.offset().top +
+                    this.listingNode.height() -
+                    $(window).scrollTop() -
+                    (this.listingNode.find(this.bulkRowSelector) || {}).height() -
+                    this.getOtherStickyElementsSize();
+
+            return stickyTopCondition < 0 && stickyBottomCondition > 0;
+        },
+
+        /**
+         * Resize sticky header and cols
+         *
+         * @returns {Object} Chainable.
+         */
+        adjustContainerElemsWidth: function () {
+            this.resizeContainer()
+                .resizeCols()
+                .resizeBulk();
+
+            return this;
+        },
+
+        /**
+         * Resize sticky header
+         *
+         * @returns {Object} Chainable.
+         */
+        resizeContainer: function () {
+            var listingWidth = this.getListingWidth();
+
+            this.stickyListingNode.innerWidth(listingWidth);
+            this.stickyListingNode.find(this.tableSelector).innerWidth(this.getTableWidth());
+
+            if (this.stickyToolbarNode) {
+                this.stickyToolbarNode.innerWidth(listingWidth);
+            }
+
+            return this;
+        },
+
+        /**
+         * Resize sticky cols
+         *
+         * @returns {Object} Chainable.
+         */
+        resizeCols: function () {
+            var cols = this.listingNode.find(this.columnSelector);
+
+            this.stickyListingNode.find(this.columnSelector).each(function (ind) {
+                var originalColWidth =  $(cols[ind]).width();
+
+                $(this).width(originalColWidth);
+            });
+
+            return this;
+        },
+
+        /**
+         * Resize bulk row header
+         *
+         * @returns {Object} Chainable.
+         */
+        resizeBulk: function () {
+            var bulk = this.containerNode.find(this.bulkRowHeaderSelector)[0];
+            if (bulk){
+                $(bulk).innerWidth(this.getListingWidth());
+            }
+        },
+
+        /**
+         * Reset viewport to the top of listing
+         */
+        resetToTop: function () {
+            var posOfTopEl = this.getTopElement().offset().top - this.getOtherStickyElementsSize() || 0;
+
+            $(window).scrollTop(posOfTopEl);
+        },
+
+        /**
+         * Adjust sticky header offset
+         *
+         * @param {Number} val
+         * @returns {Object} Chainable.
+         */
+        adjustOffset: function (val) {
+            val = val || this.listingNode.scrollLeft();
+            this.stickyListingNode.offset({
+                left: this.listingNode.offset().left - val
+            });
+
+            return this;
+        },
+
+        /**
+         * Adjust both DataGridCap position
+         *
+         * @returns {Object} Chainable.
+         */
+        adjustDataGridCapPositions: function () {
+            this.adjustLeftDataGridCapPos()
+                .adjustRightDataGridCapPos();
+
+            return this;
+        },
+
+        /**
+         * Adjust left DataGridCap position
+         *
+         * @returns {Object} Chainable.
+         */
+        adjustLeftDataGridCapPos: function () {
+            this.leftDataGridCap.offset({
+                left: this.listingNode.offset().left - this.leftDataGridCap.width()
+            });
+
+            return this;
+        },
+
+        /**
+         * Adjust right DataGridCap position
+         *
+         * @returns {Object} Chainable.
+         */
+        adjustRightDataGridCapPos: function () {
+            this.rightDataGridCap.offset({
+                left: this.listingNode.offset().left + this.listingNode.width()
+            });
+
+            return this;
+        },
+
+        /**
+         * Hides the oiginal toolbar opened dropdowns/collapsibles etc
+         */
+        collapseOriginalElements: function () {
+            this.toolbarNode
+                .find(this.toolbarCollapsiblesSelector)
+                .css('visibility', 'hidden');
+            $(this.listingNode.find(this.bulkRowSelector)[0]).css('visibility', 'hidden');
+        },
+
+        /**
+         * Restores the oiginal toolbar opened dropdowns/collapsibles etc
+         */
+        restoreOriginalElements: function () {
+            this.toolbarNode
+                .find(this.toolbarCollapsiblesSelector)
+                .css('visibility', 'visible');
+            $(this.listingNode.find(this.bulkRowSelector)[0]).css('visibility', 'visible');
+        },
+
+        /**
+         * Toggle the visibility of sticky header
+         *
+         * @returns {Object} Chainable.
+         */
+        toggleContainerVisibility: function () {
+            this.visible(!this.visible());
+
+            return this;
+        },
+
+        /**
+         * Checks position of the listing to know if need to show/hide sticky header
+         *
+         * @returns {Boolean} whether the visibility of the sticky header was toggled.
+         */
+        checkPos: function () {
+            var isSticky = this.visible(),
+                mustBeSticky = this.getMustBeSticky(),
+                needChange = isSticky !== mustBeSticky;
+
+            if (needChange) {
+                if (mustBeSticky) {
+                    this.collapseOriginalElements();
+                    this.toggleContainerVisibility();
+                    this.adjustContainerElemsWidth()
+                        .adjustOffset()
+                        .adjustDataGridCapPositions();
+
+                } else {
+                    this.toggleContainerVisibility();
+                    this.restoreOriginalElements();
+                }
+            }
+
+            return needChange;
+        }
+    });
+});
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/component/core.js b/app/code/Magento/Ui/view/base/web/js/lib/component/core.js
index fd3e81680aee113c56769ffd17786fc684a5159a..65eca7b967d7a31cb7ad750cd213ee051ad0898c 100644
--- a/app/code/Magento/Ui/view/base/web/js/lib/component/core.js
+++ b/app/code/Magento/Ui/view/base/web/js/lib/component/core.js
@@ -23,7 +23,8 @@ define([
             },
             modules: {
                 storage: '${ $.storageConfig.provider }'
-            }
+            },
+            componentType: 'container'
         },
 
         /**
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/collapsible.js b/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/collapsible.js
new file mode 100644
index 0000000000000000000000000000000000000000..0c5686fa8936ada9151c3c657b1ad0036e08401d
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/collapsible.js
@@ -0,0 +1,189 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+define([
+    'ko',
+    'jquery',
+    'underscore'
+], function (ko, $, _) {
+    'use strict';
+
+    var collapsible,
+        defaults;
+
+    defaults = {
+        closeOnOuter: true,
+        onTarget: false,
+        openClass: '_active',
+        as: '$collapsible'
+    };
+
+    collapsible = {
+
+        /**
+         * Sets 'opened' property to true.
+         */
+        open: function () {
+            this.opened(true);
+        },
+
+        /**
+         * Sets 'opened' property to false.
+         */
+        close: function () {
+            this.opened(false);
+        },
+
+        /**
+         * Toggles value of the 'opened' property.
+         */
+        toggle: function () {
+            this.opened(!this.opened());
+        }
+    };
+
+    /**
+     * Document click handler which in case if event target is not
+     * a descendant of provided container element, closes collapsible model.
+     *
+     * @param {HTMLElement} container
+     * @param {Object} model
+     * @param {EventObject} e
+     */
+    function onOuterClick(container, model, e) {
+        var target = e.target;
+
+        if (target !== container && !container.contains(target)) {
+            model.close();
+        }
+    }
+
+    /**
+     * Creates 'css' binding which toggles
+     * class specified in 'name' parameter.
+     *
+     * @param {Object} model
+     * @param {String} name
+     * @returns {Object}
+     */
+    function getClassBinding(model, name) {
+        var binding = {};
+
+        binding[name] = model.opened;
+
+        return {
+            css: binding
+        };
+    }
+
+    /**
+     * Prepares configuration for the binding based
+     * on a default properties and provided options.
+     *
+     * @param {Object} [options={}]
+     * @returns {Object} Complete instance configuration.
+     */
+    function buildConfig(options) {
+        if (typeof options !== 'object') {
+            options = {};
+        }
+
+        return _.extend({}, defaults, options);
+    }
+
+    ko.bindingHandlers.collapsible = {
+
+        /**
+         * Initializes 'collapsible' binding.
+         */
+        init: function (element, valueAccessor, allBindings, viewModel, bindingCtx) {
+            var $collapsible = Object.create(collapsible),
+                config = buildConfig(valueAccessor()),
+                outerClick,
+                bindings;
+
+            _.bindAll($collapsible, 'open', 'close', 'toggle');
+
+            $collapsible.opened = ko.observable(false);
+
+            bindingCtx[config.as] = $collapsible;
+
+            if (config.closeOnOuter) {
+                outerClick = onOuterClick.bind(null, element, $collapsible);
+
+                $(document).on('click', outerClick);
+
+                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
+                    $(document).off('click', outerClick);
+                });
+            }
+
+            if (config.openClass) {
+                bindings = getClassBinding($collapsible, config.openClass);
+
+                ko.applyBindingsToNode(element, bindings, bindingCtx);
+            }
+
+            if (config.onTarget) {
+                $(element).on('click', $collapsible.toggle);
+            }
+
+            if (viewModel && _.isFunction(viewModel.on)) {
+                viewModel.on({
+                    close:          $collapsible.close,
+                    open:           $collapsible.open,
+                    toggleOpened:   $collapsible.toggle
+                });
+            }
+        }
+    };
+
+    ko.bindingHandlers.closeCollapsible = {
+
+        /**
+         * Creates listener for the click event on provided DOM element,
+         * which closes associated with it collapsible model.
+         */
+        init: function (element, valueAccessor, allBindings, viewModel, bindingCtx) {
+            var name = valueAccessor() || defaults.as,
+                $collapsible = bindingCtx[name];
+
+            if ($collapsible) {
+                $(element).on('click', $collapsible.close);
+            }
+        }
+    };
+
+    ko.bindingHandlers.openCollapsible = {
+
+        /**
+         * Creates listener for the click event on provided DOM element,
+         * which opens associated with it collapsible model.
+         */
+        init: function (element, valueAccessor, allBindings, viewModel, bindingCtx) {
+            var name = valueAccessor() || defaults.as,
+                $collapsible = bindingCtx[name];
+
+            if ($collapsible) {
+                $(element).on('click', $collapsible.open);
+            }
+        }
+    };
+
+    ko.bindingHandlers.toggleCollapsible = {
+
+        /**
+         * Creates listener for the click event on provided DOM element,
+         * which toggles associated with it collapsible model.
+         */
+        init: function (element, valueAccessor, allBindings, viewModel, bindingCtx) {
+            var name = valueAccessor() || defaults.as,
+                $collapsible = bindingCtx[name];
+
+            if ($collapsible) {
+                $(element).on('click', $collapsible.toggle);
+            }
+        }
+    };
+});
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/optgroup.js b/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/optgroup.js
index b4ef5df5a1340ed510a92e0378953cc1a5fa09ce..b95709baf311418d8a362f9a7ad86f1f40c389fb 100644
--- a/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/optgroup.js
+++ b/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/optgroup.js
@@ -67,8 +67,13 @@ define([
                 if (typeof unwrappedArray.length === 'undefined') { // Coerce single value into array
                     unwrappedArray = [unwrappedArray];
                 }
+
                 // Filter out any entries marked as destroyed
                 filteredArray = ko.utils.arrayFilter(unwrappedArray, function (item) {
+                    if (item && !item.label) {
+                        return false;
+                    }
+
                     return includeDestroyed || item === undefined || item === null || !ko.utils.unwrapObservable(item._destroy);
                 });
                 filteredArray.map(recursivePathBuilder, null);
@@ -211,8 +216,8 @@ define([
                 // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
                 // That's why we first added them without selection. Now it's time to set the selection.
                 if (previousSelectedValues.length) {
-                    var isSelected = ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[0])) >= 0;
-                    ko.utils.setOptionNodeSelectionState(newOptions[0], isSelected);
+                    var isSelected = ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions.value)) >= 0;
+                    ko.utils.setOptionNodeSelectionState(newOptions.value, isSelected);
 
                     // If this option was changed from being selected during a single-item update, notify the change
                     if (itemUpdate && !isSelected) {
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/outer_click.js b/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/outer_click.js
index 843c126b663413058238b86dc03e5f82d2622f14..2c0bfa635587ef1c586d0f2dd9f4c8ab36ba48ed 100644
--- a/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/outer_click.js
+++ b/app/code/Magento/Ui/view/base/web/js/lib/ko/bind/outer_click.js
@@ -5,38 +5,75 @@
 /** Creates outerClick binding and registers in to ko.bindingHandlers object */
 define([
     'ko',
-    'jquery'
-], function (ko, $) {
+    'jquery',
+    'underscore'
+], function (ko, $, _) {
     'use strict';
 
-    function clickWrapper(elem, callback, e) {
-        var target = e.target;
+    var defaults = {
+        onlyIfVisible: true
+    };
+
+    /**
+     * Document click handler which in case if event target is not
+     * a descendant of provided container element,
+     * invokes specfied in configuration callback.
+     *
+     * @param {HTMLElement} container
+     * @param {Object} config
+     * @param {EventObject} e
+     */
+    function onOuterClick(container, config, e) {
+        var target = e.target,
+            callback = config.callback;
 
-        if (target !== elem && !elem.contains(target)) {
+        if (container === target || container.contains(target)) {
+            return;
+        }
+
+        if (config.onlyIfVisible) {
+            if (!_.isNull(container.offsetParent)) {
+                callback();
+            }
+        } else {
             callback();
         }
     }
 
+    /**
+     * Prepares configuration for the binding based
+     * on a default properties and provided options.
+     *
+     * @param {(Object|Function)} [options={}]
+     * @returns {Object}
+     */
+    function buildConfig(options) {
+        var config = {};
+
+        if (_.isFunction(options)) {
+            options = {
+                callback: options
+            };
+        } else if (!_.isObject(options)) {
+            options = {};
+        }
+
+        return _.extend(config, defaults, options);
+    }
+
     ko.bindingHandlers.outerClick = {
 
         /**
-         * Attaches click handler to document
-         * @param {HTMLElement} el - Element, that binding is applied to
-         * @param {Function} valueAccessor - Function that returns value, passed to binding
-         * @param  {Object} allBindings - all bindings object
-         * @param  {Object} viewModel - reference to viewmodel
+         * Initializes outer click binding.
          */
-        init: function (element, valueAccessor, allBindings, viewModel) {
-            var callback = valueAccessor(),
-                wrapper;
-
-            callback = callback.bind(viewModel);
-            wrapper = clickWrapper.bind(null, element, callback);
+        init: function (element, valueAccessor) {
+            var config = buildConfig(valueAccessor()),
+                outerClick = onOuterClick.bind(null, element, config);
 
-            $(document).on('click', wrapper);
+            $(document).on('click', outerClick);
 
             ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
-                $(document).off('click', wrapper);
+                $(document).off('click', outerClick);
             });
         }
     };
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/ko/initialize.js b/app/code/Magento/Ui/view/base/web/js/lib/ko/initialize.js
index b96c6107aafed4a3058082fb9d8304a924f5d218..2b0f3d1fc5dcadfc5f630efaea6a0bf5c4379dae 100644
--- a/app/code/Magento/Ui/view/base/web/js/lib/ko/initialize.js
+++ b/app/code/Magento/Ui/view/base/web/js/lib/ko/initialize.js
@@ -17,6 +17,7 @@ define([
     './bind/mage-init',
     './bind/after-render',
     './bind/i18n',
+    './bind/collapsible',
     './extender/observable_array',
     './extender/bound-nodes',
     './extender/observable_array'
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/async.js b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/async.js
index df89d8d435b674dd2c0d6651467687acedf2f2a4..1b5743cd9d29aea5d12841306cd3dc0c1fd4ee6f 100644
--- a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/async.js
+++ b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/async.js
@@ -86,6 +86,7 @@ define([
                 data.ctx = ctx;
             } else {
                 data.component = ctx;
+                data.ctx = '*';
             }
         } else {
             data = _.isString(selector) ?
@@ -93,8 +94,6 @@ define([
                 selector;
         }
 
-        data.ctx = data.ctx || '*';
-
         return data;
     }
 
@@ -127,7 +126,8 @@ define([
      */
     function setRootListener(data, component) {
         boundedNodes.get(component, function (root) {
-            if (!$(root).is(data.ctx)) {
+            var ctx = data.ctx || '*'
+            if (!$(root).is(data.ctx || '*')) {
                 return;
             }
 
@@ -182,9 +182,11 @@ define([
             domObserver.get(data.selector, data.fn, data.ctx);
         }
     };
+
     /*eslint-enable no-unused-vars*/
 
     _.extend($.async, {
+
         /*eslint-disable no-unused-vars*/
         /**
          * Returns collection of elements found by provided selector data.
@@ -215,6 +217,7 @@ define([
                 $(data.selector, nodes).toArray() :
                 nodes;
         },
+
         /*eslint-enable no-unused-vars*/
 
         /**
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/dom-observer.js b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/dom-observer.js
index ae5c2878e526271bf97da7521ce575a2c6547be3..25e900fe2af2994c24ae53fc1d422681bab6539c 100644
--- a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/dom-observer.js
+++ b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/dom-observer.js
@@ -42,26 +42,20 @@ define([
     }
 
     /**
-     * Removes all non-element nodes from provided array
-     * and appends to it descendant elements.
+     * Extracts node identifier. If ID is not specified,
+     * then it will be created for the provided node.
      *
-     * @param {Array} nodes
-     * @returns {Array}
+     * @param {HTMLElement} node
+     * @returns {Number}
      */
-    function formNodesList(nodes) {
-        var result = [],
-            children;
-
-        _.toArray(nodes)
-            .filter(isElementNode)
-            .forEach(function (node) {
-                result.push(node);
+    function getNodeId(node) {
+        var id = node._observeId;
 
-                children = extractChildren(node);
-                result   = result.concat(children);
-            });
+        if (!id) {
+            id = node._observeId = counter++;
+        }
 
-        return result;
+        return id;
     }
 
     /**
@@ -71,7 +65,15 @@ define([
      * @param {Object} data
      */
     function trigger(node, data) {
+        var id = getNodeId(node),
+            ids = data.invoked;
+
+        if (_.contains(ids, id)) {
+            return;
+        }
+
         data.callback(node);
+        data.invoked.push(id);
     }
 
     /**
@@ -81,12 +83,8 @@ define([
      * @returns {Object}
      */
     function createNodeData(node) {
-        var id      = node._observeId,
-            nodes   = watchers.nodes;
-
-        if (!id) {
-            id = node._observeId = counter++;
-        }
+        var nodes   = watchers.nodes,
+            id      = getNodeId(node);
 
         nodes[id] = nodes[id] || {};
 
@@ -137,10 +135,6 @@ define([
     function addSelectorListener(selector, data) {
         var storage = watchers.selectors;
 
-        if (typeof selector !== 'string') {
-            return;
-        }
-
         (storage[selector] = storage[selector] || []).push(data);
     }
 
@@ -187,51 +181,67 @@ define([
     }
 
     /**
-     * Extarcts nodes that matches specfied selector.
-     * If selector is an object, then it will be parsed as
-     * one of the possible array like values.
+     * Removes all non-element nodes from provided array
+     * and appends to it descendant elements.
      *
-     * @param {(jQueryObject|HTMLElement|Array|String)} selector
-     * @param {HTMLElement} [ctx=document.body] - Context that will be used to search for elements.
-     * @returns {Array} An array of available elements.
+     * @param {Array} nodes
+     * @returns {Array}
      */
-    function getNodes(selector, ctx) {
-        var nodes = [];
-
-        if (typeof selector === 'object') {
-            if (typeof selector.jquery === 'string' || !selector.tagName) {
-                nodes = _.toArray(selector);
-            } else if (selector.tagName) {
-                nodes = [selector];
-            }
-        } else if (typeof selector === 'string') {
-            nodes = $(selector, ctx).toArray();
-        }
+    function formNodesList(nodes) {
+        var result = [],
+            children;
+
+        nodes = _.toArray(nodes).filter(isElementNode);
 
-        return nodes;
+        nodes.forEach(function (node) {
+            result.push(node);
+
+            children = extractChildren(node);
+            result   = result.concat(children);
+        });
+
+        return result;
     }
 
     /**
-     * Processes removed and added element nodes
-     * specified in mutation record.
+     * Collects all removed and added nodes from
+     * mutation records into separate arrays
+     * while removing duplicates between both types of changes.
      *
-     * @param {MutationRecord} mutation
+     * @param {Array} mutations - An array of mutation records.
+     * @returns {Object} Object with 'removed' and 'added' nodes arrays.
      */
-    function handleMutation(mutation) {
-        var addedNodes = mutation.addedNodes,
-            removedNodes = mutation.removedNodes;
+    function formChangesLists(mutations) {
+        var removed = [],
+            added = [];
 
-        if (addedNodes.length) {
-            formNodesList(addedNodes).forEach(processAdded);
-        }
+        mutations.forEach(function (record) {
+            removed = removed.concat(_.toArray(record.removedNodes));
+            added   = added.concat(_.toArray(record.addedNodes));
+        });
 
-        if (removedNodes.length) {
-            formNodesList(removedNodes).forEach(processRemoved);
-        }
+        removed = removed.filter(function (node) {
+            var addIndex = added.indexOf(node),
+                wasAdded = !!~addIndex;
+
+            if (wasAdded) {
+                added.splice(addIndex, 1);
+            }
+
+            return !wasAdded;
+        });
+
+        return {
+            removed: formNodesList(removed),
+            added: formNodesList(added)
+        };
     }
 
     globalObserver = new MutationObserver(function (mutations) {
-        mutations.forEach(handleMutation);
+        var changes = formChangesLists(mutations);
+
+        changes.removed.forEach(processRemoved);
+        changes.added.forEach(processAdded);
     });
 
     globalObserver.observe(document.body, {
@@ -240,6 +250,7 @@ define([
     });
 
     return {
+
         /**
          * Adds listener for the appearance of nodes that matches provided
          * selector and which are inside of the provided context. Callback will be
@@ -250,15 +261,19 @@ define([
          * @param {HTMLElement} [ctx=document.body] - Context inside of which to search for the node.
          */
         get: function (selector, callback, ctx) {
-            var data;
+            var data,
+                nodes;
 
             data = {
                 ctx: ctx || document.body,
+                type: 'add',
                 callback: callback,
-                type: 'add'
+                invoked: []
             };
 
-            getNodes(selector, data.ctx).forEach(function (node) {
+            nodes = $(selector, data.ctx).toArray();
+
+            nodes.forEach(function (node) {
                 trigger(node, data);
             });
 
@@ -273,21 +288,29 @@ define([
          * @param {HTMLElement} [ctx=document.body] - Context inside of which to search for the node.
          */
         remove: function (selector, callback, ctx) {
-            var data;
+            var nodes = [],
+                data;
 
             data = {
                 ctx: ctx || document.body,
+                type: 'remove',
                 callback: callback,
-                type: 'remove'
+                invoked: []
             };
 
-            getNodes(selector, data.ctx).forEach(function (node) {
-                addRemovalListener(node, data);
-            });
+            if (typeof selector === 'object') {
+                nodes = !_.isUndefined(selector.length) ?
+                    _.toArray(selector) :
+                    [selector];
+            } else if (_.isString(selector)) {
+                nodes = $(selector, ctx).toArray();
 
-            if (typeof selector === 'string') {
                 addSelectorListener(selector, data);
             }
+
+            nodes.forEach(function (node) {
+                addRemovalListener(node, data);
+            });
         },
 
         /**
diff --git a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/raf.js b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/raf.js
new file mode 100644
index 0000000000000000000000000000000000000000..1c174a87fce56717874f85004bf286cc3faae280
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/raf.js
@@ -0,0 +1,19 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+
+define(function () {
+    'use strict';
+
+    return window.requestAnimationFrame ||
+        window.webkitRequestAnimationFrame ||
+        window.mozRequestAnimationFrame||
+        window.onRequestAnimationFrame ||
+        window.msRequestAnimationFrame ||
+        function(callback){
+            window.setTimeout(callback, 1000/60);
+        }
+});
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/actions.html b/app/code/Magento/Ui/view/base/web/templates/grid/actions.html
index 29a93df1a9805a33d0e614f70d52bc2c26bbbe5b..16b8fab2bb2877bf3a0bf039c7b55368347ce992 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/actions.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/actions.html
@@ -4,26 +4,23 @@
  * See COPYING.txt for license details.
  */
 -->
-<div class="col-xs-2">
-    <div
-        class="action-select-wrap"
-        data-bind="css: {'_active': opened},
-           click: toggleOpened,
-           outerClick: close">
-        <button
-            class="action-select"
-            data-bind="i18n: 'Select Items'">
-            <span data-bind="i18n: 'Actions'"></span>
-        </button>
-        <ul
-            class="action-menu"
-            data-bind="css: {'_active': opened},
-                       foreach: actions">
-            <li data-bind="click: $parent.applyAction.bind($parent, type)">
-            <span
-                class="action-menu-item"
-                data-bind="text: label"></span>
-            </li>
-        </ul>
-    </div>
+<div
+    class="action-select-wrap"
+    data-bind="collapsible: {onTarget: true}"
+        data-role="toolbar-menu-item">
+    <button
+        class="action-select"
+        data-bind="i18n: 'Select Items'">
+        <span data-bind="i18n: 'Actions'"></span>
+    </button>
+    <ul
+        class="action-menu"
+        data-bind="css: {'_active': $collapsible.opened},
+                   foreach: actions">
+        <li data-bind="click: $parent.applyAction.bind($parent, type)">
+        <span
+            class="action-menu-item"
+            data-bind="text: label"></span>
+        </li>
+    </ul>
 </div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/cells/actions.html b/app/code/Magento/Ui/view/base/web/templates/grid/cells/actions.html
index 2a4a46a38907496a37203eb4e6f83508c461eda4..22105aed42f6d0eb382162432c3317431a32cbeb 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/cells/actions.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/cells/actions.html
@@ -24,22 +24,17 @@
     <!-- ko if: isMultiple(row._rowIndex) -->
     <div
         class="action-select-wrap"
-        data-bind="
-            css : {
-                '_active' : opened() === row._rowIndex
-            },
-            outerClick: closeList.bind($data, row._rowIndex)">
-        <button class="action-select" data-bind="click: toggleList.bind($data, row._rowIndex)">
+        data-bind="collapsible">
+        <button class="action-select" data-bind="toggleCollapsible">
             <span data-bind="i18n: 'Select'"></span>
         </button>
         <ul
             class="action-menu"
-            data-bind="
-                css: {
-                    '_active': opened() === row._rowIndex
-                },
-                foreach: getVisibleActions(row._rowIndex)"
-                >
+            data-bind="css: {
+            '_active': $collapsible.opened
+            },
+            foreach: getVisibleActions(row._rowIndex)"
+            >
             <li>
                 <a
                     class="action-menu-item"
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/columns/multiselect.html b/app/code/Magento/Ui/view/base/web/templates/grid/columns/multiselect.html
index a2dce41dda5f53b643fe9ca7d092dc693e7dad44..4606d8e27b9a19e5236f6b7f28d66076805b43d1 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/columns/multiselect.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/columns/multiselect.html
@@ -8,8 +8,8 @@
 <th class="data-grid-multicheck-cell" data-bind="visible: visible">
     <div
         class="action-multicheck-wrap"
-        data-bind="css: { '_active': menuVisible, '_disabled': !totalRecords()},
-                   outerClick: hideMenu">
+        data-bind="css: {'_disabled': !totalRecords()},
+                   collapsible">
         <input
             id="mass-select-checkbox"
             class="admin__control-checkbox"
@@ -22,14 +22,14 @@
         <button
             class="action-multicheck-toggle"
             data-toggle="dropdown"
-            data-bind="css: { '_active': menuVisible },
-                       click: toggleMenu,
-                       enable: totalRecords">
+            data-bind="css: { '_active': $collapsible.opened },
+                       enable: totalRecords,
+                       toggleCollapsible">
             <span data-bind="i18n: 'Options'"></span>
         </button>
         <ul
             class="action-menu"
-            data-bind="click: hideMenu, foreach: actions">
+            data-bind="closeCollapsible, foreach: actions">
             <li data-bind="click: $parent[value].bind($parent),
                            visible: $parent.isActionRelevant(value)">
                 <span class="action-menu-item" data-bind="text: label"></span>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html
index 07b20b76bb1039bd2b6b33e0e579e09c284971f8..850f3c2fce793487392414a8ff1f95bc45f5daa5 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html
@@ -7,11 +7,12 @@
 
 <div
     class="admin__action-dropdown-wrap admin__data-grid-action-bookmarks"
-    data-bind="css: {_active: opened, _disabled: !collapsible}, outerClick: close">
+    data-bind="collapsible"
+    data-role="toolbar-menu-item">
     <button
         class="admin__action-dropdown"
         type="button"
-        data-bind="click: toggleOpened"
+        data-bind="toggleCollapsible"
         data-toggle="dropdown"
         aria-haspopup="true">
         <span class="admin__action-dropdown-text"
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html
index 7327208e7c520e4f694ad53f236a9f295848443a..707fad8eabbe87f3fc4120fd49e9b2afb6ce4b26 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html
@@ -5,7 +5,7 @@
  */
 -->
 <li
-    data-bind="css: {_edit: editing}, outerClick: endEdit">
+    data-bind="css: {_edit: editing}, outerClick: endEdit.bind($data)">
     <!-- ko if: editable -->
     <div class="action-dropdown-menu-item-edit">
         <input
@@ -17,18 +17,18 @@
                     placeholder: label
                 },
                 keyboard: {
-                    13: function(){ $parent.close().saveView($data) }
+                    13: function(){ $collapsible.close(); $parent.saveView($data) }
                 }"
             type="text">
         <button
             class="action-submit"
-            data-bind="click: function(){ $parent.close().saveView($data) }, attr: {title: $t('Save all changes')}"
+            data-bind="click: $parent.saveView.bind($parent, $data), closeCollapsible, attr: {title: $t('Save all changes')}"
             type="button">
             <span data-bind="i18n: 'Submit'"></span>
         </button>
         <div class="action-dropdown-menu-item-actions">
             <button
-                data-bind="click: function(){ $parent.close().removeView($data) }, attr: {title: $t('Delete bookmark')}"
+                data-bind="click: $parent.removeView.bind($parent, $data), closeCollapsible, attr: {title: $t('Delete bookmark')}"
                 class="action-delete"
                 type="button">
                 <span data-bind="i18n: 'Delete'"></span>
@@ -40,7 +40,7 @@
         <a
             class="action-dropdown-menu-link"
             href=""
-            data-bind="text: label, click: function(){ $data.active(true); $parent.close(); }"></a>
+            data-bind="text: label, click: function(){ active(true); }, closeCollapsible"></a>
         <!-- ko if: editable -->
         <div class="action-dropdown-menu-item-actions">
             <button
@@ -52,4 +52,4 @@
         </div>
         <!-- /ko -->
     </div>
-</li> 
\ No newline at end of file
+</li> 
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/controls/columns.html b/app/code/Magento/Ui/view/base/web/templates/grid/controls/columns.html
index 41b13406d504e9b0e90d4248bd789bbe8bf81f75..e2dcc3d816376756169ad5896c5e14de65475fe8 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/controls/columns.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/controls/columns.html
@@ -4,11 +4,13 @@
  * See COPYING.txt for license details.
  */
 -->
-<div data-bind="css: {_active: opened}, outerClick: close" class="admin__action-dropdown-wrap admin__data-grid-action-columns">
+<div data-bind="collapsible"
+     class="admin__action-dropdown-wrap admin__data-grid-action-columns"
+     data-role="toolbar-menu-item">
     <button
         class="admin__action-dropdown"
         type="button"
-        data-bind="click: toggleOpened"
+        data-bind="toggleCollapsible"
         data-toggle="dropdown"
         aria-haspopup="true">
         <span class="admin__action-dropdown-text" data-bind="i18n: 'Columns'"></span>
@@ -19,9 +21,22 @@
         </div>
         <div class="admin__action-dropdown-menu-content" data-bind="foreach: elems">
             <div class="admin__field-option">
-                <input data-bind="attr: {id: 'grid-controls-columns-' + index}, disable: $parent.isDisabled($data), checked: visible"
-                       class="admin__control-checkbox" type="checkbox"/>
-                <label data-bind="text: label, attr: {for: 'grid-controls-columns-' + index}" class="admin__field-label"></label>
+                <input
+                    class="admin__control-checkbox"
+                    type="checkbox"
+                    data-bind="
+                        attr: {
+                                id: ++ko.bindingHandlers['uniqueName'].currentIndex + '_uid'
+                        },
+                        disable: $parent.isDisabled($data),
+                        checked: visible"/>
+                <label
+                    class="admin__field-label"
+                    data-bind="
+                        text: label,
+                        attr: {
+                            for: ko.bindingHandlers['uniqueName'].currentIndex + '_uid'
+                        }"></label>
             </div>
         </div>
         <div class="admin__action-dropdown-menu-footer">
@@ -31,10 +46,10 @@
                 </button>
             </div>
             <div class="admin__action-dropdown-footer-main-actions">
-                <button data-bind="click: function () { $data.close().cancel(); }" class="action-tertiary" type="button">
+                <button data-bind="click: cancel, closeCollapsible" class="action-tertiary" type="button">
                     <span data-bind="i18n: 'Cancel'"></span>
                 </button>
             </div>
         </div>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/editing/bulk.html b/app/code/Magento/Ui/view/base/web/templates/grid/editing/bulk.html
index 5bdfb3db958eaf2d8c1fece8bfa2f10077fbd1f9..fe3d785f3d29a4cece894b3df02ece7467f7a6b1 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/editing/bulk.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/editing/bulk.html
@@ -8,7 +8,8 @@
     class="data-grid-bulk-edit-panel data-grid-editable-row"
     data-bind="
         visible: active,
-        foreach: fields">
+        foreach: fields"
+        data-role="data-grid-bulk-row">
     <td
         data-bind="
             visible: $parent.getColumn(index).visible,
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/exportButton.html b/app/code/Magento/Ui/view/base/web/templates/grid/exportButton.html
index d50c4fb2b9c1a169cc28fe1543154a08c468f237..84df5efc7227f9e2fde80a844b39c8017d177dea 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/exportButton.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/exportButton.html
@@ -7,11 +7,11 @@
 
 <div
     class="admin__action-dropdown-wrap admin__data-grid-action-export"
-    data-bind="css: {_active: opened, _disabled: !collapsible}, outerClick: close">
+    data-bind="collapsible">
     <button
         class="admin__action-dropdown"
         type="button"
-        data-bind="click: toggleOpened"
+        data-bind="toggleCollapsible"
         data-toggle="dropdown"
         aria-haspopup="true">
         <span class="admin__action-dropdown-text" data-bind="i18n: 'Export'"></span>
@@ -41,7 +41,7 @@
         <div class="admin__action-dropdown-footer-main-actions">
             <button
                 class="action-tertiary"
-                data-bind="click: close"
+                data-bind="closeCollapsible"
                 type="button">
                 <span data-bind="i18n: 'Cancel'"></span>
             </button>
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 78f14ebb39fe7981649700185965a5edb4bb4e74..d0e1787de6a1dc0609f6abc91419ca2f80f21946 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
@@ -20,7 +20,7 @@
                     focusout: onFocusOut,
                     keydown: keydownSwitcher
                 },
-                outerClick: outerClick">
+                outerClick: outerClick.bind($data)">
     <!-- ko ifnot: chipsEnabled -->
     <div
         class="action-select admin__action-multiselect"
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/filters/filters.html b/app/code/Magento/Ui/view/base/web/templates/grid/filters/filters.html
index e81c1fd1dbf968392c306554cdf4298a37a87e70..c3ae31d52b181ec49fa5ca785cb018cb4824275a 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/filters/filters.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/filters/filters.html
@@ -4,12 +4,21 @@
  * See COPYING.txt for license details.
  */
 -->
-<div class="data-grid-filters-actions-wrap">
+<div class="data-grid-filters-actions-wrap" data-bind="collapsible: {openClass: false, closeOnOuter: false}">
     <div class="data-grid-filters-action-wrap">
         <button
-            class="action-default _active"
+            class="action-default"
             data-action="grid-filter-expand"
-            data-bind="click: toggleOpened, attr: { disabled: !hasVisible() }, css: { _active: isOpened() }">
+            data-bind="
+                click: $collapsible.toggle,
+                attr: {
+                    disabled: !hasVisible(),
+                    'title': $t('Filters')
+                },
+                css: {
+                    _active: hasVisible() && $collapsible.opened()
+                }"
+        >
             <span data-bind="i18n: 'Filters'"></span>
         </button>
     </div>
@@ -21,7 +30,7 @@
 
 <div
     class="admin__data-grid-filters-wrap"
-    data-bind="css: { _show: isOpened() }"
+    data-bind="css: { _show: hasVisible() && $collapsible.opened() }"
     data-part="filter-form">
 
     <fieldset class="admin__fieldset admin__data-grid-filters">
@@ -49,17 +58,16 @@
                 class="action-tertiary"
                 type="button"
                 data-action="grid-filter-cancel"
-                data-bind="click: function(){ $data.close().cancel(); }">
+                data-bind="click: $data.cancel.bind($data), closeCollapsible">
                 <span data-bind="i18n: 'Cancel'"></span>
             </button>
             <button
                 class="action-secondary"
                 type="button"
                 data-action="grid-filter-apply"
-                data-bind="click: function(){ $data.close().apply(); }">
+                data-bind="click: $data.apply.bind($data), closeCollapsible">
                 <span data-bind="i18n: 'Apply Filters'"></span>
             </button>
         </div>
     </div>
-
 </div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/listing.html b/app/code/Magento/Ui/view/base/web/templates/grid/listing.html
index 49f0e2e392e2587762faefefb4a48e72407a3854..1a1428162c153627ca8d67ac3dad8199101a33c7 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/listing.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/listing.html
@@ -4,8 +4,8 @@
  * See COPYING.txt for license details.
  */
 -->
-<div class="admin__data-grid-wrap">
-    <table class="data-grid">
+<div class="admin__data-grid-wrap" data-role="grid-wrapper">
+    <table class="data-grid" data-role="grid">
         <thead>
             <tr data-bind="foreach: elems">
                 <!-- ko template: getHeader() --><!-- /ko -->
@@ -14,6 +14,7 @@
         <tbody>
             <!-- ko foreach: { data: rows, as: 'row' } -->
                 <tr class="data-row"
+                    data-role="row"
                     data-bind="
                         css: {
                             '_odd-row': !!($index() % 2)
@@ -35,4 +36,4 @@
             <!-- /ko -->
         </tbody>
     </table>
-</div>
\ No newline at end of file
+</div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/paging-total.html b/app/code/Magento/Ui/view/base/web/templates/grid/paging-total.html
new file mode 100644
index 0000000000000000000000000000000000000000..0c79131bc2f8e4c994858b5e5d157641d571c5fd
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/paging-total.html
@@ -0,0 +1,13 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+
+<div class="admin__control-support-text">
+    <span data-bind="i18n: totalRecords"></span> records found
+    <!-- ko if: totalSelected -->
+    (<span data-bind="i18n: totalSelected"></span> selected)
+    <!-- /ko -->
+</div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/paging/paging.html b/app/code/Magento/Ui/view/base/web/templates/grid/paging/paging.html
index 354193c7901b2596244f364fdc8af9a659478864..1961cfff11a3d7be640aace14992455fbb0981f8 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/paging/paging.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/paging/paging.html
@@ -4,16 +4,7 @@
  * See COPYING.txt for license details.
  */
 -->
-<div class="col-xs-3">
-    <div class="admin__control-support-text">
-        <span data-bind="text: totalRecords"></span> records found
-        <!-- ko if: totalSelected -->
-        (<span data-bind="text: totalSelected"></span> selected)
-        <!-- /ko -->
-    </div>
-</div>
-
-<div class="col-xs-7 admin__data-grid-pager-wrap">
+<div class="admin__data-grid-pager-wrap" data-role="toolbar-menu-item">
     <!-- ko scope: sizes -->
         <!-- ko template: getTemplate() --><!-- /ko -->
     <!-- /ko -->
@@ -41,5 +32,4 @@
             <span>Next page</span>
         </button>
     </div>
-
 </div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/paging/sizes.html b/app/code/Magento/Ui/view/base/web/templates/grid/paging/sizes.html
index ff61b6bdcf6f57d728cbf2225a262868ea8e675c..dcc9692793e40d2508b061fdf1a33a5f98c1bc6c 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/paging/sizes.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/paging/sizes.html
@@ -7,9 +7,10 @@
 
 <div
     class="selectmenu"
-    data-bind="css: {_active: opened}, outerClick: close">
+    data-bind="collapsible"
+    data-role="toolbar-collapsible-menu">
     <div
-        data-bind="click: open"
+        data-bind="openCollapsible"
         class="selectmenu-value">
         <input
             data-bind="value: _value, attr: {id: index}"
@@ -18,11 +19,13 @@
     <button
         class="selectmenu-toggle"
         type="button"
-        data-bind="click: toggleOpened, css: {_active: opened}"
+        data-bind="toggleCollapsible, css: {_active: $collapsible.opened}"
         aria-haspopup="true">
         <span data-bind="i18n: 'Select'"></span>
     </button>
-    <div class="selectmenu-items" data-bind="css: {_active: opened}, outerClick: $data.discardAll.bind($data)">
+    <div class="selectmenu-items" data-bind="css: {_active: $collapsible.opened},
+        outerClick: discardAll.bind($data)"
+         data-role="toolbar-collapsible-menu">
         <ul>
             <!-- ko foreach: optionsArray -->
             <li data-bind="css: { _edit: $parent.isEditing(value)}">
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html b/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html
index 1a9d495f422965f61401709e6736a7a61c4662fc..3e518b7f874f2881ede0dd2edfa318b232c061cd 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html
@@ -5,7 +5,11 @@
  */
 -->
 <div class="data-grid-search-control-wrap">
+    <label class="data-grid-search-label" for="data-grid-search" data-bind="attr: {title: $t('Search')}">
+        <span data-bind="text: $t('Search')"></span>
+    </label>
     <input
+            id="data-grid-search"
             class="admin__control-text data-grid-search-control"
             data-bind="
                 attr: {
@@ -20,4 +24,4 @@
     <button class="action-submit" data-bind="click: apply.bind($data, false)" type="button">
         <span data-bind="i18n: 'Search'"></span>
     </button>
-</div>
\ No newline at end of file
+</div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sticky/chips.html b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/chips.html
new file mode 100644
index 0000000000000000000000000000000000000000..e396c223d8a92a5486335ffb6367c95f087322e6
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/chips.html
@@ -0,0 +1,57 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<div
+    class="admin__data-grid-filters-current"
+    data-bind="css: {_show: hasData()}">
+    <div class="admin__current-filters-title-wrap">
+        <span
+            class="admin__current-filters-title"
+            data-bind="i18n: 'Active filters:'"></span>
+    </div>
+    <div class="admin__current-filters-list-wrap">
+        <ul class="admin__current-filters-list" data-role="filter-list">
+            <!-- ko foreach: elems -->
+                <!-- ko foreach: previews -->
+                <li>
+                    <span data-bind="text: label + ':'"></span>
+                    
+                    <!-- ko if: typeof preview === 'string' -->
+                    <span data-bind="text: preview"></span>
+                    <!-- /ko -->
+                    
+                    <!-- ko if: typeof preview === 'object' -->
+                    <span>
+                        <!-- ko text: preview[0] || '...' --><!-- /ko --> - <!-- ko text: preview[1] || '...' --><!-- /ko -->
+                    </span>
+                    <!-- /ko -->
+
+                    <button
+                        class="action-remove"
+                        data-action="grid-filter-remove-chip"
+                        data-bind="click: $parent.clear.bind($parent, elem)"
+                        type="button">
+                        <span data-bind="i18n: 'Remove'"></span>
+                    </button>
+                </li>
+                <!-- /ko -->
+            <!-- /ko -->
+        </ul>
+    </div>
+    <div class="admin__current-filters-actions-wrap">
+        <button
+            class="action-tertiary action-clear"
+            type="button"
+            data-action="grid-filter-reset"
+            data-bind="
+                i18n: 'Clear all',
+                click: clear,
+                attr: {
+                    'data-action': hasData() ? 'grid-filter-reset' : ''
+                }"
+            ></button>
+    </div>
+</div>
\ No newline at end of file
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sticky/filters.html b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/filters.html
new file mode 100644
index 0000000000000000000000000000000000000000..9a7e06d5699a531809b9527972df744197a6feab
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/filters.html
@@ -0,0 +1,22 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<div class="data-grid-filters-actions-wrap">
+    <div class="data-grid-filters-action-wrap">
+        <button
+                class="action-default"
+                data-action="grid-filter-expand"
+                data-bind="
+                 click: function(){
+                    window.scrollTo(0, 0);
+                    $data.trigger('open');
+                 },
+                 attr: {disabled: !hasVisible()}">
+            <span data-bind="i18n: 'Filters'"></span>
+        </button>
+        <span class="filters-active" data-bind="text: $data.active().length || ''"></span> <!-- Added the amount of selected filters -->
+    </div>
+</div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sticky/listing.html b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/listing.html
new file mode 100644
index 0000000000000000000000000000000000000000..fc0c43a4b43315304418d1e1ecbdc602236c06c9
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/listing.html
@@ -0,0 +1,17 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<div class="admin__data-grid-wrap" data-role="sticky-el-root">
+    <table class="data-grid">
+        <thead>
+            <tr data-bind="foreach: elems">
+                <!-- ko template: getHeader() --><!-- /ko -->
+            </tr>
+        </thead>
+        <tbody>
+        </tbody>
+    </table>
+</div>
\ No newline at end of file
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sticky/sticky.html b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/sticky.html
new file mode 100644
index 0000000000000000000000000000000000000000..ec79fd08472ea7a588e5611796f8e6a090b7e43e
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/sticky.html
@@ -0,0 +1,22 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+
+<div data-bind='visible: visible' class='sticky-header'>
+    <span class="data-grid-cap-left"></span>
+    <span class="data-grid-cap-right"></span>
+    <!-- ko if: enableToolbar -->
+        <!-- ko scope: toolbar -->
+            <!-- ko template: $data.stickyTmpl || getTemplate() --><!-- /ko -->
+        <!-- /ko -->
+    <!-- /ko -->
+
+    <!-- ko if: enableHeader -->
+        <!-- ko scope: listing -->
+            <!-- ko template: $data.stickyTmpl || getTemplate() --><!-- /ko -->
+        <!-- /ko -->
+    <!-- /ko -->
+</div>
\ No newline at end of file
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sticky/toolbar.html b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/toolbar.html
new file mode 100644
index 0000000000000000000000000000000000000000..156873520509f2e1c3cc25168f761102344d448d
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/sticky/toolbar.html
@@ -0,0 +1,41 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+
+<div class="admin__data-grid-header" data-role="sticky-el-root">
+    <div class="admin__data-grid-header-row">
+        <!-- ko foreach: getRegion('bottom') -->
+            <!-- ko if: $data.componentType === 'massaction' -->
+                    <!-- ko template: getTemplate() --><!-- /ko -->
+            <!-- /ko -->
+        <!-- /ko -->
+        <!-- ko foreach: getRegion('bottom') -->
+            <!-- ko if: $data.componentType === 'paging' -->
+                <!-- ko template: totalTmpl --><!-- /ko -->
+            <!-- /ko -->
+        <!-- /ko -->
+        <!-- ko foreach: getRegion('dataGridFilters') -->
+            <!-- ko template: $data.stickyTmpl || getTemplate() --><!-- /ko -->
+        <!-- /ko -->
+        <div class="admin__data-grid-actions-wrap">
+            <!-- ko foreach: getRegion('dataGridActions') -->
+                <!-- ko template: getTemplate() --><!-- /ko -->
+            <!-- /ko -->
+        </div>
+        <!-- ko foreach: getRegion('bottom') -->
+            <!-- ko if: $data.componentType === 'paging' -->
+                <!-- ko template: getTemplate() --><!-- /ko -->
+            <!-- /ko -->
+        <!-- /ko -->
+    </div>
+</div>
+<!-- ko foreach: getRegion('dataGridFilters') -->
+    <!-- ko if: $data.index === 'listing_filters' -->
+        <!-- ko scope: chips -->
+            <!-- ko template: $data.stickyTmpl || getTemplate() -->
+        <!-- /ko -->
+    <!-- /ko -->
+<!-- /ko -->
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/toolbar.html b/app/code/Magento/Ui/view/base/web/templates/grid/toolbar.html
index 0812e3fe862d2fac42f48fdd80ead31355f23107..36482b1d973bae48f5d946dff86c27f6c0bc724e 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/toolbar.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/toolbar.html
@@ -5,20 +5,43 @@
  */
 -->
 
-<div class="admin__data-grid-header">
+<div class="admin__data-grid-header"
+        data-role="data-grid-toolbar">
     <div class="admin__data-grid-header-row">
         <div class="admin__data-grid-actions-wrap">
             <!-- ko foreach: getRegion('dataGridActions') -->
-                <!-- ko template: getTemplate() --><!-- /ko -->
+            <!-- ko template: getTemplate() --><!-- /ko -->
             <!-- /ko -->
         </div>
         <!-- ko foreach: getRegion('dataGridFilters') -->
-            <!-- ko template: getTemplate() --><!-- /ko -->
+        <!-- ko template: getTemplate() --><!-- /ko -->
         <!-- /ko -->
     </div>
     <div class="admin__data-grid-header-row row row-gutter">
-        <!-- ko foreach: getRegion('bottom') -->
-            <!-- ko template: getTemplate() --><!-- /ko -->
-        <!-- /ko -->
+        <div class="col-xs-2">
+            <!-- ko foreach: getRegion('bottom') -->
+                <!-- ko if: $data.componentType === 'massaction' -->
+                    <!-- ko template: getTemplate() --><!-- /ko -->
+                <!-- /ko -->
+            <!-- /ko -->
+        </div>
+        <div class="col-xs-10">
+            <div class="row">
+                <div class="col-xs-3">
+                    <!-- ko foreach: getRegion('bottom') -->
+                        <!-- ko if: $data.componentType === 'paging' -->
+                            <!-- ko template: totalTmpl --><!-- /ko -->
+                        <!-- /ko -->
+                    <!-- /ko -->
+                </div>
+                <div class="col-xs-9">
+                    <!-- ko foreach: getRegion('bottom') -->
+                        <!-- ko if: $data.componentType === 'paging' -->
+                            <!-- ko template: getTemplate() --><!-- /ko -->
+                        <!-- /ko -->
+                    <!-- /ko -->
+                </div>
+            </div>
+        </div>
     </div>
 </div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/tree-massactions.html b/app/code/Magento/Ui/view/base/web/templates/grid/tree-massactions.html
index db716f2a01323031a01ea4538bfa4689c2574df9..5a3f2997181c8499d0675de42d62acfd16c176b1 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/tree-massactions.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/tree-massactions.html
@@ -4,35 +4,33 @@
  * See COPYING.txt for license details.
  */
 -->
-<div class="col-xs-2" data-bind="outerClick: close">
-    <div
-            class="action-select-wrap"
-            data-bind="css: {'_active': opened}">
-        <button
-                class="action-select"
-                data-bind="title: $t('Select Items'), click: toggleOpened">
-            <span data-bind="text: $t('Actions')"></span>
-        </button>
-        <div class="action-menu-items">
-            <ul
-                class="action-menu"
-                data-bind="css: {'_active': opened},
-                    foreach: {data: actions, as: 'action'}">
-                <li
+<div
+    class="action-select-wrap"
+    data-bind="css: {'_active': opened}, outerClick: close.bind($data)">
+    <button
+            class="action-select"
+            data-bind="title: $t('Select Items'), click: toggleOpened">
+        <span data-bind="text: $t('Actions')"></span>
+    </button>
+    <div class="action-menu-items">
+        <ul
+            class="action-menu"
+            data-bind="css: {'_active': opened},
+                foreach: {data: actions, as: 'action'}">
+            <li
                     data-bind="css: {
                         '_visible': $data.visible,
                         '_parent': $data.actions}">
-                    <span
-                        class="action-menu-item"
-                        data-bind="text: label,
-                            click: $parent.applyAction.bind($parent, type)">
-                    </span>
-                        <!-- ko if: $data.actions -->
-                            <!-- ko template: {name: $parent.submenuTemplate, data: $parent} -->
-                            <!-- /ko -->
-                        <!-- /ko-->
-                </li>
-            </ul>
-        </div>
+                <span
+                    class="action-menu-item"
+                    data-bind="text: label,
+                        click: $parent.applyAction.bind($parent, type)">
+                </span>
+                <!-- ko if: $data.actions -->
+                    <!-- ko template: {name: $parent.submenuTemplate, data: $parent} -->
+                    <!-- /ko -->
+                <!-- /ko-->
+            </li>
+        </ul>
     </div>
 </div>
diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less
index 080f0e60d49481314c351aa874b5663f7d8f0b2f..50d58b39e6e259760e09a629c5f1b73dac11c1cc 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less
@@ -7,6 +7,13 @@
 //  UI -> Data Grid
 //  _____________________________________________
 
+//
+//  Variables
+//  ---------------------------------------------
+
+@data-grid-sticky-header__z-index: @page-actions__fixed__z-index;
+@data-grid-overlay__z-index: @data-grid-sticky-header__z-index + 1;
+
 //
 //  Components
 //  ---------------------------------------------
@@ -32,9 +39,9 @@
 .admin__data-grid-loading-mask {
     background: rgba(255, 255, 255, .5);
     bottom: 0;
-    left: 0;
+    left: -@page-content__padding-horizontal;
     position: absolute;
-    right: 0;
+    right: -@page-content__padding-horizontal;
     top: 0;
     z-index: @data-grid-overlay__z-index;
     .spinner {
@@ -767,4 +774,4 @@ body._in-resize {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/_data-grid-header.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/_data-grid-header.less
index f83463ada82d3026424f9140080eddf41c6f3d77..61c9807d8e6b9d96001f9229e884526c70ca0fff 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/_data-grid-header.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/_data-grid-header.less
@@ -17,6 +17,7 @@
 @import 'data-grid-header/_data-grid-action-bookmarks.less';
 @import 'data-grid-header/_data-grid-action-columns.less';
 @import 'data-grid-header/_data-grid-action-export.less';
+@import 'data-grid-header/_data-grid-sticky-header.less';
 
 .admin__data-grid-header {
     font-size: @font-size__base; // ToDo UI: should be deleted, added to prevent fz override with .grid
diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less
index 8cbd9e579ff23c15420bd925add44202e4639267..773abad71b93e9b82282af0c7b51b00f8e30ec4a 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less
@@ -98,6 +98,9 @@
             background-color: @data-grid-search-menu-item__background-color;
         }
     }
+    .data-grid-search-label {
+        display: none;
+    }
 }
 
 .data-grid-search-control {
@@ -111,6 +114,7 @@
     padding-left: @indent__base;
     .action-default {
         font-size: @data-grid-filters__font-size;
+        margin-bottom: 1rem;
         padding-left: @data-grid-filters-action__padding-left;
         padding-right: @data-grid-filters-action__padding-right;
         padding-top: @action__padding-top + .1rem;
@@ -119,10 +123,8 @@
             border-bottom-color: @data-grid-filters__background-color;
             border-right-color: @data-grid-filters-action__active__border-color;
             font-weight: @font-weight__semibold;
-            margin-left: 0;
-            margin-right: 0;
-            margin-top: -.1rem;
-            padding-bottom: 1.9rem;
+            margin: -.1rem 0 0;
+            padding-bottom: 1.6rem;
             padding-top: @action__padding-top + .2rem;
             position: relative;
             z-index: @data-grid-header__z-index - 19;
@@ -138,6 +140,7 @@
         }
         &:before {
             &:extend(.abs-icon all);
+            color: @action-dropdown__color;
             content: @icon-filter__content;
             font-size: 1.8rem;
             margin-right: .4rem;
@@ -146,6 +149,9 @@
             vertical-align: top;
         }
     }
+    .filters-active {
+        display: none;
+    }
 }
 
 //
diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-sticky-header.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-sticky-header.less
new file mode 100644
index 0000000000000000000000000000000000000000..0083101d59ff94c3eb1cfb61a03a64bc49724de6
--- /dev/null
+++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-sticky-header.less
@@ -0,0 +1,271 @@
+// /**
+//  * Copyright © 2015 Magento. All rights reserved.
+//  * See COPYING.txt for license details.
+//  */
+
+//
+//  Variables
+//  ---------------------------------------------
+
+@data-grid-sticky-header__background-color: @color-white-fog;
+@data-grid-sticky-header__border-color: @color-gray89;
+@data-grid-sticky-header__box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.25);
+
+@data-grid-sticky-header-actions__background-color: @color-white;
+@data-grid-sticky-header-actions__border-color: @action__border-color;
+@data-grid-sticky-header-actions__box-shadow: @component__box-shadow__base;
+@data-grid-sticky-header-actions__margin: -@indent__xs 0 0 1.1rem;
+@data-grid-sticky-header-actions__padding: .6rem;
+@data-grid-sticky-header-actions__z-index: 210;
+
+@data-grid-sticky-header-action-select__width: percentage((2 / @grid-columns));
+
+//
+
+.sticky-header {
+    background-color: @data-grid-sticky-header__background-color;
+    border-bottom: 1px solid @data-grid-sticky-header__border-color;
+    box-shadow: @data-grid-sticky-header__box-shadow;
+    left: @page-wrapper__indent-left;
+    right: 0;
+    margin-top: -1px;
+    padding: @indent__xs @indent__l 0;
+    position: fixed;
+    top: 77px;
+    z-index: @data-grid-sticky-header__z-index;
+
+    .admin__data-grid-wrap {
+        margin-bottom: 0;
+        overflow-x: visible;
+        padding-bottom: 0;
+    }
+
+    .admin__data-grid-header-row {
+        position: relative;
+        text-align: right;
+        &:last-child {
+            margin: 0;
+        }
+    }
+
+    .data-grid-search-control-wrap,
+    .data-grid-filters-actions-wrap,
+    .admin__data-grid-filters-wrap,
+    .admin__data-grid-pager-wrap,
+    .admin__data-grid-actions-wrap {
+        display: inline-block;
+        float: none;
+        vertical-align: top;
+    }
+
+    //  Action select
+    .action-select-wrap {
+        float: left;
+        margin-right: 1.5rem;
+        width: @data-grid-sticky-header-action-select__width;
+    }
+
+    //  Total found
+    .admin__control-support-text {
+        float: left;
+    }
+
+    //  Data Grid Search
+    .data-grid-search-control-wrap {
+        margin: @data-grid-sticky-header-actions__margin;
+        width: auto;
+        .data-grid-search-label {
+            box-sizing: border-box;
+            cursor: pointer;
+            display: block;
+            min-width: 3.8rem;
+            padding: 1.2rem @data-grid-sticky-header-actions__padding 1.7rem;
+            position: relative;
+            text-align: center;
+            &:before {
+                &:extend(.abs-icon all);
+                color: @action-dropdown__color;
+                content: @icon-search__content;
+                font-size: @data-grid-search-action__size;
+                transition: @smooth__color;
+            }
+            &:hover {
+                &:before {
+                    color: @action-dropdown__hover__color;
+                }
+            }
+            span {
+                display: none;
+            }
+        }
+    }
+
+    //  Filters
+    .data-grid-filters-actions-wrap {
+        margin: @data-grid-sticky-header-actions__margin;
+        padding-left: 0;
+        position: relative;
+        .action-default {
+            background-color: transparent;
+            border: 1px solid transparent;
+            box-sizing: border-box;
+            min-width: 3.8rem;
+            padding: 1.2rem @data-grid-sticky-header-actions__padding 1.7rem;
+            text-align: center;
+            transition: all @appearing__transition-duration @apperaing__transition-timing-function;
+            span {
+                display: none;
+            }
+            &:before {
+                margin: 0;
+            }
+            &._active {
+                background-color: @data-grid-sticky-header-actions__background-color;
+                border-color: @data-grid-sticky-header-actions__border-color;
+                border-bottom-color: @data-grid-sticky-header-actions__background-color;
+                box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5);
+                z-index: @data-grid-sticky-header-actions__z-index;
+                &:after {
+                    background-color: @data-grid-sticky-header-actions__background-color;
+                    content: '';
+                    height: 6px;
+                    left: -2px;
+                    position: absolute;
+                    right: -6px;
+                    top: 100%;
+                }
+            }
+        }
+    }
+
+    .data-grid-filters-action-wrap {
+        padding: 0;
+    }
+
+    .admin__data-grid-filters-wrap {
+        background-color: @data-grid-sticky-header-actions__background-color;
+        border: 1px solid @data-grid-sticky-header-actions__border-color;
+        box-shadow: @data-grid-sticky-header__box-shadow;
+        left: 0;
+        padding-left: 3.5rem;
+        padding-right: 3.5rem;
+        position: absolute;
+        top: 100%;
+        width: 100%;
+        z-index: @data-grid-sticky-header-actions__z-index - 1;
+    }
+
+    .admin__data-grid-filters-current {
+        + .admin__data-grid-filters-wrap {
+            &._show {
+                margin-top: -6px;
+            }
+        }
+    }
+
+    .filters-active {
+        background-color: @color-phoenix-down;
+        border-radius: 10px;
+        color: @color-white;
+        display: block;
+        font-size: @font-size__base;
+        font-weight: @font-weight__bold;
+        padding: .1rem .7rem;
+        position: absolute;
+        right: -7px;
+        top: 0;
+        z-index: @data-grid-sticky-header-actions__z-index + 1;
+        &:empty {
+            padding-top: 0;
+            padding-bottom: 0;
+        }
+    }
+
+    //  Default view & columns
+    .admin__data-grid-actions-wrap {
+        margin: @data-grid-sticky-header-actions__margin;
+        padding-right: .3rem;
+        .admin__action-dropdown {
+            background-color: transparent;
+            box-sizing: border-box;
+            min-width: 3.8rem;
+            padding-left: @data-grid-sticky-header-actions__padding;
+            padding-right: @data-grid-sticky-header-actions__padding;
+            text-align: center;
+            .admin__action-dropdown-text {
+                display: inline-block;
+                min-width: 0;
+                max-width: 0;
+                overflow: hidden;
+            }
+            &:before {
+                margin: 0;
+            }
+        }
+        .admin__action-dropdown-wrap {
+            margin-right: 1.1rem;
+        }
+        .admin__action-dropdown-wrap,
+        .admin__action-dropdown {
+            &:after {
+                display: none;
+            }
+        }
+        ._active {
+            .admin__action-dropdown {
+                background-color: @color-white;
+            }
+        }
+    }
+
+    .admin__data-grid-action-bookmarks {
+        .admin__action-dropdown {
+            &:before {
+                position: relative;
+                top: -3px;
+            }
+        }
+    }
+
+    .admin__data-grid-filters-current {
+        border-top: 0;
+        border-bottom: 0;
+        margin-bottom: 0;
+        padding-bottom: 0;
+        padding-top: 0;
+    }
+
+    .data-grid-search-control-wrap .data-grid-search-control,
+    .data-grid-search-control-wrap .action-submit,
+    .admin__data-grid-pager .admin__control-text,
+    .admin__data-grid-pager-wrap .admin__control-support-text {
+        display: none;
+    }
+
+    .action-next {
+        margin: 0;
+    }
+
+    //  Table header
+    .data-grid {
+        margin-bottom: -1px;
+    }
+}
+
+.data-grid-cap-left,
+.data-grid-cap-right {
+    background-color: @data-grid-sticky-header__background-color;
+    bottom: -2px;
+    position: absolute;
+    top: 6rem;
+    width: @page-content__padding-horizontal;
+    z-index: @action-multicheck__z-index + 1;
+}
+
+.data-grid-cap-left {
+    left: 0;
+}
+
+.data-grid-cap-right {
+    right: 0;
+}
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less
index e26049d26d82a179dc9be92abf2b8d7a086a70ef..907fe6e29c951d49e54f86a3d6550ac7e5d2b845 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less
@@ -93,11 +93,11 @@
     .action-toggle-triangle(
         @_dropdown__padding-right: @action-dropdown__padding-right;
     );
-    box-shadow: none;
     background-color: @action-dropdown__background-color;
     border: 1px solid transparent;
     border-bottom: none;
     border-radius: 0;
+    box-shadow: none;
     color: @action-dropdown__color;
     display: inline-block;
     font-size: @action-dropdown__font-size;
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/editing/bulk.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/editing/bulk.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..7457a325e17e4f8476a869e71334a020c341363c
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/editing/bulk.test.js
@@ -0,0 +1,37 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+define([
+    'Magento_Ui/js/grid/editing/bulk'
+], function (Bulk) {
+    'use strict';
+
+    describe('Magento_Ui/js/grid/editing/bulk', function () {
+        var bulkObj,
+            temp;
+
+        beforeEach(function () {
+            bulkObj = new Bulk();
+        });
+        it('has initObservable', function () {
+            expect(bulkObj).toBeDefined();
+        });
+        it('has apply method', function () {
+            spyOn(bulkObj, 'isValid');
+            temp = bulkObj.apply();
+            expect(bulkObj.isValid).toHaveBeenCalled();
+            expect(temp).toBeDefined();
+        });
+        it('can apply data', function () {
+            spyOn(bulkObj, 'getData');
+            bulkObj.applyData();
+            expect(bulkObj.getData).toHaveBeenCalled();
+        });
+        it('has updateState method', function () {
+            spyOn(bulkObj, 'updateState');
+            bulkObj.updateState();
+            expect(bulkObj.updateState).toHaveBeenCalled();
+        });
+    })
+});
\ No newline at end of file
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/filters/filters.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/filters/filters.test.js
index 71cc27d7da04d57d3a572107e450d290cc4e83e0..4df1017646af7dc8846129ccc4eece119cc291b4 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/filters/filters.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/filters/filters.test.js
@@ -4,177 +4,80 @@
  */
 
 define([
-    'underscore',
     'Magento_Ui/js/grid/filters/filters'
-], function (_, Filters) {
+], function (Filter) {
     'use strict';
 
-    describe('ui/js/grid/filters/filters', function () {
-        var filters;
+    describe('Magento_Ui/js/grid/filters/filters', function () {
+        var filterObj,
+            temp;
 
         beforeEach(function () {
-            filters = new Filters({
-                elems: [],
-                index: 'index',
-                name: 'name',
-                indexField: 'id',
-                dataScope: 'scope',
-                provider: 'provider'
-            });
+            filterObj = new Filter();
         });
-
-        it('Default state - Select no fields.', function () {
-            expect(filters.elems()).toEqual([]);
-            filters.elems.push({id:1});
-            filters.elems.push({id:1});
-            expect(filters.elems()).not.toEqual([]);
+        it('has been initialized', function () {
+            expect(filterObj).toBeDefined();
         });
-        it('Checks if specified filter is active.', function () {
-            var filter = {id: 1};
-
-            expect(filters.isFilterActive(filter)).toBe(false);
-            filters.active().push(filter);
-            expect(filters.isFilterActive(filter)).toBe(true);
+        it('has initObservable', function () {
+            temp = filterObj.initObservable();
+            expect(temp).toBeDefined();
         });
-        it('Tells whether specified filter should be visible.', function () {
-            var filter = {
-                visible: function () {
-                    return false;
-                }
-            };
-
-            expect(filters.isFilterVisible(filter)).toBe(false);
-            filters.active().push(filter);
-            expect(filters.isFilterActive(filter)).toBe(true);
-            filter.visible = function() {
-                return true;
-            };
-            expect(filters.isFilterActive(filter)).toBe(true);
-            filters.active().pop();
-            expect(filters.isFilterActive(filter)).toBe(false);
+        it('has initElement', function () {
+            spyOn(filterObj, 'initElement');
+            filterObj.initElement();
+            expect(filterObj.initElement).toHaveBeenCalled();
         });
-        it('Checks if collection has visible filters.', function () {
-            var filter = {
-                visible: function () {
-                    return false;
-                }
+        it('has clear', function () {
+            temp = filterObj.clear();
+            expect(temp).toBeDefined();
+        });
+        it('has apply', function () {
+            temp = filterObj.apply();
+            expect(temp).toBeDefined();
+        });
+        it('has cancel', function () {
+            temp = filterObj.cancel();
+            expect(temp).toBeDefined();
+        });
+        it('has isOpened method', function () {
+            filterObj.opened = function () {
+                return true;
             };
-
-            filters.elems.push(filter);
-            expect(filters.hasVisible()).toBe(false);
-            filter.visible = function() {
+            filterObj.hasVisible = function () {
                 return true;
             };
-            filters.elems.push(filter);
-            expect(filters.hasVisible()).toBe(true);
-            filters.elems.removeAll();
-            expect(filters.hasVisible()).toBe(false);
-            filters.active().push(filter);
-            expect(filters.hasVisible()).toBe(false);
+            temp = filterObj.isOpened();
+            expect(temp).toBeTruthy();
         });
-        it('Tells whether filters panel should be opened.', function () {
-            var filter = {
+        it('has isFilterVisible method', function () {
+            temp = {
                 visible: function () {
                     return false;
                 }
             };
-
-            filters.opened(false);
-            filters.elems.push(filter);
-            expect(filters.isOpened()).toBe(false);
-            filter.visible = function() {
-                return true;
-            };
-            filters.elems.push(filter);
-            filters.opened(true);
-            expect(filters.isOpened()).toBe(true);
-            filters.elems.removeAll();
-            expect(filters.isOpened()).toBe(false);
-            filters.active().push(filter);
-            expect(filters.isOpened()).toBe(false);
-        });
-        it('Resets filters to the last applied state.', function () {
-            filters.applied = {};
-            filters.filters = {};
-            filters.cancel();
-            expect(filters.filters).toEqual(filters.filters);
-            filters.filters = {id:1};
-            filters.cancel();
-            expect(filters.filters).toEqual({});
-            filters.applied = {id:1};
-            filters.cancel();
-            expect(filters.filters).toEqual(filters.applied);
+            spyOn(filterObj, 'isFilterActive');
+            filterObj.isFilterVisible(temp);
+            expect(filterObj.isFilterActive).toHaveBeenCalled();
         });
-        it('Sets filters data to the applied state.', function () {
-            filters.applied = {};
-            filters.filters = {};
-            filters.apply();
-            expect(filters.applied).toEqual({});
-            filters.filters = {};
-            filters.applied = {id:2};
-            filters.apply();
-            expect(filters.applied).toEqual({});
-            filters.filters = {id:1};
-            filters.applied = {};
-            filters.apply();
-            expect(filters.applied).toEqual({id:1});
-            filters.filters = {id:1};
-            filters.applied = {id:2};
-            filters.apply();
-            expect(filters.applied).toEqual({id:1});
+        it('has isFilterActive method', function () {
+            spyOn(filterObj, 'isFilterActive');
+            filterObj.isFilterActive();
+            expect(filterObj.isFilterActive).toHaveBeenCalled();
         });
-        it('Clears filters data.', function () {
-            var elem = {
-                value: '',
-                getPreview: function () {
-                    return true;
-                },
-                clear: function () {
-                    this.value = '';
-                    return this.value;
-                }
-            };
-
-            filters.active.push(elem);
-            filters.applied = {};
-            filters.filters = {};
-            filters.clear();
-            expect(filters.active.first().value).toEqual('');
-            filters.active.first().value = 1;
-            filters.clear();
-            expect(filters.active.first().value).toEqual('');
+        it('has hasVisible method', function () {
+            spyOn(filterObj, 'hasVisible');
+            filterObj.hasVisible();
+            expect(filterObj.hasVisible).toHaveBeenCalled();
         });
-        it('Set active elements where exist value from elems.', function () {
-            var elem = {
-                getPreview: function () {
-                    return true;
-                },
-                hasData: function () {
-                    return false;
-                }
-            };
-
-            filters.elems.push(elem);
-            filters.extractActive();
-            expect(filters.active().length).toEqual(0);
-            elem.hasData = function() {
-                return true;
-            };
-            filters.elems.removeAll();
-            filters.elems().push(elem);
-            filters.extractActive();
-            expect(filters.active().length).toEqual(1);
+        it('has extractActive method', function () {
+            spyOn(filterObj, 'extractActive');
+            filterObj.extractActive();
+            expect(filterObj.extractActive).toHaveBeenCalled();
         });
-        it('Set previews from argument elements.', function () {
-            var elem = {
-                getPreview: function() {
-                    return true;
-                }
-            };
-
-            filters.elems.push(elem);
-            filters.extractActive(filters.elems);
-            expect(filters.active().length).toEqual(0);
+        it('has extractPreviews method', function () {
+            spyOn(filterObj, 'extractPreviews');
+            filterObj.extractPreviews();
+            expect(filterObj.extractPreviews).toHaveBeenCalled();
         });
     });
-});
+});
\ No newline at end of file
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/resize.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/resize.test.js
index 7a03ca73938611fdc2f935c6d9afd3849c54f752..a213081a8115aa179513df062d8c1b424db64ecf 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/resize.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/resize.test.js
@@ -51,8 +51,9 @@ define([
                 expect(type).toEqual('object');
             });
             it('Check "this.table" variable', function () {
-                obj.initTable('magento');
-                expect(obj.table).toEqual('magento');
+                arg = document.createElement('table');
+                obj.initTable(arg);
+                expect(arg.classList.contains(obj.fixedLayoutClass)).toBeTruthy();
             });
         });
         describe('"initColumn" method', function () {
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..d1b31419379e51f76c853263dd72cc7cc0d32d6c
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js
@@ -0,0 +1,57 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+define([
+    'Magento_Ui/js/grid/search/search'
+], function (Search) {
+    'use strict';
+
+    describe('Magento_Ui/js/search/search', function () {
+        var searchObj,
+            temp;
+
+        beforeEach(function () {
+            searchObj = new Search();
+        });
+        it('has initialized', function () {
+            expect(searchObj).toBeDefined();
+        });
+        it('has initObservable', function () {
+            temp = searchObj.initObservable();
+            expect(temp).toBeDefined();
+        });
+        it('has initObservable', function () {
+            spyOn(searchObj, 'initChips');
+            searchObj.initChips();
+            expect(searchObj.initChips).toHaveBeenCalled();
+        });
+        it('has initChips', function () {
+            spyOn(searchObj, 'chips');
+            searchObj.initChips();
+            expect(searchObj.chips).toHaveBeenCalled();
+        });
+        it('has clear', function () {
+            spyOn(searchObj, 'value');
+            searchObj.clear();
+            expect(searchObj.value).toHaveBeenCalled();
+        });
+        it('has clear', function () {
+            spyOn(searchObj, 'inputValue');
+            searchObj.cancel();
+            expect(searchObj.inputValue).toHaveBeenCalled();
+        });
+        it('has apply', function () {
+            spyOn(searchObj, 'value');
+            spyOn(searchObj, 'inputValue');
+            searchObj.apply();
+            expect(searchObj.value).toHaveBeenCalled();
+            expect(searchObj.inputValue).toHaveBeenCalled();
+        });
+        it('has updatePreview', function () {
+            spyOn(searchObj, 'updatePreview');
+            searchObj.updatePreview();
+            expect(searchObj.updatePreview).toHaveBeenCalled();
+        });
+    });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sticky/sticky.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sticky/sticky.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..7902ccc21444f4547924fc00f970bac4b22c87aa
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sticky/sticky.test.js
@@ -0,0 +1,172 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+    'underscore',
+    'Magento_Ui/js/grid/sticky/sticky'
+], function (_, Sticky) {
+    'use strict';
+
+    describe('ui/js/grid/sticky/sticky', function () {
+        var stickyObj,
+            data,
+            stub;
+
+        Sticky.prototype.initialize = function () {
+        };
+
+        stickyObj = new Sticky({});
+
+        describe('has initialized', function () {
+            it('has been defined', function () {
+                expect(stickyObj).toBeDefined();
+            });
+            it('has initialized observable', function () {
+                data = stickyObj.initObservable();
+                expect(data).toBeDefined();
+            });
+            it('has initListingNode method', function () {
+                spyOn(stickyObj, 'initListingNode');
+                stickyObj.initListingNode();
+                expect(stickyObj.initListingNode).toHaveBeenCalled();
+            });
+            it('has initStickyToolbarNode method', function () {
+                stickyObj.initStickyToolbarNode({});
+                expect(stickyObj.stickyToolbarNode).toBeDefined();
+            });
+            it('has initContainerNode method', function () {
+                spyOn(stickyObj, 'initContainerNode');
+                stickyObj.initContainerNode();
+                expect(stickyObj.initContainerNode).toHaveBeenCalled();
+            });
+            it('has initListeners method', function () {
+                spyOn(stickyObj, 'initListeners');
+                stickyObj.initListeners();
+                expect(stickyObj.initListeners).toHaveBeenCalled();
+            });
+            it('has initOnScroll method', function () {
+                stickyObj.initOnScroll();
+                expect(stickyObj.lastHorizontalScrollPos).toBeDefined();
+            });
+            it('has initOnListingScroll method', function () {
+                spyOn(stickyObj, 'initOnListingScroll');
+                stickyObj.initOnListingScroll();
+                expect(stickyObj.initOnListingScroll).toHaveBeenCalled();
+            });
+            it('has initOnResize method', function () {
+                spyOn(stickyObj, 'initOnResize');
+                stickyObj.initOnResize();
+                expect(stickyObj.initOnResize).toHaveBeenCalled();
+            });
+        });
+        describe('has handlers', function () {
+            it('has onWindowScroll event', function () {
+                stickyObj.adjustOffset = function (){
+                    return this;
+                };
+
+                stickyObj.lastHorizontalScrollPos = 100500;
+                spyOn(stickyObj, 'adjustDataGridCapPositions');
+                stickyObj.onWindowScroll();
+                expect(stickyObj.adjustDataGridCapPositions).toHaveBeenCalled();
+            });
+            it('has onListingScroll method', function () {
+                spyOn(stickyObj, 'adjustOffset');
+                stickyObj.onListingScroll();
+                expect(stickyObj.adjustOffset).toHaveBeenCalled();
+            });
+            it('has onResize method', function () {
+                spyOn(stickyObj, 'onResize');
+                stickyObj.onResize();
+                expect(stickyObj.onResize).toHaveBeenCalled();
+            });
+        });
+        describe('has getters', function () {
+            it('has getListingWidth', function () {
+                stickyObj.listingNode = {
+                    width: function () {
+                        return 100500;
+                    }
+                };
+                data = stickyObj.getListingWidth();
+                expect(data).toBeDefined();
+            });
+            it('has getTableWidth method', function () {
+                spyOn(stickyObj, 'getTableWidth');
+                stickyObj.getTableWidth();
+                expect(stickyObj.getTableWidth).toHaveBeenCalled();
+            });
+            it('has getTopElement', function () {
+                stickyObj.toolbarNode = {};
+                data = stickyObj.getTopElement();
+                expect(data).toBeDefined();
+            });
+            it('has getOtherStickyElementsSize', function () {
+                stickyObj.otherStickyElsSize = null;
+                data = stickyObj.getOtherStickyElementsSize();
+                expect(data).toEqual(stickyObj.otherStickyElsSize);
+            });
+            it('has getListingTopYCoord method', function () {
+                spyOn(stickyObj, 'getListingTopYCoord');
+                stickyObj.getListingTopYCoord();
+                expect(stickyObj.getListingTopYCoord).toHaveBeenCalled();
+            });
+            it('has getMustBeSticky method', function () {
+                spyOn(stickyObj, 'getMustBeSticky');
+                stickyObj.getMustBeSticky();
+                expect(stickyObj.getMustBeSticky).toHaveBeenCalled();
+            });
+        });
+        describe('has dom manipulators', function () {
+            it('has resizeContainer event', function () {
+                spyOn(stickyObj, 'resizeContainer');
+                stickyObj.resizeContainer();
+                expect(stickyObj.resizeContainer).toHaveBeenCalled();
+            });
+            it('has resizeCols event', function () {
+                spyOn(stickyObj, 'resizeCols');
+                stickyObj.resizeCols();
+                expect(stickyObj.resizeCols).toHaveBeenCalled();
+            });
+            it('has resetToTop event', function () {
+                spyOn(stickyObj, 'resetToTop');
+                stickyObj.resetToTop();
+                expect(stickyObj.resetToTop).toHaveBeenCalled();
+            });
+            it('has toggleContainerVisibility event', function () {
+                spyOn(stickyObj, 'visible');
+                stickyObj.toggleContainerVisibility();
+                expect(stickyObj.visible).toHaveBeenCalled();
+            });
+            it('has adjustContainerElemsWidth event', function () {
+                stickyObj.resizeContainer = function(){
+                    return this;
+                };
+                stickyObj.resizeCols = function(){
+                    return this;
+                };
+                spyOn(stickyObj, 'resizeBulk');
+                stickyObj.adjustContainerElemsWidth();
+                expect(stickyObj.resizeBulk).toHaveBeenCalled();
+            });
+            it('has adjustOffset event', function () {
+                spyOn(stickyObj, 'adjustOffset');
+                stickyObj.adjustOffset();
+                expect(stickyObj.adjustOffset).toHaveBeenCalled();
+            });
+            it('has checkPos event', function () {
+                stickyObj.visible = function(){
+                    return false;
+                };
+                stickyObj.getMustBeSticky = function(){
+                    return false;
+                };
+
+                data = stickyObj.checkPos();
+                expect(data).toBeDefined();
+            })
+        });
+    })
+});
\ No newline at end of file
diff --git a/dev/tests/js/jasmine/tests/lib/mage/gallery.test.js b/dev/tests/js/jasmine/tests/lib/mage/gallery.test.js
index 3f076659b49239d26ac77fc5e593d511b4caf8f4..f198fee51b045e6a270758eda15d49e788531b3d 100644
--- a/dev/tests/js/jasmine/tests/lib/mage/gallery.test.js
+++ b/dev/tests/js/jasmine/tests/lib/mage/gallery.test.js
@@ -113,7 +113,7 @@ define([
         });
 
         it('breakpoints override configs', function () {
-            expect($('.fotorama__arr').css('display')).toBe('none');
+            expect($('.fotorama__arr').css('display')).toBe('block');
         });
     });
 });
diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt
index a76f67f4761e344f9a3011e1ef6e8d4a6746048e..f7c90d331d345918b1ef11d61b5c959ca3fa63fc 100644
--- a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt
+++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt
@@ -77,6 +77,7 @@ app/code/Magento/Checkout/view/frontend/web/js/action/set-shipping-information.j
 app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js
 app/code/Magento/Checkout/view/frontend/web/js/discount-codes.js
 app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js
+app/code/Magento/Checkout/view/frontend/web/js/model/authentication-messages.js
 app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js
 app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js
 app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js
@@ -118,6 +119,7 @@ app/code/Magento/Checkout/view/frontend/web/js/proceed-to-checkout.js
 app/code/Magento/Checkout/view/frontend/web/js/region-updater.js
 app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js
 app/code/Magento/Checkout/view/frontend/web/js/sidebar.js
+app/code/Magento/Checkout/view/frontend/web/js/view/authentication-messages.js
 app/code/Magento/Checkout/view/frontend/web/js/view/authentication.js
 app/code/Magento/Checkout/view/frontend/web/js/view/beforePlaceOrder.js
 app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js
@@ -161,7 +163,10 @@ app/code/Magento/Cms/view/adminhtml/requirejs-config.js
 app/code/Magento/Cms/view/adminhtml/web/js/folder-tree.js
 app/code/Magento/ConfigurableProduct/view/adminhtml/requirejs-config.js
 app/code/Magento/ConfigurableProduct/view/adminhtml/web/catalog/product/attribute.js
+app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/advanced-pricing-handler.js
 app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/configurable.js
+app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/options/price-type-handler.js
+app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/product-grid.js
 app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/attributes_values.js
 app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/bulk.js
 app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/select_attributes.js
@@ -315,7 +320,9 @@ app/code/Magento/Sales/view/frontend/web/js/view/last-ordered-items.js
 app/code/Magento/Sales/view/frontend/web/orders-returns.js
 app/code/Magento/SalesRule/view/frontend/web/js/action/cancel-coupon.js
 app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js
+app/code/Magento/SalesRule/view/frontend/web/js/model/payment/discount-messages.js
 app/code/Magento/SalesRule/view/frontend/web/js/view/cart/totals/discount.js
+app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount-messages.js
 app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount.js
 app/code/Magento/SalesRule/view/frontend/web/js/view/summary/discount.js
 app/code/Magento/Search/view/frontend/requirejs-config.js
@@ -418,13 +425,13 @@ app/code/Magento/Ui/view/base/web/js/grid/export.js
 app/code/Magento/Ui/view/base/web/js/grid/filters/chips.js
 app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js
 app/code/Magento/Ui/view/base/web/js/grid/filters/group.js
-app/code/Magento/Ui/view/base/web/js/grid/listing.js
 app/code/Magento/Ui/view/base/web/js/grid/massactions.js
 app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js
 app/code/Magento/Ui/view/base/web/js/grid/paging/sizes.js
 app/code/Magento/Ui/view/base/web/js/grid/provider.js
 app/code/Magento/Ui/view/base/web/js/grid/resize.js
 app/code/Magento/Ui/view/base/web/js/grid/search/search.js
+app/code/Magento/Ui/view/base/web/js/grid/sticky/sticky.js
 app/code/Magento/Ui/view/base/web/js/grid/tree-massactions.js
 app/code/Magento/Ui/view/base/web/js/lib/class.js
 app/code/Magento/Ui/view/base/web/js/lib/collapsible.js
@@ -438,6 +445,7 @@ app/code/Magento/Ui/view/base/web/js/lib/events.js
 app/code/Magento/Ui/view/base/web/js/lib/key-codes.js
 app/code/Magento/Ui/view/base/web/js/lib/ko/bind/after-render.js
 app/code/Magento/Ui/view/base/web/js/lib/ko/bind/class.js
+app/code/Magento/Ui/view/base/web/js/lib/ko/bind/collapsible.js
 app/code/Magento/Ui/view/base/web/js/lib/ko/bind/datepicker.js
 app/code/Magento/Ui/view/base/web/js/lib/ko/bind/fadeVisible.js
 app/code/Magento/Ui/view/base/web/js/lib/ko/bind/i18n.js
@@ -466,11 +474,13 @@ app/code/Magento/Ui/view/base/web/js/lib/validation/validator.js
 app/code/Magento/Ui/view/base/web/js/lib/view/utils/async.js
 app/code/Magento/Ui/view/base/web/js/lib/view/utils/bindings.js
 app/code/Magento/Ui/view/base/web/js/lib/view/utils/dom-observer.js
+app/code/Magento/Ui/view/base/web/js/lib/view/utils/raf.js
 app/code/Magento/Ui/view/base/web/js/modal/alert.js
 app/code/Magento/Ui/view/base/web/js/modal/confirm.js
 app/code/Magento/Ui/view/base/web/js/modal/modal.js
 app/code/Magento/Ui/view/base/web/js/modal/modalToggle.js
 app/code/Magento/Ui/view/frontend/web/js/model/messageList.js
+app/code/Magento/Ui/view/frontend/web/js/model/messages.js
 app/code/Magento/Ui/view/frontend/web/js/view/messages.js
 app/code/Magento/Ups/view/frontend/web/js/model/shipping-rates-validation-rules.js
 app/code/Magento/Ups/view/frontend/web/js/model/shipping-rates-validator.js
@@ -532,10 +542,13 @@ dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/controls/bookmarks/b
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/controls/bookmarks/storage.test.js
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/controls/bookmarks/view.test.js
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/controls/columns.test.js
+dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/editing/bulk.test.js
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/filters/filters.test.js
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/filters/group.test.js
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/paging/paging.test.js
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/resize.test.js
+dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js
+dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sticky/sticky.test.js
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/tree-massactions.test.js
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/component/core.test.js
 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/component/links.test.js
diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/jscs/.jscsrc b/dev/tests/static/testsuite/Magento/Test/Js/_files/jscs/.jscsrc
index 49ab9e6c2cf2e3cb80b9a7cbb44a17855337ea8e..b8071503882e23246959a4b515edeb435a1f325c 100644
--- a/dev/tests/static/testsuite/Magento/Test/Js/_files/jscs/.jscsrc
+++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/jscs/.jscsrc
@@ -1,7 +1,4 @@
 {
-    "plugins": [
-        "jscs-jsdoc"
-    ],
     "disallowKeywords": [
         "with",
         "continue"
@@ -62,6 +59,7 @@
         "beforeOpeningCurlyBrace": true,
         "beforeOpeningRoundBrace": true
     },
+    "disallowNewlineBeforeBlockStatements": true,
     "disallowDanglingUnderscores": null,
     "disallowEmptyBlocks": true,
     "disallowMixedSpacesAndTabs": true,
diff --git a/dev/tools/grunt/configs/eslint.js b/dev/tools/grunt/configs/eslint.js
index 9ce30aa58156ea22b011b2503f987453a6bdec7a..5dcbccf9ad4cd50686d89e43facc5d240bcb7048 100644
--- a/dev/tools/grunt/configs/eslint.js
+++ b/dev/tools/grunt/configs/eslint.js
@@ -19,7 +19,7 @@ module.exports = {
             configFile: 'dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc',
             reset: true,
             outputFile: 'dev/tests/static/eslint-error-report.xml',
-            format: 'checkstyle',
+            format: 'junit',
             quiet: true
         },
         src: ''
diff --git a/dev/tools/grunt/configs/jscs.js b/dev/tools/grunt/configs/jscs.js
index 8b89e0293bdb5de151a19266e8ffe5fe28d83de4..338deb017017d7dd50521f7090bb6268975fc6f8 100644
--- a/dev/tools/grunt/configs/jscs.js
+++ b/dev/tools/grunt/configs/jscs.js
@@ -16,7 +16,7 @@ module.exports = {
         options: {
             config: 'dev/tests/static/testsuite/Magento/Test/Js/_files/jscs/.jscsrc',
             reporterOutput: 'dev/tests/static/jscs-error-report.xml',
-            reporter: 'checkstyle'
+            reporter: 'junit'
         },
         src: ''
     }
diff --git a/package.json b/package.json
index 54a730729fb3be86a4d8db13c15cea36367ff409..8869196abeffc1653e34681226e7293a9c71c179 100644
--- a/package.json
+++ b/package.json
@@ -21,15 +21,14 @@
         "grunt-contrib-jasmine": "^0.8.1",
         "grunt-contrib-less": "^0.12.0",
         "grunt-contrib-watch": "^0.6.1",
-        "grunt-eslint": "^16.0.0",
+        "grunt-eslint": "^17.0.0",
         "grunt-exec": "^0.4.6",
-        "grunt-jscs": "^1.8.0",
+        "grunt-jscs": "^2.1.0",
         "grunt-replace": "^0.9.2",
         "grunt-styledocco": "^0.1.4",
         "grunt-template-jasmine-requirejs": "^0.2.3",
         "grunt-text-replace": "^0.4.0",
         "imagemin-svgo": "^4.0.1",
-        "jscs-jsdoc": "^1.1.0",
         "load-grunt-config": "^0.16.0",
         "morgan": "^1.5.0",
         "node-minify": "^1.0.1",