diff --git a/src/renderer/components/filters/FilterCheckbox.test.tsx b/src/renderer/components/filters/FilterCheckbox.test.tsx
index 02fe144ea..514465d67 100644
--- a/src/renderer/components/filters/FilterCheckbox.test.tsx
+++ b/src/renderer/components/filters/FilterCheckbox.test.tsx
@@ -30,7 +30,7 @@ describe('renderer/components/filters/FilterCheckbox.tsx', () => {
expect(screen.getByRole('checkbox', { name: 'Issues' })).toBeDisabled();
- await userEvent.click(screen.getByTestId('tooltip-icon-tooltip-issues'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Issues' }));
expect(screen.getByText('Only issues assigned to you.')).toBeInTheDocument();
});
diff --git a/src/renderer/components/filters/FilterCheckbox.tsx b/src/renderer/components/filters/FilterCheckbox.tsx
index e5a873dda..70cebba6d 100644
--- a/src/renderer/components/filters/FilterCheckbox.tsx
+++ b/src/renderer/components/filters/FilterCheckbox.tsx
@@ -45,7 +45,7 @@ export function FilterCheckbox({
{label}
- {tooltip && }
+ {tooltip && }
{counter !== undefined && (
{
it('lists the supported search qualifiers including commenter', async () => {
renderWithProviders();
- const tooltipIconElement = screen.getByTestId('tooltip-icon-tooltip-search');
+ const tooltipIconElement = screen.getByRole('button', { name: 'Help: Search' });
await userEvent.click(tooltipIconElement);
expect(screen.getByText('Author (author:handle)')).toBeInTheDocument();
@@ -30,7 +30,7 @@ describe('renderer/components/filters/SearchFilter.tsx', () => {
it('marks qualifiers that require detailed notifications as disabled', async () => {
renderWithProviders(, { settings: { detailedNotifications: false } });
- const tooltipIconElement = screen.getByTestId('tooltip-icon-tooltip-search');
+ const tooltipIconElement = screen.getByRole('button', { name: 'Help: Search' });
await userEvent.click(tooltipIconElement);
expect(screen.getByText('Author (author:handle)')).toHaveAttribute('aria-disabled', 'true');
diff --git a/src/renderer/components/primitives/HelpPopover.test.tsx b/src/renderer/components/primitives/HelpPopover.test.tsx
index 85954a017..4d57e3e1c 100644
--- a/src/renderer/components/primitives/HelpPopover.test.tsx
+++ b/src/renderer/components/primitives/HelpPopover.test.tsx
@@ -8,20 +8,21 @@ import { HelpPopover, type HelpPopoverProps } from './HelpPopover';
describe('renderer/components/primitives/HelpPopover.tsx', () => {
const props: HelpPopoverProps = {
name: 'test',
+ label: 'Test setting',
content: 'This is some help text',
};
it('renders', () => {
renderWithProviders();
- expect(screen.getByTestId('tooltip-icon-test')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Help: Test setting' })).toBeInTheDocument();
expect(screen.queryByText(props.content as string)).not.toBeInTheDocument();
});
it('toggles (show/hide) help on clicking the icon', async () => {
renderWithProviders();
- const icon = screen.getByTestId('tooltip-icon-test');
+ const icon = screen.getByRole('button', { name: 'Help: Test setting' });
await userEvent.click(icon);
expect(screen.getByText(props.content as string)).toBeInTheDocument();
@@ -43,7 +44,7 @@ describe('renderer/components/primitives/HelpPopover.tsx', () => {
Object.defineProperty(scrollContainer, 'clientHeight', { configurable: true, value: 100 });
Object.defineProperty(scrollContainer, 'scrollHeight', { configurable: true, value: 400 });
- await userEvent.click(screen.getByTestId('tooltip-icon-test'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Test setting' }));
expect(screen.getByText(props.content as string)).toBeInTheDocument();
fireEvent.scroll(scrollContainer);
@@ -56,7 +57,7 @@ describe('renderer/components/primitives/HelpPopover.tsx', () => {
it('hides help when clicking outside', async () => {
renderWithProviders();
- const icon = screen.getByTestId('tooltip-icon-test');
+ const icon = screen.getByRole('button', { name: 'Help: Test setting' });
await userEvent.click(icon);
expect(screen.getByText(props.content as string)).toBeInTheDocument();
diff --git a/src/renderer/components/primitives/HelpPopover.tsx b/src/renderer/components/primitives/HelpPopover.tsx
index b9abe52ea..7e736d1a5 100644
--- a/src/renderer/components/primitives/HelpPopover.tsx
+++ b/src/renderer/components/primitives/HelpPopover.tsx
@@ -7,6 +7,8 @@ import { cn } from 'cn';
export interface HelpPopoverProps {
name: string;
+ /** Accessible name for the help button, e.g. the label of the control it explains. */
+ label: string;
content: ReactNode | string;
}
@@ -74,7 +76,7 @@ export function HelpPopover(props: Readonly) {
renderAnchor={(anchorProps) => (
}
+ label={props.children}
name={`tooltip-${name}`}
/>
);
diff --git a/src/renderer/components/settings/AlertSettings.test.tsx b/src/renderer/components/settings/AlertSettings.test.tsx
index ad347a9cf..babf904c8 100644
--- a/src/renderer/components/settings/AlertSettings.test.tsx
+++ b/src/renderer/components/settings/AlertSettings.test.tsx
@@ -35,7 +35,7 @@ describe('renderer/components/settings/AlertSettings.tsx', () => {
it('shows detailed help in tooltips', async () => {
renderWithProviders();
- await userEvent.click(screen.getByTestId('tooltip-icon-tooltip-playSound'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Play notification sound' }));
expect(
screen.getByText(
@@ -50,7 +50,7 @@ describe('renderer/components/settings/AlertSettings.tsx', () => {
expect(screen.getByText('Notification volume')).toBeInTheDocument();
expect(screen.getByTestId('reveal-volume')).toHaveAttribute('data-state', 'open');
- await userEvent.click(screen.getByTestId('tooltip-icon-tooltip-notificationVolume'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Notification volume' }));
expect(screen.getByText('Adjust the volume of the notification sound.')).toBeInTheDocument();
});
diff --git a/src/renderer/components/settings/AppearanceSettings.test.tsx b/src/renderer/components/settings/AppearanceSettings.test.tsx
index bddc6d877..66a07bebc 100644
--- a/src/renderer/components/settings/AppearanceSettings.test.tsx
+++ b/src/renderer/components/settings/AppearanceSettings.test.tsx
@@ -127,21 +127,21 @@ describe('renderer/components/settings/AppearanceSettings.tsx', () => {
renderWithProviders();
});
- await userEvent.click(screen.getByTestId('tooltip-icon-tooltip-designLanguage'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Design' }));
expect(
screen.getByText(
'Choose Classic for the original Gitify layout, or Glass for the translucent, layered design language.',
),
).toBeInTheDocument();
- await userEvent.click(screen.getByTestId('tooltip-icon-tooltip-theme'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Theme' }));
expect(
screen.getByText(
'Choose a light, dark, or system color mode. Available options depend on the selected design language.',
),
).toBeInTheDocument();
- await userEvent.click(screen.getByTestId('tooltip-icon-tooltip-zoom'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Zoom' }));
expect(
screen.getByText(
'Change the size of the Gitify interface. The setting applies immediately and is remembered for the next launch.',
diff --git a/src/renderer/components/settings/InboxSettings.test.tsx b/src/renderer/components/settings/InboxSettings.test.tsx
index 7809cf4b2..4b1868454 100644
--- a/src/renderer/components/settings/InboxSettings.test.tsx
+++ b/src/renderer/components/settings/InboxSettings.test.tsx
@@ -259,7 +259,7 @@ describe('renderer/components/settings/InboxSettings.tsx', () => {
});
});
- await userEvent.click(screen.getByTestId('tooltip-icon-tooltip-showOnlyParticipating'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Fetch only participating' }));
await userEvent.click(
screen.getByTitle('Open GitHub documentation for participating and watching notifications'),
@@ -272,19 +272,19 @@ describe('renderer/components/settings/InboxSettings.tsx', () => {
});
it.each([
- ['groupBy', 'tooltip-groupBy'],
- ['showAccountHeader', 'tooltip-showAccountHeader'],
- ['wrapNotificationTitle', 'tooltip-wrapNotificationTitle'],
- ['showNumber', 'tooltip-showNumber'],
- ['delayNotificationState', 'tooltip-delayNotificationState'],
- ] as const)('renders the %s tooltip', async (_name, tooltipName) => {
+ ['groupBy', 'Group by'],
+ ['showAccountHeader', 'Show account header'],
+ ['wrapNotificationTitle', 'Show full notification title'],
+ ['showNumber', 'Show reference'],
+ ['delayNotificationState', 'Delay notification removal'],
+ ] as const)('renders the %s tooltip', async (_name, label) => {
await act(async () => {
renderWithProviders(, {
accounts: [mockGitHubCloudAccount],
});
});
- expect(screen.getByTestId(`tooltip-icon-${tooltipName}`)).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: `Help: ${label}` })).toBeInTheDocument();
});
it('shows the fetch interval details in a tooltip', async () => {
@@ -292,7 +292,7 @@ describe('renderer/components/settings/InboxSettings.tsx', () => {
renderWithProviders();
});
- await userEvent.click(screen.getByTestId('tooltip-icon-tooltip-fetchInterval'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Fetch interval' }));
expect(
screen.getByText(
diff --git a/src/renderer/components/settings/SystemSettings.test.tsx b/src/renderer/components/settings/SystemSettings.test.tsx
index 78bafde3e..c71a6d3c5 100644
--- a/src/renderer/components/settings/SystemSettings.test.tsx
+++ b/src/renderer/components/settings/SystemSettings.test.tsx
@@ -131,7 +131,7 @@ describe('renderer/components/settings/SystemSettings.tsx', () => {
expect(screen.getByText('Global shortcut')).toBeInTheDocument();
- await userEvent.click(screen.getByTestId('tooltip-icon-tooltip-shortcut'));
+ await userEvent.click(screen.getByRole('button', { name: 'Help: Global shortcut' }));
expect(screen.getByText('Shortcuts must include:')).toBeInTheDocument();
});
diff --git a/src/renderer/components/settings/controls/SettingsChoice.tsx b/src/renderer/components/settings/controls/SettingsChoice.tsx
index 918f9a241..57be7ef4e 100644
--- a/src/renderer/components/settings/controls/SettingsChoice.tsx
+++ b/src/renderer/components/settings/controls/SettingsChoice.tsx
@@ -144,7 +144,7 @@ export function SettingsChoice({
>
{label}
- {tooltip && }
+ {tooltip && }
{control}
diff --git a/src/renderer/components/settings/controls/SettingsRow.tsx b/src/renderer/components/settings/controls/SettingsRow.tsx
index 907e0ad60..8b52c18bf 100644
--- a/src/renderer/components/settings/controls/SettingsRow.tsx
+++ b/src/renderer/components/settings/controls/SettingsRow.tsx
@@ -44,7 +44,7 @@ export function SettingsRow({
{label}
- {tooltip && }
+ {tooltip && }
{children}
diff --git a/src/renderer/components/settings/controls/SettingsToggle.tsx b/src/renderer/components/settings/controls/SettingsToggle.tsx
index f72fb35b1..c10f01c28 100644
--- a/src/renderer/components/settings/controls/SettingsToggle.tsx
+++ b/src/renderer/components/settings/controls/SettingsToggle.tsx
@@ -57,7 +57,7 @@ export function SettingsToggle({
>
{label}
- {tooltip && }
+ {tooltip && }
{caption && (
diff --git a/src/renderer/icons/ScopeStatusIcon.test.tsx b/src/renderer/icons/ScopeStatusIcon.test.tsx
index a924dc4b3..d5674c07f 100644
--- a/src/renderer/icons/ScopeStatusIcon.test.tsx
+++ b/src/renderer/icons/ScopeStatusIcon.test.tsx
@@ -11,14 +11,9 @@ describe('renderer/icons/ScopeStatusIcon.tsx', () => {
expect(tree.container).toMatchSnapshot();
});
- it('renders a success icon with test id when withTestId is true', () => {
- renderWithProviders();
- expect(screen.getByTestId('account-scopes-scope-granted')).toBeInTheDocument();
- });
-
- it('does not render a test id by default', () => {
+ it('announces the granted status', () => {
renderWithProviders();
- expect(screen.queryByTestId('account-scopes-scope-granted')).not.toBeInTheDocument();
+ expect(screen.getByRole('img', { name: 'granted' })).toBeInTheDocument();
});
});
@@ -28,14 +23,9 @@ describe('renderer/icons/ScopeStatusIcon.tsx', () => {
expect(tree.container).toMatchSnapshot();
});
- it('renders a danger icon with test id when withTestId is true', () => {
- renderWithProviders();
- expect(screen.getByTestId('account-scopes-scope-missing')).toBeInTheDocument();
- });
-
- it('does not render a test id by default', () => {
+ it('announces the missing status', () => {
renderWithProviders();
- expect(screen.queryByTestId('account-scopes-scope-missing')).not.toBeInTheDocument();
+ expect(screen.getByRole('img', { name: 'missing' })).toBeInTheDocument();
});
});
@@ -45,24 +35,17 @@ describe('renderer/icons/ScopeStatusIcon.tsx', () => {
expect(tree.container).toMatchSnapshot();
});
- it('renders a dash icon with test id when withTestId is true', () => {
- renderWithProviders(
- ,
- );
- expect(screen.getByTestId('account-scopes-scope-na')).toBeInTheDocument();
- });
-
- it('does not render a test id by default', () => {
- renderWithProviders();
- expect(screen.queryByTestId('account-scopes-scope-na')).not.toBeInTheDocument();
+ it('ignores granted=true when notApplicable is true', () => {
+ renderWithProviders();
+ expect(screen.getByRole('img', { name: 'not applicable' })).toBeInTheDocument();
+ expect(screen.queryByRole('img', { name: 'granted' })).not.toBeInTheDocument();
});
+ });
- it('ignores granted=true when notApplicable is true', () => {
- renderWithProviders(
- ,
- );
- expect(screen.getByTestId('account-scopes-scope-na')).toBeInTheDocument();
- expect(screen.queryByTestId('account-scopes-scope-granted')).not.toBeInTheDocument();
+ describe('subject prop', () => {
+ it('prefixes the subject to the accessible name', () => {
+ renderWithProviders();
+ expect(screen.getByRole('img', { name: 'repo missing' })).toBeInTheDocument();
});
});
diff --git a/src/renderer/icons/ScopeStatusIcon.tsx b/src/renderer/icons/ScopeStatusIcon.tsx
index a666fe0c0..f345f938e 100644
--- a/src/renderer/icons/ScopeStatusIcon.tsx
+++ b/src/renderer/icons/ScopeStatusIcon.tsx
@@ -4,36 +4,29 @@ export interface ScopeStatusIconProps {
granted: boolean;
notApplicable?: boolean;
size?: number;
- withTestId?: boolean;
+ /** What the status describes (e.g. a scope name), prefixed to the accessible name. */
+ subject?: string;
}
export function ScopeStatusIcon({
granted,
notApplicable = false,
size = 14,
- withTestId = false,
+ subject,
}: Readonly) {
+ let status = granted ? 'granted' : 'missing';
if (notApplicable) {
- return (
-
- );
+ status = 'not applicable';
+ }
+ const ariaLabel = subject ? `${subject} ${status}` : status;
+
+ if (notApplicable) {
+ return ;
}
return granted ? (
-
+
) : (
-
+
);
}
diff --git a/src/renderer/icons/__snapshots__/ScopeStatusIcon.test.tsx.snap b/src/renderer/icons/__snapshots__/ScopeStatusIcon.test.tsx.snap
index 5b40f633f..ac709ad7e 100644
--- a/src/renderer/icons/__snapshots__/ScopeStatusIcon.test.tsx.snap
+++ b/src/renderer/icons/__snapshots__/ScopeStatusIcon.test.tsx.snap
@@ -3,7 +3,7 @@
exports[`renderer/icons/ScopeStatusIcon.tsx > granted state > renders a success icon when granted 1`] = `