diff --git a/.changeset/orange-pugs-tie.md b/.changeset/orange-pugs-tie.md new file mode 100644 index 00000000000..e3dc21c183d --- /dev/null +++ b/.changeset/orange-pugs-tie.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": patch +--- + +Batch of sentry errors has been fixed diff --git a/package-lock.json b/package-lock.json index b80091e4a22..11c69bd7d6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@dnd-kit/core": "^6.0.8", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", - "@editorjs/editorjs": "^2.24.3", + "@editorjs/editorjs": "^2.30.7", "@editorjs/header": "^2.6.2", "@editorjs/list": "^1.7.0", "@editorjs/paragraph": "^2.8.0", @@ -1717,13 +1717,9 @@ } }, "node_modules/@editorjs/editorjs": { - "version": "2.24.3", - "license": "Apache-2.0", - "dependencies": { - "codex-notifier": "^1.1.2", - "codex-tooltip": "^1.0.5", - "nanoid": "^3.1.22" - } + "version": "2.30.7", + "resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.30.7.tgz", + "integrity": "sha512-FfdeUqrgcKWC+Cy2GW6Dxup6s2TaRKokR4FL+HKXshu6h9Y//rrx4SQkURgkZOCSbV77t9btbmAXdFXWGB+diw==" }, "node_modules/@editorjs/embed": { "version": "2.5.3", @@ -9724,14 +9720,6 @@ "graphql": "^15.5.0 || ^16.0.0" } }, - "node_modules/codex-notifier": { - "version": "1.1.2", - "license": "MIT" - }, - "node_modules/codex-tooltip": { - "version": "1.0.5", - "license": "MIT" - }, "node_modules/collect-v8-coverage": { "version": "1.0.1", "license": "MIT", @@ -17217,6 +17205,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, "funding": [ { "type": "github", @@ -22512,12 +22501,9 @@ } }, "@editorjs/editorjs": { - "version": "2.24.3", - "requires": { - "codex-notifier": "^1.1.2", - "codex-tooltip": "^1.0.5", - "nanoid": "^3.1.22" - } + "version": "2.30.7", + "resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.30.7.tgz", + "integrity": "sha512-FfdeUqrgcKWC+Cy2GW6Dxup6s2TaRKokR4FL+HKXshu6h9Y//rrx4SQkURgkZOCSbV77t9btbmAXdFXWGB+diw==" }, "@editorjs/embed": { "version": "2.5.3", @@ -28140,12 +28126,6 @@ } } }, - "codex-notifier": { - "version": "1.1.2" - }, - "codex-tooltip": { - "version": "1.0.5" - }, "collect-v8-coverage": { "version": "1.0.1", "optional": true @@ -33249,7 +33229,8 @@ "nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true }, "natural-compare": { "version": "1.4.0", diff --git a/package.json b/package.json index 6a21e20e4cd..3bc0f75162a 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@dnd-kit/core": "^6.0.8", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", - "@editorjs/editorjs": "^2.24.3", + "@editorjs/editorjs": "^2.30.7", "@editorjs/header": "^2.6.2", "@editorjs/list": "^1.7.0", "@editorjs/paragraph": "^2.8.0", diff --git a/src/auth/hooks/useAuthProvider.ts b/src/auth/hooks/useAuthProvider.ts index 30ab804c3f8..93755c14467 100644 --- a/src/auth/hooks/useAuthProvider.ts +++ b/src/auth/hooks/useAuthProvider.ts @@ -173,9 +173,19 @@ export function useAuthProvider({ intl, notify, apolloClient }: UseAuthProviderO } }; const handleRequestExternalLogin = async (pluginId: string, input: RequestExternalLoginInput) => { + let stringifyInput: string; + + try { + stringifyInput = JSON.stringify(input); + } catch (error) { + setErrors(["externalLoginError"]); + + return; + } + const result = await getExternalAuthUrl({ pluginId, - input: JSON.stringify(input), + input: stringifyInput, }); return result?.data?.externalAuthenticationUrl; diff --git a/src/components/AddressEdit/useAddressValidation.test.ts b/src/components/AddressEdit/useAddressValidation.test.ts index 560208a7531..96b0e729cc0 100644 --- a/src/components/AddressEdit/useAddressValidation.test.ts +++ b/src/components/AddressEdit/useAddressValidation.test.ts @@ -1,12 +1,13 @@ -import { useAddressValidationRulesQuery } from "@dashboard/graphql"; +import { AddressValidationRulesQuery, useAddressValidationRulesQuery } from "@dashboard/graphql"; import { renderHook } from "@testing-library/react-hooks"; -import { useAddressValidation } from "./useAddressValidation"; +import { selectRules, useAddressValidation } from "./useAddressValidation"; jest.mock("@dashboard/graphql", () => ({ CountryCode: jest.requireActual("@dashboard/graphql").CountryCode, useAddressValidationRulesQuery: jest.fn(), })); + describe("useAddressValidation", () => { it("skips loading validation rules when country is not provided", () => { // Arrange @@ -139,3 +140,54 @@ describe("useAddressValidation", () => { expect(displayValue).toEqual(""); }); }); + +describe("selectRules", () => { + it("should return select rules when available", () => { + // Arrange + const data = { + addressValidationRules: { + countryAreaChoices: [ + { raw: "AL", verbose: "Alabama" }, + { raw: "AN", verbose: "Ancona" }, + ], + allowedFields: ["country"], + }, + } as AddressValidationRulesQuery; + + // Act + const rules = selectRules(data); + + // Assert + expect(rules).toEqual({ + countryAreaChoices: [ + { raw: "AL", verbose: "Alabama" }, + { raw: "AN", verbose: "Ancona" }, + ], + allowedFields: ["country"], + }); + }); + + it("should return empty array when addressValidationRules is not provided", () => { + // Arrange + const data = { + addressValidationRules: null, + } as AddressValidationRulesQuery; + + // Act + const rules = selectRules(data); + + // Assert + expect(rules).toEqual({ countryAreaChoices: [], allowedFields: [] }); + }); + + it("should return empty array when data is not provided", () => { + // Arrange + const data = undefined; + + // Act + const rules = selectRules(data); + + // Assert + expect(rules).toEqual({ countryAreaChoices: [], allowedFields: [] }); + }); +}); diff --git a/src/components/AddressEdit/useAddressValidation.ts b/src/components/AddressEdit/useAddressValidation.ts index aa92f8671c2..b7cec8fa771 100644 --- a/src/components/AddressEdit/useAddressValidation.ts +++ b/src/components/AddressEdit/useAddressValidation.ts @@ -18,8 +18,15 @@ const prepareChoices = (values: ChoiceValue[]): AreaChoices[] => value: v.verbose, raw: v.raw, })); -const selectRules = (data: AddressValidationRulesQuery) => - data ? data.addressValidationRules : { countryAreaChoices: [], allowedFields: [] }; + +export const selectRules = (data: AddressValidationRulesQuery | null | undefined) => { + if (!data || !data.addressValidationRules) { + return { countryAreaChoices: [], allowedFields: [] }; + } + + return data.addressValidationRules; +}; + const useValidationRules = (country?: string) => { const countryCode = CountryCode[country]; const { data, loading } = useAddressValidationRulesQuery({ @@ -30,7 +37,7 @@ const useValidationRules = (country?: string) => { return { data, loading }; }; const useAreas = (data: AddressValidationRulesQuery) => { - const rawChoices = selectRules(data).countryAreaChoices; + const rawChoices = selectRules(data)?.countryAreaChoices ?? []; const choices = prepareChoices(rawChoices); return choices; diff --git a/src/components/Datagrid/customCells/PillCell.tsx b/src/components/Datagrid/customCells/PillCell.tsx index e3f39bc4f5c..9e5d97c6a75 100644 --- a/src/components/Datagrid/customCells/PillCell.tsx +++ b/src/components/Datagrid/customCells/PillCell.tsx @@ -81,12 +81,15 @@ export const pillCellRenderer = (): CustomRenderer => ({ const tileHeight = textHeight * 1.2; // Draw the tile - ctx.fillStyle = base; - ctx.strokeStyle = border; - ctx.beginPath(); - ctx.roundRect(x + 10, y + height / 2 - tileHeight / 2, tileWidth, tileHeight, 5); - ctx.stroke(); - ctx.fill(); + if ("roundRect" in ctx) { + ctx.fillStyle = base; + ctx.strokeStyle = border; + ctx.beginPath(); + ctx.roundRect(x + 10, y + height / 2 - tileHeight / 2, tileWidth, tileHeight, 5); + ctx.stroke(); + ctx.fill(); + } + // Draw the text ctx.fillStyle = text; ctx.fillText(label, x + 15, y + height / 2 + getMiddleCenterBias(ctx, theme)); diff --git a/src/components/SortableTree/hooks/useAnnouncement.ts b/src/components/SortableTree/hooks/useAnnouncement.ts index f9e2b9e25bb..7ccab19e64e 100644 --- a/src/components/SortableTree/hooks/useAnnouncement.ts +++ b/src/components/SortableTree/hooks/useAnnouncement.ts @@ -58,7 +58,7 @@ export const useAnnouncement = ({ if (!previousItem && nextItem) { announcement = `${activeId} was ${movedVerb} before ${nextItem.id}.`; - } else if (projected.depth > previousItem.depth) { + } else if (projected?.depth > previousItem?.depth) { announcement = `${activeId} was ${nestedVerb} under ${previousItem.id}.`; } else { const previousSibling = findPreviousSibling(projected, previousItem, sortedItems); diff --git a/src/customers/views/CustomerList/CustomerList.tsx b/src/customers/views/CustomerList/CustomerList.tsx index 5d757e75fc1..2d8dea79e43 100644 --- a/src/customers/views/CustomerList/CustomerList.tsx +++ b/src/customers/views/CustomerList/CustomerList.tsx @@ -114,7 +114,7 @@ export const CustomerList: React.FC = ({ params }) => { return; } - const rowsIds = rows.map(row => customers[row].id); + const rowsIds = rows.map(row => customers[row]?.id).filter(id => id !== undefined); const haveSaveValues = isEqual(rowsIds, selectedRowIds); if (!haveSaveValues) { diff --git a/src/productTypes/index.tsx b/src/productTypes/index.tsx index 5147e8deb08..fb09c1869d4 100644 --- a/src/productTypes/index.tsx +++ b/src/productTypes/index.tsx @@ -21,7 +21,13 @@ import ProductTypeListComponent from "./views/ProductTypeList"; import ProductTypeUpdateComponent from "./views/ProductTypeUpdate"; const ProductTypeList: React.FC> = ({ location }) => { - const qs = parseQs(location.search.substr(1)) as any; + const qs = parseQs(location.search, { + ignoreQueryPrefix: true, + // As a product types list still keeps ids to remove in query params, + // we need to increase the array limit to 100, default 20, + // because qs library return object instead of an array when limit is exceeded + arrayLimit: 100, + }) as any; const params: ProductTypeListUrlQueryParams = asSortParams(qs, ProductTypeListUrlSortField); return ; diff --git a/src/products/components/ProductListDatagrid/datagrid.ts b/src/products/components/ProductListDatagrid/datagrid.ts index 9888a21a808..a040c622b4a 100644 --- a/src/products/components/ProductListDatagrid/datagrid.ts +++ b/src/products/components/ProductListDatagrid/datagrid.ts @@ -250,7 +250,7 @@ function getProductTypeCellContent( theme: DefaultTheme, rowData: RelayToFlat[number], ) { - const hue = stringToHue(rowData.productType?.name); + const hue = stringToHue(rowData?.productType?.name); const color = theme === "defaultDark" ? hueToPillColorDark(hue) : hueToPillColorLight(hue); return pillCell(rowData.productType?.name, color, COMMON_CELL_PROPS); diff --git a/src/products/components/ProductUpdatePage/utils.ts b/src/products/components/ProductUpdatePage/utils.ts index d3a4a7cce74..37b47f0d169 100644 --- a/src/products/components/ProductUpdatePage/utils.ts +++ b/src/products/components/ProductUpdatePage/utils.ts @@ -58,7 +58,7 @@ function updateVaraintWithPriceFormat( const channelId = dataChange.column.split(":")[1]; const currencyCode = getChannelCurrencyCodeById(channelId, product.channelListings); - dataChange.data.value = parseCurrency(`${dataChange.data.value}`, locale, currencyCode); + dataChange.data.value = parseCurrency(`${String(dataChange.data.value)}`, locale, currencyCode); return dataChange; } diff --git a/src/searches/useAttributeValueSearch.ts b/src/searches/useAttributeValueSearch.ts index 4bc652f5d7d..1101d765a59 100644 --- a/src/searches/useAttributeValueSearch.ts +++ b/src/searches/useAttributeValueSearch.ts @@ -54,7 +54,8 @@ export default makeSearch { if ( - prev.attribute.choices.pageInfo.endCursor === next.attribute.choices.pageInfo.endCursor + prev?.attribute?.choices?.pageInfo?.endCursor === + next?.attribute?.choices?.pageInfo?.endCursor ) { return prev; } @@ -64,7 +65,7 @@ export default makeSearch false, } as any; + +/** + * + * Fixes (hacks) "crypto is not defined" error which is likely missing implementation in jsdom + */ +import nodeCrypto from "crypto"; + +global.crypto = { + getRandomValues: function (buffer: any) { + return nodeCrypto.randomFillSync(buffer); + }, + subtle: {} as SubtleCrypto, + randomUUID: () => nodeCrypto.randomUUID(), +};