Skip to content

Commit 5f1630e

Browse files
authored
Wired up member count in Explore (#24482)
ref https://linear.app/ghost/issue/PROD-1771 - if growth data sharing is enabled, show a preview with the site's members count - member count above 1000 are rendered are abbreviated, e.g. `1.2k` for `1210` members
1 parent 07ce222 commit 5f1630e

File tree

4 files changed

+90
-16
lines changed

4 files changed

+90
-16
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ jobs:
110110
- 'apps/admin-x-settings/**'
111111
- 'apps/admin-x-design-system/**'
112112
- 'apps/admin-x-framework/**'
113+
- 'apps/shade/**'
113114
admin-x-activitypub:
114115
- *shared
115116
- 'apps/shade/**'

apps/admin-x-settings/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"@testing-library/react": "14.3.1",
5050
"@tryghost/admin-x-design-system": "0.0.0",
5151
"@tryghost/admin-x-framework": "0.0.0",
52+
"@tryghost/shade": "0.0.0",
5253
"@tryghost/custom-fonts": "1.0.2",
5354
"@types/react": "18.3.23",
5455
"@types/react-dom": "18.3.7",

apps/admin-x-settings/src/components/settings/growth/Explore.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import FakeLogo from '../../../assets/images/explore-default-logo.png';
2-
import React from 'react';
2+
import React, {useEffect, useState} from 'react';
33
import SettingImg from '../../../assets/images/ghost-explore.png';
44
import TopLevelGroup from '../../TopLevelGroup';
55
import useSettingGroup from '../../../hooks/useSettingGroup';
66
import {Button, Icon, Separator, SettingGroupContent, Toggle, withErrorBoundary} from '@tryghost/admin-x-design-system';
77
import {Setting, getSettingValues, useEditSettings} from '@tryghost/admin-x-framework/api/settings';
8+
import {abbreviateNumber} from '@tryghost/shade';
9+
import {useBrowseMembers} from '@tryghost/admin-x-framework/api/members';
810
import {useGlobalData} from '../../providers/GlobalDataProvider';
911
import {useHandleError} from '@tryghost/admin-x-framework/hooks';
1012
import {useRouting} from '@tryghost/admin-x-framework/routing';
@@ -15,6 +17,21 @@ const Explore: React.FC<{ keywords: string[] }> = ({keywords}) => {
1517
const handleError = useHandleError();
1618
const {updateRoute} = useRouting();
1719

20+
// Get members count
21+
const {refetch: fetchMembers} = useBrowseMembers({
22+
searchParams: {limit: '1'}
23+
});
24+
const [membersCount, setMembersCount] = useState(0);
25+
useEffect(() => {
26+
const fetchMemberCount = async () => {
27+
const {data: members} = await fetchMembers();
28+
const count = members?.meta?.pagination?.total || 0;
29+
setMembersCount(count);
30+
};
31+
32+
fetchMemberCount();
33+
}, [fetchMembers]);
34+
1835
const [accentColor, icon] = getSettingValues<string>(settings, ['accent_color', 'icon']);
1936
const {localSettings, siteData} = useSettingGroup();
2037
const [title, description] = getSettingValues(localSettings, ['title', 'description']) as string[];
@@ -89,8 +106,8 @@ const Explore: React.FC<{ keywords: string[] }> = ({keywords}) => {
89106
<a className='group mt-8 flex h-6 w-full items-center justify-between gap-5 hover:cursor-pointer' href={url} rel="noopener noreferrer" target="_blank">
90107
<span className='text-sm font-semibold'>{siteDomain}</span>
91108
{shareGrowthData ?
92-
<span className='rounded-sm bg-black px-2 py-0.5 text-xs font-semibold text-white'>
93-
12k members
109+
<span className='rounded-sm bg-black px-2 py-0.5 text-xs font-semibold text-white' data-testid='explore-members-count'>
110+
{abbreviateNumber(membersCount)}&nbsp;{membersCount === 1 ? 'member' : 'members'}
94111
</span>
95112
:
96113
<span className='flex size-5 items-center justify-center rounded-full border border-black text-black group-hover:bg-black group-hover:text-white'>

apps/admin-x-settings/test/acceptance/growth/explore.test.ts

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ test.describe('Ghost Explore', () => {
3939
});
4040
});
4141

42-
test('can choose to share growth data', async ({page}) => {
42+
test('can share growth data with Ghost Explore', async ({page}) => {
4343
const {lastApiRequests} = await mockApi({page, requests: {
4444
...globalDataRequests,
4545
browseSettings: {
@@ -67,28 +67,79 @@ test.describe('Ghost Explore', () => {
6767
const mainToggle = section.getByTestId('explore-toggle');
6868
await expect(mainToggle).toBeChecked();
6969

70-
// Preview should be visible
71-
const preview = section.getByTestId('explore-preview');
72-
await expect(preview).toBeVisible();
73-
await expect(preview.getByText('Preview')).toBeVisible();
74-
await expect(preview.getByText('Test Site')).toBeVisible();
75-
await expect(preview.getByText('Thoughts, stories and ideas')).toBeVisible();
76-
await expect(preview.getByText('test.com')).toBeVisible();
77-
78-
// Growth data toggle should be visible and off
70+
// Growth data toggle should be off
7971
const growthDataToggle = section.getByTestId('explore-growth-toggle');
80-
await expect(growthDataToggle).toBeVisible();
8172
await expect(growthDataToggle).not.toBeChecked();
8273

83-
// Turn on growth data sharing
74+
// Enable growth data sharing
8475
await growthDataToggle.click();
8576

86-
// Verify the API call to disable explore_ping_growth
77+
// Verify the API call to enable explore_ping_growth
8778
expect(lastApiRequests.editSettings?.body).toEqual({
8879
settings: [{key: 'explore_ping_growth', value: true}]
8980
});
9081
});
9182

83+
test('renders a preview with members count', async ({page}) => {
84+
await mockApi({page, requests: {
85+
...globalDataRequests,
86+
browseSettings: {
87+
...globalDataRequests.browseSettings,
88+
response: {
89+
...responseFixtures.settings,
90+
settings: [
91+
...responseFixtures.settings.settings,
92+
{key: 'explore_ping', value: true},
93+
{key: 'explore_ping_growth', value: true}
94+
]
95+
}
96+
},
97+
browseMembers: {
98+
method: 'GET',
99+
path: '/members/?limit=1',
100+
response: {
101+
members: [{
102+
id: '0000000086b42a93546b7315',
103+
uuid: '7e0ae12f-2a44-4117-b825-2c78a1e73260',
104+
105+
name: 'Emily Langworth'
106+
}],
107+
meta: {
108+
pagination: {
109+
page: 1,
110+
limit: 1,
111+
pages: 1000,
112+
total: 1000,
113+
next: 2,
114+
prev: null
115+
}
116+
}
117+
}
118+
}
119+
}});
120+
121+
await page.goto('/');
122+
123+
const section = page.getByTestId('explore');
124+
await expect(section).toBeVisible();
125+
126+
// Check that the preview is rendered correctly
127+
const preview = section.getByTestId('explore-preview');
128+
await expect(preview).toBeVisible();
129+
130+
// Check site title
131+
await expect(preview.getByText('Test Site')).toBeVisible();
132+
133+
// Check site description
134+
await expect(preview.getByText('Thoughts, stories and ideas')).toBeVisible();
135+
136+
// Check site URL (domain)
137+
await expect(preview.getByText('test.com')).toBeVisible();
138+
139+
// Check members count
140+
await expect(preview.getByText('1k members')).toBeVisible();
141+
});
142+
92143
test('can send a testimonial', async ({page}) => {
93144
const testimonialApiUrl = 'https://mocked.com/api/testimonials';
94145

@@ -153,6 +204,10 @@ test.describe('Ghost Explore', () => {
153204
await expect(modal).toBeVisible();
154205
await expect(modal.getByText('Send a testimonial')).toBeVisible();
155206

207+
// Check that the staff user name, staff user role and site title are rendered
208+
await expect(modal.getByText('By Owner User')).toBeVisible();
209+
await expect(modal.getByText('Owner — Test Site')).toBeVisible();
210+
156211
// Try to submit with empty content
157212
const submitButton = modal.getByRole('button', {name: 'Send testimonial'});
158213
await submitButton.click();

0 commit comments

Comments
 (0)