Repository: mirumee/saleor-dashboard
Pull request #173 (2019-09-13 14:48:15)

Add sorting to product list
I want to merge this change because it adds sorting prototype to product list.

### Screenshots
<img width="1680" alt="Screenshot 2019-09-13 at 16 47 49" src="https://user-images.githubusercontent.com/6833443/64871779-3f537680-d646-11e9-9391-ef526cb00cd8.png">


### Pull Request Checklist

<!-- Please keep this section. It will make maintainer's life easier. -->

1. [ ] All visible strings are translated with proper context.
1. [ ] All data-formatting is locale-aware (dates, numbers, and so on).
1. [ ] Translated strings are extracted to `.pot` file.
1. [ ] Number of API calls is optimized.
1. [ ] The changes are tested.
1. [ ] Type definitions are up to date.
1. [ ] Changes are mentioned in the changelog.


codecov[bot]: # [Codecov](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173?src=pr&el=h1) Report
> Merging [#173](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173?src=pr&el=desc) into [master](https://codecov.io/gh/mirumee/saleor-dashboard/commit/9503c8887eb65792f31d59daef0c65208e118a70?src=pr&el=desc) will **decrease** coverage by `0.19%`.
> The diff coverage is `54%`.

[![Impacted file tree graph](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/graphs/tree.svg?width=650&token=GuvRMxAgM1&height=150&src=pr)](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##           master     #173     +/-   ##
=========================================
- Coverage   73.74%   73.54%   -0.2%     
=========================================
  Files         528      532      +4     
  Lines        5328     5375     +47     
  Branches      911      926     +15     
=========================================
+ Hits         3929     3953     +24     
- Misses       1254     1270     +16     
- Partials      145      152      +7
```


| [Impacted Files](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...cts/components/ProductListPage/ProductListPage.tsx](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL3Byb2R1Y3RzL2NvbXBvbmVudHMvUHJvZHVjdExpc3RQYWdlL1Byb2R1Y3RMaXN0UGFnZS50c3g=) | `90.9% <ø> (ø)` | :arrow_up: |
| [src/products/urls.ts](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL3Byb2R1Y3RzL3VybHMudHM=) | `61.9% <ø> (ø)` | :arrow_up: |
| [src/storybook/stories/products/ProductListPage.tsx](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL3N0b3J5Ym9vay9zdG9yaWVzL3Byb2R1Y3RzL1Byb2R1Y3RMaXN0UGFnZS50c3g=) | `100% <ø> (ø)` | :arrow_up: |
| [src/misc.ts](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL21pc2MudHM=) | `56.97% <0%> (-0.68%)` | :arrow_down: |
| [src/utils/columns/DisplayColumn.tsx](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL3V0aWxzL2NvbHVtbnMvRGlzcGxheUNvbHVtbi50c3g=) | `100% <100%> (ø)` | :arrow_up: |
| [...rc/products/components/ProductList/ProductList.tsx](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL3Byb2R1Y3RzL2NvbXBvbmVudHMvUHJvZHVjdExpc3QvUHJvZHVjdExpc3QudHN4) | `52.17% <11.11%> (-11.93%)` | :arrow_down: |
| [src/utils/sort.ts](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL3V0aWxzL3NvcnQudHM=) | `20% <20%> (ø)` | |
| [src/fixtures.ts](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL2ZpeHR1cmVzLnRz) | `29.72% <50%> (+1.15%)` | :arrow_up: |
| [...onents/TableCellHeader/TableCellHeader.stories.tsx](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL2NvbXBvbmVudHMvVGFibGVDZWxsSGVhZGVyL1RhYmxlQ2VsbEhlYWRlci5zdG9yaWVzLnRzeA==) | `61.9% <61.9%> (ø)` | |
| [src/components/TableCellHeader/TableCellHeader.tsx](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree#diff-c3JjL2NvbXBvbmVudHMvVGFibGVDZWxsSGVhZGVyL1RhYmxlQ2VsbEhlYWRlci50c3g=) | `88.88% <88.88%> (ø)` | |
| ... and [4 more](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173?src=pr&el=footer). Last update [9503c88...4a18820](https://codecov.io/gh/mirumee/saleor-dashboard/pull/173?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

dominik-zeglen: After speaking to @maarcingebala we agreed to keep sorting by name as a default sort field.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b0ec0154..727424e47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,3 +22,4 @@ All notable, unreleased changes to this project will be documented in this file.
 - Fix en locale matching - #165 by @dominik-zeglen
 - Implement the Credential Management API - #158 by @patrys
 - Add search bars - #172 by @dominik-zeglen
+- Add sorting to product list - #173 by @dominik-zeglen
diff --git a/locale/messages.pot b/locale/messages.pot
index a054058f8..ab156d25a 100644
--- a/locale/messages.pot
+++ b/locale/messages.pot
@@ -1,6 +1,6 @@
 msgid ""
 msgstr ""
-"POT-Creation-Date: 2019-09-12T15:12:49.543Z\n"
+"POT-Creation-Date: 2019-09-16T15:25:09.340Z\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "MIME-Version: 1.0\n"
@@ -131,22 +131,6 @@ msgctxt "create new menu item, header"
 msgid "Add Item"
 msgstr ""
 
-#: build/locale/src/navigation/components/MenuCreateDialog/MenuCreateDialog.json
-#. [menuCreateDialogHeader] - dialog header
-#. defaultMessage is:
-#. Add Menu
-msgctxt "dialog header"
-msgid "Add Menu"
-msgstr ""
-
-#: build/locale/src/navigation/components/MenuListPage/MenuListPage.json
-#. [menuListPageAddMenu] - button
-#. defaultMessage is:
-#. Add Menu
-msgctxt "button"
-msgid "Add Menu"
-msgstr ""
-
 #: build/locale/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.json
 #. [src.siteSettings.components.SiteSettingsKeyDialog.1238948746] - dialog header
 #. defaultMessage is:
@@ -155,14 +139,6 @@ msgctxt "dialog header"
 msgid "Add New Authorization Key"
 msgstr ""
 
-#: build/locale/src/pages/components/PageDetailsPage/PageDetailsPage.json
-#. [src.pages.components.PageDetailsPage.755314116] - page header
-#. defaultMessage is:
-#. Add Page
-msgctxt "page header"
-msgid "Add Page"
-msgstr ""
-
 #: build/locale/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.json
 #. [src.shipping.components.ShippingZoneRateDialog.2892088870] - dialog header
 #. defaultMessage is:
@@ -171,38 +147,6 @@ msgctxt "dialog header"
 msgid "Add Price Rate"
 msgstr ""
 
-#: build/locale/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.json
-#. [src.orders.components.OrderProductAddDialog.2775402904] - dialog header
-#. defaultMessage is:
-#. Add Product
-msgctxt "dialog header"
-msgid "Add Product"
-msgstr ""
-
-#: build/locale/src/products/components/ProductListPage/ProductListPage.json
-#. [src.products.components.ProductListPage.2775402904] - button
-#. defaultMessage is:
-#. Add Product
-msgctxt "button"
-msgid "Add Product"
-msgstr ""
-
-#: build/locale/src/discounts/components/SaleListPage/SaleListPage.json
-#. [src.discounts.components.SaleListPage.1742771332] - button
-#. defaultMessage is:
-#. Add Sale
-msgctxt "button"
-msgid "Add Sale"
-msgstr ""
-
-#: build/locale/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.json
-#. [src.staff.components.StaffAddMemberDialog.3490059488] - dialog header
-#. defaultMessage is:
-#. Add Staff Member
-msgctxt "dialog header"
-msgid "Add Staff Member"
-msgstr ""
-
 #: build/locale/src/attributes/components/AttributeValueEditDialog/AttributeValueEditDialog.json
 #. [src.attributes.components.AttributeValueEditDialog.1841790893] - add attribute value
 #. defaultMessage is:
@@ -211,14 +155,6 @@ msgctxt "add attribute value"
 msgid "Add Value"
 msgstr ""
 
-#: build/locale/src/products/views/ProductVariantCreate.json
-#. [src.products.views.2178905289] - header
-#. defaultMessage is:
-#. Add Variant
-msgctxt "header"
-msgid "Add Variant"
-msgstr ""
-
 #: build/locale/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.json
 #. [src.shipping.components.ShippingZoneRateDialog.1397795758] - add weight based shipping method, dialog header
 #. defaultMessage is:
@@ -235,14 +171,6 @@ msgctxt "button"
 msgid "Add address"
 msgstr ""
 
-#: build/locale/src/attributes/components/AttributeListPage/AttributeListPage.json
-#. [src.attributes.components.AttributeListPage.350498821] - button
-#. defaultMessage is:
-#. Add attribute
-msgctxt "button"
-msgid "Add attribute"
-msgstr ""
-
 #: build/locale/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.json
 #. [src.siteSettings.components.SiteSettingsKeyDialog.50561933] - button
 #. defaultMessage is:
@@ -251,14 +179,6 @@ msgctxt "button"
 msgid "Add authentication"
 msgstr ""
 
-#: build/locale/src/categories/components/CategoryListPage/CategoryListPage.json
-#. [src.categories.components.CategoryListPage.228151782] - button
-#. defaultMessage is:
-#. Add category
-msgctxt "button"
-msgid "Add category"
-msgstr ""
-
 #: build/locale/src/collections/components/CollectionCreatePage/CollectionCreatePage.json
 #. [src.collections.components.CollectionCreatePage.3958681866] - page header
 #. defaultMessage is:
@@ -267,14 +187,6 @@ msgctxt "page header"
 msgid "Add collection"
 msgstr ""
 
-#: build/locale/src/collections/components/CollectionListPage/CollectionListPage.json
-#. [src.collections.components.CollectionListPage.3958681866] - button
-#. defaultMessage is:
-#. Add collection
-msgctxt "button"
-msgid "Add collection"
-msgstr ""
-
 #: build/locale/src/customers/components/CustomerCreatePage/CustomerCreatePage.json
 #. [src.customers.components.CustomerCreatePage.1934221653] - page header
 #. defaultMessage is:
@@ -307,14 +219,6 @@ msgctxt "button"
 msgid "Add key"
 msgstr ""
 
-#: build/locale/src/navigation/components/MenuItems/MenuItems.json
-#. [menuItemsAddItem] - add new menu item
-#. defaultMessage is:
-#. Add new item
-msgctxt "add new menu item"
-msgid "Add new item"
-msgstr ""
-
 #: build/locale/src/navigation/components/MenuItems/MenuItems.json
 #. [menuItemsPlaceholder]
 #. defaultMessage is:
@@ -347,14 +251,6 @@ msgctxt "button"
 msgid "Add or Edit Link"
 msgstr ""
 
-#: build/locale/src/pages/components/PageListPage/PageListPage.json
-#. [src.pages.components.PageListPage.1767905232] - button
-#. defaultMessage is:
-#. Add page
-msgctxt "button"
-msgid "Add page"
-msgstr ""
-
 #: build/locale/src/categories/components/CategoryProducts/CategoryProducts.json
 #. [src.categories.components.CategoryProducts.3554578821] - button
 #. defaultMessage is:
@@ -363,14 +259,6 @@ msgctxt "button"
 msgid "Add product"
 msgstr ""
 
-#: build/locale/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.json
-#. [src.productTypes.components.ProductTypeListPage.889729490] - button
-#. defaultMessage is:
-#. Add product type
-msgctxt "button"
-msgid "Add product type"
-msgstr ""
-
 #: build/locale/src/orders/components/OrderDraftDetails/OrderDraftDetails.json
 #. [src.orders.components.OrderDraftDetails.2528459381] - button
 #. defaultMessage is:
@@ -379,14 +267,6 @@ msgctxt "button"
 msgid "Add products"
 msgstr ""
 
-#: build/locale/src/shipping/components/ShippingZoneRates/ShippingZoneRates.json
-#. [src.shipping.components.ShippingZoneRates.352297149] - button
-#. defaultMessage is:
-#. Add rate
-msgctxt "button"
-msgid "Add rate"
-msgstr ""
-
 #: build/locale/src/categories/components/CategoryCreatePage/CategoryCreatePage.json
 #. [src.categories.components.CategoryCreatePage.2563994280]
 #. defaultMessage is:
@@ -439,30 +319,6 @@ msgctxt "button"
 msgid "Add shipping carrier"
 msgstr ""
 
-#: build/locale/src/shipping/components/ShippingZonesList/ShippingZonesList.json
-#. [src.shipping.components.ShippingZonesList.2474410767] - button
-#. defaultMessage is:
-#. Add shipping zone
-msgctxt "button"
-msgid "Add shipping zone"
-msgstr ""
-
-#: build/locale/src/staff/components/StaffListPage/StaffListPage.json
-#. [src.staff.components.StaffListPage.802625341] - button
-#. defaultMessage is:
-#. Add staff member
-msgctxt "button"
-msgid "Add staff member"
-msgstr ""
-
-#: build/locale/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.json
-#. [src.categories.components.CategoryUpdatePage.435697837] - button
-#. defaultMessage is:
-#. Add subcategory
-msgctxt "button"
-msgid "Add subcategory"
-msgstr ""
-
 #: build/locale/src/attributes/components/AttributeProperties/AttributeProperties.json
 #. [src.attributes.components.AttributeProperties.673770329] - add attribute as column in product list table
 #. defaultMessage is:
@@ -487,34 +343,14 @@ msgctxt "dialog header"
 msgid "Add tracking code"
 msgstr ""
 
-#: build/locale/src/attributes/components/AttributeValues/AttributeValues.json
-#. [src.attributes.components.AttributeValues.254756045] - add attribute value button
-#. defaultMessage is:
-#. Add value
-msgctxt "add attribute value button"
-msgid "Add value"
-msgstr ""
-
 #: build/locale/src/products/components/ProductVariantNavigation/ProductVariantNavigation.json
 #. [src.products.components.ProductVariantNavigation.2845381934] - button
 #. defaultMessage is:
 #. Add variant
-#: build/locale/src/products/components/ProductVariants/ProductVariants.json
-#. [src.products.components.ProductVariants.2845381934] - button
-#. defaultMessage is:
-#. Add variant
 msgctxt "button"
 msgid "Add variant"
 msgstr ""
 
-#: build/locale/src/discounts/components/VoucherListPage/VoucherListPage.json
-#. [src.discounts.components.VoucherListPage.1536809153] - button
-#. defaultMessage is:
-#. Add voucher
-msgctxt "button"
-msgid "Add voucher"
-msgstr ""
-
 #: build/locale/src/attributes/views/AttributeDetails/AttributeDetails.json
 #. [src.attributes.views.AttributeDetails.634268988] - added new attribute value
 #. defaultMessage is:
@@ -571,14 +407,6 @@ msgctxt "description"
 msgid "Address line 2"
 msgstr ""
 
-#: build/locale/src/customers/components/CustomerAddress/CustomerAddress.json
-#. [src.customers.components.CustomerAddress.1320082647] - addres card header
-#. defaultMessage is:
-#. Address {addressNumber}
-msgctxt "addres card header"
-msgid "Address {addressNumber}"
-msgstr ""
-
 #: build/locale/src/attributes/components/AttributeValues/AttributeValues.json
 #. [src.attributes.components.AttributeValues.1565474525] - attribute values list: slug column header
 #. defaultMessage is:
@@ -827,6 +655,14 @@ msgctxt "description"
 msgid "Are you sure you want to delete collection's image?"
 msgstr ""
 
+#: build/locale/src/orders/components/OrderDraftCancelDialog/OrderDraftCancelDialog.json
+#. [src.orders.components.OrderDraftCancelDialog.36681973]
+#. defaultMessage is:
+#. Are you sure you want to delete draft #{number}?
+msgctxt "description"
+msgid "Are you sure you want to delete draft #{number}?"
+msgstr ""
+
 #: build/locale/src/navigation/views/MenuDetails/index.json
 #. [menuDetailsDeleteMenuContent]
 #. defaultMessage is:
@@ -843,6 +679,14 @@ msgctxt "description"
 msgid "Are you sure you want to delete this address from users address book?"
 msgstr ""
 
+#: build/locale/src/products/views/ProductImage.json
+#. [src.products.views.2880386427]
+#. defaultMessage is:
+#. Are you sure you want to delete this image?
+msgctxt "description"
+msgid "Are you sure you want to delete this image?"
+msgstr ""
+
 #: build/locale/src/attributes/components/AttributeDeleteDialog/AttributeDeleteDialog.json
 #. [src.attributes.components.AttributeDeleteDialog.3738429348] - dialog content
 #. defaultMessage is:
@@ -995,6 +839,22 @@ msgctxt "dialog content"
 msgid "Are you sure you want to delete {counter,plural,one{this voucher} other{{displayQuantity} vouchers}}?"
 msgstr ""
 
+#: build/locale/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.json
+#. [src.shipping.views.ShippingZoneDetails.131963671] - unassign country
+#. defaultMessage is:
+#. Are you sure you want to delete {countryName} from this shipping zone?
+msgctxt "unassign country"
+msgid "Are you sure you want to delete {countryName} from this shipping zone?"
+msgstr ""
+
+#: build/locale/src/staff/views/StaffDetails.json
+#. [src.staff.views.8878988]
+#. defaultMessage is:
+#. Are you sure you want to delete {email} from staff members?
+msgctxt "description"
+msgid "Are you sure you want to delete {email} from staff members?"
+msgstr ""
+
 #: build/locale/src/customers/views/CustomerDetails.json
 #. [src.customers.views.1927691511] - delete customer, dialog content
 #. defaultMessage is:
@@ -1131,30 +991,6 @@ msgctxt "dialog content"
 msgid "Are you sure you want to publish {counter,plural,one{this product} other{{displayQuantity} products}}?"
 msgstr ""
 
-#: build/locale/src/orders/components/OrderDraftCancelDialog/OrderDraftCancelDialog.json
-#. [src.orders.components.OrderDraftCancelDialog.3199827590]
-#. defaultMessage is:
-#. Are you sure you want to remove draft #{number}?
-msgctxt "description"
-msgid "Are you sure you want to remove draft #{number}?"
-msgstr ""
-
-#: build/locale/src/products/views/ProductImage.json
-#. [src.products.views.672103788]
-#. defaultMessage is:
-#. Are you sure you want to remove this image?
-msgctxt "description"
-msgid "Are you sure you want to remove this image?"
-msgstr ""
-
-#: build/locale/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.json
-#. [src.shipping.views.ShippingZoneDetails.1790261672] - unassign country
-#. defaultMessage is:
-#. Are you sure you want to remove {countryName} from this shipping zone?
-msgctxt "unassign country"
-msgid "Are you sure you want to remove {countryName} from this shipping zone?"
-msgstr ""
-
 #: build/locale/src/staff/views/StaffDetails.json
 #. [src.staff.views.3945766678]
 #. defaultMessage is:
@@ -1163,14 +999,6 @@ msgctxt "description"
 msgid "Are you sure you want to remove {email} avatar?"
 msgstr ""
 
-#: build/locale/src/staff/views/StaffDetails.json
-#. [src.staff.views.2728048444]
-#. defaultMessage is:
-#. Are you sure you want to remove {email} from staff members?
-msgctxt "description"
-msgid "Are you sure you want to remove {email} from staff members?"
-msgstr ""
-
 #: build/locale/src/productTypes/components/ProductTypeAttributeUnassignDialog/ProductTypeAttributeUnassignDialog.json
 #. [src.productTypes.components.ProductTypeAttributeUnassignDialog.722498450]
 #. defaultMessage is:
@@ -1383,6 +1211,14 @@ msgctxt "button"
 msgid "Assign products"
 msgstr ""
 
+#: build/locale/src/attributes/components/AttributeValues/AttributeValues.json
+#. [src.attributes.components.AttributeValues.351422234] - assign attribute value button
+#. defaultMessage is:
+#. Assign value
+msgctxt "assign attribute value button"
+msgid "Assign value"
+msgstr ""
+
 #: build/locale/src/attributes/components/AttributeDetails/AttributeDetails.json
 #. [src.attributes.components.AttributeDetails.3605174225] - attribute's slug short code label
 #. defaultMessage is:
@@ -1459,6 +1295,14 @@ msgctxt "product attributes, section header"
 msgid "Attributes"
 msgstr ""
 
+#: build/locale/src/configuration/index.json
+#. [src.configuration.2326418019]
+#. defaultMessage is:
+#. Attributes and Product Typess
+msgctxt "description"
+msgid "Attributes and Product Typess"
+msgstr ""
+
 #: build/locale/src/attributes/views/AttributeList/AttributeList.json
 #. [src.attributes.views.AttributeList.3218248395] - deleted multiple attributes
 #. defaultMessage is:
@@ -2227,6 +2071,22 @@ msgctxt "description"
 msgid "Country area"
 msgstr ""
 
+#: build/locale/src/navigation/components/MenuCreateDialog/MenuCreateDialog.json
+#. [menuCreateDialogHeader] - dialog header
+#. defaultMessage is:
+#. Create Menu
+msgctxt "dialog header"
+msgid "Create Menu"
+msgstr ""
+
+#: build/locale/src/navigation/components/MenuListPage/MenuListPage.json
+#. [menuListPageAddMenu] - button
+#. defaultMessage is:
+#. Create Menu
+msgctxt "button"
+msgid "Create Menu"
+msgstr ""
+
 #: build/locale/src/attributes/components/AttributePage/AttributePage.json
 #. [src.attributes.components.AttributePage.2855501559] - page title
 #. defaultMessage is:
@@ -2251,6 +2111,30 @@ msgctxt "header"
 msgid "Create New Shipping Zone"
 msgstr ""
 
+#: build/locale/src/pages/components/PageDetailsPage/PageDetailsPage.json
+#. [src.pages.components.PageDetailsPage.1068617485] - page header
+#. defaultMessage is:
+#. Create Page
+msgctxt "page header"
+msgid "Create Page"
+msgstr ""
+
+#: build/locale/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.json
+#. [src.orders.components.OrderProductAddDialog.1542417144] - dialog header
+#. defaultMessage is:
+#. Create Product
+msgctxt "dialog header"
+msgid "Create Product"
+msgstr ""
+
+#: build/locale/src/products/components/ProductListPage/ProductListPage.json
+#. [src.products.components.ProductListPage.1542417144] - button
+#. defaultMessage is:
+#. Create Product
+msgctxt "button"
+msgid "Create Product"
+msgstr ""
+
 #: build/locale/src/products/views/ProductCreate.json
 #. [src.products.views.1542417144] - window title
 #. defaultMessage is:
@@ -2283,11 +2167,19 @@ msgctxt "page header"
 msgid "Create Sale"
 msgstr ""
 
+#: build/locale/src/discounts/components/SaleListPage/SaleListPage.json
+#. [src.discounts.components.SaleListPage.3866518732] - button
+#. defaultMessage is:
+#. Create Sale
+msgctxt "button"
+msgid "Create Sale"
+msgstr ""
+
 #: build/locale/src/products/views/ProductVariantCreate.json
-#. [src.products.views.2362587265] - window title
+#. [src.products.views.2362587265] - header
 #. defaultMessage is:
 #. Create Variant
-msgctxt "window title"
+msgctxt "header"
 msgid "Create Variant"
 msgstr ""
 
@@ -2299,6 +2191,22 @@ msgctxt "page header"
 msgid "Create Voucher"
 msgstr ""
 
+#: build/locale/src/attributes/components/AttributeListPage/AttributeListPage.json
+#. [src.attributes.components.AttributeListPage.3824684885] - button
+#. defaultMessage is:
+#. Create attribute
+msgctxt "button"
+msgid "Create attribute"
+msgstr ""
+
+#: build/locale/src/categories/components/CategoryListPage/CategoryListPage.json
+#. [src.categories.components.CategoryListPage.1140231710] - button
+#. defaultMessage is:
+#. Create category
+msgctxt "button"
+msgid "Create category"
+msgstr ""
+
 #: build/locale/src/categories/views/CategoryCreate.json
 #. [src.categories.views.1140231710] - window title
 #. defaultMessage is:
@@ -2307,6 +2215,14 @@ msgctxt "window title"
 msgid "Create category"
 msgstr ""
 
+#: build/locale/src/collections/components/CollectionListPage/CollectionListPage.json
+#. [src.collections.components.CollectionListPage.686910896] - button
+#. defaultMessage is:
+#. Create collection
+msgctxt "button"
+msgid "Create collection"
+msgstr ""
+
 #: build/locale/src/collections/views/CollectionCreate.json
 #. [src.collections.views.686910896] - window title
 #. defaultMessage is:
@@ -2323,6 +2239,14 @@ msgctxt "window title"
 msgid "Create customer"
 msgstr ""
 
+#: build/locale/src/navigation/components/MenuItems/MenuItems.json
+#. [menuItemsAddItem] - add new menu item
+#. defaultMessage is:
+#. Create new item
+msgctxt "add new menu item"
+msgid "Create new item"
+msgstr ""
+
 #: build/locale/src/orders/components/OrderDraftListPage/OrderDraftListPage.json
 #. [src.orders.components.OrderDraftListPage.2826235371] - button
 #. defaultMessage is:
@@ -2335,6 +2259,14 @@ msgctxt "button"
 msgid "Create order"
 msgstr ""
 
+#: build/locale/src/pages/components/PageListPage/PageListPage.json
+#. [src.pages.components.PageListPage.3785394515] - button
+#. defaultMessage is:
+#. Create page
+msgctxt "button"
+msgid "Create page"
+msgstr ""
+
 #: build/locale/src/pages/views/PageCreate.json
 #. [src.pages.views.3785394515] - header
 #. defaultMessage is:
@@ -2347,10 +2279,54 @@ msgstr ""
 #. [src.shipping.components.ShippingZoneRateDialog.16061680] - button
 #. defaultMessage is:
 #. Create rate
+#: build/locale/src/shipping/components/ShippingZoneRates/ShippingZoneRates.json
+#. [src.shipping.components.ShippingZoneRates.16061680] - button
+#. defaultMessage is:
+#. Create rate
 msgctxt "button"
 msgid "Create rate"
 msgstr ""
 
+#: build/locale/src/shipping/components/ShippingZonesList/ShippingZonesList.json
+#. [src.shipping.components.ShippingZonesList.2673544109] - button
+#. defaultMessage is:
+#. Create shipping zone
+msgctxt "button"
+msgid "Create shipping zone"
+msgstr ""
+
+#: build/locale/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.json
+#. [src.categories.components.CategoryUpdatePage.770296100] - button
+#. defaultMessage is:
+#. Create subcategory
+msgctxt "button"
+msgid "Create subcategory"
+msgstr ""
+
+#: build/locale/src/products/components/ProductVariants/ProductVariants.json
+#. [src.products.components.ProductVariants.3989383405] - button
+#. defaultMessage is:
+#. Create variant
+msgctxt "button"
+msgid "Create variant"
+msgstr ""
+
+#: build/locale/src/products/views/ProductVariantCreate.json
+#. [src.products.views.3989383405] - window title
+#. defaultMessage is:
+#. Create variant
+msgctxt "window title"
+msgid "Create variant"
+msgstr ""
+
+#: build/locale/src/discounts/components/VoucherListPage/VoucherListPage.json
+#. [src.discounts.components.VoucherListPage.614836274] - button
+#. defaultMessage is:
+#. Create voucher
+msgctxt "button"
+msgid "Create voucher"
+msgstr ""
+
 #: build/locale/src/collections/views/CollectionCreate.json
 #. [src.collections.views.1597339737]
 #. defaultMessage is:
@@ -2647,6 +2623,14 @@ msgctxt "dialog title"
 msgid "Delete Collection"
 msgstr ""
 
+#: build/locale/src/products/views/ProductImage.json
+#. [src.products.views.1731766393] - dialog header
+#. defaultMessage is:
+#. Delete Image
+msgctxt "dialog header"
+msgid "Delete Image"
+msgstr ""
+
 #: build/locale/src/orders/views/OrderDraftList/OrderDraftList.json
 #. [src.orders.views.OrderDraftList.1161115149] - dialog header
 #. defaultMessage is:
@@ -2887,6 +2871,22 @@ msgctxt "dialog header"
 msgid "Delete customers"
 msgstr ""
 
+#: build/locale/src/orders/components/OrderDraftCancelDialog/OrderDraftCancelDialog.json
+#. [src.orders.components.OrderDraftCancelDialog.632633254] - dialog header
+#. defaultMessage is:
+#. Delete draft order
+msgctxt "dialog header"
+msgid "Delete draft order"
+msgstr ""
+
+#: build/locale/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.json
+#. [src.shipping.views.ShippingZoneDetails.1947090060] - unassign country, dialog header
+#. defaultMessage is:
+#. Delete from shipping zone
+msgctxt "unassign country, dialog header"
+msgid "Delete from shipping zone"
+msgstr ""
+
 #: build/locale/src/collections/views/CollectionDetails.json
 #. [src.collections.views.942133001] - dialog title
 #. defaultMessage is:
@@ -2947,6 +2947,14 @@ msgctxt "description"
 msgid "Deleted collection"
 msgstr ""
 
+#: build/locale/src/orders/views/OrderDraftList/OrderDraftList.json
+#. [src.orders.views.OrderDraftList.1648805446]
+#. defaultMessage is:
+#. Deleted draft orders
+msgctxt "description"
+msgid "Deleted draft orders"
+msgstr ""
+
 #: build/locale/src/navigation/views/MenuList.json
 #. [menuListDeletedMenu]
 #. defaultMessage is:
@@ -3688,11 +3696,11 @@ msgid "If empty, the preview shows what will be autogenerated."
 msgstr ""
 
 #: build/locale/src/attributes/components/AttributeProperties/AttributeProperties.json
-#. [src.attributes.components.AttributeProperties.2519239682]
+#. [src.attributes.components.AttributeProperties.4048785456]
 #. defaultMessage is:
-#. If enable this attribute can be used as a column in product table.
+#. If enabled this attribute can be used as a column in product table.
 msgctxt "description"
-msgid "If enable this attribute can be used as a column in product table."
+msgid "If enabled this attribute can be used as a column in product table."
 msgstr ""
 
 #: build/locale/src/attributes/components/AttributeProperties/AttributeProperties.json
@@ -3787,6 +3795,22 @@ msgctxt "product variant stock"
 msgid "Inventory"
 msgstr ""
 
+#: build/locale/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.json
+#. [src.staff.components.StaffAddMemberDialog.1481503299] - dialog header
+#. defaultMessage is:
+#. Invite Staff Member
+msgctxt "dialog header"
+msgid "Invite Staff Member"
+msgstr ""
+
+#: build/locale/src/staff/components/StaffListPage/StaffListPage.json
+#. [src.staff.components.StaffListPage.797461521] - button
+#. defaultMessage is:
+#. Invite staff member
+msgctxt "button"
+msgid "Invite staff member"
+msgstr ""
+
 #: build/locale/src/productTypes/components/ProductTypeShipping/ProductTypeShipping.json
 #. [src.productTypes.components.ProductTypeShipping.2143413921] - switch button
 #. defaultMessage is:
@@ -4131,6 +4155,14 @@ msgctxt "voucher requirement"
 msgid "Minimum quantity of items"
 msgstr ""
 
+#: build/locale/src/configuration/index.json
+#. [src.configuration.1233229030]
+#. defaultMessage is:
+#. Miscellaneous
+msgctxt "description"
+msgid "Miscellaneous"
+msgstr ""
+
 #: build/locale/src/attributes/components/AttributeDetails/AttributeDetails.json
 #. [src.attributes.components.AttributeDetails.3334509011] - product attribute type
 #. defaultMessage is:
@@ -5631,6 +5663,14 @@ msgctxt "description"
 msgid "Product Name"
 msgstr ""
 
+#: build/locale/src/configuration/index.json
+#. [src.configuration.3655543906]
+#. defaultMessage is:
+#. Product Settings
+msgctxt "description"
+msgid "Product Settings"
+msgstr ""
+
 #: build/locale/src/discounts/components/DiscountProducts/DiscountProducts.json
 #. [src.discounts.components.DiscountProducts.4257289053]
 #. defaultMessage is:
@@ -5784,11 +5824,11 @@ msgid "Products were added to draft order"
 msgstr ""
 
 #: build/locale/src/orders/components/OrderHistory/OrderHistory.json
-#. [src.orders.components.OrderHistory.2574162126] - order history message
+#. [src.orders.components.OrderHistory.4067959693] - order history message
 #. defaultMessage is:
-#. Products were removed from draft order
+#. Products were deleted from draft order
 msgctxt "order history message"
-msgid "Products were removed from draft order"
+msgid "Products were deleted from draft order"
 msgstr ""
 
 #: build/locale/src/intl.json
@@ -6083,14 +6123,6 @@ msgctxt "switch button"
 msgid "Release all stock allocated to this order"
 msgstr ""
 
-#: build/locale/src/auth/components/LoginPage/LoginPage.json
-#. [src.auth.components.LoginPage.3182284913] - login
-#. defaultMessage is:
-#. Remember me
-msgctxt "login"
-msgid "Remember me"
-msgstr ""
-
 #: build/locale/src/categories/views/CategoryDetails.json
 #. [src.categories.views.3488150607]
 #. defaultMessage is:
@@ -6111,38 +6143,6 @@ msgctxt "button"
 msgid "Remove"
 msgstr ""
 
-#: build/locale/src/products/views/ProductImage.json
-#. [src.products.views.490753666] - dialog header
-#. defaultMessage is:
-#. Remove Image
-msgctxt "dialog header"
-msgid "Remove Image"
-msgstr ""
-
-#: build/locale/src/orders/components/OrderDraftCancelDialog/OrderDraftCancelDialog.json
-#. [src.orders.components.OrderDraftCancelDialog.500287069] - dialog header
-#. defaultMessage is:
-#. Remove draft order
-msgctxt "dialog header"
-msgid "Remove draft order"
-msgstr ""
-
-#: build/locale/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.json
-#. [src.shipping.views.ShippingZoneDetails.254167659] - unassign country, dialog header
-#. defaultMessage is:
-#. Remove from shipping zone
-msgctxt "unassign country, dialog header"
-msgid "Remove from shipping zone"
-msgstr ""
-
-#: build/locale/src/orders/views/OrderDraftList/OrderDraftList.json
-#. [src.orders.views.OrderDraftList.3880993240]
-#. defaultMessage is:
-#. Removed draft orders
-msgctxt "description"
-msgid "Removed draft orders"
-msgstr ""
-
 #: build/locale/src/pages/views/PageDetails.json
 #. [src.pages.views.1457312643]
 #. defaultMessage is:
@@ -6779,6 +6779,14 @@ msgctxt "description"
 msgid "Set plugin as Active"
 msgstr ""
 
+#: build/locale/src/components/VisibilityCard/VisibilityCard.json
+#. [src.components.VisibilityCard.292404896]
+#. defaultMessage is:
+#. Set publication date
+msgctxt "description"
+msgid "Set publication date"
+msgstr ""
+
 #: build/locale/src/discounts/translations.json
 #. [src.discounts.shipment] - voucher discount
 #. defaultMessage is:
@@ -7039,6 +7047,14 @@ msgctxt "staff section name"
 msgid "Staff Members"
 msgstr ""
 
+#: build/locale/src/configuration/index.json
+#. [src.configuration.3140151600]
+#. defaultMessage is:
+#. Staff Settings
+msgctxt "description"
+msgid "Staff Settings"
+msgstr ""
+
 #: build/locale/src/misc.json
 #. [src.standard] - tax rate
 #. defaultMessage is:
@@ -8491,6 +8507,22 @@ msgctxt "description"
 msgid "ZIP / Postal code"
 msgstr ""
 
+#: build/locale/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.json
+#. [src.productTypes.components.ProductTypeListPage.3479705616] - button
+#. defaultMessage is:
+#. create product type
+msgctxt "button"
+msgid "create product type"
+msgstr ""
+
+#: build/locale/src/staff/views/StaffDetails.json
+#. [src.staff.views.1308770978] - dialog header
+#. defaultMessage is:
+#. delete Staff User
+msgctxt "dialog header"
+msgid "delete Staff User"
+msgstr ""
+
 #: build/locale/src/orders/components/OrderPayment/OrderPayment.json
 #. [orderPaymentVATDoesNotApply] - vat not included in order price
 #. defaultMessage is:
@@ -8563,19 +8595,35 @@ msgctxt "product status is set as"
 msgid "is set as"
 msgstr ""
 
-#: build/locale/src/staff/views/StaffDetails.json
-#. [src.staff.views.2240444792] - dialog header
+#: build/locale/src/collections/components/CollectionCreatePage/CollectionCreatePage.json
+#. [src.collections.components.CollectionCreatePage.1815688500] - collection
 #. defaultMessage is:
-#. remove Staff User
-msgctxt "dialog header"
-msgid "remove Staff User"
+#. since {date}
+#: build/locale/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.json
+#. [src.collections.components.CollectionDetailsPage.1815688500] - collection
+#. defaultMessage is:
+#. since {date}
+msgctxt "collection"
+msgid "since {date}"
 msgstr ""
 
-#: build/locale/src/components/VisibilityCard/VisibilityCard.json
-#. [src.components.VisibilityCard.1815688500]
+#: build/locale/src/pages/components/PageDetailsPage/PageDetailsPage.json
+#. [src.pages.components.PageDetailsPage.1815688500] - page
 #. defaultMessage is:
 #. since {date}
-msgctxt "description"
+msgctxt "page"
+msgid "since {date}"
+msgstr ""
+
+#: build/locale/src/products/components/ProductCreatePage/ProductCreatePage.json
+#. [src.products.components.ProductCreatePage.1815688500] - product
+#. defaultMessage is:
+#. since {date}
+#: build/locale/src/products/components/ProductUpdatePage/ProductUpdatePage.json
+#. [src.products.components.ProductUpdatePage.1815688500] - product
+#. defaultMessage is:
+#. since {date}
+msgctxt "product"
 msgid "since {date}"
 msgstr ""
 
@@ -8603,11 +8651,35 @@ msgctxt "weight"
 msgid "to {value} {unit}"
 msgstr ""
 
-#: build/locale/src/components/VisibilityCard/VisibilityCard.json
-#. [src.components.VisibilityCard.2001551496]
+#: build/locale/src/collections/components/CollectionCreatePage/CollectionCreatePage.json
+#. [src.collections.components.CollectionCreatePage.2001551496] - collection
 #. defaultMessage is:
 #. will be visible from {date}
-msgctxt "description"
+#: build/locale/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.json
+#. [src.collections.components.CollectionDetailsPage.2001551496] - collection
+#. defaultMessage is:
+#. will be visible from {date}
+msgctxt "collection"
+msgid "will be visible from {date}"
+msgstr ""
+
+#: build/locale/src/pages/components/PageDetailsPage/PageDetailsPage.json
+#. [src.pages.components.PageDetailsPage.2001551496] - page
+#. defaultMessage is:
+#. will be visible from {date}
+msgctxt "page"
+msgid "will be visible from {date}"
+msgstr ""
+
+#: build/locale/src/products/components/ProductCreatePage/ProductCreatePage.json
+#. [src.products.components.ProductCreatePage.2001551496] - product
+#. defaultMessage is:
+#. will be visible from {date}
+#: build/locale/src/products/components/ProductUpdatePage/ProductUpdatePage.json
+#. [src.products.components.ProductUpdatePage.2001551496] - product
+#. defaultMessage is:
+#. will be visible from {date}
+msgctxt "product"
 msgid "will be visible from {date}"
 msgstr ""
 
diff --git a/schema.graphql b/schema.graphql
index 2329832e6..8c8d128f4 100644
--- a/schema.graphql
+++ b/schema.graphql
@@ -3303,7 +3303,7 @@ type ShippingZoneUpdate {
 type Shop {
   geolocalization: Geolocalization
   authorizationKeys: [AuthorizationKey]!
-  countries: [CountryDisplay]!
+  countries(languageCode: LanguageCodeEnum): [CountryDisplay]!
   currencies: [String]!
   defaultCurrency: String!
   defaultCountry: CountryDisplay
diff --git a/src/components/TableCellHeader/TableCellHeader.stories.tsx b/src/components/TableCellHeader/TableCellHeader.stories.tsx
new file mode 100644
index 000000000..f2d609822
--- /dev/null
+++ b/src/components/TableCellHeader/TableCellHeader.stories.tsx
@@ -0,0 +1,83 @@
+import Card from "@material-ui/core/Card";
+import Table from "@material-ui/core/Table";
+import TableBody from "@material-ui/core/TableBody";
+import TableCell from "@material-ui/core/TableCell";
+import TableHead from "@material-ui/core/TableHead";
+import TableRow from "@material-ui/core/TableRow";
+import { storiesOf } from "@storybook/react";
+import React from "react";
+
+import Decorator from "../../storybook/Decorator";
+import TableCellHeader, {
+  TableCellHeaderArrowDirection
+} from "./TableCellHeader";
+
+type Field = "name" | "type";
+interface StoryProps {
+  direction: TableCellHeaderArrowDirection;
+  field?: Field;
+  onHeaderClick?: (field: Field) => void;
+}
+
+const Story: React.FC<StoryProps> = ({
+  direction,
+  field = "name",
+  onHeaderClick = () => undefined
+}) => (
+  <Card style={{ margin: "auto", width: 400 }}>
+    <Table>
+      <TableHead>
+        <TableRow>
+          <TableCellHeader
+            arrowPosition="right"
+            direction={field === "name" ? direction : undefined}
+            onClick={() => onHeaderClick("name")}
+          >
+            Name
+          </TableCellHeader>
+          <TableCellHeader
+            direction={field === "type" ? direction : undefined}
+            onClick={() => onHeaderClick("type")}
+          >
+            Type
+          </TableCellHeader>
+        </TableRow>
+      </TableHead>
+      <TableBody>
+        <TableRow>
+          <TableCell>Apple Juice</TableCell>
+          <TableCell>Juice</TableCell>
+        </TableRow>
+      </TableBody>
+    </Table>
+  </Card>
+);
+const InteractiveStory: React.FC = () => {
+  const [direction, setDirection] = React.useState<
+    TableCellHeaderArrowDirection
+  >("asc");
+  const [field, setField] = React.useState<Field>("name");
+
+  const handleHeaderClick = (selectedField: Field) => {
+    if (field === selectedField) {
+      setDirection(direction === "asc" ? "desc" : "asc");
+    } else {
+      setField(selectedField);
+      setDirection("asc");
+    }
+  };
+
+  return (
+    <Story
+      direction={direction}
+      field={field}
+      onHeaderClick={handleHeaderClick}
+    />
+  );
+};
+
+storiesOf("Generics / Table header", module)
+  .addDecorator(Decorator)
+  .add("ascending", () => <Story direction="asc" />)
+  .add("descending", () => <Story direction="desc" />)
+  .add("interactive", () => <InteractiveStory />);
diff --git a/src/components/TableCellHeader/TableCellHeader.tsx b/src/components/TableCellHeader/TableCellHeader.tsx
new file mode 100644
index 000000000..4da908fae
--- /dev/null
+++ b/src/components/TableCellHeader/TableCellHeader.tsx
@@ -0,0 +1,97 @@
+import { Theme } from "@material-ui/core/styles";
+import TableCell, { TableCellProps } from "@material-ui/core/TableCell";
+import makeStyles from "@material-ui/styles/makeStyles";
+import classNames from "classnames";
+import React from "react";
+
+import ArrowSort from "../../icons/ArrowSort";
+
+const useStyles = makeStyles((theme: Theme) => ({
+  arrow: {
+    transition: theme.transitions.duration.short + "ms"
+  },
+  arrowLeft: {
+    marginLeft: -24
+  },
+  arrowUp: {
+    transform: "rotate(180deg)"
+  },
+  label: {
+    alignSelf: "center",
+    display: "inline-block"
+  },
+  labelContainer: {
+    "&:hover": {
+      color: theme.palette.text.primary
+    },
+    display: "flex",
+    height: 24
+  },
+  labelContainerActive: {
+    color: theme.palette.text.primary
+  },
+  labelContainerCenter: {
+    justifyContent: "center"
+  },
+  labelContainerRight: {
+    justifyContent: "flex-end"
+  },
+  root: {
+    cursor: "pointer"
+  }
+}));
+
+export type TableCellHeaderArrowDirection = "asc" | "desc";
+export type TableCellHeaderArrowPosition = "left" | "right";
+export interface TableCellHeaderProps extends TableCellProps {
+  arrowPosition?: TableCellHeaderArrowPosition;
+  direction?: TableCellHeaderArrowDirection;
+  textAlign?: "left" | "center" | "right";
+}
+
+const TableCellHeader: React.FC<TableCellHeaderProps> = props => {
+  const classes = useStyles(props);
+  const {
+    arrowPosition,
+    children,
+    className,
+    direction,
+    textAlign,
+    ...rest
+  } = props;
+
+  return (
+    <TableCell {...rest} className={classNames(classes.root, className)}>
+      <div
+        className={classNames(classes.labelContainer, {
+          [classes.labelContainerActive]: !!direction && !!arrowPosition,
+          [classes.labelContainerCenter]: textAlign === "center",
+          [classes.labelContainerRight]: textAlign === "right"
+        })}
+      >
+        {!!direction && arrowPosition === "left" && (
+          <ArrowSort
+            className={classNames(classes.arrow, classes.arrowLeft, {
+              [classes.arrowUp]: direction === "asc"
+            })}
+          />
+        )}
+        <div className={classes.label}>{children}</div>
+        {!!direction && arrowPosition === "right" && (
+          <ArrowSort
+            className={classNames(classes.arrow, {
+              [classes.arrowUp]: direction === "asc"
+            })}
+          />
+        )}
+      </div>
+    </TableCell>
+  );
+};
+
+TableCellHeader.displayName = "TableCellHeader";
+TableCellHeader.defaultProps = {
+  arrowPosition: "left",
+  textAlign: "left"
+};
+export default TableCellHeader;
diff --git a/src/components/TableCellHeader/index.ts b/src/components/TableCellHeader/index.ts
new file mode 100644
index 000000000..cfd465863
--- /dev/null
+++ b/src/components/TableCellHeader/index.ts
@@ -0,0 +1,2 @@
+export { default } from "./TableCellHeader";
+export * from "./TableCellHeader";
diff --git a/src/fixtures.ts b/src/fixtures.ts
index e0c4676db..d76e1d667 100644
--- a/src/fixtures.ts
+++ b/src/fixtures.ts
@@ -5,6 +5,7 @@ import {
   ListActions,
   PageListProps,
   SearchPageProps,
+  SortPage,
   TabPageProps
 } from "./types";
 
@@ -141,3 +142,10 @@ export const fetchMoreProps: FetchMoreProps = {
   loading: false,
   onFetchMore: () => undefined
 };
+
+export const sortPageProps: SortPage<string> = {
+  onSort: () => undefined,
+  sort: {
+    asc: true
+  }
+};
diff --git a/src/icons/Navigation.tsx b/src/icons/Navigation.tsx
index c0eb5e4c2..37056dad9 100644
--- a/src/icons/Navigation.tsx
+++ b/src/icons/Navigation.tsx
@@ -4,8 +4,8 @@ import React from "react";
 export const Navigation = createSvgIcon(
   <>
     <path
-      fill-rule="evenodd"
-      clip-rule="evenodd"
+      fillRule="evenodd"
+      clipRule="evenodd"
       d="M35.9075 11.3232C40.8772 19.931 37.928 30.9378 29.3202 35.9075C20.7123 40.8772 9.70553 37.928 4.7358 29.3201C-0.233933 20.7123 2.71533 9.70553 11.3232 4.7358C19.931 -0.233933 30.9378 2.71533 35.9075 11.3232ZM37.6393 10.3233C43.1612 19.8876 39.8842 32.1173 30.32 37.6393C20.7557 43.1612 8.52596 39.8842 3.00404 30.32C-2.51789 20.7557 0.759069 8.52596 10.3233 3.00404C19.8876 -2.51789 32.1173 0.759069 37.6393 10.3233ZM14.9402 14.5709L31.0335 9.60975L26.0724 25.7031L21.7789 21.4096C22.3117 20.6975 22.2544 19.6836 21.6072 19.0364C20.9599 18.3891 19.946 18.3319 19.2339 18.8647L14.9402 14.5709ZM19.0359 21.6077C18.3884 20.9602 18.3314 19.946 18.8646 19.2338L14.571 14.9402L9.6098 31.0335L25.7032 26.0723L21.4097 21.7789C20.6976 22.3122 19.6833 22.2551 19.0359 21.6077ZM18.7012 15.504L28.0085 12.6348L25.1393 21.9421L18.7012 15.504Z"
       fill="#06847B"
     />
diff --git a/src/icons/Orders.tsx b/src/icons/Orders.tsx
index 7a655a2e9..40cba08f6 100644
--- a/src/icons/Orders.tsx
+++ b/src/icons/Orders.tsx
@@ -4,8 +4,8 @@ import React from "react";
 export const Orders = createSvgIcon(
   <>
     <path
-      fill-rule="evenodd"
-      clip-rule="evenodd"
+      fillRule="evenodd"
+      clipRule="evenodd"
       d="M43 13H16V51H43V45.5H41V49H18V15H41V33.5H43V13ZM40 43.9142L48.2071 35.7071L46.7928 34.2929L40 41.0858L36.7071 37.7929L35.2928 39.2071L40 43.9142ZM22 19H25V22H22V19ZM36 19H27V22H36V19ZM22 25H25V28H22V25ZM36 25H27V28H36V25ZM22 31H25V34H22V31ZM36 31H27V34H36V31Z"
       fill="#06847B"
     />
diff --git a/src/icons/Pages.tsx b/src/icons/Pages.tsx
index 1eb46ada9..6db2775af 100644
--- a/src/icons/Pages.tsx
+++ b/src/icons/Pages.tsx
@@ -4,8 +4,8 @@ import React from "react";
 export const Pages = createSvgIcon(
   <>
     <path
-      fill-rule="evenodd"
-      clip-rule="evenodd"
+      fillRule="evenodd"
+      clipRule="evenodd"
       d="M18.179 1.20349H32.8068L40 8.39671V34.0421H24.6825V32.0528H38.0107V9.2207L37.79 9H32V3.20999L31.9828 3.19278H20.1683V7.15606H18.179V1.20349ZM0 6.16142H14.6278L21.821 13.3546V39H20.8264H0V6.16142ZM14 8.34692V15H19.8317V37.0107H1.98929V8.15071H13.8038L14 8.34692ZM18 19H4V22H18V19ZM4 24H18V27H4V24ZM35 12H25V15H35V12ZM25 17H35V28H25V17ZM18 29H4V32H18V29Z"
       fill="#06847B"
     />
diff --git a/src/icons/ProductTypes.tsx b/src/icons/ProductTypes.tsx
index 2b218491d..54fdbda6a 100644
--- a/src/icons/ProductTypes.tsx
+++ b/src/icons/ProductTypes.tsx
@@ -4,8 +4,8 @@ import React from "react";
 export const ProductTypes = createSvgIcon(
   <>
     <path
-      fill-rule="evenodd"
-      clip-rule="evenodd"
+      fillRule="evenodd"
+      clipRule="evenodd"
       d="M0 6.5V33L20 40L40 33V6.5L20 0L0 6.5ZM2.5 8.69935V31.2263L18.75 36.9138V14.3869L2.5 8.69935ZM21.25 14.3869V36.9138L37.5 31.2263V8.69935L21.25 14.3869ZM34.1436 7.22539L20 2.62872L5.8564 7.22539L20 12.1756L34.1436 7.22539Z"
       fill="#06847B"
     />
diff --git a/src/icons/Sales.tsx b/src/icons/Sales.tsx
index b3276b05a..7b9525679 100644
--- a/src/icons/Sales.tsx
+++ b/src/icons/Sales.tsx
@@ -4,8 +4,8 @@ import React from "react";
 export const Sales = createSvgIcon(
   <>
     <path
-      fill-rule="evenodd"
-      clip-rule="evenodd"
+      fillRule="evenodd"
+      clipRule="evenodd"
       d="M40.1974 14H49.7682V22.7732L45.9287 19.2537L35.0969 30.0855L25.1555 28.0973L15.5364 39.6402L14 38.3598L24.3809 25.9027L34.4395 27.9145L44.453 17.901L40.1974 14ZM21.2682 44H17.2682V49H21.2682V44ZM15.2682 42V51H23.2682V42H15.2682ZM26.2682 34H30.2682V49H26.2682V34ZM24.2682 51V32H32.2682V51H24.2682ZM39.2682 37H35.2682V49H39.2682V37ZM33.2682 35V51H41.2682V35H33.2682ZM44.2682 28H48.2682V49H44.2682V28ZM42.2682 51V26H50.2682V51H42.2682Z"
       fill="#06847B"
     />
diff --git a/src/icons/ShippingMethods.tsx b/src/icons/ShippingMethods.tsx
index eeb1db2f1..4ee47bf8f 100644
--- a/src/icons/ShippingMethods.tsx
+++ b/src/icons/ShippingMethods.tsx
@@ -4,8 +4,8 @@ import React from "react";
 export const ShippingMethods = createSvgIcon(
   <>
     <path
-      fill-rule="evenodd"
-      clip-rule="evenodd"
+      fillRule="evenodd"
+      clipRule="evenodd"
       d="M0 0H29.3333V12.2666H40V32.9751L32.9752 40H7.4667V19.8248L9.15816 18.1333H0V0ZM27.2 3.35694V12.2666H15.0249L11.2915 16H2.13333V3.07966L14.9333 9.73563L27.2 3.35694ZM24.929 2.13333H4.93756L14.9333 7.33111L24.929 2.13333ZM12.2667 18.1333H12.1752L11.1085 19.2H31.5582L36.3582 14.4H15.9085L12.2667 18.0418V18.1333ZM32.5334 21.2418L37.8667 15.9085V32.0915L32.5334 37.4248V21.2418ZM9.60003 21.3333V37.8666H30.4V21.3333H9.60003ZM18.6667 30.9333H26.6667V28.8H18.6667V30.9333ZM18.6667 34.6666H25.0667V32.5333H18.6667V34.6666Z"
       fill="#06847B"
     />
diff --git a/src/icons/SiteSettings.tsx b/src/icons/SiteSettings.tsx
index f8590aea4..53cc0388a 100644
--- a/src/icons/SiteSettings.tsx
+++ b/src/icons/SiteSettings.tsx
@@ -4,8 +4,8 @@ import React from "react";
 export const SiteSettings = createSvgIcon(
   <>
     <path
-      fill-rule="evenodd"
-      clip-rule="evenodd"
+      fillRule="evenodd"
+      clipRule="evenodd"
       d="M31.8208 5.55248L30.7926 8.35093L31.545 9.18475C33.147 10.9601 34.3637 13.1054 35.0581 15.4817L35.3837 16.596L38.1395 17.0712V23.1349L35.3838 23.6101L35.0582 24.7244C34.4073 26.9518 33.2976 28.9763 31.8417 30.6834L31.1357 31.5111L32.2146 34.4475L26.7281 37.6084L24.9142 35.4411L23.7814 35.7276C22.572 36.0334 21.3058 36.1961 20 36.1961C18.6942 36.1961 17.428 36.0334 16.2186 35.7276L15.0858 35.4411L13.2719 37.6084L7.78535 34.4475L8.86424 31.5111L8.15831 30.6834C6.70243 28.9763 5.59267 26.9518 4.94182 24.7244L4.61622 23.6101L1.86047 23.1349L1.86047 17.0712L4.61625 16.596L4.94186 15.4817C5.63625 13.1054 6.85296 10.9601 8.45495 9.18475L9.20736 8.35093L8.17916 5.55248L13.6657 2.39164L15.5348 4.62486L16.6397 4.37851C17.7208 4.1375 18.8448 4.01018 20 4.01018C21.1552 4.01018 22.2792 4.1375 23.3603 4.37851L24.4652 4.62486L26.3343 2.39164L31.8208 5.55248ZM40 24.702V15.5042L36.8439 14.9599C36.0699 12.311 34.7134 9.91885 32.9263 7.93836L34.1077 4.72285L25.9098 0L23.7651 2.56262C22.5521 2.2922 21.2924 2.14972 20 2.14972C18.7076 2.14972 17.4479 2.2922 16.2349 2.56262L14.0902 0L5.89227 4.72285L7.07371 7.93836C5.28657 9.91885 3.93009 12.311 3.15607 14.9599L0 15.5042V24.702L3.15603 25.2462C3.88152 27.7291 5.11876 29.9864 6.74273 31.8906L5.49846 35.2772L13.6964 40L15.7625 37.5313C17.1197 37.8744 18.539 38.0565 20 38.0565C21.461 38.0565 22.8803 37.8744 24.2375 37.5313L26.3036 40L34.5015 35.2772L33.2573 31.8906C34.8812 29.9864 36.1185 27.7291 36.844 25.2462L40 24.702ZM28.4425 20C28.4425 24.6627 24.6627 28.4425 20 28.4425C15.3373 28.4425 11.5574 24.6627 11.5574 20C11.5574 15.3373 15.3373 11.5574 20 11.5574C24.6627 11.5574 28.4425 15.3373 28.4425 20ZM30.303 20C30.303 25.6902 25.6902 30.303 20 30.303C14.3097 30.303 9.69693 25.6902 9.69693 20C9.69693 14.3097 14.3097 9.69693 20 9.69693C25.6902 9.69693 30.303 14.3097 30.303 20Z"
       fill="#06847B"
     />
diff --git a/src/icons/StaffMembers.tsx b/src/icons/StaffMembers.tsx
index 99a1de1f7..2b1e0a05b 100644
--- a/src/icons/StaffMembers.tsx
+++ b/src/icons/StaffMembers.tsx
@@ -4,8 +4,8 @@ import React from "react";
 export const StaffMembers = createSvgIcon(
   <>
     <path
-      fill-rule="evenodd"
-      clip-rule="evenodd"
+      fillRule="evenodd"
+      clipRule="evenodd"
       d="M20 8C20 11.3137 17.3137 14 14 14C10.6863 14 8 11.3137 8 8C8 4.68629 10.6863 2 14 2C17.3137 2 20 4.68629 20 8ZM22 8C22 12.4183 18.4183 16 14 16C9.58172 16 6 12.4183 6 8C6 3.58172 9.58172 0 14 0C18.4183 0 22 3.58172 22 8ZM2.31344 38L4.94331 20H22.0567L23.0395 26.7268L23 27H23.0794L24.6866 38H2.31344ZM25.1006 27L27 40H0L3.21429 18H23.7857L24.0447 19.7729L25.0238 13H31.5H37.9762L40 27H25.1006ZM31.5 13C28.4624 13 26 10.5376 26 7.5C26 4.46243 28.4624 2 31.5 2C34.5376 2 37 4.46243 37 7.5C37 10.5376 34.5376 13 31.5 13ZM31.5 11C33.433 11 35 9.433 35 7.5C35 5.567 33.433 4 31.5 4C29.567 4 28 5.567 28 7.5C28 9.433 29.567 11 31.5 11ZM25.3099 25L26.7555 15H36.2445L37.6901 25H25.3099Z"
       fill="#06847B"
     />
diff --git a/src/icons/Taxes.tsx b/src/icons/Taxes.tsx
index 970d8d349..7e3ba83cd 100644
--- a/src/icons/Taxes.tsx
+++ b/src/icons/Taxes.tsx
@@ -4,8 +4,8 @@ import React from "react";
 export const Taxes = createSvgIcon(
   <>
     <path
-      fill-rule="evenodd"
-      clip-rule="evenodd"
+      fillRule="evenodd"
+      clipRule="evenodd"
       d="M2 0H34.6316V21.0526H32.5263V2.10526H4.10526V37.8947H32.5263V35.2632H34.6316V40H2V0ZM8.31579 20H11.4737V23.1579H8.31579V20ZM16.7368 20H13.5789V23.1579H16.7368V20ZM18.8421 20H22V23.1579H18.8421V20ZM27.2632 20H24.1053V23.1579H27.2632V20ZM8.31579 25.2632H11.4737V28.4211H8.31579V25.2632ZM16.7368 25.2632H13.5789V28.4211H16.7368V25.2632ZM18.8421 25.2632H22V28.4211H18.8421V25.2632ZM11.4737 30.5263H8.31579V33.6842H11.4737V30.5263ZM13.5789 30.5263H16.7368V33.6842H13.5789V30.5263ZM22 30.5263H18.8421V33.6842H22V30.5263ZM26.2105 9.47368H10.4211V14.7368H26.2105V9.47368ZM8.31579 7.36842V16.8421H28.3158V7.36842H8.31579ZM37.7895 30.5263H34.6316V33.6842H37.7895V30.5263ZM28.3158 24.2105H31.4737V27.3684H28.3158V24.2105ZM29.0602 34.4285L38.5339 24.9548L37.0452 23.4662L27.5715 32.9398L29.0602 34.4285Z"
       fill="#06847B"
     />
diff --git a/src/misc.ts b/src/misc.ts
index 517e1a00e..74f410669 100644
--- a/src/misc.ts
+++ b/src/misc.ts
@@ -478,3 +478,7 @@ export function findInEnum<TEnum extends object>(
 
   throw new Error(`Key ${needle} not found in enum`);
 }
+
+export function parseBoolean(a: string): boolean {
+  return a === "true";
+}
diff --git a/src/products/components/ProductList/ProductList.tsx b/src/products/components/ProductList/ProductList.tsx
index 88508d18a..a69c7b877 100644
--- a/src/products/components/ProductList/ProductList.tsx
+++ b/src/products/components/ProductList/ProductList.tsx
@@ -20,6 +20,7 @@ import StatusLabel from "@saleor/components/StatusLabel";
 import TableCellAvatar, {
   AVATAR_MARGIN
 } from "@saleor/components/TableCellAvatar";
+import TableCellHeader from "@saleor/components/TableCellHeader";
 import TableHead from "@saleor/components/TableHead";
 import TablePagination from "@saleor/components/TablePagination";
 import { ProductListColumns } from "@saleor/config";
@@ -30,8 +31,12 @@ import {
 } from "@saleor/products/components/ProductListPage/utils";
 import { AvailableInGridAttributes_grid_edges_node } from "@saleor/products/types/AvailableInGridAttributes";
 import { ProductList_products_edges_node } from "@saleor/products/types/ProductList";
-import { ListActions, ListProps } from "@saleor/types";
-import TDisplayColumn from "@saleor/utils/columns/DisplayColumn";
+import { ProductListUrlSortField } from "@saleor/products/urls";
+import { ListActions, ListProps, SortPage } from "@saleor/types";
+import TDisplayColumn, {
+  DisplayColumnProps
+} from "@saleor/utils/columns/DisplayColumn";
+import { getArrowDirection } from "@saleor/utils/sort";
 
 const styles = (theme: Theme) =>
   createStyles({
@@ -87,9 +92,14 @@ const styles = (theme: Theme) =>
     }
   });
 
+const DisplayColumn = TDisplayColumn as React.FunctionComponent<
+  DisplayColumnProps<ProductListColumns>
+>;
+
 interface ProductListProps
   extends ListProps<ProductListColumns>,
     ListActions,
+    SortPage<ProductListUrlSortField>,
     WithStyles<typeof styles> {
   gridAttributes: AvailableInGridAttributes_grid_edges_node[];
   products: ProductList_products_edges_node[];
@@ -105,19 +115,18 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
     pageInfo,
     products,
     selected,
+    sort,
     toggle,
     toggleAll,
     toolbar,
     onNextPage,
     onPreviousPage,
     onUpdateListSettings,
-    onRowClick
+    onRowClick,
+    onSort
   }: ProductListProps) => {
     const intl = useIntl();
 
-    const DisplayColumn: React.FC<{ column: ProductListColumns }> = props => (
-      <TDisplayColumn displayColumns={settings.columns} {...props} />
-    );
     const gridAttributesFromSettings = settings.columns.filter(
       isAttributeColumnValue
     );
@@ -129,16 +138,22 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
           <colgroup>
             <col />
             <col className={classes.colName} />
-            <DisplayColumn column="productType">
+            <DisplayColumn
+              column="productType"
+              displayColumns={settings.columns}
+            >
               <col className={classes.colType} />
             </DisplayColumn>
-            <DisplayColumn column="isPublished">
+            <DisplayColumn
+              column="isPublished"
+              displayColumns={settings.columns}
+            >
               <col className={classes.colPublished} />
             </DisplayColumn>
             {gridAttributesFromSettings.map(gridAttribute => (
               <col className={classes.colAttribute} key={gridAttribute} />
             ))}
-            <DisplayColumn column="price">
+            <DisplayColumn column="price" displayColumns={settings.columns}>
               <col className={classes.colPrice} />
             </DisplayColumn>
           </colgroup>
@@ -150,30 +165,59 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
             toggleAll={toggleAll}
             toolbar={toolbar}
           >
-            <TableCell
+            <TableCellHeader
+              arrowPosition="right"
               className={classNames(classes.colName, {
                 [classes.colNameFixed]: settings.columns.length > 4
               })}
+              direction={
+                sort.sort === ProductListUrlSortField.name
+                  ? getArrowDirection(sort.asc)
+                  : undefined
+              }
+              onClick={() => onSort(ProductListUrlSortField.name)}
             >
               <span className={classes.colNameHeader}>
                 <FormattedMessage defaultMessage="Name" description="product" />
               </span>
-            </TableCell>
-            <DisplayColumn column="productType">
-              <TableCell className={classes.colType}>
+            </TableCellHeader>
+            <DisplayColumn
+              column="productType"
+              displayColumns={settings.columns}
+            >
+              <TableCellHeader
+                className={classes.colType}
+                direction={
+                  sort.sort === ProductListUrlSortField.productType
+                    ? getArrowDirection(sort.asc)
+                    : undefined
+                }
+                onClick={() => onSort(ProductListUrlSortField.productType)}
+              >
                 <FormattedMessage
                   defaultMessage="Type"
                   description="product type"
                 />
-              </TableCell>
+              </TableCellHeader>
             </DisplayColumn>
-            <DisplayColumn column="isPublished">
-              <TableCell className={classes.colPublished}>
+            <DisplayColumn
+              column="isPublished"
+              displayColumns={settings.columns}
+            >
+              <TableCellHeader
+                className={classes.colPublished}
+                direction={
+                  sort.sort === ProductListUrlSortField.status
+                    ? getArrowDirection(sort.asc)
+                    : undefined
+                }
+                onClick={() => onSort(ProductListUrlSortField.status)}
+              >
                 <FormattedMessage
                   defaultMessage="Published"
                   description="product status"
                 />
-              </TableCell>
+              </TableCellHeader>
             </DisplayColumn>
             {gridAttributesFromSettings.map(gridAttributeFromSettings => (
               <TableCell
@@ -192,13 +236,22 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
                 )}
               </TableCell>
             ))}
-            <DisplayColumn column="price">
-              <TableCell className={classes.colPrice}>
+            <DisplayColumn column="price" displayColumns={settings.columns}>
+              <TableCellHeader
+                className={classes.colPrice}
+                direction={
+                  sort.sort === ProductListUrlSortField.price
+                    ? getArrowDirection(sort.asc)
+                    : undefined
+                }
+                textAlign="right"
+                onClick={() => onSort(ProductListUrlSortField.price)}
+              >
                 <FormattedMessage
                   defaultMessage="Price"
                   description="product price"
                 />
-              </TableCell>
+              </TableCellHeader>
             </DisplayColumn>
           </TableHead>
           <TableFooter>
@@ -246,7 +299,10 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
                     >
                       {maybe<React.ReactNode>(() => product.name, <Skeleton />)}
                     </TableCellAvatar>
-                    <DisplayColumn column="productType">
+                    <DisplayColumn
+                      column="productType"
+                      displayColumns={settings.columns}
+                    >
                       <TableCell className={classes.colType}>
                         {product && product.productType ? (
                           product.productType.name
@@ -255,7 +311,10 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
                         )}
                       </TableCell>
                     </DisplayColumn>
-                    <DisplayColumn column="isPublished">
+                    <DisplayColumn
+                      column="isPublished"
+                      displayColumns={settings.columns}
+                    >
                       <TableCell className={classes.colPublished}>
                         {product &&
                         maybe(() => product.isAvailable !== undefined) ? (
@@ -299,7 +358,10 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
                         }, <Skeleton />)}
                       </TableCell>
                     ))}
-                    <DisplayColumn column="price">
+                    <DisplayColumn
+                      column="price"
+                      displayColumns={settings.columns}
+                    >
                       <TableCell className={classes.colPrice}>
                         {maybe(() => product.basePrice) &&
                         maybe(() => product.basePrice.amount) !== undefined &&
diff --git a/src/products/components/ProductListPage/ProductListPage.tsx b/src/products/components/ProductListPage/ProductListPage.tsx
index 7f89ff158..61ba40a77 100644
--- a/src/products/components/ProductListPage/ProductListPage.tsx
+++ b/src/products/components/ProductListPage/ProductListPage.tsx
@@ -1,7 +1,6 @@
 import Button from "@material-ui/core/Button";
 import Card from "@material-ui/core/Card";
 import { Theme } from "@material-ui/core/styles";
-
 import makeStyles from "@material-ui/styles/makeStyles";
 import React from "react";
 import { FormattedMessage, useIntl } from "react-intl";
@@ -22,8 +21,10 @@ import {
   FetchMoreProps,
   FilterPageProps,
   ListActions,
-  PageListProps
+  PageListProps,
+  SortPage
 } from "@saleor/types";
+import { ProductListUrlSortField } from "../../urls";
 import ProductList from "../ProductList";
 import ProductListFilter, { ProductFilterKeys } from "../ProductListFilter";
 
@@ -31,7 +32,8 @@ export interface ProductListPageProps
   extends PageListProps<ProductListColumns>,
     ListActions,
     FilterPageProps<ProductFilterKeys>,
-    FetchMoreProps {
+    FetchMoreProps,
+    SortPage<ProductListUrlSortField> {
   availableInGridAttributes: AvailableInGridAttributes_availableInGrid_edges_node[];
   currencySymbol: string;
   gridAttributes: AvailableInGridAttributes_grid_edges_node[];
diff --git a/src/products/index.tsx b/src/products/index.tsx
index 0a8a1e767..ca16a2e59 100644
--- a/src/products/index.tsx
+++ b/src/products/index.tsx
@@ -4,6 +4,7 @@ import { useIntl } from "react-intl";
 import { Route, RouteComponentProps, Switch } from "react-router-dom";
 
 import { sectionNames } from "@saleor/intl";
+import { findInEnum, parseBoolean } from "@saleor/misc";
 import { WindowTitle } from "../components/WindowTitle";
 import {
   productAddPath,
@@ -11,6 +12,7 @@ import {
   ProductImageUrlQueryParams,
   productListPath,
   ProductListUrlQueryParams,
+  ProductListUrlSortField,
   productPath,
   ProductUrlQueryParams,
   productVariantAddPath,
@@ -28,7 +30,13 @@ const ProductList: React.StatelessComponent<RouteComponentProps<any>> = ({
   location
 }) => {
   const qs = parseQs(location.search.substr(1));
-  const params: ProductListUrlQueryParams = qs;
+  const params: ProductListUrlQueryParams = {
+    ...qs,
+    asc: parseBoolean(qs.asc),
+    sort: qs.sort
+      ? findInEnum(qs.sort, ProductListUrlSortField)
+      : ProductListUrlSortField.name
+  };
   return <ProductListComponent params={params} />;
 };
 
diff --git a/src/products/queries.ts b/src/products/queries.ts
index 5d5086ec4..65c5948d7 100644
--- a/src/products/queries.ts
+++ b/src/products/queries.ts
@@ -218,6 +218,7 @@ const productListQuery = gql`
     $last: Int
     $before: String
     $filter: ProductFilterInput
+    $sort: ProductOrder
   ) {
     products(
       before: $before
@@ -225,6 +226,7 @@ const productListQuery = gql`
       first: $first
       last: $last
       filter: $filter
+      sortBy: $sort
     ) {
       edges {
         node {
diff --git a/src/products/types/ProductList.ts b/src/products/types/ProductList.ts
index ae5de0235..ab392aa3e 100644
--- a/src/products/types/ProductList.ts
+++ b/src/products/types/ProductList.ts
@@ -2,7 +2,7 @@
 /* eslint-disable */
 // This file was automatically generated and should not be edited.
 
-import { ProductFilterInput } from "./../../types/globalTypes";
+import { ProductFilterInput, ProductOrder } from "./../../types/globalTypes";
 
 // ====================================================
 // GraphQL query operation: ProductList
@@ -82,4 +82,5 @@ export interface ProductListVariables {
   last?: number | null;
   before?: string | null;
   filter?: ProductFilterInput | null;
+  sort?: ProductOrder | null;
 }
diff --git a/src/products/urls.ts b/src/products/urls.ts
index 7c0348d3f..7a408857d 100644
--- a/src/products/urls.ts
+++ b/src/products/urls.ts
@@ -7,6 +7,7 @@ import {
   Dialog,
   Filters,
   Pagination,
+  Sort,
   TabActionDialog
 } from "../types";
 
@@ -29,9 +30,17 @@ export enum ProductListUrlFiltersEnum {
   query = "query"
 }
 export type ProductListUrlFilters = Filters<ProductListUrlFiltersEnum>;
+export enum ProductListUrlSortField {
+  name = "name",
+  productType = "productType",
+  status = "status",
+  price = "price"
+}
+export type ProductListUrlSort = Sort<ProductListUrlSortField>;
 export type ProductListUrlQueryParams = BulkAction &
   Dialog<ProductListUrlDialog> &
   ProductListUrlFilters &
+  ProductListUrlSort &
   Pagination &
   ActiveTab;
 export const productListUrl = (params?: ProductListUrlQueryParams): string =>
diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx
index a15798fa7..366834bfc 100644
--- a/src/products/views/ProductList/ProductList.tsx
+++ b/src/products/views/ProductList/ProductList.tsx
@@ -22,7 +22,9 @@ import usePaginator, {
 import useShop from "@saleor/hooks/useShop";
 import { commonMessages } from "@saleor/intl";
 import { getMutationState, maybe } from "@saleor/misc";
+import { ProductListVariables } from "@saleor/products/types/ProductList";
 import { ListViews } from "@saleor/types";
+import { getSortUrlVariables } from "@saleor/utils/sort";
 import ProductListPage from "../../components/ProductListPage";
 import {
   TypedProductBulkDeleteMutation,
@@ -52,6 +54,7 @@ import {
   getFilterVariables,
   saveFilterTab
 } from "./filters";
+import { getSortQueryVariables } from "./sort";
 
 interface ProductListProps {
   params: ProductListUrlQueryParams;
@@ -152,10 +155,13 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
 
   const paginationState = createPaginationState(settings.rowNumber, params);
   const currencySymbol = maybe(() => shop.defaultCurrency, "USD");
-  const queryVariables = React.useMemo(
+  const filter = getFilterVariables(params);
+  const sort = getSortQueryVariables(params);
+  const queryVariables = React.useMemo<ProductListVariables>(
     () => ({
       ...paginationState,
-      filter: getFilterVariables(params)
+      filter,
+      sort
     }),
     [params, settings.rowNumber]
   );
@@ -224,6 +230,18 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
                       return (
                         <>
                           <ProductListPage
+                            sort={{
+                              asc: params.asc,
+                              sort: params.sort
+                            }}
+                            onSort={field =>
+                              navigate(
+                                productListUrl({
+                                  ...params,
+                                  ...getSortUrlVariables(field, params)
+                                })
+                              )
+                            }
                             availableInGridAttributes={maybe(
                               () =>
                                 attributes.data.availableInGrid.edges.map(
diff --git a/src/products/views/ProductList/sort.ts b/src/products/views/ProductList/sort.ts
new file mode 100644
index 000000000..e5cbbe905
--- /dev/null
+++ b/src/products/views/ProductList/sort.ts
@@ -0,0 +1,30 @@
+import {
+  ProductListUrlQueryParams,
+  ProductListUrlSortField
+} from "@saleor/products/urls";
+import { ProductOrder, ProductOrderField } from "@saleor/types/globalTypes";
+import { getOrderDirection } from "@saleor/utils/sort";
+
+export function getSortQueryField(
+  sort: ProductListUrlSortField
+): ProductOrderField {
+  switch (sort) {
+    case ProductListUrlSortField.name:
+      return ProductOrderField.NAME;
+    case ProductListUrlSortField.price:
+      return ProductOrderField.PRICE;
+    case ProductListUrlSortField.productType:
+      return ProductOrderField.TYPE;
+    case ProductListUrlSortField.status:
+      return ProductOrderField.PUBLISHED;
+  }
+}
+
+export function getSortQueryVariables(
+  params: ProductListUrlQueryParams
+): ProductOrder {
+  return {
+    direction: getOrderDirection(params.asc),
+    field: getSortQueryField(params.sort)
+  };
+}
diff --git a/src/storybook/stories/products/ProductListPage.tsx b/src/storybook/stories/products/ProductListPage.tsx
index d5f792e97..f9203dac4 100644
--- a/src/storybook/stories/products/ProductListPage.tsx
+++ b/src/storybook/stories/products/ProductListPage.tsx
@@ -4,6 +4,7 @@ import React from "react";
 import placeholderImage from "@assets/images/placeholder255x255.png";
 import { defaultListSettings } from "@saleor/config";
 import { products as productListFixture } from "@saleor/products/fixtures";
+import { ProductListUrlSortField } from "@saleor/products/urls";
 import { attributes } from "@saleor/productTypes/fixtures";
 import { ListViews } from "@saleor/types";
 import {
@@ -11,7 +12,8 @@ import {
   filterPageProps,
   filters,
   listActionsProps,
-  pageListProps
+  pageListProps,
+  sortPageProps
 } from "../../../fixtures";
 import ProductListPage, {
   ProductListPageProps
@@ -25,6 +27,13 @@ const props: ProductListPageProps = {
   ...pageListProps.default,
   ...filterPageProps,
   ...fetchMoreProps,
+  ...{
+    ...sortPageProps,
+    sort: {
+      ...sortPageProps.sort,
+      sort: ProductListUrlSortField.name
+    }
+  },
   availableInGridAttributes: attributes,
   defaultSettings: defaultListSettings[ListViews.PRODUCT_LIST],
   gridAttributes: attributes,
diff --git a/src/types.ts b/src/types.ts
index f47628fa7..1ff50fea8 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -47,6 +47,11 @@ export interface ListProps<TColumns extends string = string> {
   ) => void;
   onListSettingsReset?: () => void;
 }
+
+export interface SortPage<TSortKey extends string> {
+  sort: Sort<TSortKey>;
+  onSort: (field: TSortKey) => void;
+}
 export interface ListActionsWithoutToolbar {
   toggle: (id: string) => void;
   toggleAll: (items: React.ReactNodeArray, selected: number) => void;
@@ -131,6 +136,10 @@ export type FiltersWithMultipleValues<TFilters extends string> = Partial<
 export type SingleAction = Partial<{
   id: string;
 }>;
+export type Sort<TSort extends string = string> = Partial<{
+  asc: boolean;
+  sort: TSort;
+}>;
 export type BulkAction = Partial<{
   ids: string[];
 }>;
diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts
index 420441a4f..e4d1e84ac 100644
--- a/src/types/globalTypes.ts
+++ b/src/types/globalTypes.ts
@@ -112,6 +112,11 @@ export enum OrderAction {
   VOID = "VOID",
 }
 
+export enum OrderDirection {
+  ASC = "ASC",
+  DESC = "DESC",
+}
+
 export enum OrderEventsEmailsEnum {
   DIGITAL_LINKS = "DIGITAL_LINKS",
   FULFILLMENT_CONFIRMATION = "FULFILLMENT_CONFIRMATION",
@@ -187,6 +192,15 @@ export enum PermissionEnum {
   MANAGE_USERS = "MANAGE_USERS",
 }
 
+export enum ProductOrderField {
+  DATE = "DATE",
+  MINIMAL_PRICE = "MINIMAL_PRICE",
+  NAME = "NAME",
+  PRICE = "PRICE",
+  PUBLISHED = "PUBLISHED",
+  TYPE = "TYPE",
+}
+
 export enum ProductTypeConfigurable {
   CONFIGURABLE = "CONFIGURABLE",
   SIMPLE = "SIMPLE",
@@ -574,6 +588,11 @@ export interface ProductFilterInput {
   minimalPrice?: PriceRangeInput | null;
 }
 
+export interface ProductOrder {
+  field: ProductOrderField;
+  direction: OrderDirection;
+}
+
 export interface ProductTypeFilterInput {
   search?: string | null;
   configurable?: ProductTypeConfigurable | null;
diff --git a/src/utils/columns/DisplayColumn.tsx b/src/utils/columns/DisplayColumn.tsx
index 72dd32898..af48b0f93 100644
--- a/src/utils/columns/DisplayColumn.tsx
+++ b/src/utils/columns/DisplayColumn.tsx
@@ -12,12 +12,12 @@ const DisplayColumn: React.FC<DisplayColumnProps> = ({
   children,
   column
 }) => {
-  const displayColumn = React.useCallback(
-    (column: string) => isSelected(column, displayColumns, (a, b) => a === b),
-    [displayColumns]
+  const display = React.useMemo(
+    () => isSelected(column, displayColumns, (a, b) => a === b),
+    [column, displayColumns]
   );
 
-  return <>{displayColumn(column) && children}</>;
+  return <>{display && children}</>;
 };
 
 DisplayColumn.displayName = "DisplayColumn";
diff --git a/src/utils/sort.ts b/src/utils/sort.ts
new file mode 100644
index 000000000..f5eabbd33
--- /dev/null
+++ b/src/utils/sort.ts
@@ -0,0 +1,28 @@
+import { TableCellHeaderArrowDirection } from "../components/TableCellHeader";
+import { Sort } from "../types";
+import { OrderDirection } from "../types/globalTypes";
+
+export function getSortUrlVariables<TSortKey extends string>(
+  field: TSortKey,
+  params: Sort<TSortKey>
+): Sort<TSortKey> {
+  if (field === params.sort) {
+    return {
+      asc: !params.asc,
+      sort: field
+    };
+  }
+
+  return {
+    asc: true,
+    sort: field
+  };
+}
+
+export function getOrderDirection(asc: boolean): OrderDirection {
+  return asc ? OrderDirection.ASC : OrderDirection.DESC;
+}
+
+export function getArrowDirection(asc: boolean): TableCellHeaderArrowDirection {
+  return asc ? "asc" : "desc";
+}


Test patch:
diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap
index e25d6d037..ff25993c7 100644
--- a/src/storybook/__snapshots__/Stories.test.ts.snap
+++ b/src/storybook/__snapshots__/Stories.test.ts.snap
@@ -4591,6 +4591,273 @@ exports[`Storyshots Generics / StatusLabel when success 1`] = `
 </div>
 `;
 
+exports[`Storyshots Generics / Table header ascending 1`] = `
+<div
+  style="padding:24px"
+>
+  <div
+    class="MuiPaper-root-id MuiPaper-elevation1-id MuiPaper-rounded-id MuiCard-root-id"
+    style="margin:auto;width:400px"
+  >
+    <table
+      class="MuiTable-root-id"
+    >
+      <thead
+        class="MuiTableHead-root-id"
+      >
+        <tr
+          class="MuiTableRow-root-id MuiTableRow-head-id"
+        >
+          <th
+            class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id"
+            scope="col"
+          >
+            <div
+              class="Hook-labelContainer-id Hook-labelContainerActive-id"
+            >
+              <div
+                class="Hook-label-id"
+              >
+                Name
+              </div>
+              <svg
+                aria-hidden="true"
+                class="MuiSvgIcon-root-id Hook-arrow-id Hook-arrowUp-id"
+                focusable="false"
+                role="presentation"
+                viewBox="0 0 24 24"
+              >
+                <rect
+                  fill="transparent"
+                  height="24"
+                  width="24"
+                />
+                <path
+                  clip-rule="evenodd"
+                  d="M11.0328 17.1401V4H12.9672V17.1401L14.6322 15.4751L16 16.8429L12 20.8429L8 16.8429L9.36782 15.4751L11.0328 17.1401Z"
+                  fill="currentColor"
+                  fill-rule="evenodd"
+                />
+              </svg>
+            </div>
+          </th>
+          <th
+            class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id"
+            scope="col"
+          >
+            <div
+              class="Hook-labelContainer-id"
+            >
+              <div
+                class="Hook-label-id"
+              >
+                Type
+              </div>
+            </div>
+          </th>
+        </tr>
+      </thead>
+      <tbody
+        class="MuiTableBody-root-id"
+      >
+        <tr
+          class="MuiTableRow-root-id"
+        >
+          <td
+            class="MuiTableCell-root-id MuiTableCell-body-id"
+          >
+            Apple Juice
+          </td>
+          <td
+            class="MuiTableCell-root-id MuiTableCell-body-id"
+          >
+            Juice
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</div>
+`;
+
+exports[`Storyshots Generics / Table header descending 1`] = `
+<div
+  style="padding:24px"
+>
+  <div
+    class="MuiPaper-root-id MuiPaper-elevation1-id MuiPaper-rounded-id MuiCard-root-id"
+    style="margin:auto;width:400px"
+  >
+    <table
+      class="MuiTable-root-id"
+    >
+      <thead
+        class="MuiTableHead-root-id"
+      >
+        <tr
+          class="MuiTableRow-root-id MuiTableRow-head-id"
+        >
+          <th
+            class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id"
+            scope="col"
+          >
+            <div
+              class="Hook-labelContainer-id Hook-labelContainerActive-id"
+            >
+              <div
+                class="Hook-label-id"
+              >
+                Name
+              </div>
+              <svg
+                aria-hidden="true"
+                class="MuiSvgIcon-root-id Hook-arrow-id"
+                focusable="false"
+                role="presentation"
+                viewBox="0 0 24 24"
+              >
+                <rect
+                  fill="transparent"
+                  height="24"
+                  width="24"
+                />
+                <path
+                  clip-rule="evenodd"
+                  d="M11.0328 17.1401V4H12.9672V17.1401L14.6322 15.4751L16 16.8429L12 20.8429L8 16.8429L9.36782 15.4751L11.0328 17.1401Z"
+                  fill="currentColor"
+                  fill-rule="evenodd"
+                />
+              </svg>
+            </div>
+          </th>
+          <th
+            class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id"
+            scope="col"
+          >
+            <div
+              class="Hook-labelContainer-id"
+            >
+              <div
+                class="Hook-label-id"
+              >
+                Type
+              </div>
+            </div>
+          </th>
+        </tr>
+      </thead>
+      <tbody
+        class="MuiTableBody-root-id"
+      >
+        <tr
+          class="MuiTableRow-root-id"
+        >
+          <td
+            class="MuiTableCell-root-id MuiTableCell-body-id"
+          >
+            Apple Juice
+          </td>
+          <td
+            class="MuiTableCell-root-id MuiTableCell-body-id"
+          >
+            Juice
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</div>
+`;
+
+exports[`Storyshots Generics / Table header interactive 1`] = `
+<div
+  style="padding:24px"
+>
+  <div
+    class="MuiPaper-root-id MuiPaper-elevation1-id MuiPaper-rounded-id MuiCard-root-id"
+    style="margin:auto;width:400px"
+  >
+    <table
+      class="MuiTable-root-id"
+    >
+      <thead
+        class="MuiTableHead-root-id"
+      >
+        <tr
+          class="MuiTableRow-root-id MuiTableRow-head-id"
+        >
+          <th
+            class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id"
+            scope="col"
+          >
+            <div
+              class="Hook-labelContainer-id Hook-labelContainerActive-id"
+            >
+              <div
+                class="Hook-label-id"
+              >
+                Name
+              </div>
+              <svg
+                aria-hidden="true"
+                class="MuiSvgIcon-root-id Hook-arrow-id Hook-arrowUp-id"
+                focusable="false"
+                role="presentation"
+                viewBox="0 0 24 24"
+              >
+                <rect
+                  fill="transparent"
+                  height="24"
+                  width="24"
+                />
+                <path
+                  clip-rule="evenodd"
+                  d="M11.0328 17.1401V4H12.9672V17.1401L14.6322 15.4751L16 16.8429L12 20.8429L8 16.8429L9.36782 15.4751L11.0328 17.1401Z"
+                  fill="currentColor"
+                  fill-rule="evenodd"
+                />
+              </svg>
+            </div>
+          </th>
+          <th
+            class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id"
+            scope="col"
+          >
+            <div
+              class="Hook-labelContainer-id"
+            >
+              <div
+                class="Hook-label-id"
+              >
+                Type
+              </div>
+            </div>
+          </th>
+        </tr>
+      </thead>
+      <tbody
+        class="MuiTableBody-root-id"
+      >
+        <tr
+          class="MuiTableRow-root-id"
+        >
+          <td
+            class="MuiTableCell-root-id MuiTableCell-body-id"
+          >
+            Apple Juice
+          </td>
+          <td
+            class="MuiTableCell-root-id MuiTableCell-body-id"
+          >
+            Juice
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</div>
+`;
+
 exports[`Storyshots Generics / TablePagination both previous and next pages are available 1`] = `
 <div
   style="padding:24px"
@@ -93583,32 +93850,83 @@ exports[`Storyshots Views / Products / Product list default 1`] = `
                 </button>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colName-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colName-id"
                 scope="col"
               >
-                <span
-                  class="ProductList-colNameHeader-id"
+                <div
+                  class="Hook-labelContainer-id Hook-labelContainerActive-id"
                 >
-                  Name
-                </span>
+                  <div
+                    class="Hook-label-id"
+                  >
+                    <span
+                      class="ProductList-colNameHeader-id"
+                    >
+                      Name
+                    </span>
+                  </div>
+                  <svg
+                    aria-hidden="true"
+                    class="MuiSvgIcon-root-id Hook-arrow-id Hook-arrowUp-id"
+                    focusable="false"
+                    role="presentation"
+                    viewBox="0 0 24 24"
+                  >
+                    <rect
+                      fill="transparent"
+                      height="24"
+                      width="24"
+                    />
+                    <path
+                      clip-rule="evenodd"
+                      d="M11.0328 17.1401V4H12.9672V17.1401L14.6322 15.4751L16 16.8429L12 20.8429L8 16.8429L9.36782 15.4751L11.0328 17.1401Z"
+                      fill="currentColor"
+                      fill-rule="evenodd"
+                    />
+                  </svg>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colType-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colType-id"
                 scope="col"
               >
-                Type
+                <div
+                  class="Hook-labelContainer-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Type
+                  </div>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colPublished-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colPublished-id"
                 scope="col"
               >
-                Published
+                <div
+                  class="Hook-labelContainer-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Published
+                  </div>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colPrice-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colPrice-id"
                 scope="col"
               >
-                Price
+                <div
+                  class="Hook-labelContainer-id Hook-labelContainerRight-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Price
+                  </div>
+                </div>
               </th>
             </tr>
           </thead>
@@ -95157,32 +95475,83 @@ exports[`Storyshots Views / Products / Product list loading 1`] = `
                 </button>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colName-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colName-id"
                 scope="col"
               >
-                <span
-                  class="ProductList-colNameHeader-id"
+                <div
+                  class="Hook-labelContainer-id Hook-labelContainerActive-id"
                 >
-                  Name
-                </span>
+                  <div
+                    class="Hook-label-id"
+                  >
+                    <span
+                      class="ProductList-colNameHeader-id"
+                    >
+                      Name
+                    </span>
+                  </div>
+                  <svg
+                    aria-hidden="true"
+                    class="MuiSvgIcon-root-id Hook-arrow-id Hook-arrowUp-id"
+                    focusable="false"
+                    role="presentation"
+                    viewBox="0 0 24 24"
+                  >
+                    <rect
+                      fill="transparent"
+                      height="24"
+                      width="24"
+                    />
+                    <path
+                      clip-rule="evenodd"
+                      d="M11.0328 17.1401V4H12.9672V17.1401L14.6322 15.4751L16 16.8429L12 20.8429L8 16.8429L9.36782 15.4751L11.0328 17.1401Z"
+                      fill="currentColor"
+                      fill-rule="evenodd"
+                    />
+                  </svg>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colType-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colType-id"
                 scope="col"
               >
-                Type
+                <div
+                  class="Hook-labelContainer-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Type
+                  </div>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colPublished-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colPublished-id"
                 scope="col"
               >
-                Published
+                <div
+                  class="Hook-labelContainer-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Published
+                  </div>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colPrice-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colPrice-id"
                 scope="col"
               >
-                Price
+                <div
+                  class="Hook-labelContainer-id Hook-labelContainerRight-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Price
+                  </div>
+                </div>
               </th>
             </tr>
           </thead>
@@ -95618,32 +95987,83 @@ exports[`Storyshots Views / Products / Product list no data 1`] = `
               class="MuiTableRow-root-id MuiTableRow-head-id"
             >
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colName-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colName-id"
                 scope="col"
               >
-                <span
-                  class="ProductList-colNameHeader-id"
+                <div
+                  class="Hook-labelContainer-id Hook-labelContainerActive-id"
                 >
-                  Name
-                </span>
+                  <div
+                    class="Hook-label-id"
+                  >
+                    <span
+                      class="ProductList-colNameHeader-id"
+                    >
+                      Name
+                    </span>
+                  </div>
+                  <svg
+                    aria-hidden="true"
+                    class="MuiSvgIcon-root-id Hook-arrow-id Hook-arrowUp-id"
+                    focusable="false"
+                    role="presentation"
+                    viewBox="0 0 24 24"
+                  >
+                    <rect
+                      fill="transparent"
+                      height="24"
+                      width="24"
+                    />
+                    <path
+                      clip-rule="evenodd"
+                      d="M11.0328 17.1401V4H12.9672V17.1401L14.6322 15.4751L16 16.8429L12 20.8429L8 16.8429L9.36782 15.4751L11.0328 17.1401Z"
+                      fill="currentColor"
+                      fill-rule="evenodd"
+                    />
+                  </svg>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colType-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colType-id"
                 scope="col"
               >
-                Type
+                <div
+                  class="Hook-labelContainer-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Type
+                  </div>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colPublished-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colPublished-id"
                 scope="col"
               >
-                Published
+                <div
+                  class="Hook-labelContainer-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Published
+                  </div>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colPrice-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colPrice-id"
                 scope="col"
               >
-                Price
+                <div
+                  class="Hook-labelContainer-id Hook-labelContainerRight-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Price
+                  </div>
+                </div>
               </th>
             </tr>
           </thead>
@@ -96475,32 +96895,83 @@ exports[`Storyshots Views / Products / Product list with custom filters 1`] = `
                 </button>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colName-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colName-id"
                 scope="col"
               >
-                <span
-                  class="ProductList-colNameHeader-id"
+                <div
+                  class="Hook-labelContainer-id Hook-labelContainerActive-id"
                 >
-                  Name
-                </span>
+                  <div
+                    class="Hook-label-id"
+                  >
+                    <span
+                      class="ProductList-colNameHeader-id"
+                    >
+                      Name
+                    </span>
+                  </div>
+                  <svg
+                    aria-hidden="true"
+                    class="MuiSvgIcon-root-id Hook-arrow-id Hook-arrowUp-id"
+                    focusable="false"
+                    role="presentation"
+                    viewBox="0 0 24 24"
+                  >
+                    <rect
+                      fill="transparent"
+                      height="24"
+                      width="24"
+                    />
+                    <path
+                      clip-rule="evenodd"
+                      d="M11.0328 17.1401V4H12.9672V17.1401L14.6322 15.4751L16 16.8429L12 20.8429L8 16.8429L9.36782 15.4751L11.0328 17.1401Z"
+                      fill="currentColor"
+                      fill-rule="evenodd"
+                    />
+                  </svg>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colType-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colType-id"
                 scope="col"
               >
-                Type
+                <div
+                  class="Hook-labelContainer-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Type
+                  </div>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colPublished-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colPublished-id"
                 scope="col"
               >
-                Published
+                <div
+                  class="Hook-labelContainer-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Published
+                  </div>
+                </div>
               </th>
               <th
-                class="MuiTableCell-root-id MuiTableCell-head-id ProductList-colPrice-id"
+                class="MuiTableCell-root-id MuiTableCell-head-id Hook-root-id ProductList-colPrice-id"
                 scope="col"
               >
-                Price
+                <div
+                  class="Hook-labelContainer-id Hook-labelContainerRight-id"
+                >
+                  <div
+                    class="Hook-label-id"
+                  >
+                    Price
+                  </div>
+                </div>
               </th>
             </tr>
           </thead>