Drop dynamic column toggles in column picker (#3878)
* Drop dynamic column toggles * Replace removal icon * Add changeset * Add docs * Update docs * Fix types on order draft datagrid * Adjust collections column picker to new architecture * Adjust categories column picker to new architecture * Test column picker: adding, removing and searching for dynamic columns in picker (#3901) * test - column picker. Adding, removing and searching for dynamic columns in picker. * update tests tame with TC ids * removed unused row checks on products list view * Lint files --------- Co-authored-by: wojteknowacki <124166231+wojteknowacki@users.noreply.github.com>
This commit is contained in:
parent
2ab11bb407
commit
66976d547b
28 changed files with 478 additions and 339 deletions
5
.changeset/empty-scissors-grow.md
Normal file
5
.changeset/empty-scissors-grow.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-dashboard": minor
|
||||
---
|
||||
|
||||
Drop dynamic column toggles in column picker
|
137
cypress/e2e/products/productsList/columnPicker.js
Normal file
137
cypress/e2e/products/productsList/columnPicker.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
/// <reference types="cypress"/>
|
||||
/// <reference types="../../../support"/>
|
||||
|
||||
import { PRODUCT_DETAILS, SHARED_ELEMENTS } from "../../../elements";
|
||||
import { PRODUCTS_LIST } from "../../../elements/catalog/products/products-list";
|
||||
import { LOCAL_STORAGE_FOR_COLUMN_PICKER } from "../../../fixtures";
|
||||
import { urlList } from "../../../fixtures/urlList";
|
||||
import { ensureCanvasStatic } from "../../../support/customCommands/sharedElementsOperations/canvas";
|
||||
import { columnPickerPage } from "../../../support/pages";
|
||||
|
||||
describe("As an admin I should be able to use column picker", () => {
|
||||
beforeEach(() => {
|
||||
cy.clearSessionData().loginUserViaRequest();
|
||||
});
|
||||
|
||||
it(
|
||||
"should be able to add new dynamic column to grid on product list via search. TC: SALEOR_2610",
|
||||
{ tags: ["@critical", "@allEnv", "@stable"] },
|
||||
() => {
|
||||
const dynamicColumnToBeSearched = "ABV";
|
||||
cy.addAliasToGraphRequest("AvailableColumnAttributes");
|
||||
cy.visit(urlList.products);
|
||||
|
||||
ensureCanvasStatic(PRODUCTS_LIST.dataGridTable);
|
||||
columnPickerPage.openColumnPicker();
|
||||
columnPickerPage.openDynamicColumnsSearch();
|
||||
columnPickerPage.typeNameInSearchColumnInput(dynamicColumnToBeSearched);
|
||||
cy.waitForRequestAndCheckIfNoErrors("@AvailableColumnAttributes");
|
||||
cy.get(SHARED_ELEMENTS.dynamicColumnSelector)
|
||||
.should("have.length", 1)
|
||||
.should("contain.text", dynamicColumnToBeSearched)
|
||||
.find("button")
|
||||
.click();
|
||||
cy.get(SHARED_ELEMENTS.dynamicColumnSelector)
|
||||
.invoke("text")
|
||||
.then(selectedColumnName => {
|
||||
cy.get(SHARED_ELEMENTS.dynamicColumnContainer)
|
||||
// do not check by visible text just data-test-id since often text has ellipsis
|
||||
.find(`[data-test-id="column-name-${selectedColumnName}"]`)
|
||||
.should("be.visible");
|
||||
// newly added dynamic column is alway placed as last one on grid
|
||||
cy.get(SHARED_ELEMENTS.dataGridTable)
|
||||
.find("th")
|
||||
.last()
|
||||
.should("have.text", selectedColumnName);
|
||||
});
|
||||
},
|
||||
);
|
||||
it(
|
||||
"should be able to remove dynamic column from picker on products list. TC: SALEOR_2611",
|
||||
{ tags: ["@productsList", "@allEnv", "@stable"] },
|
||||
() => {
|
||||
const listConfigLocalStorage = JSON.stringify(
|
||||
LOCAL_STORAGE_FOR_COLUMN_PICKER.listConfigWithAttributeColumnPicker,
|
||||
);
|
||||
// local storage is updated to avoid not necessary action of adding dynamic column in the beginning of test
|
||||
cy.window().then(win => {
|
||||
win.localStorage.setItem("listConfig", listConfigLocalStorage);
|
||||
});
|
||||
cy.visit(urlList.products);
|
||||
ensureCanvasStatic(PRODUCTS_LIST.dataGridTable);
|
||||
columnPickerPage.openColumnPicker();
|
||||
cy.get(SHARED_ELEMENTS.dynamicColumnContainer)
|
||||
.find(SHARED_ELEMENTS.selectedDynamicColumnNameSelector)
|
||||
.should("have.length", 1)
|
||||
.invoke("text")
|
||||
.then(columnName => {
|
||||
cy.get(SHARED_ELEMENTS.dynamicColumnContainer)
|
||||
.find(SHARED_ELEMENTS.removeSelectedDynamicColumnButton)
|
||||
.should("be.visible")
|
||||
.should("have.length", 1)
|
||||
.click();
|
||||
cy.get(SHARED_ELEMENTS.dynamicColumnContainer)
|
||||
.find(SHARED_ELEMENTS.removeSelectedDynamicColumnButton)
|
||||
.should("not.exist");
|
||||
cy.get(SHARED_ELEMENTS.dynamicColumnContainer)
|
||||
.find(columnName)
|
||||
.should("not.exist");
|
||||
});
|
||||
},
|
||||
);
|
||||
it(
|
||||
"should validate: that there is always at least one active static column, use pagination when searching dynamic columns, hiding column picker works. TC: SALEOR_2612",
|
||||
{ tags: ["@productsList", "@allEnv", "@stable"] },
|
||||
() => {
|
||||
cy.addAliasToGraphRequest("ProductDetails");
|
||||
// local storage accepts only strings
|
||||
const listConfigLocalStorage = JSON.stringify(
|
||||
LOCAL_STORAGE_FOR_COLUMN_PICKER.localStorageWithSingleStaticColumn,
|
||||
);
|
||||
// local storage is updated to make sure only one static column is active
|
||||
cy.window().then(win => {
|
||||
win.localStorage.setItem("listConfig", listConfigLocalStorage);
|
||||
});
|
||||
cy.visit(urlList.products);
|
||||
ensureCanvasStatic(PRODUCTS_LIST.dataGridTable);
|
||||
columnPickerPage.openColumnPicker();
|
||||
cy.get(SHARED_ELEMENTS.activeStaticColumnOnGridButton).should(
|
||||
"have.length",
|
||||
1,
|
||||
"There should be only one active static column",
|
||||
);
|
||||
columnPickerPage.openDynamicColumnsSearch();
|
||||
|
||||
cy.get(SHARED_ELEMENTS.paginationBackOnColumnPicker).should(
|
||||
"have.attr",
|
||||
"disabled",
|
||||
);
|
||||
cy.get(SHARED_ELEMENTS.paginationForwardOnColumnPicker).click();
|
||||
cy.get(SHARED_ELEMENTS.paginationBackOnColumnPicker).should(
|
||||
"not.have.attr",
|
||||
"disabled",
|
||||
);
|
||||
columnPickerPage
|
||||
.selectDynamicColumnAtIndex(1)
|
||||
.invoke("text")
|
||||
.then(selectedColumnName => {
|
||||
cy.get(SHARED_ELEMENTS.dynamicColumnContainer)
|
||||
// do not check by visible text just data-test-id since often text has ellipsis
|
||||
.find(`[data-test-id*="${selectedColumnName}"]`)
|
||||
.should("be.visible");
|
||||
// newly added dynamic column is alway placed as last one on grid
|
||||
cy.get(SHARED_ELEMENTS.dataGridTable)
|
||||
.find("th")
|
||||
.last()
|
||||
.should("have.text", selectedColumnName);
|
||||
//next line hides picker
|
||||
cy.get(SHARED_ELEMENTS.pageHeader).click({ force: true });
|
||||
cy.get(SHARED_ELEMENTS.dynamicColumnContainer).should("not.exist");
|
||||
// now it checks does picking record from grid works when picker is gone
|
||||
cy.clickGridCell(1, 1);
|
||||
cy.waitForRequestAndCheckIfNoErrors("@ProductDetails");
|
||||
cy.get(PRODUCT_DETAILS.productUpdateFormSection).should("be.visible");
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
|
@ -28,4 +28,5 @@ export const PRODUCT_DETAILS = {
|
|||
editVariant: '[data-test-id="row-action-button"]',
|
||||
firstRowDataGrid: "[data-testid='glide-cell-1-0']",
|
||||
dataGridTable: "[data-testid='data-grid-canvas']",
|
||||
productUpdateFormSection: "[data-test-id='product-update-form']",
|
||||
};
|
||||
|
|
|
@ -9,7 +9,19 @@ export const SHARED_ELEMENTS = {
|
|||
table: 'table[class*="Table"]',
|
||||
firstRowDataGrid: "[data-testid='glide-cell-1-0']",
|
||||
secondRowDataGrid: "[id='glide-cell-1-1']",
|
||||
openColumnPickerButton: "[data-test-id='open-column-picker-button']",
|
||||
openDynamicColumnsSearchButton: "[data-test-id='open-dynamic-search']",
|
||||
tableRow: '[data-test-id*="id"], [class*="MuiTableRow"]',
|
||||
activeStaticColumnOnGridButton: '[data-state="on"]',
|
||||
dynamicColumnSelector: '[data-test-id="dynamic-column"]',
|
||||
dynamicColumnNameSelector: '[data-test-id^="dynamic-column-name"]',
|
||||
dynamicColumnSearchInput: '[data-test-id="search-columns"]',
|
||||
selectedDynamicColumnNameSelector: '[data-test-id^="column-name-"]',
|
||||
removeSelectedDynamicColumnButton:
|
||||
'[data-test-id^="remove-dynamic-col-button"]',
|
||||
dynamicColumnContainer: '[data-test-id="dynamic-col-container"]',
|
||||
paginationForwardOnColumnPicker: '[data-test-id="pagination-forward"]',
|
||||
paginationBackOnColumnPicker: '[data-test-id="pagination-back"]',
|
||||
notificationSuccess:
|
||||
'[data-test-id="notification"][data-test-type="success"]',
|
||||
notificationFailure: '[data-test-id="notification"][data-test-type="error"]',
|
||||
|
|
|
@ -3,3 +3,4 @@ export { orderDraftCreateDemoResponse } from "./errors/demo/orderDratCreate";
|
|||
export { urlList } from "./urlList";
|
||||
export { ONE_PERMISSION_USERS, TEST_ADMIN_USER } from "./users";
|
||||
export { MESSAGES } from "./messages";
|
||||
export * as LOCAL_STORAGE_FOR_COLUMN_PICKER from "./localStorage/columnPickerMocks";
|
||||
|
|
139
cypress/fixtures/localStorage/columnPickerMocks.js
Normal file
139
cypress/fixtures/localStorage/columnPickerMocks.js
Normal file
|
@ -0,0 +1,139 @@
|
|||
export const listConfigWithAttributeColumnPicker = {
|
||||
APPS_LIST: {
|
||||
rowNumber: 100,
|
||||
},
|
||||
ATTRIBUTE_VALUE_LIST: {
|
||||
rowNumber: 10,
|
||||
},
|
||||
CATEGORY_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
COLLECTION_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
CUSTOMER_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
DRAFT_LIST: {
|
||||
rowNumber: 20,
|
||||
columns: ["number", "date", "customer", "total"],
|
||||
},
|
||||
NAVIGATION_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
ORDER_LIST: {
|
||||
rowNumber: 20,
|
||||
columns: ["number", "date", "customer", "payment", "status", "total"],
|
||||
},
|
||||
PAGES_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
PLUGIN_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
PRODUCT_LIST: {
|
||||
columns: [
|
||||
"name",
|
||||
"availability",
|
||||
"description",
|
||||
"price",
|
||||
"productType",
|
||||
"date",
|
||||
"attribute:QXR0cmlidXRlOjIx",
|
||||
],
|
||||
rowNumber: 20,
|
||||
},
|
||||
SALES_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
SHIPPING_METHODS_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
STAFF_MEMBERS_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
PERMISSION_GROUP_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
VOUCHER_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
WAREHOUSE_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
WEBHOOK_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
TRANSLATION_ATTRIBUTE_VALUE_LIST: {
|
||||
rowNumber: 10,
|
||||
},
|
||||
" GIFT_CARD_LIST": {
|
||||
rowNumber: 20,
|
||||
},
|
||||
};
|
||||
|
||||
export const localStorageWithSingleStaticColumn = {
|
||||
APPS_LIST: {
|
||||
rowNumber: 100,
|
||||
},
|
||||
ATTRIBUTE_VALUE_LIST: {
|
||||
rowNumber: 10,
|
||||
},
|
||||
CATEGORY_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
COLLECTION_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
CUSTOMER_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
DRAFT_LIST: {
|
||||
rowNumber: 20,
|
||||
columns: ["number", "date", "customer", "total"],
|
||||
},
|
||||
NAVIGATION_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
ORDER_LIST: {
|
||||
rowNumber: 20,
|
||||
columns: ["number", "date", "customer", "payment", "status", "total"],
|
||||
},
|
||||
PAGES_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
PLUGIN_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
PRODUCT_LIST: {
|
||||
columns: ["name"],
|
||||
rowNumber: 20,
|
||||
},
|
||||
SALES_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
SHIPPING_METHODS_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
STAFF_MEMBERS_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
PERMISSION_GROUP_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
VOUCHER_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
WAREHOUSE_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
WEBHOOK_LIST: {
|
||||
rowNumber: 20,
|
||||
},
|
||||
TRANSLATION_ATTRIBUTE_VALUE_LIST: {
|
||||
rowNumber: 10,
|
||||
},
|
||||
" GIFT_CARD_LIST": {
|
||||
rowNumber: 20,
|
||||
},
|
||||
};
|
22
cypress/support/pages/columnPicker.js
Normal file
22
cypress/support/pages/columnPicker.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { SHARED_ELEMENTS } from "../../elements/shared/sharedElements";
|
||||
|
||||
export function openColumnPicker() {
|
||||
cy.get(SHARED_ELEMENTS.openColumnPickerButton).click();
|
||||
}
|
||||
export function openDynamicColumnsSearch() {
|
||||
cy.get(SHARED_ELEMENTS.openDynamicColumnsSearchButton).click();
|
||||
}
|
||||
export function selectDynamicColumnAtIndex(columnIndex) {
|
||||
return cy
|
||||
.get(SHARED_ELEMENTS.dynamicColumnNameSelector)
|
||||
.eq(columnIndex)
|
||||
.click();
|
||||
}
|
||||
export function typeNameInSearchColumnInput(columnName) {
|
||||
return cy
|
||||
.get(SHARED_ELEMENTS.dynamicColumnSearchInput)
|
||||
.should("be.visible")
|
||||
.click()
|
||||
.type(columnName)
|
||||
.blur();
|
||||
}
|
|
@ -38,4 +38,5 @@ export * as priceListComponent from "./catalog/products/priceListComponent";
|
|||
export * as variantsPage from "./catalog/products/VariantsPage";
|
||||
export * as channelsPage from "./channelsPage";
|
||||
export * as pagesPage from "./pagesPage";
|
||||
export * as columnPickerPage from "./columnPicker";
|
||||
export * as pageDetailsPage from "./pageDetailsPage";
|
||||
|
|
|
@ -110,7 +110,7 @@ export const CategoryListDatagrid = ({
|
|||
onRowSelectionChange={onSelectCategoriesIds}
|
||||
renderColumnPicker={() => (
|
||||
<ColumnPicker
|
||||
onSave={handlers.onChange}
|
||||
onToggle={handlers.onToggle}
|
||||
selectedColumns={selectedColumns}
|
||||
staticColumns={staticColumns}
|
||||
/>
|
||||
|
|
|
@ -27,7 +27,6 @@ interface CollectionListDatagridProps
|
|||
SortPage<CollectionListUrlSortField> {
|
||||
collections: Collections;
|
||||
loading: boolean;
|
||||
columnPickerSettings: string[];
|
||||
selectedChannelId: string;
|
||||
hasRowHover?: boolean;
|
||||
onSelectCollectionIds: (
|
||||
|
@ -48,7 +47,6 @@ export const CollectionListDatagrid = ({
|
|||
onRowClick,
|
||||
rowAnchor,
|
||||
disabled,
|
||||
columnPickerSettings,
|
||||
onSelectCollectionIds,
|
||||
onSort,
|
||||
filterDependency,
|
||||
|
@ -76,9 +74,7 @@ export const CollectionListDatagrid = ({
|
|||
handlers,
|
||||
visibleColumns,
|
||||
staticColumns,
|
||||
dynamicColumns,
|
||||
selectedColumns,
|
||||
columnCategories,
|
||||
recentlyAddedColumn,
|
||||
} = useColumns({
|
||||
staticColumns: collectionListStaticColumns,
|
||||
|
@ -181,12 +177,8 @@ export const CollectionListDatagrid = ({
|
|||
renderColumnPicker={() => (
|
||||
<ColumnPicker
|
||||
staticColumns={staticColumns}
|
||||
dynamicColumns={dynamicColumns}
|
||||
selectedColumns={selectedColumns}
|
||||
columnCategories={columnCategories}
|
||||
onDynamicColumnSelect={handlers.onDynamicColumnSelect}
|
||||
columnPickerSettings={columnPickerSettings}
|
||||
onSave={handlers.onChange}
|
||||
onToggle={handlers.onToggle}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -34,7 +34,6 @@ const props: CollectionListPageProps = {
|
|||
collections,
|
||||
selectedChannelId: "123",
|
||||
filterOpts: collectionListFilterOpts,
|
||||
columnPickerSettings: ["name"],
|
||||
selectedCollectionIds: [],
|
||||
hasPresetsChanged: () => false,
|
||||
onAll: () => undefined,
|
||||
|
|
|
@ -34,7 +34,6 @@ export interface CollectionListPageProps
|
|||
SortPage<CollectionListUrlSortField> {
|
||||
onTabUpdate: (tabName: string) => void;
|
||||
selectedChannelId: string;
|
||||
columnPickerSettings: string[];
|
||||
collections: Collections;
|
||||
loading: boolean;
|
||||
selectedCollectionIds: string[];
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// @ts-strict-ignore
|
||||
import ActionDialog from "@dashboard/components/ActionDialog";
|
||||
import useAppChannel from "@dashboard/components/AppLayout/AppChannelContext";
|
||||
import { useColumnPickerSettings } from "@dashboard/components/Datagrid/ColumnPicker/useColumnPickerSettings";
|
||||
import DeleteFilterTabDialog from "@dashboard/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog from "@dashboard/components/SaveFilterTabDialog";
|
||||
import {
|
||||
|
@ -56,7 +55,6 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
|||
const { updateListSettings, settings } = useListSettings(
|
||||
ListViews.COLLECTION_LIST,
|
||||
);
|
||||
const { columnPickerSettings } = useColumnPickerSettings("COLLECTION_LIST");
|
||||
|
||||
usePaginationReset(collectionListUrl, params, settings.rowNumber);
|
||||
const { channel } = useAppChannel(false);
|
||||
|
@ -211,7 +209,6 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
|||
tabs={presets.map(tab => tab.name)}
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
columnPickerSettings={columnPickerSettings}
|
||||
collections={collections}
|
||||
settings={settings}
|
||||
onSort={handleSort}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
### System Architecture
|
||||
|
||||
<img width="977" alt="image" src="https://user-images.githubusercontent.com/41952692/233042483-d2cb30f3-26b7-40b5-9d08-2ea42f7f0242.png">
|
||||
<img width="858" alt="image" src="https://github.com/saleor/saleor-dashboard/assets/41952692/8ce49003-e2af-4901-9098-fa5deb9bbe66">
|
||||
|
||||
### Column types
|
||||
|
||||
|
@ -34,35 +34,32 @@ attribute:QXR0cmlidXRlOjIx
|
|||
- dynamic columns - array of dynamic columns for the column picker
|
||||
- column categories - array of column categories, which is abstraction for dynamic column. For example attributes is a column category, whereas Flavor attribute is an actual column value. This object has all API-related properties, like search handler, fetch more props, etc.
|
||||
- selected columns - array of column IDs which are selected in the column picker. It is saved in local storage
|
||||
- dynamic column settings - array of column IDs which are selected in the left section of the column picker. It is saved in local storage.
|
||||
- recently added column - this value is used in datagrid component to enable auto-scroll to newly added column
|
||||
- handlers:
|
||||
- column resize handler (for datagrid)
|
||||
- column reorder handler (for datagrid)
|
||||
- column visibility handler (for column picker)
|
||||
- dynamic column selection handler (for column picker)
|
||||
- column selection toggle (for column picker)
|
||||
- customUpdateVisible - used to manually update visible columns state. For now it is required to update arrow icon in the datagrid columns
|
||||
|
||||
In order to use this hook, you need to provide four things:
|
||||
In order to use this hook, you need to provide two/three things:
|
||||
|
||||
- `staticColumns` - array of static columns in datagrid-ready format (`AvailableColumns[]`)
|
||||
- `columnCategories` - array of column categories
|
||||
- `columnCategories` - array of column categories (only if there are any dynamic columns)
|
||||
- state & setter of column settings which we get from `useListSettings`
|
||||
- state of column picker settings which we get from `useColumnPickerSettings`
|
||||
|
||||
## Adapting new views
|
||||
|
||||
### Column picker settings
|
||||
### Selected columns in LS
|
||||
|
||||
Firstly, in the view file, we need to provide two settings object, one for the selected columns and one for the dynamic column settings. We should use `useColumnPickerSettings` and `useListSettings` hook for that. The first settings object manages columns selected for the datagrid (visible columns). The second manages state of seleceted dynamic columns (if we pick a value from left side of column picked, it is then displayed on the right side of the picker as dynamic column with togglable visibility). Toggling the visiblity saves the column in the first settings object.
|
||||
|
||||
The reason why column picker settings object needs to be in the view file and cannot be integrated into internal logic of useColumns is because we use column picker settings in the query. We need to know which columns are selected in order to fetch the correct data from the API.
|
||||
Firstly, in the view file, we need to provide settings object which holds seleted columns IDs. We should use `useListSettings` hook for that.
|
||||
|
||||
```tsx
|
||||
const { columnPickerSettings, setDynamicColumnsSettings } =
|
||||
useColumnPickerSettings("PRODUCT_LIST");
|
||||
const { updateListSettings, settings } = useListSettings(
|
||||
ListViews.PRODUCT_LIST,
|
||||
);
|
||||
|
||||
// Translates columnIDs to api IDs
|
||||
const filteredColumnIds = columnPickerSettings
|
||||
const filteredColumnIds = settings.columns
|
||||
.filter(isAttributeColumnValue)
|
||||
.map(getAttributeIdFromColumnValue);
|
||||
|
||||
|
@ -131,12 +128,12 @@ export const parseDynamicColumnsForProductListView = ({
|
|||
name: "Attributes",
|
||||
prefix: "attribute",
|
||||
availableNodes: parseAttributesColumns(
|
||||
attributesData,
|
||||
attributesData, // all attributes
|
||||
activeAttributeSortId,
|
||||
sort,
|
||||
),
|
||||
selectedNodes: parseAttributesColumns(
|
||||
gridAttributesData,
|
||||
gridAttributesData, // selected attributes
|
||||
activeAttributeSortId,
|
||||
sort,
|
||||
),
|
||||
|
@ -151,7 +148,7 @@ export const parseDynamicColumnsForProductListView = ({
|
|||
|
||||
Here we only have 1 column category, attributes. `attributesData` is the result of the first query, `gridAttributesData` is the result of the second query. We also provide pagination props, which are used in the column picker.
|
||||
|
||||
Queries which are used in this case are for categories. Let's look at the first query:
|
||||
Let's have a look at the first query:
|
||||
|
||||
```tsx
|
||||
export const availableColumnAttribues = gql`
|
||||
|
@ -185,7 +182,7 @@ export const availableColumnAttribues = gql`
|
|||
|
||||
This query is used to fetch all **available** attributes. It is paginated and has a search filter and results are displayed in the left part of the column picker.
|
||||
|
||||
The second query is similar, but it has a filter of IDs, which come from local storage settings (useColumnPickerSettngs):
|
||||
The second query is similar, but it has a filter of IDs, which come from local storage settings (useListSettings):
|
||||
|
||||
```tsx
|
||||
export const gridAttributes = gql`
|
|
@ -22,9 +22,7 @@ export interface ColumnPickerProps {
|
|||
dynamicColumns?: AvailableColumn[] | null | undefined;
|
||||
selectedColumns: string[];
|
||||
columnCategories?: ColumnCategory[];
|
||||
columnPickerSettings?: string[];
|
||||
onSave: (columns: string[]) => void;
|
||||
onDynamicColumnSelect?: (columns: string[]) => void;
|
||||
onToggle: (columnId: string) => void;
|
||||
}
|
||||
|
||||
export const ColumnPicker = ({
|
||||
|
@ -32,23 +30,11 @@ export const ColumnPicker = ({
|
|||
selectedColumns,
|
||||
columnCategories,
|
||||
dynamicColumns,
|
||||
columnPickerSettings,
|
||||
onDynamicColumnSelect,
|
||||
onSave,
|
||||
onToggle,
|
||||
}: ColumnPickerProps) => {
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const renderCategories =
|
||||
columnCategories &&
|
||||
typeof onDynamicColumnSelect === "function" &&
|
||||
columnPickerSettings;
|
||||
|
||||
const handleToggle = (id: string) =>
|
||||
selectedColumns.includes(id)
|
||||
? onSave(selectedColumns.filter(currentId => currentId !== id))
|
||||
: onSave([...selectedColumns, id]);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
modal
|
||||
|
@ -60,6 +46,7 @@ export const ColumnPicker = ({
|
|||
>
|
||||
<Popover.Trigger>
|
||||
<Button
|
||||
data-test-id="open-column-picker-button"
|
||||
variant="tertiary"
|
||||
icon={<TableEditIcon />}
|
||||
pointerEvents={pickerOpen ? "none" : undefined}
|
||||
|
@ -79,11 +66,11 @@ export const ColumnPicker = ({
|
|||
gridTemplateColumns={expanded ? 2 : 1}
|
||||
overflow="hidden"
|
||||
>
|
||||
{expanded && renderCategories && (
|
||||
{expanded && columnCategories && (
|
||||
<ColumnPickerCategories
|
||||
columnCategories={columnCategories}
|
||||
columnPickerSettings={columnPickerSettings}
|
||||
onDynamicColumnSelect={onDynamicColumnSelect}
|
||||
selectedColumns={selectedColumns}
|
||||
onToggle={onToggle}
|
||||
onClose={() => setExpanded(false)}
|
||||
/>
|
||||
)}
|
||||
|
@ -102,15 +89,14 @@ export const ColumnPicker = ({
|
|||
</Box>
|
||||
<ColumnPickerStaticColumns
|
||||
staticColumns={staticColumns}
|
||||
handleToggle={handleToggle}
|
||||
handleToggle={onToggle}
|
||||
selectedColumns={selectedColumns}
|
||||
/>
|
||||
{columnCategories && (
|
||||
<ColumnPickerDynamicColumns
|
||||
dynamicColumns={dynamicColumns}
|
||||
selectedColumns={selectedColumns}
|
||||
setExpanded={setExpanded}
|
||||
handleToggle={handleToggle}
|
||||
onToggle={onToggle}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
|
|
@ -9,18 +9,18 @@ import { ColumnCategory } from "./useColumns";
|
|||
|
||||
export interface ColumnPickerAvailableNodesProps {
|
||||
currentCategory: ColumnCategory;
|
||||
columnPickerSettings: string[];
|
||||
selectedColumns: string[];
|
||||
query: string;
|
||||
setQuery: React.Dispatch<React.SetStateAction<string>>;
|
||||
changeHandler: (column: string) => void;
|
||||
onToggle: (column: string) => void;
|
||||
}
|
||||
|
||||
export const ColumnPickerAvailableNodes = ({
|
||||
currentCategory,
|
||||
columnPickerSettings,
|
||||
selectedColumns,
|
||||
query,
|
||||
setQuery,
|
||||
changeHandler,
|
||||
onToggle,
|
||||
}: ColumnPickerAvailableNodesProps) => {
|
||||
const areNodesLoading = currentCategory.availableNodes === undefined;
|
||||
const areNodesEmpty = currentCategory.availableNodes?.length === 0;
|
||||
|
@ -51,11 +51,16 @@ export const ColumnPickerAvailableNodes = ({
|
|||
return currentCategory.availableNodes!.map(node => (
|
||||
<Box padding={2} key={node.id}>
|
||||
<Checkbox
|
||||
onCheckedChange={() => changeHandler(node.id)}
|
||||
checked={columnPickerSettings.includes(node.id)}
|
||||
data-test-id={`search-dynamic-${node.id}`}
|
||||
onCheckedChange={() => onToggle(node.id)}
|
||||
checked={selectedColumns.includes(node.id)}
|
||||
data-test-id={`dynamic-column`}
|
||||
>
|
||||
<Text
|
||||
data-test-id={`dynamic-column-name-${node.title}`}
|
||||
size="small"
|
||||
color="textNeutralSubdued"
|
||||
ellipsis
|
||||
>
|
||||
<Text size="small" color="textNeutralSubdued" ellipsis>
|
||||
{node.title}
|
||||
</Text>
|
||||
</Checkbox>
|
||||
|
|
|
@ -13,28 +13,21 @@ import { getExitIcon, getExitOnClick } from "./utils";
|
|||
|
||||
export interface ColumnPickerCategoriesProps {
|
||||
columnCategories: ColumnCategory[];
|
||||
columnPickerSettings: string[];
|
||||
selectedColumns: string[];
|
||||
onClose: () => void;
|
||||
onDynamicColumnSelect: (columns: string[]) => void;
|
||||
onToggle: (columnId: string) => void;
|
||||
}
|
||||
|
||||
export const ColumnPickerCategories = ({
|
||||
columnCategories,
|
||||
onClose,
|
||||
onDynamicColumnSelect,
|
||||
columnPickerSettings,
|
||||
onToggle,
|
||||
selectedColumns,
|
||||
}: ColumnPickerCategoriesProps) => {
|
||||
const { currentCategory, setCurrentCategory } =
|
||||
useCategorySelection(columnCategories);
|
||||
const { query, setQuery } = useAvailableColumnsQuery(currentCategory);
|
||||
|
||||
const changeHandler = (column: string) =>
|
||||
columnPickerSettings.includes(column)
|
||||
? onDynamicColumnSelect(
|
||||
columnPickerSettings.filter(currentCol => currentCol !== column),
|
||||
)
|
||||
: onDynamicColumnSelect([...columnPickerSettings, column]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
backgroundColor="subdued"
|
||||
|
@ -73,10 +66,10 @@ export const ColumnPickerCategories = ({
|
|||
{currentCategory ? (
|
||||
<ColumnPickerAvailableNodes
|
||||
currentCategory={currentCategory}
|
||||
columnPickerSettings={columnPickerSettings}
|
||||
selectedColumns={selectedColumns}
|
||||
query={query}
|
||||
setQuery={setQuery}
|
||||
changeHandler={changeHandler}
|
||||
onToggle={onToggle}
|
||||
/>
|
||||
) : (
|
||||
<ColumnPickerCategoryList
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Box, Button, PlusIcon, Text, Toggle } from "@saleor/macaw-ui/next";
|
||||
import { Box, Button, PlusIcon, RemoveIcon, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
|
@ -8,15 +8,13 @@ import messages from "./messages";
|
|||
export interface ColumnPickerDynamicColumnsProps {
|
||||
dynamicColumns?: AvailableColumn[] | null | undefined;
|
||||
setExpanded: (value: React.SetStateAction<boolean>) => void;
|
||||
handleToggle: (id: string) => void;
|
||||
selectedColumns: string[];
|
||||
onToggle: (id: string) => void;
|
||||
}
|
||||
|
||||
export const ColumnPickerDynamicColumns = ({
|
||||
dynamicColumns,
|
||||
setExpanded,
|
||||
handleToggle,
|
||||
selectedColumns,
|
||||
onToggle,
|
||||
}: ColumnPickerDynamicColumnsProps) => (
|
||||
<Box data-test-id="dynamic-col-container">
|
||||
<Box
|
||||
|
@ -37,12 +35,22 @@ export const ColumnPickerDynamicColumns = ({
|
|||
/>
|
||||
</Box>
|
||||
{dynamicColumns?.map(column => (
|
||||
<Box padding={1} key={column.id}>
|
||||
<Toggle
|
||||
onPressedChange={() => handleToggle(column.id)}
|
||||
pressed={selectedColumns.includes(column.id)}
|
||||
data-test-id={`dynamic-col-${column.id}`}
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={2}
|
||||
padding={1}
|
||||
key={column.id}
|
||||
>
|
||||
<Button
|
||||
onClick={() => onToggle(column.id)}
|
||||
data-test-id={`remove-dynamic-col-button-${column.title}`}
|
||||
variant="tertiary"
|
||||
size="small"
|
||||
icon={<RemoveIcon color="iconNeutralPlain" />}
|
||||
__width="20px"
|
||||
__height="20px"
|
||||
/>
|
||||
<Text
|
||||
variant="body"
|
||||
size="small"
|
||||
|
@ -51,10 +59,15 @@ export const ColumnPickerDynamicColumns = ({
|
|||
>
|
||||
{`${column.metaGroup} /`}
|
||||
</Text>
|
||||
<Text variant="body" size="small" color="textNeutralDefault" ellipsis>
|
||||
<Text
|
||||
variant="body"
|
||||
size="small"
|
||||
color="textNeutralDefault"
|
||||
ellipsis
|
||||
data-test-id={`column-name-${column.title}`}
|
||||
>
|
||||
{column.title}
|
||||
</Text>
|
||||
</Toggle>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import useLocalStorage from "@dashboard/hooks/useLocalStorage";
|
||||
|
||||
const COLUMN_PICKER_KEY = "columnPickerConfig";
|
||||
|
||||
export type DatagridViews =
|
||||
| "PRODUCT_LIST"
|
||||
| "PRODUCT_DETAILS"
|
||||
| "ORDER_LIST"
|
||||
| "ORDER_DETAILS"
|
||||
| "ORDER_DRAFT_DETAILS"
|
||||
| "COLLECTION_LIST";
|
||||
|
||||
type DynamicColumnSettings = {
|
||||
[view in DatagridViews]: string[];
|
||||
};
|
||||
|
||||
export const defaultDynamicColumns: DynamicColumnSettings = {
|
||||
PRODUCT_LIST: [],
|
||||
PRODUCT_DETAILS: [],
|
||||
ORDER_LIST: [],
|
||||
ORDER_DETAILS: [],
|
||||
ORDER_DRAFT_DETAILS: [],
|
||||
COLLECTION_LIST: [],
|
||||
};
|
||||
|
||||
export const useColumnPickerSettings = (view: DatagridViews) => {
|
||||
const [config, setConfig] = useLocalStorage(
|
||||
COLUMN_PICKER_KEY,
|
||||
defaultDynamicColumns,
|
||||
);
|
||||
|
||||
const setDynamicColumnsSettings = (cols: string[]) =>
|
||||
setConfig(currentSettings => ({
|
||||
...currentSettings,
|
||||
[view]: cols,
|
||||
}));
|
||||
|
||||
const columnPickerSettings = config[view] ?? [];
|
||||
|
||||
return { columnPickerSettings, setDynamicColumnsSettings };
|
||||
};
|
|
@ -8,21 +8,22 @@ const mockedColumns: AvailableColumn[] = [
|
|||
id: "name",
|
||||
title: "Name",
|
||||
width: 200,
|
||||
metaGroup: "Product",
|
||||
hasMenu: false,
|
||||
icon: "arrowUp",
|
||||
},
|
||||
{
|
||||
id: "description",
|
||||
title: "Description",
|
||||
width: 100,
|
||||
metaGroup: "Sales Information",
|
||||
hasMenu: false,
|
||||
icon: "arrowUp",
|
||||
},
|
||||
];
|
||||
|
||||
const mockedSelectedColumns = ["name", "attribute:QXR0cmlidXRlOjE0"];
|
||||
// dynamic - color and ABV
|
||||
const mockedSelectedColumns = [
|
||||
"name",
|
||||
"attribute:QXR0cmlidXRlOjE0",
|
||||
"attribute:QXR0cmlidXRlOjIx",
|
||||
];
|
||||
|
||||
const mockedCategories: ColumnCategory[] = [
|
||||
{
|
||||
|
@ -67,30 +68,12 @@ const mockedCategories: ColumnCategory[] = [
|
|||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjE1",
|
||||
title: "Bottle Size",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjE0",
|
||||
title: "Color",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjUwOQ==",
|
||||
title: "storage size ",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjI5",
|
||||
title: "Tag",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
],
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
|
@ -100,21 +83,11 @@ const mockedCategories: ColumnCategory[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const mockedColumnPickerSettings = [
|
||||
"attribute:QXR0cmlidXRlOjIx",
|
||||
"attribute:QXR0cmlidXRlOjE1",
|
||||
"attribute:QXR0cmlidXRlOjUwOQ==",
|
||||
"attribute:QXR0cmlidXRlOjI5",
|
||||
"attribute:QXR0cmlidXRlOjE0",
|
||||
];
|
||||
|
||||
const expectedVisibleColumns = [
|
||||
{
|
||||
id: "name",
|
||||
title: "Name",
|
||||
width: 200,
|
||||
metaGroup: "Product",
|
||||
hasMenu: false,
|
||||
icon: "arrowUp",
|
||||
},
|
||||
{
|
||||
|
@ -123,43 +96,29 @@ const expectedVisibleColumns = [
|
|||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
// In order of mockedColumnPickerSettings
|
||||
const expectedDynamicColumns = [
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjIx",
|
||||
title: "ABV",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjE1",
|
||||
title: "Bottle Size",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjUwOQ==",
|
||||
title: "storage size ",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjI5",
|
||||
title: "Tag",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
const expectedDynamicColumns = [
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjE0",
|
||||
title: "Color",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjIx",
|
||||
title: "ABV",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
const setDynamicColumnSettings = jest.fn();
|
||||
const onSave = jest.fn();
|
||||
|
||||
describe("useColumns", () => {
|
||||
|
@ -174,8 +133,6 @@ describe("useColumns", () => {
|
|||
selectedColumns: mockedSelectedColumns,
|
||||
columnCategories: mockedCategories,
|
||||
onSave,
|
||||
columnPickerSettings: mockedColumnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
}),
|
||||
);
|
||||
// Assert
|
||||
|
@ -184,9 +141,6 @@ describe("useColumns", () => {
|
|||
expect(result.current.dynamicColumns).toEqual(expectedDynamicColumns);
|
||||
expect(result.current.selectedColumns).toEqual(mockedSelectedColumns);
|
||||
expect(result.current.columnCategories).toEqual(mockedCategories);
|
||||
expect(result.current.columnPickerSettings).toEqual(
|
||||
mockedColumnPickerSettings,
|
||||
);
|
||||
});
|
||||
it("should update visible column info when resized", () => {
|
||||
// Arrange
|
||||
|
@ -196,8 +150,6 @@ describe("useColumns", () => {
|
|||
selectedColumns: mockedSelectedColumns,
|
||||
columnCategories: mockedCategories,
|
||||
onSave,
|
||||
columnPickerSettings: mockedColumnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -217,8 +169,6 @@ describe("useColumns", () => {
|
|||
selectedColumns: mockedSelectedColumns,
|
||||
columnCategories: mockedCategories,
|
||||
onSave,
|
||||
columnPickerSettings: mockedColumnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -226,11 +176,28 @@ describe("useColumns", () => {
|
|||
act(() => result.current.handlers.onMove(1, 0));
|
||||
|
||||
// Assert
|
||||
expect(result.current.visibleColumns).toEqual(
|
||||
expectedVisibleColumns.reverse(),
|
||||
);
|
||||
expect(result.current.visibleColumns).toEqual([
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjE0",
|
||||
title: "Color",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "name",
|
||||
title: "Name",
|
||||
width: 200,
|
||||
icon: "arrowUp",
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjIx",
|
||||
title: "ABV",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it("should call onSave when column is changed", () => {
|
||||
it("should call onSave when column is toggled", () => {
|
||||
// Arrange
|
||||
const { result } = renderHook(() =>
|
||||
useColumns({
|
||||
|
@ -238,13 +205,11 @@ describe("useColumns", () => {
|
|||
selectedColumns: mockedSelectedColumns,
|
||||
columnCategories: mockedCategories,
|
||||
onSave,
|
||||
columnPickerSettings: mockedColumnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
}),
|
||||
);
|
||||
|
||||
// Act
|
||||
act(() => result.current.handlers.onChange(["name"]));
|
||||
act(() => result.current.handlers.onToggle("name"));
|
||||
|
||||
// Assert
|
||||
expect(onSave).toHaveBeenCalledTimes(1);
|
||||
|
@ -257,23 +222,15 @@ describe("useColumns", () => {
|
|||
selectedColumns: mockedSelectedColumns,
|
||||
columnCategories: mockedCategories,
|
||||
onSave,
|
||||
columnPickerSettings: mockedColumnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
}),
|
||||
);
|
||||
|
||||
// Act
|
||||
act(() =>
|
||||
result.current.handlers.onChange([
|
||||
"name",
|
||||
"attribute:QXR0cmlidXRlOjE0",
|
||||
"attribute:QXR0cmlidXRlOjIx",
|
||||
]),
|
||||
);
|
||||
act(() => result.current.handlers.onToggle("attribute:QXR0cmlidXRlOjI3"));
|
||||
|
||||
// Assert
|
||||
expect(result.current.recentlyAddedColumn).toEqual(
|
||||
"attribute:QXR0cmlidXRlOjIx",
|
||||
"attribute:QXR0cmlidXRlOjI3",
|
||||
);
|
||||
});
|
||||
it("should update dynamic columns when new column is picked", () => {
|
||||
|
@ -284,21 +241,14 @@ describe("useColumns", () => {
|
|||
selectedColumns: mockedSelectedColumns,
|
||||
columnCategories: mockedCategories,
|
||||
onSave,
|
||||
columnPickerSettings: mockedColumnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
}),
|
||||
);
|
||||
|
||||
// Act
|
||||
act(() =>
|
||||
result.current.handlers.onDynamicColumnSelect([
|
||||
...mockedColumnPickerSettings,
|
||||
"attribute:QXR0cmlidXRlOjI3",
|
||||
]),
|
||||
);
|
||||
act(() => result.current.handlers.onToggle("attribute:QXR0cmlidXRlOjI3"));
|
||||
|
||||
// Assert
|
||||
expect(setDynamicColumnSettings).toHaveBeenCalledTimes(1);
|
||||
expect(onSave).toHaveBeenCalledTimes(1);
|
||||
expect(result.current.dynamicColumns).toEqual([
|
||||
...expectedDynamicColumns,
|
||||
{
|
||||
|
@ -317,42 +267,20 @@ describe("useColumns", () => {
|
|||
selectedColumns: mockedSelectedColumns,
|
||||
columnCategories: mockedCategories,
|
||||
onSave,
|
||||
columnPickerSettings: mockedColumnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
}),
|
||||
);
|
||||
|
||||
// Act
|
||||
act(() =>
|
||||
result.current.handlers.onDynamicColumnSelect([
|
||||
result.current.handlers.onToggle(
|
||||
// ABV - which is already selected
|
||||
"attribute:QXR0cmlidXRlOjIx",
|
||||
"attribute:QXR0cmlidXRlOjE1",
|
||||
"attribute:QXR0cmlidXRlOjI5",
|
||||
"attribute:QXR0cmlidXRlOjE0",
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(setDynamicColumnSettings).toHaveBeenCalledTimes(1);
|
||||
expect(onSave).toHaveBeenCalledTimes(1);
|
||||
expect(result.current.dynamicColumns).toEqual([
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjIx",
|
||||
title: "ABV",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjE1",
|
||||
title: "Bottle Size",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjI5",
|
||||
title: "Tag",
|
||||
metaGroup: "Attributes",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
id: "attribute:QXR0cmlidXRlOjE0",
|
||||
title: "Color",
|
||||
|
|
|
@ -2,16 +2,13 @@
|
|||
import useStateFromProps from "@dashboard/hooks/useStateFromProps";
|
||||
import { addAtIndex, removeAtIndex } from "@dashboard/utils/lists";
|
||||
import { GridColumn } from "@glideapps/glide-data-grid";
|
||||
import difference from "lodash/difference";
|
||||
import React from "react";
|
||||
|
||||
import { AvailableColumn } from "../types";
|
||||
import {
|
||||
areCategoriesLoaded,
|
||||
extractAvailableNodesFromCategories,
|
||||
extractSelectedNodesFromCategories,
|
||||
filterSelectedColumns,
|
||||
mergeCurrentDynamicColumnsWithCandidates,
|
||||
findDynamicColumn,
|
||||
mergeSelectedColumns,
|
||||
sortColumns,
|
||||
} from "./utils";
|
||||
|
@ -34,8 +31,6 @@ export interface UseColumnsProps {
|
|||
columnCategories?: ColumnCategory[];
|
||||
selectedColumns: string[];
|
||||
onSave: (columns: string[]) => void;
|
||||
columnPickerSettings?: string[];
|
||||
setDynamicColumnSettings?: (cols: string[]) => void;
|
||||
}
|
||||
|
||||
export const useColumns = ({
|
||||
|
@ -43,8 +38,6 @@ export const useColumns = ({
|
|||
selectedColumns,
|
||||
columnCategories,
|
||||
onSave,
|
||||
columnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
}: UseColumnsProps) => {
|
||||
const [dynamicColumns, updateDynamicColumns] = React.useState<
|
||||
AvailableColumn[] | null | undefined
|
||||
|
@ -58,11 +51,11 @@ export const useColumns = ({
|
|||
updateDynamicColumns(
|
||||
sortColumns(
|
||||
extractSelectedNodesFromCategories(columnCategories),
|
||||
columnPickerSettings,
|
||||
selectedColumns,
|
||||
),
|
||||
);
|
||||
}
|
||||
}, [columnCategories, columnPickerSettings, dynamicColumns]);
|
||||
}, [columnCategories, selectedColumns, dynamicColumns]);
|
||||
|
||||
const initialColumnsState = React.useMemo(
|
||||
() =>
|
||||
|
@ -120,47 +113,37 @@ export const useColumns = ({
|
|||
[setVisibleColumns],
|
||||
);
|
||||
|
||||
const onChange = (columns: string[]) => {
|
||||
// Recently added is used by datagrid to auto-scroll to the column
|
||||
setRecentlyAddedColumn(difference(columns, selectedColumns)[0]);
|
||||
// Saves in LS
|
||||
onSave(columns);
|
||||
const onToggle = (columnId: string) => {
|
||||
const isAdded = !selectedColumns.includes(columnId);
|
||||
const isDynamic = columnId.includes(":");
|
||||
if (isAdded) {
|
||||
onSave([...selectedColumns, columnId]);
|
||||
setRecentlyAddedColumn(columnId);
|
||||
} else {
|
||||
onSave(selectedColumns.filter(id => id !== columnId));
|
||||
}
|
||||
if (isDynamic) {
|
||||
if (isAdded) {
|
||||
updateDynamicColumns(prevDynamicColumns => [
|
||||
...(prevDynamicColumns ?? []),
|
||||
findDynamicColumn(columnCategories, columnId),
|
||||
]);
|
||||
} else {
|
||||
updateDynamicColumns(prevDynamicColumns =>
|
||||
(prevDynamicColumns ?? []).filter(column => column.id !== columnId),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Should be used only for special cases
|
||||
const onCustomUpdateVisible = setVisibleColumns;
|
||||
|
||||
const onDynamicColumnSelect = (selected: string[]) => {
|
||||
if (typeof setDynamicColumnSettings !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is optimistic update - dynamic columns are only synced
|
||||
// with the API on the initial render
|
||||
setDynamicColumnSettings(selected);
|
||||
updateDynamicColumns(prevDynamicColumns =>
|
||||
filterSelectedColumns(
|
||||
sortColumns(
|
||||
mergeCurrentDynamicColumnsWithCandidates(
|
||||
prevDynamicColumns,
|
||||
filterSelectedColumns(
|
||||
extractAvailableNodesFromCategories(columnCategories),
|
||||
selected,
|
||||
),
|
||||
),
|
||||
selected,
|
||||
),
|
||||
selected,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
handlers: {
|
||||
onMove,
|
||||
onResize,
|
||||
onChange,
|
||||
onDynamicColumnSelect,
|
||||
onToggle,
|
||||
onCustomUpdateVisible,
|
||||
},
|
||||
visibleColumns,
|
||||
|
@ -168,7 +151,6 @@ export const useColumns = ({
|
|||
dynamicColumns,
|
||||
selectedColumns,
|
||||
columnCategories,
|
||||
columnPickerSettings,
|
||||
recentlyAddedColumn,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { ArrowLeftIcon, CloseIcon } from "@saleor/macaw-ui/next";
|
||||
import uniqBy from "lodash/uniqBy";
|
||||
import React, { Dispatch, SetStateAction } from "react";
|
||||
|
||||
import { AvailableColumn } from "../types";
|
||||
|
@ -60,11 +59,6 @@ export const sortColumns = (
|
|||
order: string[],
|
||||
) => columns?.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
|
||||
|
||||
export const filterSelectedColumns = (
|
||||
columns: AvailableColumn[] | undefined,
|
||||
selected: string[],
|
||||
) => columns?.filter(column => selected.includes(column.id));
|
||||
|
||||
export const areCategoriesLoaded = (categories: ColumnCategory[] | undefined) =>
|
||||
categories?.every(category => Array.isArray(category.selectedNodes));
|
||||
|
||||
|
@ -72,9 +66,13 @@ export const extractSelectedNodesFromCategories = (
|
|||
categories: ColumnCategory[] | undefined,
|
||||
) => categories?.flatMap(category => category.selectedNodes);
|
||||
|
||||
export const extractAvailableNodesFromCategories = (
|
||||
categories: ColumnCategory[] | undefined,
|
||||
) => categories?.flatMap(category => category.availableNodes);
|
||||
export const findDynamicColumn = (
|
||||
categories: ColumnCategory[],
|
||||
columnId: string,
|
||||
) =>
|
||||
categories
|
||||
.flatMap(category => category.availableNodes)
|
||||
.find(column => column?.id === columnId);
|
||||
|
||||
export const mergeSelectedColumns = ({
|
||||
staticColumns,
|
||||
|
@ -88,8 +86,3 @@ export const mergeSelectedColumns = ({
|
|||
[...staticColumns, ...(dynamicColumns ?? [])].filter(
|
||||
column => selectedColumns.includes(column.id) || column.id === "empty",
|
||||
);
|
||||
|
||||
export const mergeCurrentDynamicColumnsWithCandidates = (
|
||||
dynamicColumns: AvailableColumn[] | undefined,
|
||||
candidates: AvailableColumn[],
|
||||
) => uniqBy([...(dynamicColumns ?? []), ...candidates], "id");
|
||||
|
|
|
@ -129,7 +129,7 @@ export const OrderDraftListDatagrid = ({
|
|||
<ColumnPicker
|
||||
staticColumns={staticColumns}
|
||||
selectedColumns={selectedColumns}
|
||||
onSave={handlers.onChange}
|
||||
onToggle={handlers.onToggle}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -130,7 +130,7 @@ export const OrderListDatagrid: React.FC<OrderListDatagridProps> = ({
|
|||
<ColumnPicker
|
||||
staticColumns={staticColumns}
|
||||
selectedColumns={selectedColumns}
|
||||
onSave={handlers.onChange}
|
||||
onToggle={handlers.onToggle}
|
||||
/>
|
||||
)}
|
||||
fullScreenTitle={intl.formatMessage(messages.orders)}
|
||||
|
|
|
@ -69,8 +69,6 @@ interface ProductListDatagridProps
|
|||
>;
|
||||
onSelectProductIds: (rowsIndex: number[], clearSelection: () => void) => void;
|
||||
hasRowHover?: boolean;
|
||||
columnPickerSettings: string[];
|
||||
setDynamicColumnSettings: (cols: string[]) => void;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
|
@ -91,8 +89,6 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
|||
onSelectProductIds,
|
||||
hasRowHover,
|
||||
rowAnchor,
|
||||
columnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const searchProductType = useSearchProductTypes();
|
||||
|
@ -149,8 +145,6 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
|||
}),
|
||||
selectedColumns: settings.columns,
|
||||
onSave: handleColumnChange,
|
||||
columnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
});
|
||||
|
||||
// Logic for updating sort icon in dynamic columns
|
||||
|
@ -291,9 +285,7 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
|||
dynamicColumns={dynamicColumns}
|
||||
selectedColumns={selectedColumns}
|
||||
columnCategories={columnCategories}
|
||||
onDynamicColumnSelect={handlers.onDynamicColumnSelect}
|
||||
columnPickerSettings={columnPickerSettings}
|
||||
onSave={handlers.onChange}
|
||||
onToggle={handlers.onToggle}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -66,8 +66,6 @@ const props: ProductListPageProps = {
|
|||
...pageListProps.default.settings,
|
||||
columns: ["availability", "productType", "price"],
|
||||
},
|
||||
columnPickerSettings: [],
|
||||
setDynamicColumnSettings: () => undefined,
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ProductListPage> = {
|
||||
|
|
|
@ -72,8 +72,6 @@ export interface ProductListPageProps
|
|||
onExport: () => void;
|
||||
onTabUpdate: (tabName: string) => void;
|
||||
onTabDelete: (tabIndex: number) => void;
|
||||
columnPickerSettings: string[];
|
||||
setDynamicColumnSettings: (cols: string[]) => void;
|
||||
availableColumnsAttributesOpts: ReturnType<
|
||||
typeof useAvailableColumnAttributesLazyQuery
|
||||
>;
|
||||
|
@ -111,8 +109,6 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
|||
tabs,
|
||||
onTabUpdate,
|
||||
hasPresetsChanged,
|
||||
columnPickerSettings,
|
||||
setDynamicColumnSettings,
|
||||
selectedProductIds,
|
||||
onProductsDelete,
|
||||
clearRowSelection,
|
||||
|
@ -302,8 +298,6 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
|||
onRowClick={id => {
|
||||
navigate(productUrl(id));
|
||||
}}
|
||||
columnPickerSettings={columnPickerSettings}
|
||||
setDynamicColumnSettings={setDynamicColumnSettings}
|
||||
/>
|
||||
) : (
|
||||
<ProductListTiles
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import { filterable } from "@dashboard/attributes/utils/data";
|
||||
import ActionDialog from "@dashboard/components/ActionDialog";
|
||||
import useAppChannel from "@dashboard/components/AppLayout/AppChannelContext";
|
||||
import { useColumnPickerSettings } from "@dashboard/components/Datagrid/ColumnPicker/useColumnPickerSettings";
|
||||
import DeleteFilterTabDialog from "@dashboard/components/DeleteFilterTabDialog";
|
||||
import SaveFilterTabDialog from "@dashboard/components/SaveFilterTabDialog";
|
||||
import { useShopLimitsQuery } from "@dashboard/components/Shop/queries";
|
||||
|
@ -90,9 +89,6 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
|||
ListViews.PRODUCT_LIST,
|
||||
);
|
||||
|
||||
const { columnPickerSettings, setDynamicColumnsSettings } =
|
||||
useColumnPickerSettings("PRODUCT_LIST");
|
||||
|
||||
usePaginationReset(productListUrl, params, settings.rowNumber);
|
||||
|
||||
const intl = useIntl();
|
||||
|
@ -289,7 +285,7 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
|||
[params, settings.rowNumber],
|
||||
);
|
||||
|
||||
const filteredColumnIds = (columnPickerSettings ?? [])
|
||||
const filteredColumnIds = (settings.columns ?? [])
|
||||
.filter(isAttributeColumnValue)
|
||||
.map(getAttributeIdFromColumnValue);
|
||||
|
||||
|
@ -420,8 +416,6 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
|||
tabs={presets.map(tab => tab.name)}
|
||||
onExport={() => openModal("export")}
|
||||
selectedChannelId={selectedChannel?.id}
|
||||
columnPickerSettings={columnPickerSettings}
|
||||
setDynamicColumnSettings={setDynamicColumnsSettings}
|
||||
selectedProductIds={selectedRowIds}
|
||||
onSelectProductIds={handleSetSelectedProductIds}
|
||||
clearRowSelection={clearRowSelection}
|
||||
|
|
Loading…
Reference in a new issue