diff --git a/package.json b/package.json
index 426ef8667..006493382 100644
--- a/package.json
+++ b/package.json
@@ -102,6 +102,7 @@
"@parcel/watcher": "2.6.0",
"@primer/css": "22.3.0",
"@primer/octicons-react": "19.33.0",
+ "@primer/primitives": "11.10.0",
"@primer/react": "38.38.0",
"@tailwindcss/vite": "4.3.3",
"@tanstack/react-query": "5.102.8",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 23005bad1..26bb2f5fe 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -93,6 +93,9 @@ importers:
'@primer/octicons-react':
specifier: 19.33.0
version: 19.33.0(react@19.2.8)
+ '@primer/primitives':
+ specifier: 11.10.0
+ version: 11.10.0
'@primer/react':
specifier: 38.38.0
version: 38.38.0(@types/react-dom@19.2.7(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.8)(react@19.2.8)
diff --git a/src/renderer/App.css b/src/renderer/App.css
index c1eb988e7..0d91f8dc5 100644
--- a/src/renderer/App.css
+++ b/src/renderer/App.css
@@ -4,6 +4,9 @@
/** GitHub Primer Design System */
@import '@primer/css/dist/primer.css';
+/** GitHub Primer Primitives - motion tokens (required by Primer React's Spinner & Skeleton animations) */
+@import '@primer/primitives/dist/css/base/motion/motion.css';
+
/** Tailwind CSS Configuration */
@config "../../tailwind.config.mts";
diff --git a/src/renderer/components/Sidebar.test.tsx b/src/renderer/components/Sidebar.test.tsx
index 8ba1d00f6..febc0fe05 100644
--- a/src/renderer/components/Sidebar.test.tsx
+++ b/src/renderer/components/Sidebar.test.tsx
@@ -208,24 +208,37 @@ describe('renderer/components/Sidebar.tsx', () => {
expect(fetchNotificationsMock).not.toHaveBeenCalled();
});
- it('animates the refresh icon while a background fetch is in flight, regardless of settled status', () => {
+ it('shows a loading spinner on the refresh icon while the initial fetch is loading', () => {
renderWithProviders(, {
+ accounts: [mockGitHubCloudAccount],
+ status: 'loading',
+ });
+
+ expect(screen.getByTestId('sidebar-refresh')).toHaveAttribute('data-loading', 'true');
+ expect(screen.getByTestId('sidebar-refresh')).toBeDisabled();
+ });
+
+ it('shows a loading spinner on the refresh icon while a background fetch is in flight, regardless of settled status', () => {
+ const tree = renderWithProviders(, {
accounts: [mockGitHubCloudAccount],
status: 'error',
isFetching: true,
});
- expect(screen.getByTestId('sidebar-refresh')).toHaveClass('animate-spin');
+ expect(screen.getByTestId('sidebar-refresh')).toHaveAttribute('data-loading', 'true');
+ expect(tree.container.querySelector('[data-component="Spinner"]')).toBeInTheDocument();
+ expect(screen.getByTestId('sidebar-refresh')).toBeDisabled();
});
- it('does not animate the refresh icon when settled and no fetch is in flight', () => {
+ it('does not show a loading spinner on the refresh icon when settled and no fetch is in flight', () => {
renderWithProviders(, {
accounts: [mockGitHubCloudAccount],
status: 'error',
isFetching: false,
});
- expect(screen.getByTestId('sidebar-refresh')).not.toHaveClass('animate-spin');
+ expect(screen.getByTestId('sidebar-refresh')).toHaveAttribute('data-loading', 'false');
+ expect(screen.getByTestId('sidebar-refresh')).toBeEnabled();
});
});
diff --git a/src/renderer/components/Sidebar.tsx b/src/renderer/components/Sidebar.tsx
index bcc0e1c68..6bafb3bba 100644
--- a/src/renderer/components/Sidebar.tsx
+++ b/src/renderer/components/Sidebar.tsx
@@ -124,13 +124,12 @@ export const Sidebar: FC = () => {
<>
shortcuts.refresh.action()}
size="small"
tooltipDirection="e"
diff --git a/src/renderer/components/__snapshots__/Sidebar.test.tsx.snap b/src/renderer/components/__snapshots__/Sidebar.test.tsx.snap
index 7611c82dd..e3c9fccfe 100644
--- a/src/renderer/components/__snapshots__/Sidebar.test.tsx.snap
+++ b/src/renderer/components/__snapshots__/Sidebar.test.tsx.snap
@@ -637,36 +637,40 @@ exports[`renderer/components/Sidebar.tsx > should render itself & its children (
data-padding="normal"
data-wrap="nowrap"
>
-
+
+
+