Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ac390fa
chore(ci): add --scope to vercel deploy and alias commands (#2847)
matthewvolk Jan 27, 2026
9259348
chore: update contributing file with some notes from latest release (…
jorgemoya Jan 28, 2026
207ee86
chore(ci): extract e2e tests to separate workflow (#2848)
matthewvolk Jan 28, 2026
a7395f1
chore: use dompurify instead of isomorphic version (#2852)
chanceaclark Jan 29, 2026
74dee6e
chore(catalyst): CATALYST-1267 Translate form field errors (#2844)
jordanarldt Jan 30, 2026
0633612
fix(core): use state abbreviation instead of entityId for shipping fo…
jorgemoya Feb 3, 2026
6d0d52b
[CATALYST-1686] Clarify REST Management API in Contributing (#2861)
jamesqquick Feb 4, 2026
254dbc5
[CATALYST-1743] - Create Project Commands (#2865)
jamesqquick Feb 6, 2026
d78bc85
feat: Add items stock/backorder messages to cart page (#2758)
Tharaae Feb 9, 2026
6a23c90
feat(core): enable product image pagination (#2863)
jorgemoya Feb 10, 2026
f5330c7
feat(core): add canonical URLs and hreflang alternates for SEO (#2856)
jorgemoya Feb 11, 2026
18cfdc8
feat: Remove caching on fetching product inventory data (#2801)
Tharaae Feb 11, 2026
169cbb5
feat(other): LOCAL-1444 delivery translation (#2886)
funivan Feb 18, 2026
7889cfa
fix(core): preview deployments now use correct metadataBase (#2890)
jorgemoya Feb 18, 2026
a161583
feat(ci): enable unlighthouse performance audits for vercel (#2882)
jorgemoya Feb 18, 2026
46ee3de
fix(core): conditionally include optional SEO metadata (#2898)
jorgemoya Feb 25, 2026
8d128fc
Update translations (#2897)
bc-svc-local Feb 25, 2026
5365977
Merge branch 'canary' into sync-integrations-makeswift
jorgemoya Feb 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 0 additions & 85 deletions .github/workflows/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,88 +85,3 @@ jobs:

- name: Run Tests
run: pnpm run test

e2e-tests:
name: E2E Functional Tests (${{ matrix.name }})

runs-on: ubuntu-latest

strategy:
matrix:
include:
- name: default
browsers: chromium webkit
test-filter: tests/ui/e2e
trailing-slash: true
locale-var: TESTS_LOCALE
artifact-name: playwright-report
- name: TRAILING_SLASH=false
browsers: chromium
test-filter: tests/ui/e2e --grep @no-trailing-slash
trailing-slash: false
locale-var: TESTS_LOCALE
artifact-name: playwright-report-no-trailing
- name: alternate locale
browsers: chromium
test-filter: tests/ui/e2e --grep @alternate-locale
trailing-slash: true
locale-var: TESTS_ALTERNATE_LOCALE
artifact-name: playwright-report-alternate-locale

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 2

- uses: pnpm/action-setup@v3

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps ${{ matrix.browsers }}
working-directory: ./core

# - name: Build catalyst-client
# run: pnpm --filter @bigcommerce/catalyst-client build

# - name: Build catalyst-core
# run: pnpm --filter @bigcommerce/catalyst-core build

- name: Build catalyst
run: pnpm build

- name: Start server
run: |
pnpm start &
npx wait-on http://localhost:3000 --timeout 60000
working-directory: ./core
env:
PORT: 3000
AUTH_SECRET: ${{ secrets.TESTS_AUTH_SECRET }}
AUTH_TRUST_HOST: ${{ vars.TESTS_AUTH_TRUST_HOST }}
BIGCOMMERCE_TRUSTED_PROXY_SECRET: ${{ secrets.BIGCOMMERCE_TRUSTED_PROXY_SECRET }}
TESTS_LOCALE: ${{ vars[matrix.locale-var] }}
TRAILING_SLASH: ${{ matrix.trailing-slash }}
DEFAULT_REVALIDATE_TARGET: ${{ matrix.name == 'default' && '1' || '' }}

- name: Run E2E tests
run: pnpm exec playwright test ${{ matrix.test-filter }}
working-directory: ./core
env:
PLAYWRIGHT_TEST_BASE_URL: http://localhost:3000

- name: Upload test results
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: ./core/.tests/reports/
retention-days: 3
4 changes: 2 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ jobs:
DEPLOY_ARGS+=(--env BIGCOMMERCE_ACCESS_TOKEN="$BIGCOMMERCE_ACCESS_TOKEN")
fi

DEPLOYMENT_URL=$(vercel deploy "${DEPLOY_ARGS[@]}")
DEPLOYMENT_URL=$(vercel deploy --scope="${{ vars.VERCEL_TEAM_SLUG }}" "${DEPLOY_ARGS[@]}")
echo "deployment_url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT

- name: Set Vercel Domain Alias
timeout-minutes: 5
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
vercel alias ${{ steps.deploy.outputs.deployment_url }} $DOMAIN --token="$VERCEL_TOKEN"
vercel alias ${{ steps.deploy.outputs.deployment_url }} $DOMAIN --scope="${{ vars.VERCEL_TEAM_SLUG }}" --token="$VERCEL_TOKEN"
105 changes: 105 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: E2E Tests

on:
pull_request:
types: [opened, synchronize]
branches: [canary, integrations/makeswift, integrations/b2b-makeswift]

env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
BIGCOMMERCE_STORE_HASH: ${{ vars.BIGCOMMERCE_STORE_HASH }}
BIGCOMMERCE_CHANNEL_ID: ${{ vars.BIGCOMMERCE_CHANNEL_ID }}
BIGCOMMERCE_CLIENT_ID: ${{ secrets.BIGCOMMERCE_CLIENT_ID }}
BIGCOMMERCE_CLIENT_SECRET: ${{ secrets.BIGCOMMERCE_CLIENT_SECRET }}
BIGCOMMERCE_STOREFRONT_TOKEN: ${{ secrets.BIGCOMMERCE_STOREFRONT_TOKEN }}
BIGCOMMERCE_ACCESS_TOKEN: ${{ secrets.BIGCOMMERCE_ACCESS_TOKEN }}
TEST_CUSTOMER_ID: ${{ vars.TEST_CUSTOMER_ID }}
TEST_CUSTOMER_EMAIL: ${{ vars.TEST_CUSTOMER_EMAIL }}
TEST_CUSTOMER_PASSWORD: ${{ secrets.TEST_CUSTOMER_PASSWORD }}
TESTS_FALLBACK_LOCALE: ${{ vars.TESTS_FALLBACK_LOCALE }}
TESTS_READ_ONLY: ${{ vars.TESTS_READ_ONLY }}
DEFAULT_PRODUCT_ID: ${{ vars.DEFAULT_PRODUCT_ID }}
DEFAULT_COMPLEX_PRODUCT_ID: ${{ vars.DEFAULT_COMPLEX_PRODUCT_ID }}

jobs:
e2e-tests:
name: E2E Functional Tests (${{ matrix.name }})

runs-on: ubuntu-latest

strategy:
matrix:
include:
- name: default
browsers: chromium webkit
test-filter: tests/ui/e2e
trailing-slash: true
locale-var: TESTS_LOCALE
artifact-name: playwright-report
- name: TRAILING_SLASH=false
browsers: chromium
test-filter: tests/ui/e2e --grep @no-trailing-slash
trailing-slash: false
locale-var: TESTS_LOCALE
artifact-name: playwright-report-no-trailing
- name: alternate locale
browsers: chromium
test-filter: tests/ui/e2e --grep @alternate-locale
trailing-slash: true
locale-var: TESTS_ALTERNATE_LOCALE
artifact-name: playwright-report-alternate-locale

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 2

- uses: pnpm/action-setup@v3

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps ${{ matrix.browsers }}
working-directory: ./core

- name: Build catalyst
run: pnpm build

- name: Start server
run: |
mkdir -p ./.tests/reports/
pnpm start > ./.tests/reports/nextjs.app.log 2>&1 &
npx wait-on http://localhost:3000 --timeout 60000
working-directory: ./core
env:
PORT: 3000
AUTH_SECRET: ${{ secrets.TESTS_AUTH_SECRET }}
AUTH_TRUST_HOST: ${{ vars.TESTS_AUTH_TRUST_HOST }}
BIGCOMMERCE_TRUSTED_PROXY_SECRET: ${{ secrets.BIGCOMMERCE_TRUSTED_PROXY_SECRET }}
TESTS_LOCALE: ${{ vars[matrix.locale-var] }}
TRAILING_SLASH: ${{ matrix.trailing-slash }}
DEFAULT_REVALIDATE_TARGET: ${{ matrix.name == 'default' && '1' || '' }}

- name: Run E2E tests
run: pnpm exec playwright test ${{ matrix.test-filter }}
working-directory: ./core
env:
PLAYWRIGHT_TEST_BASE_URL: http://localhost:3000

- name: Upload test results
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: ./core/.tests/reports/
retention-days: 3
2 changes: 1 addition & 1 deletion .github/workflows/regression-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ jobs:
if: failure() || success()
uses: actions/upload-artifact@v4
with:
name: unlighthouse-${{ matrix.device }}-report
name: unlighthouse-vercel-${{ matrix.device }}-report
path: './.unlighthouse/'
include-hidden-files: 'true'
30 changes: 29 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ The default branch for this repository is called `canary`. This is the primary d

To contribute to the `canary` branch, you can create a new branch off of `canary` and submit a PR against that branch.

## API Scope

Catalyst is intended to work with the [BigCommerce Storefront GraphQL API](https://developer.bigcommerce.com/docs/storefront/graphql) and not directly integrate out of the box with the [REST Management API](https://developer.bigcommerce.com/docs/rest-management).

You're welcome to integrate the REST Management API in your own fork, but we will not accept pull requests that incorporate or depend on the REST Management API. If your contribution requires Management API functionality, it is out of scope for this project.

## Makeswift Integration

In addition to `canary`, we also maintain the `integrations/makeswift` branch, which contains additional code required to integrate with [Makeswift](https://www.makeswift.com).
Expand Down Expand Up @@ -60,6 +66,7 @@ In order to complete the following steps, you will need to have met the followin
>
> - The `name` field in `core/package.json` should remain `@bigcommerce/catalyst-makeswift`
> - The `version` field in `core/package.json` should remain whatever the latest published `@bigcommerce/catalyst-makeswift` version was
> - The latest release in `core/CHANGELOG.md` should remain whatever the latest published `@bigcommerce/catalyst-makeswift` version was

4. After resolving any merge conflicts, open a new PR in GitHub to merge your `sync-integrations-makeswift` into `integrations/makeswift`. This PR should be code reviewed and approved before the next steps.

Expand Down Expand Up @@ -104,14 +111,35 @@ This ensures `integrations/makeswift` remains a faithful mirror of `canary` whil
- From this new `bump-version` branch, run `pnpm changeset`
- Select `@bigcommerce/catalyst-makeswift`
- For choosing between a `patch/minor/major` bump, you should copy the bump from Stage 1. (e.g., if `@bigcommerce/catalyst-core` went from `1.1.0` to `1.2.0`, choose `minor`)
- Example changeset:

```
---
"@bigcommerce/catalyst-makeswift": patch
---

Pulls in changes from the `@bigcommerce/catalyst-core@1.4.1` patch.
```

- Commit the generated changeset file and open a PR to merge this branch into `integrations/makeswift`
- Once merged, you can proceed to the next step

4. Merge the **Version Packages (`integrations/makeswift`)** PR: Changesets will open another PR (similar to Stage 1) bumping `@bigcommerce/catalyst-makeswift`. Merge it following the same process. This cuts a new release of the Makeswift variant.

5. **Tags and Releases:** Confirm tags exist for both `@bigcommerce/catalyst-core` and `@bigcommerce/catalyst-makeswift`. If needed, update `latest` tags in GitHub manually.

- Push manually:
```
git checkout canary
# Make sure you have the latest code
git fetch origin
git pull
git tag @bigcommerce/catalyst-core@latest -f
git push origin @bigcommerce/catalyst-core@latest -f
```

### Additional Notes

- **Tags and Releases:** Confirm tags exist for both `@bigcommerce/catalyst-core` and `@bigcommerce/catalyst-makeswift`. If needed, update `latest` tags in GitHub manually.
- **Release cadence:** Teams typically review on Wednesdays whether to cut a release, but you may cut releases more frequently as needed.

## Other Ways to Contribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { BigCommerceGQLError } from '@bigcommerce/catalyst-client';
import { SubmissionResult } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import { getTranslations } from 'next-intl/server';
import { z } from 'zod';

import { schema } from '@/vibes/soul/sections/reset-password-section/schema';
import { client } from '~/client';
import { graphql } from '~/client/graphql';

Expand All @@ -25,6 +25,10 @@ const ChangePasswordMutation = graphql(`
}
`);

const schema = z.object({
password: z.string(),
});

export async function changePassword(
{ token, customerEntityId }: { token: string; customerEntityId: string },
_prevState: { lastResult: SubmissionResult | null; successMessage?: string },
Expand Down
39 changes: 39 additions & 0 deletions core/app/[locale]/(default)/(auth)/change-password/page-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { cache } from 'react';

import { client } from '~/client';
import { graphql } from '~/client/graphql';
import { revalidate } from '~/client/revalidate-target';

const ChangePasswordQuery = graphql(`
query ChangePasswordQuery {
site {
settings {
customers {
passwordComplexitySettings {
minimumNumbers
minimumPasswordLength
minimumSpecialCharacters
requireLowerCase
requireNumbers
requireSpecialCharacters
requireUpperCase
}
}
}
}
}
`);

export const getChangePasswordQuery = cache(async () => {
const response = await client.fetch({
document: ChangePasswordQuery,
fetchOptions: { next: { revalidate } },
});

const passwordComplexitySettings =
response.data.site.settings?.customers?.passwordComplexitySettings;

return {
passwordComplexitySettings,
};
});
4 changes: 4 additions & 0 deletions core/app/[locale]/(default)/(auth)/change-password/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Metadata } from 'next';
import { getTranslations, setRequestLocale } from 'next-intl/server';

import { ResetPasswordSection } from '@/vibes/soul/sections/reset-password-section';
import { getChangePasswordQuery } from '~/app/[locale]/(default)/(auth)/change-password/page-data';
import { redirect } from '~/i18n/routing';

import { changePassword } from './_actions/change-password';
Expand Down Expand Up @@ -37,11 +38,14 @@ export default async function ChangePassword({ params, searchParams }: Props) {
return redirect({ href: '/login', locale });
}

const { passwordComplexitySettings } = await getChangePasswordQuery();

return (
<ResetPasswordSection
action={changePassword.bind(null, { customerEntityId, token })}
confirmPasswordLabel={t('confirmPassword')}
newPasswordLabel={t('newPassword')}
passwordComplexitySettings={passwordComplexitySettings}
title={t('title')}
/>
);
Expand Down
Loading
Loading