Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughReplaces the modal-based announcements flow with a new dismissible AnnouncementBanner rendered in AppContent, removes the AnnouncementsModal, and adds a Playwright smoke-test suite that logs in and exercises main-screen navigation across multiple app areas. Changes
Sequence Diagram(s)sequenceDiagram
participant App as App (renders)
participant Banner as AnnouncementBanner
participant API as Server API
participant Store as LocalStorage
rect rgba(200,200,255,0.5)
App->>Banner: mount
end
rect rgba(200,255,200,0.5)
Banner->>API: GET /server/announcements (authenticated)
API-->>Banner: announcement list
Banner->>Banner: normalize, filter by expiry, sort by date
Banner->>Store: read lastViewedDate
alt has unseen announcement
Banner-->>App: render banner with content
else no valid announcement
Banner-->>App: render nothing
end
end
rect rgba(255,200,200,0.5)
App->>Banner: user clicks dismiss
Banner->>Store: write lastViewedDate (announcement.date)
Banner-->>App: hide banner
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
Paragon SummaryThis pull request review identified 2 issues across 2 categories in 2 files. The review analyzed code changes, potential bugs, security vulnerabilities, performance issues, and code quality concerns using automated analysis tools. This PR introduces the first Playwright smoke screen test suite for the application, establishing basic end-to-end testing capability that can be run via Docker. It adds a new smoke-screens test specification and makes corresponding updates to the App component to support testability. Key changes:
Confidence score: 3/5
2 files reviewed, 2 comments Severity breakdown: High: 1, Medium: 1 Tip: |
| </Box> | ||
| )} | ||
| <AnnouncementsModal /> | ||
| {/* <AnnouncementsModal /> */} |
There was a problem hiding this comment.
Bug: Production feature disabled for test convenience
Production feature disabled for test convenience. Users won't see announcements. Handle modal in test code instead.
View Details
Location: src/renderer/App.tsx (lines 203)
Analysis
Production feature disabled for test convenience. Users won't see announcements
| What fails | AnnouncementsModal component is commented out in production code to make tests pass |
| Result | Users do not see any announcement notifications that should be displayed |
| Expected | AnnouncementsModal should render normally; tests should dismiss or handle the modal in test code |
| Impact | Production functionality is broken for users; anti-pattern of modifying production code to suit tests |
How to reproduce
Start the app and navigate to any screen - announcements modal will never appearPatch Details
- {/* <AnnouncementsModal /> */}
+ <AnnouncementsModal />AI Fix Prompt
Fix this issue: Production feature disabled for test convenience. Users won't see announcements. Handle modal in test code instead.
Location: src/renderer/App.tsx (lines 203)
Problem: AnnouncementsModal component is commented out in production code to make tests pass
Current behavior: Users do not see any announcement notifications that should be displayed
Expected: AnnouncementsModal should render normally; tests should dismiss or handle the modal in test code
Steps to reproduce: Start the app and navigate to any screen - announcements modal will never appear
Provide a code fix.
Tip: Reply with @paragon-run to automatically fix this issue
| }); | ||
| } | ||
|
|
||
| async function selectFirstExperiment(page: Page) { |
There was a problem hiding this comment.
Bug: Test assumes existing experiment data
Test assumes existing experiment data. Fresh environments cause timeout errors. Add fixture to create experiment.
View Details
Location: test/playwright/smoke-screens.spec.ts (lines 24)
Analysis
Test assumes existing experiment data. Fresh environments cause timeout errors
| What fails | selectFirstExperiment function times out when no experiments exist in the test environment |
| Result | Tests for Interact, Tasks, Documents, Notes screens fail with timeout waiting for menuitem |
| Expected | Tests should either create an experiment fixture or skip gracefully when none exist |
| Impact | Flaky tests that fail in CI/fresh environments; unreliable test suite |
How to reproduce
Run tests in fresh environment with no experiments: npx playwright test smoke-screensAI Fix Prompt
Fix this issue: Test assumes existing experiment data. Fresh environments cause timeout errors. Add fixture to create experiment.
Location: test/playwright/smoke-screens.spec.ts (lines 24)
Problem: selectFirstExperiment function times out when no experiments exist in the test environment
Current behavior: Tests for Interact, Tasks, Documents, Notes screens fail with timeout waiting for menuitem
Expected: Tests should either create an experiment fixture or skip gracefully when none exist
Steps to reproduce: Run tests in fresh environment with no experiments: npx playwright test smoke-screens
Provide a code fix.
Tip: Reply with @paragon-run to automatically fix this issue
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/App.tsx`:
- Line 203: The AnnouncementsModal component was commented out in App.tsx (the
line with {/* <AnnouncementsModal /> */}) which disables announcements for all
users; restore the normal product behavior by re-enabling the
<AnnouncementsModal /> render inside the App component, and if you must avoid it
during automated tests, add a clear, explicit runtime guard (e.g., an isE2ETest
/ process.env.E2E flag or a window.__PLAYWRIGHT__ check) around the
<AnnouncementsModal /> render so it only skips in test runs—do not leave it
commented out globally and prefer handling test-specific suppression from
Playwright or an e2e-only flag.
In `@test/playwright/smoke-screens.spec.ts`:
- Around line 10-16: The login helper currently hard-codes credentials in the
login(page: Page) function; change it to read email and password from
environment variables (e.g., process.env.E2E_ADMIN_EMAIL and
process.env.E2E_ADMIN_PASSWORD), validate they are defined and throw a clear
error (fail fast) if missing, then use those variables in the calls to
page.getByPlaceholder('Email Address').fill(...) and
page.getByPlaceholder('Password').fill(...) before clicking the Sign In button;
this removes secrets from source and makes CI configurable.
| </Box> | ||
| )} | ||
| <AnnouncementsModal /> | ||
| {/* <AnnouncementsModal /> */} |
There was a problem hiding this comment.
Restore AnnouncementsModal for non-test runtime.
Line 203 disables announcements for all users, which is a product behavior change unrelated to smoke-test scope. Please render it again and handle test interference inside Playwright (or via an explicit e2e-only flag).
Suggested minimal fix
- {/* <AnnouncementsModal /> */}
+ <AnnouncementsModal />📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {/* <AnnouncementsModal /> */} | |
| <AnnouncementsModal /> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/App.tsx` at line 203, The AnnouncementsModal component was
commented out in App.tsx (the line with {/* <AnnouncementsModal /> */}) which
disables announcements for all users; restore the normal product behavior by
re-enabling the <AnnouncementsModal /> render inside the App component, and if
you must avoid it during automated tests, add a clear, explicit runtime guard
(e.g., an isE2ETest / process.env.E2E flag or a window.__PLAYWRIGHT__ check)
around the <AnnouncementsModal /> render so it only skips in test runs—do not
leave it commented out globally and prefer handling test-specific suppression
from Playwright or an e2e-only flag.
| async function login(page: Page) { | ||
| await page.goto('/'); | ||
|
|
||
| // Fill in credentials and submit | ||
| await page.getByPlaceholder('Email Address').fill('[email protected]'); | ||
| await page.getByPlaceholder('Password').fill('admin123'); | ||
| await page.getByRole('button', { name: 'Sign In' }).click(); |
There was a problem hiding this comment.
Avoid hard-coded login credentials in e2e tests.
Lines 14-16 hard-code credentials, which is brittle in CI and a security hygiene risk. Read credentials from environment variables and fail fast when missing.
Suggested fix
async function login(page: Page) {
await page.goto('/');
+ const email = process.env.E2E_EMAIL;
+ const password = process.env.E2E_PASSWORD;
+ if (!email || !password) {
+ throw new Error('Missing E2E_EMAIL/E2E_PASSWORD for smoke tests');
+ }
// Fill in credentials and submit
- await page.getByPlaceholder('Email Address').fill('[email protected]');
- await page.getByPlaceholder('Password').fill('admin123');
+ await page.getByPlaceholder('Email Address').fill(email);
+ await page.getByPlaceholder('Password').fill(password);
await page.getByRole('button', { name: 'Sign In' }).click();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/playwright/smoke-screens.spec.ts` around lines 10 - 16, The login helper
currently hard-codes credentials in the login(page: Page) function; change it to
read email and password from environment variables (e.g.,
process.env.E2E_ADMIN_EMAIL and process.env.E2E_ADMIN_PASSWORD), validate they
are defined and throw a clear error (fail fast) if missing, then use those
variables in the calls to page.getByPlaceholder('Email Address').fill(...) and
page.getByPlaceholder('Password').fill(...) before clicking the Sign In button;
this removes secrets from source and makes CI configurable.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/renderer/components/Shared/AnnouncementBanner.tsx (1)
16-19: Add unit tests for the utility and hook decision logic.This change adds utility/date logic and a complex selection flow (expired vs unseen vs latest) but no targeted unit coverage.
As per coding guidelines, "Write unit tests for all utility functions and complex hooks in frontend".
Also applies to: 54-145
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/Shared/AnnouncementBanner.tsx` around lines 16 - 19, Add unit tests covering the new date utility and the announcement selection logic: write tests for parseLocalDate (verify correct Date for various YYYY-MM-DD inputs, invalid strings, and timezone neutrality) and for the announcement selection flow in the hook that decides expired vs unseen vs latest (exercise cases where announcements are expired, already seen, unseen, and where multiple candidates exist to ensure the hook returns the correct announcement). Target the exported utility parseLocalDate and the hook/function responsible for selection (the selection logic within lines ~54-145) and include edge cases (empty lists, all expired, duplicate timestamps) to satisfy the "utility functions and complex hooks" guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/components/Shared/AnnouncementBanner.tsx`:
- Around line 180-188: The IconButton in AnnouncementBanner.tsx that calls
handleDismiss is icon-only and lacks an accessible name; update the IconButton
(the one rendering XIcon and using onClick={handleDismiss}) to provide an
accessible label by adding an aria-label (e.g., aria-label="Dismiss
announcement") or aria-labelledby pointing to visible/visually-hidden text, or a
title prop as fallback, so screen readers can announce the button's purpose
while preserving current styling and behavior.
- Around line 16-19: The issue is that parseLocalDate returns a Date at midnight
so comparisons against now make same-day expiries effective at 00:00; change
parseLocalDate(dateString: string) to produce the end of that local day (e.g.,
new Date(year, month - 1, day, 23, 59, 59, 999)) so that checks in the
AnnouncementBanner logic (places where parseLocalDate() is compared to now in
the component) treat the entire expiry day as valid; update the single
parseLocalDate function (used by the expiry checks) accordingly so all
comparisons (the existing comparisons that currently use parseLocalDate) behave
correctly.
- Around line 54-57: The effect calls API_URL() but doesn't list it as a
dependency, so extract the result of API_URL() to a component-level constant
(e.g., const apiUrl = API_URL()) and update the useEffect to depend on apiUrl
and hasChecked; change the early-return to use apiUrl (if (!apiUrl ||
hasChecked) return) and ensure any logic that previously called API_URL() inside
the effect now uses apiUrl so the effect re-runs when API_URL transitions from
null to a value.
- Around line 23-25: Remove the unnecessary any casts around window.storage in
getStoredDate (and any other uses of (window as any).storage) and use the
globally declared StorageAPI type directly (e.g., reference
window.storage.get/localStorageKey) to restore type-safety; also define a typed
interface for the announcement API response (e.g., IAnnouncementResponse) and
replace untyped accesses to result.data with the typed shape when parsing the
fetch result in the component (the code path that reads result.data), ensuring
you validate/guard optional fields before use.
---
Nitpick comments:
In `@src/renderer/components/Shared/AnnouncementBanner.tsx`:
- Around line 16-19: Add unit tests covering the new date utility and the
announcement selection logic: write tests for parseLocalDate (verify correct
Date for various YYYY-MM-DD inputs, invalid strings, and timezone neutrality)
and for the announcement selection flow in the hook that decides expired vs
unseen vs latest (exercise cases where announcements are expired, already seen,
unseen, and where multiple candidates exist to ensure the hook returns the
correct announcement). Target the exported utility parseLocalDate and the
hook/function responsible for selection (the selection logic within lines
~54-145) and include edge cases (empty lists, all expired, duplicate timestamps)
to satisfy the "utility functions and complex hooks" guideline.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/renderer/App.tsxsrc/renderer/components/Shared/AnnouncementBanner.tsxsrc/renderer/components/Shared/AnnouncementsModal.tsx
💤 Files with no reviewable changes (1)
- src/renderer/components/Shared/AnnouncementsModal.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/renderer/App.tsx
| function parseLocalDate(dateString: string): Date { | ||
| const [year, month, day] = dateString.split('-').map(Number); | ||
| return new Date(year, month - 1, day); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd -t f "AnnouncementBanner.tsx"Repository: transformerlab/transformerlab-app
Length of output: 130
🏁 Script executed:
cat -n src/renderer/components/Shared/AnnouncementBanner.tsxRepository: transformerlab/transformerlab-app
Length of output: 6891
Fix same-day expiry handling (currently expires at start of day).
Lines 87, 100, and 117 compare parseLocalDate() results (which return dates at midnight) against now (current time including hours/minutes). This causes announcements with expires: "YYYY-MM-DD" to expire immediately after 00:00 on that day instead of remaining valid throughout the day.
Proposed fix
function parseLocalDate(dateString: string): Date {
const [year, month, day] = dateString.split('-').map(Number);
return new Date(year, month - 1, day);
}
+
+function startOfDay(date: Date): Date {
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
+}
@@
- const now = new Date();
+ const now = new Date();
+ const today = startOfDay(now);
@@
- if (parseLocalDate(latestOverall.expires) < now) {
+ if (parseLocalDate(latestOverall.expires) < today) {
@@
- if (isNewer && a.expires && parseLocalDate(a.expires) < now) {
+ if (isNewer && a.expires && parseLocalDate(a.expires) < today) {
@@
- (a) => !a.expires || parseLocalDate(a.expires) >= now,
+ (a) => !a.expires || parseLocalDate(a.expires) >= today,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/components/Shared/AnnouncementBanner.tsx` around lines 16 - 19,
The issue is that parseLocalDate returns a Date at midnight so comparisons
against now make same-day expiries effective at 00:00; change
parseLocalDate(dateString: string) to produce the end of that local day (e.g.,
new Date(year, month - 1, day, 23, 59, 59, 999)) so that checks in the
AnnouncementBanner logic (places where parseLocalDate() is compared to now in
the component) treat the entire expiry day as valid; update the single
parseLocalDate function (used by the expiry checks) accordingly so all
comparisons (the existing comparisons that currently use parseLocalDate) behave
correctly.
| async function getStoredDate(): Promise<string | null> { | ||
| if ((window as any).storage) { | ||
| return (window as any).storage.get(localStorageKey); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd src/renderer/components/Shared && wc -l AnnouncementBanner.tsx && head -80 AnnouncementBanner.tsxRepository: transformerlab/transformerlab-app
Length of output: 2453
🏁 Script executed:
cd src/renderer/components/Shared && tail -120 AnnouncementBanner.tsxRepository: transformerlab/transformerlab-app
Length of output: 3422
🏁 Script executed:
rg "window\.storage|\.storage\." --type ts --type tsx -B 2 -A 2Repository: transformerlab/transformerlab-app
Length of output: 104
🏁 Script executed:
rg "window\.storage|\.storage\." -B 2 -A 2Repository: transformerlab/transformerlab-app
Length of output: 7662
🏁 Script executed:
rg "interface.*Window|Window.*interface|declare.*global" -B 3 -A 5 | head -50Repository: transformerlab/transformerlab-app
Length of output: 621
🏁 Script executed:
cat -n src/renderer/preload.d.tsRepository: transformerlab/transformerlab-app
Length of output: 811
🏁 Script executed:
cat -n src/renderer/preload.d.ts | grep -A 5 "StorageAPI"Repository: transformerlab/transformerlab-app
Length of output: 433
Remove any casts—window.storage is already properly typed.
Lines 24 and 31 use (window as any).storage, but window.storage is already declared as StorageAPI in the global scope (src/renderer/preload.d.ts). The casts are unnecessary and violate the type-safety guideline.
Additionally, line 68 should type the API response structure to avoid relying on unvalidated result.data.
Proposed fix
+interface AnnouncementsResponse {
+ data?: Announcement[];
+}
+
async function getStoredDate(): Promise<string | null> {
- if ((window as any).storage) {
- return (window as any).storage.get(localStorageKey);
+ if (window.storage) {
+ return window.storage.get(localStorageKey);
}
return localStorage.getItem(localStorageKey);
}
async function setStoredDate(date: string): Promise<void> {
- if ((window as any).storage) {
- await (window as any).storage.set(localStorageKey, date);
+ if (window.storage) {
+ await window.storage.set(localStorageKey, date);
} else {
localStorage.setItem(localStorageKey, date);
}
@@
- const result = await response.json();
- const announcements: Announcement[] = result.data || [];
+ const result = (await response.json()) as AnnouncementsResponse;
+ const announcements: Announcement[] = Array.isArray(result.data)
+ ? result.data
+ : [];Per coding guidelines: "Avoid any in TypeScript; define interfaces for all props and API responses to ensure type safety."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/components/Shared/AnnouncementBanner.tsx` around lines 23 - 25,
Remove the unnecessary any casts around window.storage in getStoredDate (and any
other uses of (window as any).storage) and use the globally declared StorageAPI
type directly (e.g., reference window.storage.get/localStorageKey) to restore
type-safety; also define a typed interface for the announcement API response
(e.g., IAnnouncementResponse) and replace untyped accesses to result.data with
the typed shape when parsing the fetch result in the component (the code path
that reads result.data), ensuring you validate/guard optional fields before use.
| useEffect(() => { | ||
| const isConnected = API_URL() !== null; | ||
| if (!isConnected || hasChecked) return; | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "AnnouncementBanner.tsx" | head -5Repository: transformerlab/transformerlab-app
Length of output: 132
🏁 Script executed:
cat -n ./src/renderer/components/Shared/AnnouncementBanner.tsxRepository: transformerlab/transformerlab-app
Length of output: 6891
🏁 Script executed:
find . -type f -name "urls.ts" -o -name "urls.js" | grep -E "api-client|lib" | head -10Repository: transformerlab/transformerlab-app
Length of output: 114
🏁 Script executed:
cat -n ./src/renderer/lib/api-client/urls.tsRepository: transformerlab/transformerlab-app
Length of output: 3140
Add API_URL to the effect dependency array.
The useEffect calls API_URL() on lines 55 and 61, but the dependency array at line 145 only includes hasChecked. If API_URL() becomes available after the initial render (i.e., transitions from null to a value), the effect will never re-run to fetch announcements.
Extract API_URL() to a component-level variable and add it to the dependency array:
Proposed fix
export default function AnnouncementBanner() {
+ const apiUrl = API_URL();
const [currentAnnouncement, setCurrentAnnouncement] =
@@
useEffect(() => {
- const isConnected = API_URL() !== null;
- if (!isConnected || hasChecked) return;
+ if (!apiUrl || hasChecked) return;
@@
const response = await chatAPI.authenticatedFetch(
- `${API_URL()}server/announcements`,
+ `${apiUrl}server/announcements`,
);
@@
- }, [hasChecked]);
+ }, [apiUrl, hasChecked]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/components/Shared/AnnouncementBanner.tsx` around lines 54 - 57,
The effect calls API_URL() but doesn't list it as a dependency, so extract the
result of API_URL() to a component-level constant (e.g., const apiUrl =
API_URL()) and update the useEffect to depend on apiUrl and hasChecked; change
the early-return to use apiUrl (if (!apiUrl || hasChecked) return) and ensure
any logic that previously called API_URL() inside the effect now uses apiUrl so
the effect re-runs when API_URL transitions from null to a value.
| <IconButton | ||
| size="sm" | ||
| variant="plain" | ||
| color="neutral" | ||
| onClick={handleDismiss} | ||
| sx={{ mt: 0.25 }} | ||
| > | ||
| <XIcon size={16} /> | ||
| </IconButton> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd src/renderer/components/Shared && wc -l AnnouncementBanner.tsxRepository: transformerlab/transformerlab-app
Length of output: 103
🏁 Script executed:
cd src/renderer/components/Shared && sed -n '175,195p' AnnouncementBanner.tsxRepository: transformerlab/transformerlab-app
Length of output: 440
🏁 Script executed:
# Also check what IconButton is being imported from
cd src/renderer/components/Shared && head -30 AnnouncementBanner.tsx | grep -E "import|from"Repository: transformerlab/transformerlab-app
Length of output: 410
🏁 Script executed:
# Check if aria-label is used on other IconButton instances in the codebase
rg "IconButton" src/renderer --type tsx -A 3 | grep -i "aria-label" | head -20Repository: transformerlab/transformerlab-app
Length of output: 104
🏁 Script executed:
# Get broader context - check the entire AnnouncementBanner component
cat -n src/renderer/components/Shared/AnnouncementBanner.tsxRepository: transformerlab/transformerlab-app
Length of output: 6891
🏁 Script executed:
# Search for aria-label usage patterns in similar button/icon components
rg "aria-label" src/renderer --type tsx -B 2 -A 1 | head -40Repository: transformerlab/transformerlab-app
Length of output: 104
🏁 Script executed:
# Search for aria-label usage in the codebase (without file type)
rg "aria-label" src/renderer -B 2 -A 1 | head -50Repository: transformerlab/transformerlab-app
Length of output: 3153
🏁 Script executed:
# Also check MUI Joy documentation knowledge - search for IconButton with aria in codebase
rg "IconButton" src/renderer -B 1 -A 5 | head -60Repository: transformerlab/transformerlab-app
Length of output: 4254
🏁 Script executed:
# Search more specifically for IconButton usage with children but looking for aria patterns
rg "IconButton" src/renderer -B 2 -A 10 | grep -E "(IconButton|aria-label|title)" | head -40Repository: transformerlab/transformerlab-app
Length of output: 3800
🏁 Script executed:
# Check if there are any icon-only buttons with aria-label in the codebase
rg -B 3 "IconButton" src/renderer | rg -B 3 "aria-label"Repository: transformerlab/transformerlab-app
Length of output: 1005
🏁 Script executed:
# Let's look at one of those examples more closely to see the full IconButton usage with aria-label
rg -B 5 -A 5 "aria-label=\"Stop generation\"" src/rendererRepository: transformerlab/transformerlab-app
Length of output: 1161
🏁 Script executed:
# Check MUI Joy IconButton component - search for its usage with aria attributes in other files
rg -B 3 -A 8 "IconButton" src/renderer/components/Experiment/Interact/VisualizeGeneration.tsx | grep -A 8 "aria-label"Repository: transformerlab/transformerlab-app
Length of output: 334
Add an accessible name to the dismiss button.
The IconButton at lines 180-188 is icon-only without an accessible label, so screen readers won't announce its purpose. This violates WCAG 2.1 Level A requirements for button names.
Proposed fix
<IconButton
+ aria-label="Dismiss announcement"
size="sm"
variant="plain"
color="neutral"
onClick={handleDismiss}
sx={{ mt: 0.25 }}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <IconButton | |
| size="sm" | |
| variant="plain" | |
| color="neutral" | |
| onClick={handleDismiss} | |
| sx={{ mt: 0.25 }} | |
| > | |
| <XIcon size={16} /> | |
| </IconButton> | |
| <IconButton | |
| aria-label="Dismiss announcement" | |
| size="sm" | |
| variant="plain" | |
| color="neutral" | |
| onClick={handleDismiss} | |
| sx={{ mt: 0.25 }} | |
| > | |
| <XIcon size={16} /> | |
| </IconButton> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/components/Shared/AnnouncementBanner.tsx` around lines 180 -
188, The IconButton in AnnouncementBanner.tsx that calls handleDismiss is
icon-only and lacks an accessible name; update the IconButton (the one rendering
XIcon and using onClick={handleDismiss}) to provide an accessible label by
adding an aria-label (e.g., aria-label="Dismiss announcement") or
aria-labelledby pointing to visible/visually-hidden text, or a title prop as
fallback, so screen readers can announce the button's purpose while preserving
current styling and behavior.
To tests, run the docker container using:
npm run docker-test:upThen run the tests using:
npx playwright test smoke-screensor
npx playwright test smoke-screens --headedto see itSummary by CodeRabbit