From d22886fe47c92e880472f02ad45a8d9d56967405 Mon Sep 17 00:00:00 2001 From: "naoto.kido" Date: Mon, 24 Mar 2025 05:54:48 +0900 Subject: [PATCH 1/8] =?UTF-8?q?fix:=20Storybook=E3=81=AEtest=E3=81=A7?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=81=8C=E5=87=BA=E3=82=8B=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/preview.ts | 2 +- vite.config.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.storybook/preview.ts b/.storybook/preview.ts index cfa3024..c548c84 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,4 +1,4 @@ -import type { Preview } from '@storybook/react' +import type { Preview } from '@storybook/react'; const preview: Preview = { parameters: { diff --git a/vite.config.ts b/vite.config.ts index f06fe9a..b706ce8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,5 +4,9 @@ import { defineConfig } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; export default defineConfig({ - plugins: [tailwindcss(), reactRouter(), tsconfigPaths()], + plugins: [ + tailwindcss(), + !process.env.VITEST && reactRouter(), + tsconfigPaths() + ], }); \ No newline at end of file From aec3f79f5bae790cace1dfefaed852713ca967cf Mon Sep 17 00:00:00 2001 From: "naoto.kido" Date: Mon, 24 Mar 2025 05:55:26 +0900 Subject: [PATCH 2/8] =?UTF-8?q?fix:=20.react-router=E3=82=92eslint?= =?UTF-8?q?=E3=81=AE=E5=AF=BE=E8=B1=A1=E5=A4=96=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eslint.config.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 243afff..6f2e684 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,11 +1,16 @@ +import js from "@eslint/js"; +import pluginReact from "eslint-plugin-react"; import { defineConfig } from "eslint/config"; import globals from "globals"; -import js from "@eslint/js"; import tseslint from "typescript-eslint"; -import pluginReact from "eslint-plugin-react"; export default defineConfig([ + { + ignores: [ + "**/.react-router/**", + ], + }, { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] }, { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], languageOptions: { globals: globals.browser } }, { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], plugins: { js }, extends: ["js/recommended"] }, From 60a52b5bda8c5646317d865b6b36b024c9df7e9e Mon Sep 17 00:00:00 2001 From: "naoto.kido" Date: Mon, 24 Mar 2025 08:12:48 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20devicon,=20tooltip=E3=81=AE?= =?UTF-8?q?=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/preview.ts | 2 + app/app.css | 3 + app/components/Devicon/Devicon.stories.ts | 34 +++++++++ app/components/Devicon/Devicon.tsx | 36 ++++++++++ app/components/Devicon/devicon.css | 0 app/components/Tooltip/Tooltip.stories.tsx | 28 ++++++++ app/components/Tooltip/Tooltip.tsx | 52 ++++++++++++++ app/components/Tooltip/tooltip.css | 84 ++++++++++++++++++++++ app/root.tsx | 1 + package-lock.json | 7 ++ package.json | 1 + 11 files changed, 248 insertions(+) create mode 100644 app/components/Devicon/Devicon.stories.ts create mode 100644 app/components/Devicon/Devicon.tsx create mode 100644 app/components/Devicon/devicon.css create mode 100644 app/components/Tooltip/Tooltip.stories.tsx create mode 100644 app/components/Tooltip/Tooltip.tsx create mode 100644 app/components/Tooltip/tooltip.css diff --git a/.storybook/preview.ts b/.storybook/preview.ts index c548c84..de08da7 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,4 +1,6 @@ import type { Preview } from '@storybook/react'; +import 'devicon/devicon.min.css'; +import '../app/app.css'; const preview: Preview = { parameters: { diff --git a/app/app.css b/app/app.css index 99345d8..22ce22e 100644 --- a/app/app.css +++ b/app/app.css @@ -1,4 +1,5 @@ @import "tailwindcss"; +@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100..900&display=swap'); @theme { --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, @@ -12,4 +13,6 @@ body { @media (prefers-color-scheme: dark) { color-scheme: dark; } + + font-family: "Noto Sans JP"; } diff --git a/app/components/Devicon/Devicon.stories.ts b/app/components/Devicon/Devicon.stories.ts new file mode 100644 index 0000000..e7d3b29 --- /dev/null +++ b/app/components/Devicon/Devicon.stories.ts @@ -0,0 +1,34 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Devicon } from './Devicon'; + +const meta = { + title: 'Common/Devicon', + component: Devicon, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + color: { control: 'color' }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Icon: Story = { + args: { + icon: 'storybook', + tooltip: 'disable', + size: '100px' + }, +}; + +export const IconWithTooltip: Story = { + args: { + icon: 'storybook', + tooltip: 'enable', + size: '100px' + }, +}; diff --git a/app/components/Devicon/Devicon.tsx b/app/components/Devicon/Devicon.tsx new file mode 100644 index 0000000..421500f --- /dev/null +++ b/app/components/Devicon/Devicon.tsx @@ -0,0 +1,36 @@ + +import { Tooltip } from '../Tooltip/Tooltip'; +import './devicon.css'; + +export interface DeviconProps { + icon: string; + tooltip?: 'enable' | 'disable'; + color?: string; + size?: string; + className?: string; +} + +export const Devicon = ({ + icon, + tooltip = 'enable', + color = "#242525", + size = "20px", + className = "", + ...props +}: DeviconProps) => { + const Icon = ( + + ); + + return ( + (tooltip === 'disable') ? + Icon : + + {Icon} + + ); +}; diff --git a/app/components/Devicon/devicon.css b/app/components/Devicon/devicon.css new file mode 100644 index 0000000..e69de29 diff --git a/app/components/Tooltip/Tooltip.stories.tsx b/app/components/Tooltip/Tooltip.stories.tsx new file mode 100644 index 0000000..1aab0d1 --- /dev/null +++ b/app/components/Tooltip/Tooltip.stories.tsx @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Tooltip } from './Tooltip'; + +const meta = { + title: 'Common/Tooltip', + component: Tooltip, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + backgroundColor: { control: 'color' }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const BottomTooltip: Story = { + args: { + content: 'content', + location: 'bottom', + children: ( + + ) + }, +}; diff --git a/app/components/Tooltip/Tooltip.tsx b/app/components/Tooltip/Tooltip.tsx new file mode 100644 index 0000000..7719f1a --- /dev/null +++ b/app/components/Tooltip/Tooltip.tsx @@ -0,0 +1,52 @@ + +import { useRef, useState, type ReactNode } from 'react'; +import './tooltip.css'; + +export interface TooltipProps { + content: string; + children: ReactNode; + location?: 'top' | 'bottom' | 'left' | 'right'; + duration?: number, + color?: string; + backgroundColor?: string; +} + +export const Tooltip = ({ + content, + children, + duration = 1000, + location = 'bottom', + color = "#ffffff", + backgroundColor = "#242525", + ...props +}: TooltipProps) => { + const [visible, setVisible] = useState(false); + const timerRef = useRef(null); + + const click = () => { + setVisible(true); + + if (timerRef.current) { + clearTimeout(timerRef.current); + } + + timerRef.current = setTimeout(() => setVisible(false), duration); + } + + return ( +
+ { children } +
+ { content } +
+
+ ); +}; diff --git a/app/components/Tooltip/tooltip.css b/app/components/Tooltip/tooltip.css new file mode 100644 index 0000000..03c7dfd --- /dev/null +++ b/app/components/Tooltip/tooltip.css @@ -0,0 +1,84 @@ +.tooltip-container { + position: relative; + cursor: pointer; +} + +.tooltip-box { + position: absolute; + font-size: 16px; + background-color: var(--tooltip-bg); + opacity: 0; + z-index: -99999; + padding: 2px 10px; + border-radius: 0.2em; + transition: opacity ease 0.3s; +} + +.tooltip-box[data-visible="true"] { + opacity: 1; + z-index: 0; +} + +.tooltip-top { + bottom: 115%; + left: 50%; + transform: translateX(-50%); +} + +.tooltip-bottom { + top: 115%; + left: 50%; + transform: translateX(-50%); +} + +.tooltip-left { + right: 115%; + top: 50%; + transform: translateY(-50%); +} + +.tooltip-right { + left: 115%; + top: 50%; + transform: translateY(-50%); +} + +.tooltip-box::after { + content: ''; + position: absolute; + width: 0; + height: 0; + border-style: solid; +} + +.tooltip-top::after { + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0 5px; + border-color: var(--tooltip-bg) transparent transparent transparent; +} + +.tooltip-bottom::after { + bottom: 100%; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px 5px; + border-color: transparent transparent var(--tooltip-bg) transparent; +} + +.tooltip-left::after { + left: 100%; + top: 50%; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-color: transparent transparent transparent var(--tooltip-bg); +} + +.tooltip-right::after { + right: 100%; + top: 50%; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-color: transparent var(--tooltip-bg) transparent transparent; +} \ No newline at end of file diff --git a/app/root.tsx b/app/root.tsx index 9fc6636..6e8511a 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -7,6 +7,7 @@ import { ScrollRestoration, } from "react-router"; +import 'devicon/devicon.min.css'; import type { Route } from "./+types/root"; import "./app.css"; diff --git a/package-lock.json b/package-lock.json index c2071dc..8543b56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "dependencies": { "@react-router/node": "^7.3.0", "@react-router/serve": "^7.3.0", + "devicon": "^2.16.0", "isbot": "^5.1.17", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -5998,6 +5999,12 @@ "dev": true, "license": "MIT" }, + "node_modules/devicon": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/devicon/-/devicon-2.16.0.tgz", + "integrity": "sha512-PE5a2HBNeN4av+Iu975OiiWEwS8LJPw5HAvlv0JUHb62jZTdYxTpz4ga+cQyvdtb3x1side2P9Sr1mmOmUkO/g==", + "license": "MIT" + }, "node_modules/diff": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", diff --git a/package.json b/package.json index 8f91084..ea04382 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dependencies": { "@react-router/node": "^7.3.0", "@react-router/serve": "^7.3.0", + "devicon": "^2.16.0", "isbot": "^5.1.17", "react": "^19.0.0", "react-dom": "^19.0.0", From 0d3d9f60d532db393b1106baf719ae72f963863e Mon Sep 17 00:00:00 2001 From: "naoto.kido" Date: Mon, 24 Mar 2025 09:38:52 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20member-card=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 3 + app/app.css | 7 ++ app/components/Devicon/Devicon.tsx | 9 +- app/components/Devicon/devicon.css | 0 .../MemberCard/MemberCard.stories.ts | 27 ++++++ app/components/MemberCard/MemberCard.tsx | 73 +++++++++++++++ app/components/MemberCard/member-card.css | 92 +++++++++++++++++++ 7 files changed, 208 insertions(+), 3 deletions(-) delete mode 100644 app/components/Devicon/devicon.css create mode 100644 app/components/MemberCard/MemberCard.stories.ts create mode 100644 app/components/MemberCard/MemberCard.tsx create mode 100644 app/components/MemberCard/member-card.css diff --git a/Makefile b/Makefile index 23bd1ea..21b77ee 100644 --- a/Makefile +++ b/Makefile @@ -15,3 +15,6 @@ init: tree: @tree -da -I "node_modules|.git|.react-router" + +test: + @npx eslint \ No newline at end of file diff --git a/app/app.css b/app/app.css index 22ce22e..4185f7a 100644 --- a/app/app.css +++ b/app/app.css @@ -6,6 +6,13 @@ "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } +:root { + --main-text-color: #242525; + --grey-text-color: #9D9F9F; + --accent-color: #F5F5F5; + --based-color: #242525; +} + html, body { @apply bg-white dark:bg-gray-950; diff --git a/app/components/Devicon/Devicon.tsx b/app/components/Devicon/Devicon.tsx index 421500f..8fd384d 100644 --- a/app/components/Devicon/Devicon.tsx +++ b/app/components/Devicon/Devicon.tsx @@ -1,6 +1,5 @@ import { Tooltip } from '../Tooltip/Tooltip'; -import './devicon.css'; export interface DeviconProps { icon: string; @@ -10,17 +9,21 @@ export interface DeviconProps { className?: string; } +const abbreviation: Record = { + "aws": "amazonwebservices", +} + export const Devicon = ({ icon, tooltip = 'enable', - color = "#242525", + color = "var(--based-color)", size = "20px", className = "", ...props }: DeviconProps) => { const Icon = ( diff --git a/app/components/Devicon/devicon.css b/app/components/Devicon/devicon.css deleted file mode 100644 index e69de29..0000000 diff --git a/app/components/MemberCard/MemberCard.stories.ts b/app/components/MemberCard/MemberCard.stories.ts new file mode 100644 index 0000000..c893a8b --- /dev/null +++ b/app/components/MemberCard/MemberCard.stories.ts @@ -0,0 +1,27 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { MemberCard } from './MemberCard'; + +const meta = { + title: 'Card/MemberCard', + component: MemberCard, + tags: ['autodocs'], + parameters: { + layout: 'centered', + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const LoggedIn: Story = { + args: { + name: "Naoto Kido", + role: "代表", + description: "よわよわプログラマ", + stacks: ["kubernetes", "aws", "java", "go", "flutter"], + headerImage: "https://pbs.twimg.com/profile_banners/1846395762277826560/1737992837/1500x500", + iconImage: "https://avatars.githubusercontent.com/u/54303857", + githubName: "naoido", + }, +}; diff --git a/app/components/MemberCard/MemberCard.tsx b/app/components/MemberCard/MemberCard.tsx new file mode 100644 index 0000000..06a07d7 --- /dev/null +++ b/app/components/MemberCard/MemberCard.tsx @@ -0,0 +1,73 @@ + +import { Devicon } from '../Devicon/Devicon'; +import './member-card.css'; + +export interface MemberCardProps { + name: string; + role: string; + description: string, + stacks: string[]; + headerImage: string; + iconImage: string; + githubName: string; +} + +export const MemberCard = ({ + name, + role, + description, + stacks, + headerImage, + iconImage, + githubName, + ...props +}: MemberCardProps) => { + return ( +
+
+
+
+
+

{ name }

+

{ role }

+

{ description }

+

得意技術

+
+ { + stacks.map((stack, i) => ( + + )) + } +
+ +
+
+ ); +}; diff --git a/app/components/MemberCard/member-card.css b/app/components/MemberCard/member-card.css new file mode 100644 index 0000000..911bd64 --- /dev/null +++ b/app/components/MemberCard/member-card.css @@ -0,0 +1,92 @@ +.container { + width: 435px; + background-color: var(--accent-color); +} + +.header { + height: 166px; + background-size: cover; + position: relative; + display: flex; + justify-content: center; +} + +.header::after { + content: ""; + background-color: #0000003d; + display: block; + height: 100%; + width: 100%; +} + +.icon { + position: absolute; + height: 106px; + width: 106px; + border-radius: 100%; + bottom: -53px; + background-size: cover; + box-shadow: #afafaf 0 4px 4px; +} + +.name { + text-align: center; + font-size: 30px; + font-weight: 300; + margin-top: 64px; + letter-spacing: 1px; + color: var(--main-text-color); +} + +.role { + text-align: center; + font-size: 16px; + font-weight: 300; + letter-spacing: 1px; + color: var(--main-text-color); +} + +.description { + text-align: center; + font-size: 16px; + font-weight: 300; + margin-top: 28px; + letter-spacing: 1px; + color: var(--main-text-color); +} + +.stack-title { + text-align: center; + font-size: 16px; + font-weight: 300; + margin-top: 27px; + color: var(--grey-text-color); +} + +.stack-container { + height: 50px; + width: 100%; + border-bottom: solid 1px var(--based-color); + display: flex; + align-items: center; + justify-content: center; +} + +.stack-container > * { + margin: 0 8px; +} + +.github-container { + display: flex; + height: 40px; + justify-content: center; + align-items: center; +} + +.github-username { + text-align: center; + font-size: 16px; + font-weight: 300; + margin-left: 8px; + color: var(--main-text-color); +} \ No newline at end of file From f5f9664ece67d098078abfb3eac2637a2b44d45f Mon Sep 17 00:00:00 2001 From: "naoto.kido" Date: Mon, 24 Mar 2025 10:10:32 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20i18n=E3=81=AE=E3=83=99=E3=83=BC?= =?UTF-8?q?=E3=82=B9=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/preview.tsx | 25 +++++++++ app/components/Button/Button.stories.ts | 8 +-- app/components/Button/Button.tsx | 5 +- app/i18n/config.ts | 27 +++++++++ app/i18n/en.json | 10 ++++ app/i18n/ja.json | 10 ++++ app/root.tsx | 1 + package-lock.json | 75 ++++++++++++++++++++++++- package.json | 2 + 9 files changed, 155 insertions(+), 8 deletions(-) create mode 100644 .storybook/preview.tsx create mode 100644 app/i18n/config.ts create mode 100644 app/i18n/en.json create mode 100644 app/i18n/ja.json diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 0000000..df9e0d8 --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,25 @@ +import type { Preview } from '@storybook/react'; +import React from 'react'; +import { I18nextProvider } from 'react-i18next'; +import "../app/i18n/config"; +import i18n from '../app/i18n/config'; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, + decorators: [ + (Story) => ( + + + + ), + ], +}; + +export default preview; \ No newline at end of file diff --git a/app/components/Button/Button.stories.ts b/app/components/Button/Button.stories.ts index 2a05e01..c633593 100644 --- a/app/components/Button/Button.stories.ts +++ b/app/components/Button/Button.stories.ts @@ -28,26 +28,26 @@ type Story = StoryObj; export const Primary: Story = { args: { primary: true, - label: 'Button', + label: 'common.button', }, }; export const Secondary: Story = { args: { - label: 'Button', + label: 'common.button', }, }; export const Large: Story = { args: { size: 'large', - label: 'Button', + label: 'common.button', }, }; export const Small: Story = { args: { size: 'small', - label: 'Button', + label: 'common.button', }, }; diff --git a/app/components/Button/Button.tsx b/app/components/Button/Button.tsx index f35dafd..69ce3aa 100644 --- a/app/components/Button/Button.tsx +++ b/app/components/Button/Button.tsx @@ -1,5 +1,5 @@ -import React from 'react'; +import { useTranslation } from 'react-i18next'; import './button.css'; export interface ButtonProps { @@ -24,6 +24,7 @@ export const Button = ({ ...props }: ButtonProps) => { const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; + const { t } = useTranslation(); return ( ); }; diff --git a/app/i18n/config.ts b/app/i18n/config.ts new file mode 100644 index 0000000..63002a5 --- /dev/null +++ b/app/i18n/config.ts @@ -0,0 +1,27 @@ +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; + +import translation_en from "./en.json"; +import translation_ja from "./ja.json"; + +const resources = { + ja: { + translation: translation_ja + }, + en: { + translation: translation_en + } + }; + + i18n + .use(initReactI18next) + .init({ + resources, + lng: "ja", + fallbackLng: "ja", + interpolation: { + escapeValue: false + } + }); + + export default i18n; \ No newline at end of file diff --git a/app/i18n/en.json b/app/i18n/en.json new file mode 100644 index 0000000..c99594b --- /dev/null +++ b/app/i18n/en.json @@ -0,0 +1,10 @@ +{ + "common": { + "button": "Button" + }, + "card": { + "memberCard": { + "stack": "Stacks" + } + } +} \ No newline at end of file diff --git a/app/i18n/ja.json b/app/i18n/ja.json new file mode 100644 index 0000000..b05ddd5 --- /dev/null +++ b/app/i18n/ja.json @@ -0,0 +1,10 @@ +{ + "common": { + "button": "ボタン" + }, + "card": { + "memberCard": { + "stack": "得意技術" + } + } +} \ No newline at end of file diff --git a/app/root.tsx b/app/root.tsx index 6e8511a..a0df061 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -10,6 +10,7 @@ import { import 'devicon/devicon.min.css'; import type { Route } from "./+types/root"; import "./app.css"; +import "./i18n/config"; export const links: Route.LinksFunction = () => [ { rel: "preconnect", href: "https://fonts.googleapis.com" }, diff --git a/package-lock.json b/package-lock.json index 8543b56..87f6377 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,11 @@ "@react-router/node": "^7.3.0", "@react-router/serve": "^7.3.0", "devicon": "^2.16.0", + "i18next": "^24.2.3", "isbot": "^5.1.17", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-i18next": "^15.4.1", "react-router": "^7.3.0" }, "devDependencies": { @@ -492,7 +494,6 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", - "dev": true, "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -7500,6 +7501,15 @@ "dev": true, "license": "MIT" }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -7516,6 +7526,37 @@ "node": ">= 0.8" } }, + "node_modules/i18next": { + "version": "24.2.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz", + "integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -10011,6 +10052,28 @@ "react-dom": ">=16.8.1" } }, + "node_modules/react-i18next": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.4.1.tgz", + "integrity": "sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -10327,7 +10390,6 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true, "license": "MIT" }, "node_modules/regexp.prototype.flags": { @@ -12620,6 +12682,15 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/warning": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", diff --git a/package.json b/package.json index ea04382..1ebc0d2 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,12 @@ "dependencies": { "@react-router/node": "^7.3.0", "@react-router/serve": "^7.3.0", + "i18next": "^24.2.3", "devicon": "^2.16.0", "isbot": "^5.1.17", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-i18next": "^15.4.1", "react-router": "^7.3.0" }, "devDependencies": { From a731733374afcf31bbef4fb9ca455daa48b1cd01 Mon Sep 17 00:00:00 2001 From: "naoto.kido" Date: Mon, 24 Mar 2025 05:54:48 +0900 Subject: [PATCH 6/8] =?UTF-8?q?fix:=20Storybook=E3=81=AEtest=E3=81=A7?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=81=8C=E5=87=BA=E3=82=8B=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/preview.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.storybook/preview.ts b/.storybook/preview.ts index de08da7..9db1c7c 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,6 +1,9 @@ import type { Preview } from '@storybook/react'; +<<<<<<< HEAD import 'devicon/devicon.min.css'; import '../app/app.css'; +======= +>>>>>>> 5b0b5a5 (fix: Storybookのtestでエラーが出る問題を修正) const preview: Preview = { parameters: { From 788762d63ec362738cb87364285373e78fdc4e48 Mon Sep 17 00:00:00 2001 From: "naoto.kido" Date: Mon, 24 Mar 2025 08:12:48 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20devicon,=20tooltip=E3=81=AE?= =?UTF-8?q?=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/preview.ts | 19 ------------------- app/components/Devicon/devicon.css | 0 package-lock.json | 1 + 3 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 .storybook/preview.ts create mode 100644 app/components/Devicon/devicon.css diff --git a/.storybook/preview.ts b/.storybook/preview.ts deleted file mode 100644 index 9db1c7c..0000000 --- a/.storybook/preview.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Preview } from '@storybook/react'; -<<<<<<< HEAD -import 'devicon/devicon.min.css'; -import '../app/app.css'; -======= ->>>>>>> 5b0b5a5 (fix: Storybookのtestでエラーが出る問題を修正) - -const preview: Preview = { - parameters: { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i, - }, - }, - }, -}; - -export default preview; \ No newline at end of file diff --git a/app/components/Devicon/devicon.css b/app/components/Devicon/devicon.css new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json index 87f6377..23ec992 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "@react-router/serve": "^7.3.0", "devicon": "^2.16.0", "i18next": "^24.2.3", + "devicon": "^2.16.0", "isbot": "^5.1.17", "react": "^19.0.0", "react-dom": "^19.0.0", From 21e198b1796d65a0b18fdc07ec49952c09951db8 Mon Sep 17 00:00:00 2001 From: "naoto.kido" Date: Mon, 24 Mar 2025 10:33:03 +0900 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20i18n=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/preview.tsx | 2 ++ app/components/Devicon/devicon.css | 0 app/components/MemberCard/MemberCard.tsx | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) delete mode 100644 app/components/Devicon/devicon.css diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index df9e0d8..a1bd4bc 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,6 +1,8 @@ import type { Preview } from '@storybook/react'; +import 'devicon/devicon.min.css'; import React from 'react'; import { I18nextProvider } from 'react-i18next'; +import '../app/app.css'; import "../app/i18n/config"; import i18n from '../app/i18n/config'; diff --git a/app/components/Devicon/devicon.css b/app/components/Devicon/devicon.css deleted file mode 100644 index e69de29..0000000 diff --git a/app/components/MemberCard/MemberCard.tsx b/app/components/MemberCard/MemberCard.tsx index 06a07d7..2aaf00b 100644 --- a/app/components/MemberCard/MemberCard.tsx +++ b/app/components/MemberCard/MemberCard.tsx @@ -1,4 +1,4 @@ - +import { useTranslation } from 'react-i18next'; import { Devicon } from '../Devicon/Devicon'; import './member-card.css'; @@ -22,6 +22,8 @@ export const MemberCard = ({ githubName, ...props }: MemberCardProps) => { + const { t } = useTranslation(); + return (
{ name }

{ role }

{ description }

-

得意技術

+

{ t("card.memberCard.stack") }

{ stacks.map((stack, i) => (