Skip to content

Commit 263ee04

Browse files
authored
Added loading state to post analytics export (TryGhost#27998)
closes https://linear.app/ghost/issue/NY-1296 ## Summary - Added a loading and disabled state to the Post analytics export button while the CSV export is running - Guarded against duplicate export clicks during an in-flight request - Added acceptance coverage for the loading and disabled button state ### Before https://github.com/user-attachments/assets/6be392d2-33ec-41ef-8344-407028c9472a ### After https://github.com/user-attachments/assets/9d2989da-24bf-4d00-bf44-fbab25fdc414
1 parent 70d5ac9 commit 263ee04

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

apps/admin-x-settings/src/components/settings/advanced/migration-tools/migration-tools-export.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {useHandleError} from '@tryghost/admin-x-framework/hooks';
55
import {usePostsExports} from '@tryghost/admin-x-framework/api/posts';
66

77
const MigrationToolsExport: React.FC = () => {
8+
const [isExportingPosts, setIsExportingPosts] = React.useState(false);
89
const {refetch: postsData} = usePostsExports({
910
searchParams: {
1011
limit: '1000'
@@ -14,6 +15,12 @@ const MigrationToolsExport: React.FC = () => {
1415
const handleError = useHandleError();
1516

1617
const exportPosts = async () => {
18+
if (isExportingPosts) {
19+
return;
20+
}
21+
22+
setIsExportingPosts(true);
23+
1724
try {
1825
const {data} = await postsData();
1926
if (data) {
@@ -30,13 +37,15 @@ const MigrationToolsExport: React.FC = () => {
3037
}
3138
} catch (e) {
3239
handleError(e);
40+
} finally {
41+
setIsExportingPosts(false);
3342
}
3443
};
3544

3645
return (
3746
<div className='grid grid-cols-1 gap-4 pt-4 md:grid-cols-2 lg:grid-cols-3'>
3847
<Button className='h-9! font-semibold!' color='grey' icon='export' iconColorClass='h-4! w-auto!' label='Content & settings' onClick={() => downloadAllContent()} />
39-
<Button className='h-9! font-semibold!' color='grey' icon='baseline-chart' iconColorClass='h-4! w-auto!' label='Post analytics' onClick={exportPosts} />
48+
<Button className='h-9! font-semibold!' color='grey' disabled={isExportingPosts} icon='baseline-chart' iconColorClass='h-4! w-auto!' label='Post analytics' loading={isExportingPosts} testId='post-analytics-export-button' onClick={exportPosts} />
4049
</div>
4150
);
4251
};

apps/admin-x-settings/test/acceptance/membership/analytics.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,45 @@ test.describe('Analytics settings', async () => {
135135
expect(hasDownloadUrl).toBe(true);
136136
});
137137

138+
test('Disables post analytics export button and shows loading state while downloading', async ({page}) => {
139+
await mockApi({page, requests: createMockApiConfig({})});
140+
141+
let exportRequestCount = 0;
142+
let resolveExportRequest: (() => void) | undefined;
143+
144+
await page.route(/\/ghost\/api\/admin\/posts\/export\/\?limit=1000$/, async (route) => {
145+
exportRequestCount += 1;
146+
await new Promise<void>((resolve) => {
147+
resolveExportRequest = resolve;
148+
});
149+
await route.fulfill({
150+
status: 200,
151+
body: 'csv data',
152+
headers: {
153+
'content-type': 'text/csv'
154+
}
155+
});
156+
});
157+
158+
await page.goto('/');
159+
160+
const section = page.getByTestId('migrationtools');
161+
162+
await section.getByRole('tab', {name: 'Export'}).click();
163+
164+
const postAnalyticsButton = section.getByTestId('post-analytics-export-button');
165+
await postAnalyticsButton.click();
166+
167+
await expect.poll(() => exportRequestCount).toBe(1);
168+
await expect(postAnalyticsButton).toBeDisabled();
169+
await expect(postAnalyticsButton).toContainText('Loading...');
170+
171+
resolveExportRequest?.();
172+
173+
await expect(postAnalyticsButton).toBeEnabled();
174+
expect(exportRequestCount).toBe(1);
175+
});
176+
138177
test('Supports read only settings', async ({page}) => {
139178
await mockApi({page, requests: {
140179
...globalDataRequests,

0 commit comments

Comments
 (0)