フロントエンド開発のためのテスト入門のサンプルコードをnextからreact-router-domに置き換えたメモ

## 概要
[フロントエンド開発のためのテスト入門 今からでも知っておきたい自動テスト戦略の必須知識](https://www.shoeisha.co.jp/book/detail/9784798178639)の[サンプルコード](https://github.com/frontend-testing-book/nextjs)をNextではなくReact＋react-router-domで書き換えた。

[この時点のコード](https://github.com/hibohiboo/RinneCircle/tree/cc70b226aa8deb84b38fea8710918b53a9983ef9/packages/ui_components)
[Storybook](https://hibohiboo.github.io/RinneCircle/storybook-static/index.html?path=/story/components-providers-toastprovider-toast--succeed)

## stroybookをv6からv7に。



```ts
import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
```

deprecatedなので下記に修正。

```ts
import { Meta, StoryObj } from "@storybook/react";
```

## S3
sdkをv3に。

services/client/UploadImage/type.ts

```ts
import type { PresignedPost } from "@aws-sdk/s3-presigned-post";

export type UploadImageData = {
  url: string;
  filename: string;
  fields: PresignedPost["fields"];
};
```

## react-router-dom
nextの使用をやめたので、代わりにnext-router-domを使っている。
書き方はそこそこ異なっている。

```ts
import { SelectFilterOption } from "@/components/molecules/SelectFilterOption";
import { parseAsNonEmptyString } from "@/lib/util";
import styles from "./styles.module.css";
import { useNavigate, useSearchParams } from "react-router-dom";

const options = [
  { value: "all", label: "すべて" },
  { value: "public", label: "公開" },
  { value: "private", label: "下書き" },
];

export const Header = () => {
  const navigate = useNavigate();
  const [searchParams] = useSearchParams();
  const defaultValue =
    parseAsNonEmptyString(searchParams.get("status")) || "all";
  return (
    <header className={styles.header}>
      <h2 className={styles.heading}>投稿記事一覧</h2>
      <SelectFilterOption
        title="公開ステータス"
        options={options}
        selectProps={{
          defaultValue,
          onChange: (event) => {
            const status = event.target.value;
            searchParams.set("status", status);
            navigate({
              pathname: `/my/posts`,
              search: searchParams.toString(),
            });
          },
        }}
      />
    </header>
  );
};

```

### jest

```ts

test("公開ステータスを変更すると、status が変わる", async () => {
  // すでにある page=1 が消えていないこともあわせて検証
  const router = createMemoryRouter(
    [
      { path: "/my/posts", element: <Header /> }
    ],
    { initialEntries: ["/my/posts?page=1&status=public"] },
  );
  render(<RouterProvider router={router} />);
  const combobox = screen.getByRole("combobox", { name: "公開ステータス" });
  async function selectOption(label: string) {
    await user.selectOptions(combobox, label);
  }
  await waitFor(() => {
    selectOption("公開");
    expect(router.state.location.pathname).toBe(`/my/posts`);
    expect(router.state.location.search).toBe(`?page=1&status=public`);
  });
  await selectOption("下書き");
  await waitFor(() => {
    expect(router.state.location.pathname).toBe(`/my/posts`);
    expect(router.state.location.search).toBe(`?page=1&status=private`);
  });
});

```
### Storybook

```diff
+ import { withRouter } from "storybook-addon-react-router-v6";

export default {
  component: Profile,
  args: getMyProfileData,
+   decorators: [withRouter],
} as Meta<typeof Profile>;

type Story = StoryObj<typeof Profile>;

export const Default: Story = {};
```

## svg
webpackからviteに変更しており、SVGを読み込む設定をしていないのでimgとして読み込む必要がある。
これは設定変更だけでできるならならいたい。

```diff
import GitHub from "./assets/github.svg";
import Twitter from "./assets/twitter.svg";
import styles from "./styles.module.css";

type Props = {
  githubAccount?: string;
  twitterAccount?: string;
};

export const Accounts = (props: Props) => {
  return (
    <div className={styles.accounts}>
      {props.githubAccount && (
        <p className={styles.github}>
          <a
            href={`https://github.com/${props.githubAccount}`}
            target="_blank"
            rel="noopener noreferrer"
          >
-           <Github />
+            <img src={GitHub} /> <span>{props.githubAccount}</span>
          </a>
        </p>
      )}
      {props.twitterAccount && (
        <p className={styles.twitter}>
          <a
            href={`https://twitter.com/${props.twitterAccount}`}
            target="_blank"
            rel="noopener noreferrer"
          >
-            <Twitter /> <span>{props.twitterAccount}</span>
+            <img src={Twitter} /> <span>{props.twitterAccount}</span>
          </a>
        </p>
      )}
    </div>
  );
};
```