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" > - + + +