成功/失敗メッセージを自前トースト基盤へ統一#308
Conversation
職務経歴書・ブログ連携・ログイン・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
|
Worried about impact? Review this PR in Change Stack to explore blast radius before you approve or request changes. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThis 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. ChangesToast Notification System
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
frontend/src/components/ui/toast/ToastProvider.test.tsx (1)
166-207: ⚡ Quick winConsider adding test coverage for
errorIdbeing undefined.The current
useAppErrorToasttests usemakeError(errorId)which always provides anerrorId. Consider adding a test case whereAppErrorStatehaserrorId: undefinedto 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
📒 Files selected for processing (23)
.claude/rules/common/duplication.md.claude/rules/frontend/architecture.mddocs/adr/0009-frontend-toast-notification.mdfrontend/src/components/auth/LoginForm.module.cssfrontend/src/components/auth/LoginForm.tsxfrontend/src/components/blog/BlogPage.module.cssfrontend/src/components/blog/BlogPage.tsxfrontend/src/components/forms/CareerResumeForm.tsxfrontend/src/components/github-link/GitHubLinkDashboard.tsxfrontend/src/components/ui/ErrorToast.module.cssfrontend/src/components/ui/ErrorToast.tsxfrontend/src/components/ui/toast/ToastItem.tsxfrontend/src/components/ui/toast/ToastProvider.test.tsxfrontend/src/components/ui/toast/ToastProvider.tsxfrontend/src/components/ui/toast/ToastViewport.tsxfrontend/src/components/ui/toast/index.tsfrontend/src/components/ui/toast/toast.module.cssfrontend/src/components/ui/toast/toastContext.tsfrontend/src/components/ui/toast/useToastBridge.tsfrontend/src/constants/messages.tsfrontend/src/main.tsxfrontend/src/styles/shared.module.cssfrontend/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
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
概要
職務経歴書(Resume)を含む全画面の「ページ全体の成功/失敗メッセージ」を、フォーム内のインライン表示から画面右上のトースト表示に統一しました。外部ライブラリは導入せず、自前の Toast 基盤を新設しています(採用判断は ADR-0009 に記録)。
変更点
新規トースト基盤
frontend/src/components/ui/toast/ToastProvider+useToast()(main.tsx最上位に設置し全ルートを覆う)ToastViewport(createPortalで body 直下・position:fixedの右上スタック)ToastItem(成功=数秒で自動消去 / エラー=手動クローズ、AppErrorStateの回復アクション・エラーID 表示に対応)useMessageToast(文字列)/useAppErrorToast(AppErrorState)移行した画面
据え置き(意図的にインライン維持)
!表示)と密結合のためその他
ErrorToastをToastItemに統合し削除(回復アクション表示は維持)SUCCESS_MESSAGES(CAREER_SAVED/CAREER_PDF_DOWNLOADED)へ集約設計判断(ADR-0009)
useEffectで文字列/errorId の変化を監視し、StrictMode の二重発火・同一文言の重複表示をrefでガード(テストで固定)。テスト
renderWithProvidersにToastProviderを追加(既存 GitHubLinkDashboard テストの provider 不足を解消)レビュー観点
ErrorToast.tsx/ErrorToast.module.css(唯一の利用者 GitHubLinkDashboard を移行済み)https://claude.ai/code/session_01JmjJ6Z4Dd1QPKV49kMRz8R
Generated by Claude Code
Summary by CodeRabbit
New Features
Bug Fixes / UX
Documentation
Tests