Skip to content

Commit a50fa6f

Browse files
authored
fix(catalyst): Fix WishlistDetails page from exceeding complexity limit, fix e2e tests (#2820)
1 parent 52ee85e commit a50fa6f

File tree

7 files changed

+184
-20
lines changed

7 files changed

+184
-20
lines changed

.changeset/silent-pianos-search.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
"@bigcommerce/catalyst-core": patch
3+
---
4+
5+
Fix WishlistDetails page from exceeding GraphQL complexity limit, and fix wishlist e2e tests.
6+
7+
Additionally, add the `required` prop to `core/components/wishlist/modals/new.tsx` and `core/components/wishlist/modals/rename.tsx`
8+
9+
## Migration
10+
11+
### Step 1: Update wishlist GraphQL fragments
12+
13+
In `core/components/wishlist/fragment.ts`, replace the `WishlistItemProductFragment` to use explicit fields instead of `ProductCardFragment`:
14+
15+
```typescript
16+
export const WishlistItemProductFragment = graphql(
17+
`
18+
fragment WishlistItemProductFragment on Product {
19+
entityId
20+
name
21+
defaultImage {
22+
altText
23+
url: urlTemplate(lossy: true)
24+
}
25+
path
26+
brand {
27+
name
28+
path
29+
}
30+
reviewSummary {
31+
numberOfReviews
32+
averageRating
33+
}
34+
sku
35+
showCartAction
36+
inventory {
37+
isInStock
38+
}
39+
availabilityV2 {
40+
status
41+
}
42+
...PricingFragment
43+
}
44+
`,
45+
[PricingFragment],
46+
);
47+
```
48+
49+
Remove `ProductCardFragment` from all fragment dependencies in the same file.
50+
51+
### Step 2: Update product card transformer
52+
53+
In `core/data-transformers/product-card-transformer.ts`:
54+
55+
1. Import the `WishlistItemProductFragment`:
56+
```typescript
57+
import { WishlistItemProductFragment } from '~/components/wishlist/fragment';
58+
```
59+
60+
2. Update the `singleProductCardTransformer` function signature to accept both fragment types:
61+
```typescript
62+
product: ResultOf<typeof ProductCardFragment | typeof WishlistItemProductFragment>
63+
```
64+
65+
3. Add a conditional check for the `inventoryMessage` field:
66+
```typescript
67+
inventoryMessage:
68+
'variants' in product
69+
? getInventoryMessage(product, outOfStockMessage, showBackorderMessage)
70+
: undefined,
71+
```
72+
73+
4. Update the `productCardTransformer` function signature similarly:
74+
```typescript
75+
products: Array<ResultOf<typeof ProductCardFragment | typeof WishlistItemProductFragment>>
76+
```
77+
78+
### Step 3: Fix wishlist e2e tests
79+
80+
In `core/tests/ui/e2e/account/wishlists.spec.ts`, update label selectors to use `{ exact: true }` for specificity:
81+
82+
Update all locators for the wishlist name input selectors:
83+
```diff
84+
- page.getByLabel(t('Form.nameLabel'))
85+
+ page.getByLabel(t('Form.nameLabel'), { exact: true })
86+
```
87+
88+
### Step 4: Fix mobile wishlist e2e tests
89+
90+
In `core/tests/ui/e2e/account/wishlists.mobile.spec.ts`, update translation calls to use namespace prefixes:
91+
92+
1. Update the translation initialization:
93+
```diff
94+
- const t = await getTranslations('Account.Wishlist');
95+
+ const t = await getTranslations();
96+
```
97+
98+
2. Update all translation keys to include the namespace:
99+
```diff
100+
- await locator.getByRole('button', { name: t('actionsTitle') }).click();
101+
- await page.getByRole('menuitem', { name: t('share') }).click();
102+
+ await locator.getByRole('button', { name: t('Wishlist.actionsTitle') }).click();
103+
+ await page.getByRole('menuitem', { name: t('Wishlist.share') }).click();
104+
```
105+
106+
```diff
107+
- await expect(page.getByText(t('shareSuccess'))).toBeVisible();
108+
+ await expect(page.getByText(t('Wishlist.shareSuccess'))).toBeVisible();
109+
```
110+
111+
### Step 5: Add `required` prop to wishlist modals
112+
113+
Update the modal forms to include the `required` prop on the name input field:
114+
115+
In `core/components/wishlist/modals/new.tsx`:
116+
```diff
117+
<Input
118+
{...getInputProps(fields.wishlistName, { type: 'text' })}
119+
defaultValue={defaultValue.current}
120+
errors={fields.wishlistName.errors}
121+
key={fields.wishlistName.id}
122+
label={nameLabel}
123+
onChange={(e) => {
124+
defaultValue.current = e.target.value;
125+
}}
126+
+ required
127+
/>
128+
```
129+
130+
In `core/components/wishlist/modals/rename.tsx`:
131+
```diff
132+
<Input
133+
{...getInputProps(fields.wishlistName, { type: 'text' })}
134+
defaultValue={defaultValue.current}
135+
errors={fields.wishlistName.errors}
136+
key={fields.wishlistName.id}
137+
label={nameLabel}
138+
onChange={(e) => {
139+
defaultValue.current = e.target.value;
140+
}}
141+
+ required
142+
/>
143+
```
144+

core/components/wishlist/fragment.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
import { PaginationFragment } from '~/client/fragments/pagination';
2+
import { PricingFragment } from '~/client/fragments/pricing';
23
import { graphql } from '~/client/graphql';
3-
import { ProductCardFragment } from '~/components/product-card/fragment';
44

55
export const WishlistItemProductFragment = graphql(
66
`
77
fragment WishlistItemProductFragment on Product {
8-
...ProductCardFragment
8+
entityId
9+
name
10+
defaultImage {
11+
altText
12+
url: urlTemplate(lossy: true)
13+
}
14+
path
15+
brand {
16+
name
17+
path
18+
}
19+
reviewSummary {
20+
numberOfReviews
21+
averageRating
22+
}
923
sku
1024
showCartAction
1125
inventory {
@@ -14,9 +28,10 @@ export const WishlistItemProductFragment = graphql(
1428
availabilityV2 {
1529
status
1630
}
31+
...PricingFragment
1732
}
1833
`,
19-
[ProductCardFragment],
34+
[PricingFragment],
2035
);
2136

2237
export const WishlistItemFragment = graphql(
@@ -30,7 +45,7 @@ export const WishlistItemFragment = graphql(
3045
}
3146
}
3247
`,
33-
[WishlistItemProductFragment, ProductCardFragment],
48+
[WishlistItemProductFragment],
3449
);
3550

3651
export const WishlistFragment = graphql(
@@ -52,7 +67,7 @@ export const WishlistFragment = graphql(
5267
}
5368
}
5469
`,
55-
[WishlistItemFragment, ProductCardFragment],
70+
[WishlistItemFragment],
5671
);
5772

5873
export const WishlistsFragment = graphql(
@@ -68,7 +83,7 @@ export const WishlistsFragment = graphql(
6883
}
6984
}
7085
`,
71-
[WishlistFragment, ProductCardFragment, PaginationFragment],
86+
[WishlistFragment, PaginationFragment],
7287
);
7388

7489
export const WishlistPaginatedItemsFragment = graphql(
@@ -93,7 +108,7 @@ export const WishlistPaginatedItemsFragment = graphql(
93108
}
94109
}
95110
`,
96-
[WishlistItemFragment, ProductCardFragment, PaginationFragment],
111+
[WishlistItemFragment, PaginationFragment],
97112
);
98113

99114
export const PublicWishlistFragment = graphql(
@@ -117,5 +132,5 @@ export const PublicWishlistFragment = graphql(
117132
}
118133
}
119134
`,
120-
[WishlistItemFragment, ProductCardFragment, PaginationFragment],
135+
[WishlistItemFragment, PaginationFragment],
121136
);

core/components/wishlist/modals/new.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const NewWishlistModal = ({
3232
onChange={(e) => {
3333
defaultValue.current = e.target.value;
3434
}}
35+
required
3536
/>
3637
{state.lastResult?.status === 'error' && (
3738
<div className="mt-4">

core/components/wishlist/modals/rename.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export const RenameWishlistModal = ({
3333
onChange={(e) => {
3434
defaultValue.current = e.target.value;
3535
}}
36+
required
3637
/>
3738
{state.lastResult?.status === 'error' && (
3839
<div className="mt-4">

core/data-transformers/product-card-transformer.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { getFormatter } from 'next-intl/server';
55
import { Product } from '@/vibes/soul/primitives/product-card';
66
import { ExistingResultType } from '~/client/util';
77
import { ProductCardFragment } from '~/components/product-card/fragment';
8+
import { WishlistItemProductFragment } from '~/components/wishlist/fragment';
89

910
import { pricesTransformer } from './prices-transformer';
1011

@@ -46,7 +47,7 @@ const getInventoryMessage = (
4647
};
4748

4849
export const singleProductCardTransformer = (
49-
product: ResultOf<typeof ProductCardFragment>,
50+
product: ResultOf<typeof ProductCardFragment | typeof WishlistItemProductFragment>,
5051
format: ExistingResultType<typeof getFormatter>,
5152
outOfStockMessage?: string,
5253
showBackorderMessage?: boolean,
@@ -62,12 +63,15 @@ export const singleProductCardTransformer = (
6263
subtitle: product.brand?.name ?? undefined,
6364
rating: product.reviewSummary.averageRating,
6465
numberOfReviews: product.reviewSummary.numberOfReviews,
65-
inventoryMessage: getInventoryMessage(product, outOfStockMessage, showBackorderMessage),
66+
inventoryMessage:
67+
'variants' in product
68+
? getInventoryMessage(product, outOfStockMessage, showBackorderMessage)
69+
: undefined,
6670
};
6771
};
6872

6973
export const productCardTransformer = (
70-
products: Array<ResultOf<typeof ProductCardFragment>>,
74+
products: Array<ResultOf<typeof ProductCardFragment | typeof WishlistItemProductFragment>>,
7175
format: ExistingResultType<typeof getFormatter>,
7276
outOfStockMessage?: string,
7377
showBackorderMessage?: boolean,

core/tests/ui/e2e/account/wishlists.mobile.spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,21 @@ test('Share button calls navigator.share with the correct URL', async ({ page, c
2828
};
2929
});
3030

31-
const t = await getTranslations('Wishlist');
31+
const t = await getTranslations();
3232
const { id: customerId } = await customer.login();
3333
const { name, token } = await customer.createWishlist({
3434
customerId,
3535
isPublic: true,
3636
});
3737

3838
await page.goto('/account/wishlists/');
39-
await expect(page.getByRole('heading', { name: t('title'), exact: true })).toBeVisible();
4039

4140
const locator = page.getByRole('region', { name });
4241

43-
await locator.getByRole('button', { name: t('actionsTitle') }).click();
44-
await page.getByRole('menuitem', { name: t('share') }).click();
42+
await locator.getByRole('button', { name: t('Wishlist.actionsTitle') }).click();
43+
await page.getByRole('menuitem', { name: t('Wishlist.share') }).click();
4544

46-
await expect(page.getByText(t('shareSuccess'))).toBeVisible();
45+
await expect(page.getByText(t('Wishlist.shareSuccess'))).toBeVisible();
4746

4847
const expectedUrl = `${testEnv.PLAYWRIGHT_TEST_BASE_URL}/wishlist/${token}`;
4948

core/tests/ui/e2e/account/wishlists.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,11 @@ test.describe('Wishlist actions menu', () => {
234234
page.getByRole('heading', { name: t('Modal.renameTitle', { name }) }),
235235
).toBeVisible();
236236

237-
await expect(page.getByLabel(t('Form.nameLabel'))).toHaveValue(name);
237+
await expect(page.getByLabel(t('Form.nameLabel'), { exact: true })).toHaveValue(name);
238238

239239
const newName = `${name} (renamed)`;
240240

241-
await page.getByLabel(t('Form.nameLabel')).fill(newName);
241+
await page.getByLabel(t('Form.nameLabel'), { exact: true }).fill(newName);
242242
await page.getByRole('button', { name: t('Modal.save') }).click();
243243

244244
await expect(page.getByText(t('Result.updateSuccess'))).toBeVisible();
@@ -258,7 +258,7 @@ test.describe('Wishlist actions menu', () => {
258258
await locator.getByRole('button', { name: t('actionsTitle') }).click();
259259
await page.getByRole('menuitem', { name: t('rename') }).click();
260260

261-
await page.getByLabel(t('Form.nameLabel')).fill('');
261+
await page.getByLabel(t('Form.nameLabel'), { exact: true }).fill('');
262262
await page.getByRole('button', { name: t('Modal.save') }).click();
263263

264264
await expect(page.getByText(t('Errors.nameRequired'))).toBeVisible();
@@ -282,7 +282,7 @@ test.describe('Wishlist actions menu', () => {
282282

283283
await locator.getByRole('button', { name: t('actionsTitle') }).click();
284284
await page.getByRole('menuitem', { name: t('rename') }).click();
285-
await page.getByLabel(t('Form.nameLabel')).fill(`${name} (renamed)`);
285+
await page.getByLabel(t('Form.nameLabel'), { exact: true }).fill(`${name} (renamed)`);
286286
await page.getByRole('button', { name: t('Modal.save') }).click();
287287

288288
await expect(page.getByText(t('Errors.unauthorized'))).toBeVisible();

0 commit comments

Comments
 (0)