diff --git a/src/components/PopulationSelector.tsx b/src/components/PopulationSelector.tsx new file mode 100644 index 0000000..d0ec13c --- /dev/null +++ b/src/components/PopulationSelector.tsx @@ -0,0 +1,80 @@ +import React, { useState } from "react"; +import { fetchPopulation } from "../api/population"; // 人口データを取得するAPI +import { Prefecture } from "../types/prefecture"; // 都道府県の型 +import { PopulationCategory } from "../types/population"; // 人口データのカテゴリ型 + +interface PopulationSelectorProps { + prefectures: Prefecture[]; +} + +const PopulationSelector: React.FC = ({ + prefectures, +}) => { + const [selectedPrefecture, setSelectedPrefecture] = useState( + null + ); + const [populationData, setPopulationData] = useState< + PopulationCategory[] | null + >(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const handlePrefectureChange = async ( + event: React.ChangeEvent + ) => { + const prefCode = event.target.value; + setSelectedPrefecture(prefCode); + setLoading(true); + setError(null); + + try { + const data = await fetchPopulation(prefCode); + setPopulationData(data); + } catch (error) { + setError("人口データの取得に失敗しました. " + error); + } finally { + setLoading(false); + } + }; + + return ( +
+

人口データ選択

+ + + {loading &&

読み込み中...

} + + {error &&

{error}

} + + {populationData && ( +
+

年別人口データ

+ {populationData.map((category) => ( +
+

{category.label}

+
    + {category.data.map((entry) => ( +
  • + {entry.year}: {entry.value}人 (比率: {entry.rate}%) +
  • + ))} +
+
+ ))} +
+ )} +
+ ); +}; + +export default PopulationSelector; diff --git a/src/tests/PopulationSelector.test.tsx b/src/tests/PopulationSelector.test.tsx new file mode 100644 index 0000000..cb4ea06 --- /dev/null +++ b/src/tests/PopulationSelector.test.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import PopulationSelector from "./../components/PopulationSelector"; +import { fetchPopulation } from "../api/population"; +import { Prefecture } from "../types/prefecture"; +import { PopulationCategory } from "../types/population"; + +jest.mock("../api/population"); + +describe("PopulationSelector", () => { + const mockPrefectures: Prefecture[] = [ + { prefCode: 1, prefName: "北海道" }, + { prefCode: 2, prefName: "青森県" }, + ]; + + it("都道府県を選択し、人口データが表示される", async () => { + const mockPopulationData: PopulationCategory[] = [ + { + label: "年少人口", + data: [{ year: 1960, value: 1681479, rate: 33.37 }], + }, + ]; + + (fetchPopulation as jest.Mock).mockResolvedValue(mockPopulationData); + + render(); + + fireEvent.change(screen.getByRole("combobox"), { target: { value: "1" } }); + + await waitFor(() => screen.getByText("年少人口")); + + expect(screen.getByText("年少人口")).toBeInTheDocument(); + expect( + screen.getByText("1960: 1681479人 (比率: 33.37%)") + ).toBeInTheDocument(); + }); + + it("人口データの取得中にエラーメッセージが表示される", async () => { + (fetchPopulation as jest.Mock).mockRejectedValue(new Error("API Error")); + + render(); + + fireEvent.change(screen.getByRole("combobox"), { target: { value: "1" } }); + + await waitFor(() => screen.getByText(/人口データの取得に失敗しました/i)); + + expect( + screen.getByText(/人口データの取得に失敗しました/i) + ).toBeInTheDocument(); + }); +});