Skip to content

成功/失敗メッセージを自前トースト基盤へ統一#308

Merged
yusuke0610 merged 3 commits into
mainfrom
claude/career-history-toast-messages-iYHRt
Jun 6, 2026
Merged

成功/失敗メッセージを自前トースト基盤へ統一#308
yusuke0610 merged 3 commits into
mainfrom
claude/career-history-toast-messages-iYHRt

Conversation

@yusuke0610

@yusuke0610 yusuke0610 commented Jun 6, 2026

Copy link
Copy Markdown
Owner

概要

職務経歴書(Resume)を含む全画面の「ページ全体の成功/失敗メッセージ」を、フォーム内のインライン表示から画面右上のトースト表示に統一しました。外部ライブラリは導入せず、自前の Toast 基盤を新設しています(採用判断は ADR-0009 に記録)。

変更点

新規トースト基盤 frontend/src/components/ui/toast/

  • ToastProvider + useToast()main.tsx 最上位に設置し全ルートを覆う)
  • ToastViewportcreatePortal で body 直下・position:fixed の右上スタック)
  • ToastItem成功=数秒で自動消去 / エラー=手動クローズAppErrorState の回復アクション・エラーID 表示に対応)
  • 表示層ブリッジ useMessageToast(文字列)/ useAppErrorToastAppErrorState

移行した画面

  • CareerResumeForm(保存 / 削除 / PDF 出力・DL)
  • BlogPage(連携 / 同期 / 解除)
  • LoginForm(GitHub ログインエラー)
  • GitHubLinkDashboard(連携・ポーリング失敗、回復アクション付き)

据え置き(意図的にインライン維持)

  • 項目バリデーション(保存前の入力チェック):フォーカス・赤枠と密結合のため
  • 取り込み補助パネルのエラー:パネル状態(折りたたみ時の ! 表示)と密結合のため

その他

  • ErrorToastToastItem に統合し削除(回復アクション表示は維持)
  • 職務経歴書のハードコード文言を SUCCESS_MESSAGESCAREER_SAVED / CAREER_PDF_DOWNLOADED)へ集約
  • 正常系トーストの背景はティントを付けずテーマ背景色(ライト/ダーク追従)を使用、success の識別は枠線の緑ティントで担保

設計判断(ADR-0009)

  • 既存フックは無変更とし、表示層ブリッジでトースト化。これにより各フックの error/success 単体テストを壊さず、フックを Provider 非依存に保つ。
  • ブリッジは useEffect で文字列/errorId の変化を監視し、StrictMode の二重発火・同一文言の重複表示を ref でガード(テストで固定)。

テスト

  • ✅ ESLint / messages-lint / 全 267 テスト(新規トースト 6 ケース含む)/ build すべてグリーン
  • renderWithProvidersToastProvider を追加(既存 GitHubLinkDashboard テストの provider 不足を解消)
  • ⚠️ E2E 未実行:実装環境に Playwright ブラウザが無く実行不可。各 E2E spec は移行メッセージを参照していないため spec 修正は不要だが、レイアウト直下に Provider を追加したため CI での E2E 実行を推奨

レビュー観点

  • ファイル削除:ErrorToast.tsx / ErrorToast.module.css(唯一の利用者 GitHubLinkDashboard を移行済み)
  • 既存挙動の変更:ページ全体の成功/失敗メッセージの表示方式(インライン → トースト)

https://claude.ai/code/session_01JmjJ6Z4Dd1QPKV49kMRz8R


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Unified toast notification system for success/error feedback, with auto-dismiss for successes and persistent dismissible errors (optionally offering recovery actions).
    • App-level toast provider enabled so components can show toasts globally.
  • Bug Fixes / UX

    • Replaced many inline success/error messages with toast notifications across login, blog, forms, and dashboard flows.
  • Documentation

    • Added ADR documenting the toast approach.
  • Tests

    • Added tests covering toast behavior and de-duplication.

claude added 2 commits June 5, 2026 19:19
職務経歴書・ブログ連携・ログイン・GitHub連携のページ全体の成功/失敗
メッセージを、インライン表示から画面右上のトースト表示に統一する。

- ui/toast に ToastProvider/useToast と表示層ブリッジを新設
  (成功=自動消去 / エラー=手動クローズ、AppErrorState の回復アクション対応)
- 既存フックは無変更とし、useMessageToast/useAppErrorToast で橋渡し
- ErrorToast を ToastItem に統合し削除
- 項目バリデーション・取り込み補助のエラーはインライン維持
- 採用判断を ADR-0009 に記録

Refs: docs/adr/0009-frontend-toast-notification.md

https://claude.ai/code/session_01JmjJ6Z4Dd1QPKV49kMRz8R
success トーストの緑ティント背景をやめ、テーマの背景色(--bg-card、
ライト/ダーク追従)をそのまま使う。success の識別は枠線の緑ティントで維持。

https://claude.ai/code/session_01JmjJ6Z4Dd1QPKV49kMRz8R
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Worried about impact? Review this PR in Change Stack to explore blast radius before you approve or request changes.

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4caa65f7-4bb3-42ff-9422-40081b96d0fd

📥 Commits

Reviewing files that changed from the base of the PR and between e8f91dd and 134e0b4.

📒 Files selected for processing (6)
  • frontend/src/components/forms/CareerResumeForm.tsx
  • frontend/src/components/ui/toast/ToastItem.tsx
  • frontend/src/components/ui/toast/ToastProvider.test.tsx
  • frontend/src/components/ui/toast/toast.module.css
  • frontend/src/components/ui/toast/useToastBridge.ts
  • frontend/src/constants/messages.ts
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/constants/messages.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/components/ui/toast/useToastBridge.ts
  • frontend/src/components/ui/toast/ToastProvider.test.tsx
  • frontend/src/components/ui/toast/toast.module.css

📝 Walkthrough

Walkthrough

This PR introduces a custom toast notification system and migrates multiple components from inline success/error UI to provider-driven toasts, adding type-safe context, bridge hooks, styling, tests, app/test wiring, and documentation.

Changes

Toast Notification System

Layer / File(s) Summary
Toast context and type contract
frontend/src/components/ui/toast/toastContext.ts
Defines ToastVariant, ToastData, ToastContextValue, SUCCESS_TOAST_DURATION_MS, ToastContext, and useToast() hook that throws outside provider.
Toast provider and rendering components
frontend/src/components/ui/toast/ToastProvider.tsx, ToastViewport.tsx, ToastItem.tsx
ToastProvider manages toasts with monotonic ids and exposes showSuccess/showError/dismiss; ToastViewport portals a fixed top-right container to document.body; ToastItem renders individual toast cards with variant styling, auto-dismiss for success, recovery/action lookup for error codes, optional error-id display, and dismiss button.
Styling, constants, and exports
frontend/src/components/ui/toast/toast.module.css, toast/index.ts, frontend/src/constants/messages.ts
Adds CSS for viewport and toast cards (variants, animation, responsive), message/UI constants (CAREER_SAVED, CAREER_PDF_DOWNLOADED, TOAST_REGION_LABEL, TOAST_DISMISS, TOAST_ERROR_ID_LABEL), and new toast entrypoint exports.
Bridge hooks for component state integration
frontend/src/components/ui/toast/useToastBridge.ts
Adds useMessageToast(message, variant) and useAppErrorToast(error) to convert existing message/error state into toasts with deduplication behavior.
Toast system test coverage
frontend/src/components/ui/toast/ToastProvider.test.tsx
Tests showSuccess auto-dismiss, showError persistence and dismissal, showError(AppErrorState) message/action/errorId rendering, useMessageToast de-duplication and null reset, and useAppErrorToast errorId-based de-duplication.
LoginForm and BlogPage migration
frontend/src/components/auth/LoginForm.tsx, BlogPage.tsx, and associated .module.css files
Migrate inline githubError, success, and accountError rendering to use useMessageToast; remove inline <p> renderings and .errorMessage/.successMessage CSS selectors.
CareerResumeForm migration with validation split
frontend/src/components/forms/CareerResumeForm.tsx
Uses useMessageToast for form and PDF outcomes, introduces validationError state for inline field validation (cleared on edits/submission), and switches to standardized success messages.
GitHubLinkDashboard migration
frontend/src/components/github-link/GitHubLinkDashboard.tsx
Replaces inline ErrorToast component with useAppErrorToast(error) hook and updates imports to the new toast APIs.
Application and test wiring
frontend/src/main.tsx, frontend/src/test/renderWithProviders.tsx, frontend/src/styles/shared.module.css
Wraps App with ToastProvider; updates test helper to include ToastProvider; removes unused .success CSS class from shared styles.
Architecture documentation and ADR
docs/adr/0009-frontend-toast-notification.md, .claude/rules/frontend/architecture.md, .claude/rules/common/duplication.md
Adds ADR 0009 describing the toast decision and updates architecture/duplication guidance to reference the new toast system instead of ErrorToast.

Sequence Diagram

sequenceDiagram
  participant Component as LoginForm/BlogPage/CareerResumeForm/etc.
  participant Bridge as useMessageToast/useAppErrorToast
  participant ToastContext as ToastProvider/useToast
  participant Viewport as ToastViewport
  participant Item as ToastItem
  participant DOM

  Component->>Bridge: pass message or AppErrorState
  Bridge->>ToastContext: call showSuccess/showError
  ToastContext->>ToastContext: append toast and generate id
  ToastContext->>Viewport: pass toasts list and dismiss callback
  Viewport->>Item: render each ToastItem
  Item->>Item: setup auto-dismiss for success variant
  Item->>DOM: render toast card with message/actions/errorId
  Item->>ToastContext: call dismiss(id) on button click
  ToastContext->>ToastContext: remove toast from state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • yusuke0610/devforge#281: Also modifies GitHubLinkDashboard.tsx to change error presentation via toast mechanism (related component migration).

Poem

🐰 A toast system springs to life,
Portal-placed in the DOM so bright,
From inline chaos, now unified bliss,
Success auto-fades, errors persist—
Bridge hooks keep the old state alive! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: unifying success/failure messages across screens into a custom toast system, which is the primary objective of this pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/career-history-toast-messages-iYHRt

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
frontend/src/components/ui/toast/ToastProvider.test.tsx (1)

166-207: ⚡ Quick win

Consider adding test coverage for errorId being undefined.

The current useAppErrorToast tests use makeError(errorId) which always provides an errorId. Consider adding a test case where AppErrorState has errorId: undefined to verify the deduplication behavior doesn't incorrectly suppress distinct errors that lack error IDs.

Example:

it("shows multiple errors when errorId is undefined", () => {
  const error1 = { ...makeError("e1"), errorId: undefined };
  const error2 = { ...makeError("e2"), errorId: undefined };
  // Test that both errors are shown despite errorId being undefined
});

This would help prevent regression if the deduplication logic changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/ui/toast/ToastProvider.test.tsx` around lines 166 -
207, Add a new test in the useAppErrorToast suite that verifies deduplication
doesn't collapse errors when errorId is undefined: create two AppErrorState
instances by reusing makeError(...) but set errorId: undefined for both, render
them sequentially via AppErrorBridgeHarness inside ToastProvider (similar
pattern to the existing test using rerender), and assert that both toast
messages appear (expect getAllByText(...).toHaveLength(2)); reference makeError,
AppErrorBridgeHarness and useAppErrorToast in the new spec to mirror existing
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/components/forms/CareerResumeForm.tsx`:
- Around line 116-117: The current use of the nullish-coalescing operator
(pdfSuccess ?? formSuccess and pdfError ?? formError) in CareerResumeForm
prevents form messages from showing when a pdf message is already present;
instead call useMessageToast twice for each channel so PDF and form messages are
handled independently (e.g., invoke useMessageToast(pdfSuccess, "success") and
useMessageToast(formSuccess, "success") and likewise useMessageToast(pdfError,
"error") and useMessageToast(formError, "error")), ensuring useMessageToast,
pdfSuccess, formSuccess, pdfError and formError are used separately to allow
independent notification streams.

In `@frontend/src/components/ui/toast/toast.module.css`:
- Around line 25-26: The keyframe name toastIn violates the project's
keyframes-name-pattern (expects kebab-case); rename the keyframes block
currently defined as toastIn (lines ~92-101) to toast-in and update any
references such as the animation property (animation: toastIn 0.18s ease-out;)
to use animation: toast-in 0.18s ease-out; (search for other uses of toastIn and
replace them as well).

In `@frontend/src/components/ui/toast/ToastItem.tsx`:
- Line 59: The Japanese label "エラーID:" in ToastItem.tsx should be moved to the
messages single source of truth: add a constant UI_MESSAGES.ERROR_ID_LABEL in
messages.ts (e.g., value "エラーID:") and then replace the hard-coded string in the
ToastItem component (the JSX line rendering errorId) to use
UI_MESSAGES.ERROR_ID_LABEL; also add the appropriate import for UI_MESSAGES at
the top of ToastItem.tsx so the component references the new constant.

In `@frontend/src/components/ui/toast/useToastBridge.ts`:
- Around line 47-55: The deduplication in the useEffect (referencing
lastErrorIdRef, showError, and error.errorId on AppErrorState) incorrectly
suppresses errors when error.errorId is undefined; update the effect to guard
for a missing errorId by immediately calling showError(error) and returning
(i.e., do not compare or set lastErrorIdRef) when !error.errorId, otherwise keep
the existing dedupe: if (error.errorId === lastErrorIdRef.current) return;
lastErrorIdRef.current = error.errorId; showError(error). Ensure error and
showError references remain in the dependency array.

---

Nitpick comments:
In `@frontend/src/components/ui/toast/ToastProvider.test.tsx`:
- Around line 166-207: Add a new test in the useAppErrorToast suite that
verifies deduplication doesn't collapse errors when errorId is undefined: create
two AppErrorState instances by reusing makeError(...) but set errorId: undefined
for both, render them sequentially via AppErrorBridgeHarness inside
ToastProvider (similar pattern to the existing test using rerender), and assert
that both toast messages appear (expect getAllByText(...).toHaveLength(2));
reference makeError, AppErrorBridgeHarness and useAppErrorToast in the new spec
to mirror existing behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dd1e7ff7-9c12-4754-b3fc-f719ff6a7210

📥 Commits

Reviewing files that changed from the base of the PR and between 347f14b and e8f91dd.

📒 Files selected for processing (23)
  • .claude/rules/common/duplication.md
  • .claude/rules/frontend/architecture.md
  • docs/adr/0009-frontend-toast-notification.md
  • frontend/src/components/auth/LoginForm.module.css
  • frontend/src/components/auth/LoginForm.tsx
  • frontend/src/components/blog/BlogPage.module.css
  • frontend/src/components/blog/BlogPage.tsx
  • frontend/src/components/forms/CareerResumeForm.tsx
  • frontend/src/components/github-link/GitHubLinkDashboard.tsx
  • frontend/src/components/ui/ErrorToast.module.css
  • frontend/src/components/ui/ErrorToast.tsx
  • frontend/src/components/ui/toast/ToastItem.tsx
  • frontend/src/components/ui/toast/ToastProvider.test.tsx
  • frontend/src/components/ui/toast/ToastProvider.tsx
  • frontend/src/components/ui/toast/ToastViewport.tsx
  • frontend/src/components/ui/toast/index.ts
  • frontend/src/components/ui/toast/toast.module.css
  • frontend/src/components/ui/toast/toastContext.ts
  • frontend/src/components/ui/toast/useToastBridge.ts
  • frontend/src/constants/messages.ts
  • frontend/src/main.tsx
  • frontend/src/styles/shared.module.css
  • frontend/src/test/renderWithProviders.tsx
💤 Files with no reviewable changes (5)
  • frontend/src/components/ui/ErrorToast.tsx
  • frontend/src/styles/shared.module.css
  • frontend/src/components/auth/LoginForm.module.css
  • frontend/src/components/ui/ErrorToast.module.css
  • frontend/src/components/blog/BlogPage.module.css

Comment thread frontend/src/components/forms/CareerResumeForm.tsx Outdated
Comment thread frontend/src/components/ui/toast/toast.module.css Outdated
Comment thread frontend/src/components/ui/toast/ToastItem.tsx Outdated
Comment thread frontend/src/components/ui/toast/useToastBridge.ts
CodeRabbit のレビュー指摘に対応する。

- CareerResumeForm: PDF とフォームの成否を `??` で統合すると、片方の
  メッセージが残っている間にもう片方が更新されてもトーストが出ない問題を修正。
  チャンネルごとに useMessageToast を個別呼び出しして独立通知にする。
- useToastBridge: errorId が空のとき重複判定の基準にできず別エラーを取りこぼす
  ケースに防御ガードを追加(空なら毎回表示)。回帰テストも追加。
- ToastItem: ハードコードの「エラーID:」を UI_MESSAGES.TOAST_ERROR_ID_LABEL へ集約。
- toast.module.css: keyframe 名を既存慣習に合わせ kebab-case(toast-in)に変更。

https://claude.ai/code/session_01JmjJ6Z4Dd1QPKV49kMRz8R
@yusuke0610
yusuke0610 merged commit d6e9a1b into main Jun 6, 2026
18 checks passed
@yusuke0610
yusuke0610 deleted the claude/career-history-toast-messages-iYHRt branch June 10, 2026 05:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants