From 7f4ced22ebac9299da66b3b139c487694e388bc5 Mon Sep 17 00:00:00 2001
From: crab85193 <crab85193@gmail.com>
Date: Tue, 17 Dec 2024 23:20:02 +0900
Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E4=BA=BA=E5=8F=A3=E3=83=87?=
 =?UTF-8?q?=E3=83=BC=E3=82=BF=E3=82=92=E5=8F=96=E5=BE=97=E3=81=99=E3=82=8B?=
 =?UTF-8?q?API=E5=91=BC=E3=81=B3=E5=87=BA=E3=81=97=E6=A9=9F=E8=83=BD?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/api/population.ts   | 32 ++++++++++++++++++++++++++++++++
 src/types/population.ts | 18 ++++++++++++++++++
 2 files changed, 50 insertions(+)
 create mode 100644 src/api/population.ts
 create mode 100644 src/types/population.ts

diff --git a/src/api/population.ts b/src/api/population.ts
new file mode 100644
index 0000000..5792d1f
--- /dev/null
+++ b/src/api/population.ts
@@ -0,0 +1,32 @@
+import axios from "axios";
+import { PopulationCategory, PopulationResponse } from "../types/population";
+
+export const fetchPopulation = async (
+  prefCode: string
+): Promise<PopulationCategory[]> => {
+  try {
+    const response = await axios.get<PopulationResponse>(
+      `${process.env.REACT_APP_YUMEMI_API_URL}/population/${prefCode}`,
+      {
+        headers: {
+          "X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY!,
+        },
+      }
+    );
+
+    if (response.status !== 200) {
+      throw new Error("Failed to fetch population data");
+    }
+
+    return response.data.result.data.map((category) => ({
+      label: category.label,
+      data: category.data.map((dataItem) => ({
+        year: dataItem.year,
+        value: dataItem.value,
+        rate: dataItem.rate,
+      })),
+    }));
+  } catch (error) {
+    throw new Error("Error fetching population data: " + error);
+  }
+};
diff --git a/src/types/population.ts b/src/types/population.ts
new file mode 100644
index 0000000..dd09694
--- /dev/null
+++ b/src/types/population.ts
@@ -0,0 +1,18 @@
+export interface PopulationData {
+  year: number;
+  value: number;
+  rate: number;
+}
+
+export interface PopulationCategory {
+  label: string;
+  data: PopulationData[];
+}
+
+export interface PopulationResponse {
+  message: string;
+  result: {
+    boundaryYear: number;
+    data: PopulationCategory[];
+  };
+}

From 2a90a74e56bd06eafc145baee5531d60563509df Mon Sep 17 00:00:00 2001
From: crab85193 <crab85193@gmail.com>
Date: Tue, 17 Dec 2024 23:20:34 +0900
Subject: [PATCH 2/4] =?UTF-8?q?test:=20fetchPopulation=E9=96=A2=E6=95=B0?=
 =?UTF-8?q?=E3=81=AE=E3=83=A6=E3=83=8B=E3=83=83=E3=83=88=E3=83=86=E3=82=B9?=
 =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/tests/population.test.ts | 59 ++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 src/tests/population.test.ts

diff --git a/src/tests/population.test.ts b/src/tests/population.test.ts
new file mode 100644
index 0000000..ef58e3c
--- /dev/null
+++ b/src/tests/population.test.ts
@@ -0,0 +1,59 @@
+import axios from "axios";
+import { fetchPopulation } from "../api/population";
+import { PopulationCategory } from "../types/population";
+
+jest.mock("axios");
+const mockedAxios = axios as jest.Mocked<typeof axios>;
+
+describe("fetchPopulation", () => {
+  it("正常に人口データを取得できる", async () => {
+    const mockData: PopulationCategory[] = [
+      {
+        label: "年少人口",
+        data: [{ year: 1960, value: 1681479, rate: 33.37 }],
+      },
+    ];
+
+    const mockResponse = {
+      message: "success",
+      result: {
+        boundaryYear: 2020,
+        data: mockData,
+      },
+    };
+
+    mockedAxios.get.mockResolvedValue({ status: 200, data: mockResponse });
+
+    const prefCode = "1";
+
+    const result = await fetchPopulation(prefCode);
+
+    expect(result).toEqual(mockData);
+    expect(mockedAxios.get).toHaveBeenCalledWith(
+      `${process.env.REACT_APP_YUMEMI_API_URL}/population/${prefCode}`,
+      {
+        headers: {
+          "X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY!,
+        },
+      }
+    );
+  });
+
+  it("APIがエラーを返した場合、エラーをスローする", async () => {
+    mockedAxios.get.mockRejectedValue(new Error("API Error"));
+
+    const prefCode = "1";
+
+    await expect(fetchPopulation(prefCode)).rejects.toThrow(
+      "Error fetching population data: Error: API Error"
+    );
+    expect(mockedAxios.get).toHaveBeenCalledWith(
+      `${process.env.REACT_APP_YUMEMI_API_URL}/population/${prefCode}`,
+      {
+        headers: {
+          "X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY!,
+        },
+      }
+    );
+  });
+});

From 2440a712341a264b0fae6952e5c65f5e82bd90bf Mon Sep 17 00:00:00 2001
From: crab85193 <crab85193@gmail.com>
Date: Tue, 17 Dec 2024 23:42:25 +0900
Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E9=83=BD=E9=81=93=E5=BA=9C?=
 =?UTF-8?q?=E7=9C=8C=E9=81=B8=E6=8A=9E=E7=94=A8=E3=83=81=E3=82=A7=E3=83=83?=
 =?UTF-8?q?=E3=82=AF=E3=83=9C=E3=83=83=E3=82=AF=E3=82=B9=E3=82=B3=E3=83=B3?=
 =?UTF-8?q?=E3=83=9D=E3=83=BC=E3=83=8D=E3=83=B3=E3=83=88=EF=BC=88Prefectur?=
 =?UTF-8?q?eCheckbox=EF=BC=89=E3=82=92=E4=BD=9C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/PrefectureCheckbox.tsx | 47 +++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 src/components/PrefectureCheckbox.tsx

diff --git a/src/components/PrefectureCheckbox.tsx b/src/components/PrefectureCheckbox.tsx
new file mode 100644
index 0000000..039dbd3
--- /dev/null
+++ b/src/components/PrefectureCheckbox.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+import { Prefecture } from "../types/prefecture";
+
+interface PrefectureCheckboxProps {
+  prefectures: Prefecture[];
+  selectedPrefectures: string[];
+  onSelect: (selected: string[]) => void;
+}
+
+const PrefectureCheckbox: React.FC<PrefectureCheckboxProps> = ({
+  prefectures,
+  selectedPrefectures,
+  onSelect,
+}) => {
+  const handleCheckboxChange = (prefCode: string) => {
+    if (selectedPrefectures.includes(prefCode)) {
+      onSelect(selectedPrefectures.filter((code) => code !== prefCode));
+    } else {
+      onSelect([...selectedPrefectures, prefCode]);
+    }
+  };
+
+  return (
+    <div>
+      <h3>都道府県選択</h3>
+      {prefectures.map((prefecture) => (
+        <div key={prefecture.prefCode}>
+          <input
+            type="checkbox"
+            id={prefecture.prefCode.toString()}
+            checked={selectedPrefectures.includes(
+              prefecture.prefCode.toString()
+            )}
+            onChange={() =>
+              handleCheckboxChange(prefecture.prefCode.toString())
+            }
+          />
+          <label htmlFor={prefecture.prefCode.toString()}>
+            {prefecture.prefName}
+          </label>
+        </div>
+      ))}
+    </div>
+  );
+};
+
+export default PrefectureCheckbox;

From bc06e3713884230b441c7f0d38f5bfdf943e74c7 Mon Sep 17 00:00:00 2001
From: crab85193 <crab85193@gmail.com>
Date: Tue, 17 Dec 2024 23:42:39 +0900
Subject: [PATCH 4/4] =?UTF-8?q?test:=20=E9=83=BD=E9=81=93=E5=BA=9C?=
 =?UTF-8?q?=E7=9C=8C=E9=81=B8=E6=8A=9E=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF?=
 =?UTF-8?q?=E3=83=9C=E3=83=83=E3=82=AF=E3=82=B9=E3=81=AE=E5=8B=95=E4=BD=9C?=
 =?UTF-8?q?=E3=81=AB=E9=96=A2=E3=81=99=E3=82=8B=E3=83=86=E3=82=B9=E3=83=88?=
 =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=82=92=E8=BF=BD=E5=8A=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 package.json                          |  3 ++
 src/tests/PrefectureCheckbox.test.tsx | 54 +++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)
 create mode 100644 src/tests/PrefectureCheckbox.test.tsx

diff --git a/package.json b/package.json
index 718f5c3..0489116 100644
--- a/package.json
+++ b/package.json
@@ -65,6 +65,9 @@
     "setupFiles": [
       "dotenv/config"
     ],
+    "setupFilesAfterEnv": [
+      "./src/tests/setupTests.ts"
+    ],
     "transform": {
       "^.+\\.tsx?$": "ts-jest"
     },
diff --git a/src/tests/PrefectureCheckbox.test.tsx b/src/tests/PrefectureCheckbox.test.tsx
new file mode 100644
index 0000000..0f84690
--- /dev/null
+++ b/src/tests/PrefectureCheckbox.test.tsx
@@ -0,0 +1,54 @@
+import React from "react";
+import { render, screen, fireEvent } from "@testing-library/react";
+import PrefectureCheckbox from "./../components/PrefectureCheckbox";
+import { Prefecture } from "../types/prefecture";
+
+const mockPrefectures: Prefecture[] = [
+  { prefCode: 1, prefName: "北海道" },
+  { prefCode: 2, prefName: "青森県" },
+];
+
+describe("PrefectureCheckbox", () => {
+  it("都道府県のチェックボックスを表示する", () => {
+    render(
+      <PrefectureCheckbox
+        prefectures={mockPrefectures}
+        selectedPrefectures={[]}
+        onSelect={() => {}}
+      />
+    );
+
+    mockPrefectures.forEach((pref) => {
+      expect(screen.getByLabelText(pref.prefName)).toBeInTheDocument();
+    });
+  });
+
+  it("チェックボックスをクリックすると選択状態が変更される", () => {
+    const mockOnSelect = jest.fn();
+    render(
+      <PrefectureCheckbox
+        prefectures={mockPrefectures}
+        selectedPrefectures={[]}
+        onSelect={mockOnSelect}
+      />
+    );
+
+    const checkbox = screen.getByLabelText("北海道");
+    fireEvent.click(checkbox);
+
+    expect(mockOnSelect).toHaveBeenCalledWith(["1"]);
+  });
+
+  it("選択済みの都道府県のチェックボックスはチェックされている", () => {
+    render(
+      <PrefectureCheckbox
+        prefectures={mockPrefectures}
+        selectedPrefectures={["1"]}
+        onSelect={() => {}}
+      />
+    );
+
+    expect(screen.getByLabelText("北海道")).toBeChecked();
+    expect(screen.getByLabelText("青森県")).not.toBeChecked();
+  });
+});