diff --git a/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js b/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js
new file mode 100644
index 0000000000000..cdb7ed784c150
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js
@@ -0,0 +1,78 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+define([
+ 'uiElement'
+], function (Element) {
+ 'use strict';
+
+ return Element.extend({
+ defaults: {
+ template: 'ui/grid/sortBy',
+ options: [],
+ applied: {},
+ sorting: 'desc',
+ columnsProvider: 'ns = ${ $.ns }, componentType = columns',
+ selectedOption: '',
+ isVisible: true,
+ listens: {
+ 'selectedOption': 'applyChanges'
+ },
+ statefull: {
+ selectedOption: true,
+ applied: true
+ },
+ exports: {
+ applied: '${ $.provider }:params.sorting'
+ },
+ imports: {
+ preparedOptions: '${ $.columnsProvider }:elems'
+ },
+ modules: {
+ columns: '${ $.columnsProvider }'
+ }
+ },
+
+ /**
+ * @inheritdoc
+ */
+ initObservable: function () {
+ return this._super()
+ .observe([
+ 'applied',
+ 'selectedOption',
+ 'isVisible'
+ ]);
+ },
+
+ /**
+ * Prepared sort order options
+ */
+ preparedOptions: function (columns) {
+ if (columns && columns.length > 0) {
+ columns.map(function (column) {
+ if (column.sortable === true) {
+ this.options.push({
+ value: column.index,
+ label: column.label
+ });
+ this.isVisible(true);
+ } else {
+ this.isVisible(false);
+ }
+ }.bind(this));
+ }
+ },
+
+ /**
+ * Apply changes
+ */
+ applyChanges: function () {
+ this.applied({
+ field: this.selectedOption(),
+ direction: this.sorting
+ });
+ }
+ });
+});
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html
new file mode 100644
index 0000000000000..e0bcf6cbe5514
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html
@@ -0,0 +1,15 @@
+
+
+ :
+
+
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js
new file mode 100644
index 0000000000000..ec6fb261c8d47
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js
@@ -0,0 +1,117 @@
+define([
+ 'Magento_Ui/js/grid/sortBy'
+], function (sortBy) {
+ 'use strict';
+ describe('Magento_Ui/js/grid/sortBy', function () {
+
+ var sortByObj;
+
+ beforeEach(function () {
+ sortByObj = new sortBy({
+ options: []
+ });
+ });
+
+ describe('"initObservable" method', function () {
+ it('Check for defined ', function () {
+ expect(sortByObj.hasOwnProperty('initObservable')).toBeDefined();
+ });
+ it('Check method type', function () {
+ var type = typeof sortByObj.initObservable;
+
+ expect(type).toEqual('function');
+ });
+ it('Check returned value if method called without arguments', function () {
+ expect(sortByObj.initObservable()).toBeDefined();
+ });
+ it('Check returned value type if method called without arguments', function () {
+ var type = typeof sortByObj.initObservable();
+ expect(type).toEqual('object');
+ });
+ });
+
+ describe('"preparedOptions" method', function () {
+ it('Check for defined ', function () {
+ expect(sortByObj.hasOwnProperty('preparedOptions')).toBeDefined();
+ });
+ it('Check method type', function () {
+ var type = typeof sortByObj.preparedOptions;
+ expect(type).toEqual('function');
+ });
+
+ it('Check "options" array is empty if sortable is set false', function () {
+ var columns = [{
+ sortable: false,
+ label: 'magento',
+ index: 'test'
+ }],
+ expectedValue = [];
+ sortByObj.preparedOptions(columns);
+ expect(sortByObj.options).toEqual(expectedValue);
+ });
+
+ it('Check "options" array is set the correct value', function () {
+ var columns = [{
+ sortable: true,
+ label: 'magento',
+ index: 'test'
+ }],
+ expectedValue = [{
+ value: 'test',
+ label: 'magento'
+ }];
+ sortByObj.preparedOptions(columns);
+ expect(sortByObj.options).toEqual(expectedValue);
+ });
+
+ it('Check "isVisible" set true if column is sortable', function () {
+ var columns = [{
+ sortable: true,
+ label: 'magento',
+ index: 'test'
+ }];
+ spyOn(sortByObj, "isVisible").and.callFake(function () {
+ return true;
+ });
+ sortByObj.preparedOptions(columns);
+ expect(sortByObj.isVisible).toHaveBeenCalled();
+ expect(sortByObj.isVisible()).toBeTruthy();
+ });
+
+ it('Check "isVisible" set true if column is sortable', function () {
+ var columns = [{
+ sortable: true,
+ label: 'magento',
+ index: 'test'
+ }];
+ spyOn(sortByObj, "isVisible").and.callFake(function () {
+ return false;
+ });
+ sortByObj.preparedOptions(columns);
+ expect(sortByObj.isVisible).toHaveBeenCalled();
+ expect(sortByObj.isVisible()).toBeFalsy();
+ });
+ });
+ describe('"applyChanges" method', function () {
+ it('Check for defined ', function () {
+ expect(sortByObj.hasOwnProperty('applyChanges')).toBeDefined();
+ });
+ it('Check method type', function () {
+ var type = typeof sortByObj.applyChanges;
+ expect(type).toEqual('function');
+ });
+
+ it('Check "selectedOption" method has been called', function () {
+ spyOn(sortByObj, 'selectedOption');
+ sortByObj.applyChanges();
+ expect(sortByObj.selectedOption).toHaveBeenCalled();
+ });
+
+ it('Check "applied" method has been called', function () {
+ spyOn(sortByObj, 'applied');
+ sortByObj.applyChanges();
+ expect(sortByObj.applied).toHaveBeenCalled();
+ });
+ });
+ });
+});