Skip to content

Commit

Permalink
chore(core): enforce use of next-intl's navigation APIs (bigcommerce#…
Browse files Browse the repository at this point in the history
…1361)

* chore(core): enforce use of next-intl's navigation APIs

* fix: add issue note and fix tests

* fix: update eslint message to use correct path
  • Loading branch information
jorgemoya authored Sep 13, 2024
1 parent 0814afe commit dd10d06
Show file tree
Hide file tree
Showing 23 changed files with 57 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-clouds-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

Enforce use of next-intl's wrapper navigation APIs.
22 changes: 21 additions & 1 deletion core/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ const config = {
name: 'next/link',
message: "Please import 'Link' from '~/components/Link' instead.",
},
{
name: '~/i18n/routing',
importNames: ['Link'],
message: "Please import 'Link' from '~/components/Link' instead.",
},
{
name: 'next/router',
importNames: ['useRouter'],
message: 'Please import from `~/i18n/routing` instead.',
},
{
name: 'next/navigation',
importNames: ['redirect', 'permanentRedirect', 'useRouter', 'usePathname'],
message: 'Please import from `~/i18n/routing` instead.',
},
],
},
],
Expand All @@ -37,7 +52,12 @@ const config = {
},
],
},
ignorePatterns: ['client/generated/**/*.ts', 'playwright-report/**', 'test-results/**', '**/google_analytics4.js'],
ignorePatterns: [
'client/generated/**/*.ts',
'playwright-report/**',
'test-results/**',
'**/google_analytics4.js',
],
};

module.exports = config;
3 changes: 2 additions & 1 deletion core/app/[locale]/(default)/(auth)/change-password/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { redirect } from 'next/navigation';
import { useTranslations } from 'next-intl';

import { redirect } from '~/i18n/routing';

import { ChangePasswordForm } from './_components/change-password-form';

export const metadata = {
Expand Down
2 changes: 1 addition & 1 deletion core/app/[locale]/(default)/(auth)/login/_actions/login.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use server';

import { isRedirectError } from 'next/dist/client/components/redirect';
import { redirect } from 'next/navigation';

import { Credentials, signIn } from '~/auth';
import { redirect } from '~/i18n/routing';

export const login = async (_previousState: unknown, formData: FormData) => {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const LoginForm = () => {
const [state, formAction] = useFormState(login, { status: 'idle' });
const { accountState } = useAccountStatusContext();

const isFormInvalid = state.status === 'error';
const isFormInvalid = state?.status === 'error';

const handleInputValidation = (e: ChangeEvent<HTMLInputElement>) => {
const validationStatus = e.target.validity.valueMissing;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use server';

import { isRedirectError } from 'next/dist/client/components/redirect';
import { redirect } from 'next/navigation';

import { Credentials, signIn } from '~/auth';
import { redirect } from '~/i18n/routing';

export const login = async (formData: FormData) => {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';

import { useRouter } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import { useFormStatus } from 'react-dom';
Expand All @@ -19,6 +18,7 @@ import {
Input,
} from '~/components/ui/form';
import { Message } from '~/components/ui/message';
import { useRouter } from '~/i18n/routing';

import { resetPassword } from '../../_actions/reset-password';

Expand Down
3 changes: 2 additions & 1 deletion core/app/[locale]/(default)/(faceted)/_components/facets.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useSearchParams } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { FormEvent, useRef, useTransition } from 'react';

Expand All @@ -9,6 +9,7 @@ import { Accordions } from '~/components/ui/accordions';
import { Button } from '~/components/ui/button';
import { Checkbox, Input, Label } from '~/components/ui/form';
import { Rating } from '~/components/ui/rating';
import { usePathname, useRouter } from '~/i18n/routing';
import { cn } from '~/lib/utils';

import type { Facet, PageType } from '../types';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use client';

import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useSearchParams } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { useTransition } from 'react';

import { Tag } from '~/components/ui/tag';
import { usePathname, useRouter } from '~/i18n/routing';

import type { Facet, PageType, PublicParamKeys } from '../types';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use client';

import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useSearchParams } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { useTransition } from 'react';

import { Select } from '~/components/ui/form';
import { usePathname, useRouter } from '~/i18n/routing';

export function SortBy() {
const router = useRouter();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use client';

import { usePathname } from 'next/navigation';
import { createContext, ReactNode, useContext, useEffect, useState } from 'react';

import { usePathname } from '~/i18n/routing';

import { State as AccountState } from '../settings/change-password/_actions/change-password';

const defaultState: AccountState = { status: 'idle', message: '' };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';

import { useRouter } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { MouseEvent, useEffect, useRef, useState } from 'react';
import { useFormStatus } from 'react-dom';
Expand Down Expand Up @@ -35,6 +34,7 @@ import { Link } from '~/components/link';
import { Button } from '~/components/ui/button';
import { Field, Form, FormSubmit } from '~/components/ui/form';
import { Message } from '~/components/ui/message';
import { useRouter } from '~/i18n/routing';

import { useAccountStatusContext } from '../../../_components/account-status-provider';
import { addAddress } from '../_actions/add-address';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';

import { useRouter } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { MouseEvent, useEffect, useRef, useState } from 'react';
import { useFormStatus } from 'react-dom';
Expand Down Expand Up @@ -36,6 +35,7 @@ import { Link } from '~/components/link';
import { Button } from '~/components/ui/button';
import { Field, Form, FormSubmit } from '~/components/ui/form';
import { Message } from '~/components/ui/message';
import { useRouter } from '~/i18n/routing';

import { useAccountStatusContext } from '../../../../_components/account-status-provider';
import { Modal } from '../../../../_components/modal';
Expand Down
2 changes: 1 addition & 1 deletion core/app/[locale]/(default)/account/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { redirect } from 'next/navigation';
import { PropsWithChildren } from 'react';

import { auth } from '~/auth';
import { redirect } from '~/i18n/routing';

export default async function AccountLayout({ children }: PropsWithChildren) {
const session = await auth();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
'use server';

import { redirect } from 'next/navigation';
import { z } from 'zod';

import { getSessionCustomerId } from '~/auth';
import { client } from '~/client';
import { graphql } from '~/client/graphql';
import { redirect } from '~/i18n/routing';

const CheckoutRedirectMutation = graphql(`
mutation CheckoutRedirectMutation($cartId: String!) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { removeEdgesAndNodes } from '@bigcommerce/catalyst-client';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useSearchParams } from 'next/navigation';

import { FragmentOf } from '~/client/graphql';
import { Label, PickList, RadioGroup, RectangleList, Select, Swatch } from '~/components/ui/form';
import { usePathname, useRouter } from '~/i18n/routing';

import { useProductFieldController } from '../../use-product-form';
import { ErrorMessage } from '../shared/error-message';
Expand Down
2 changes: 1 addition & 1 deletion core/app/admin/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { redirect } from 'next/navigation';
import { redirect } from '~/i18n/routing';

const canonicalDomain: string = process.env.BIGCOMMERCE_GRAPHQL_API_DOMAIN ?? 'mybigcommerce.com';
const BIGCOMMERCE_STORE_HASH = process.env.BIGCOMMERCE_STORE_HASH;
Expand Down
2 changes: 1 addition & 1 deletion core/app/xmlsitemap.php/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable check-file/folder-naming-convention */
import { permanentRedirect } from 'next/navigation';
import { permanentRedirect } from '~/i18n/routing';

/*
* This route is used to redirect the legacy Stencil sitemap that lives on /xmlsitemap.php
Expand Down
3 changes: 1 addition & 2 deletions core/components/header/_actions/logout.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use server';

import { redirect } from 'next/navigation';

import { signOut } from '~/auth';
import { redirect } from '~/i18n/routing';

export const logout = async () => {
await signOut({ redirect: false });
Expand Down
3 changes: 2 additions & 1 deletion core/components/ui/pagination/pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { usePathname, useSearchParams } from 'next/navigation';
import { useSearchParams } from 'next/navigation';

import { Link as CustomLink } from '~/components/link';
import { usePathname } from '~/i18n/routing';
import { cn } from '~/lib/utils';

interface Props {
Expand Down
5 changes: 4 additions & 1 deletion core/i18n/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,7 @@ export const routing = defineRouting({

// Lightweight wrappers around Next.js' navigation APIs
// that will consider the routing configuration
export const { Link, redirect, usePathname, useRouter } = createSharedPathnamesNavigation(routing);
// Redirect will append locale prefix even when in default locale
// More info: https://github.com/amannn/next-intl/issues/1335
export const { Link, redirect, usePathname, useRouter, permanentRedirect } =
createSharedPathnamesNavigation(routing);
2 changes: 1 addition & 1 deletion core/tests/ui/desktop/e2e/login.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ test('Account login and logout', async ({ page }) => {

await page.getByRole('menuitem', { name: 'Log out' }).click();

await page.waitForURL('/login/');
await page.waitForURL('/en/login/');

await expect(page.getByRole('heading', { name: 'Log in' })).toBeVisible();
});
2 changes: 1 addition & 1 deletion core/tests/ui/desktop/e2e/register.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ test('Account register', async ({ page }) => {

await page.getByRole('button', { name: 'Create account' }).click();

await expect(page).toHaveURL('/account/');
await expect(page).toHaveURL('/en/account/');
await expect(page.getByText('Your account has been successfully created')).toBeVisible();
});

0 comments on commit dd10d06

Please sign in to comment.