From 7f4ced22ebac9299da66b3b139c487694e388bc5 Mon Sep 17 00:00:00 2001 From: crab85193 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 => { + try { + const response = await axios.get( + `${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 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; + +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 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 = ({ + prefectures, + selectedPrefectures, + onSelect, +}) => { + const handleCheckboxChange = (prefCode: string) => { + if (selectedPrefectures.includes(prefCode)) { + onSelect(selectedPrefectures.filter((code) => code !== prefCode)); + } else { + onSelect([...selectedPrefectures, prefCode]); + } + }; + + return ( +
+

都道府県選択

+ {prefectures.map((prefecture) => ( +
+ + handleCheckboxChange(prefecture.prefCode.toString()) + } + /> + +
+ ))} +
+ ); +}; + +export default PrefectureCheckbox; From bc06e3713884230b441c7f0d38f5bfdf943e74c7 Mon Sep 17 00:00:00 2001 From: crab85193 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( + {}} + /> + ); + + mockPrefectures.forEach((pref) => { + expect(screen.getByLabelText(pref.prefName)).toBeInTheDocument(); + }); + }); + + it("チェックボックスをクリックすると選択状態が変更される", () => { + const mockOnSelect = jest.fn(); + render( + + ); + + const checkbox = screen.getByLabelText("北海道"); + fireEvent.click(checkbox); + + expect(mockOnSelect).toHaveBeenCalledWith(["1"]); + }); + + it("選択済みの都道府県のチェックボックスはチェックされている", () => { + render( + {}} + /> + ); + + expect(screen.getByLabelText("北海道")).toBeChecked(); + expect(screen.getByLabelText("青森県")).not.toBeChecked(); + }); +});