Skip to content

Commit 6bc1d68

Browse files
peterzimonsagzy
andauthored
Improved Explore testimonials UI and copy (#24487)
ref https://linear.app/ghost/issue/PROD-2286/settings-ui - Updated design details and copy for Explore testimonials --------- Co-authored-by: Sag <[email protected]>
1 parent b147545 commit 6bc1d68

File tree

7 files changed

+120
-66
lines changed

7 files changed

+120
-66
lines changed
Lines changed: 1 addition & 0 deletions
Loading
31.4 KB
Loading
31 KB
Loading
18.5 KB
Loading

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,10 @@ const Explore: React.FC<{ keywords: string[] }> = ({keywords}) => {
119119
</div>
120120
<div className='-mx-5 -mb-5 flex items-center justify-between gap-4 rounded-b-xl border-t border-[rgba(142,66,255,0.1)] bg-gradient-to-tr from-[rgba(142,66,255,0.07)] to-[rgba(142,66,255,0.02)] p-6 px-7 md:-mx-7 md:-mb-7'>
121121
<div className='flex flex-col'>
122-
<span className='font-medium'>Get featured on Ghost Explore!</span>
123-
<span className='text-pretty text-sm text-black/80'>Share your love about Ghost and get ranked higher on Explore.</span>
122+
<span className='font-medium'>Get featured on the Ghost.org homepage</span>
123+
<span className='text-pretty text-sm text-black/80 dark:text-white/80'>Send us a quote we can use to highlight your site</span>
124124
</div>
125-
<Button className='border border-purple bg-white text-purple hover:bg-purple/5 hover:text-purple' icon="quote" label="Send a testimonial" onClick={() => {
125+
<Button className='border border-purple bg-white text-purple hover:bg-purple/5 hover:text-purple dark:bg-transparent' icon="send" label="Send testimonial" onClick={() => {
126126
updateRoute('explore/testimonial');
127127
}} />
128128
</div>

apps/admin-x-settings/src/components/settings/growth/explore/TestimonialsModal.tsx

Lines changed: 113 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import AliAbdaal from '../../../../assets/images/ali-abdaal.png';
2+
import IsaacSaul from '../../../../assets/images/isaac-saul.png';
3+
import JoelWarner from '../../../../assets/images/joel-warner.png';
14
import NiceModal, {useModal} from '@ebay/nice-modal-react';
25
import {Avatar, Button, Form, Modal, Select, TextArea, showToast} from '@tryghost/admin-x-design-system';
36
import {generateAvatarColor, getInitials} from '../../../../utils/helpers';
@@ -76,13 +79,13 @@ const TestimonialsModal = NiceModal.create(() => {
7679
});
7780

7881
const migratedFromOptions: Array<{value: string; label: string;}> = [
79-
{value: 'wordpress', label: 'Wordpress'},
82+
{value: '', label: 'None - This is a new site'},
8083
{value: 'substack', label: 'Substack'},
81-
{value: 'medium', label: 'Medium'},
8284
{value: 'beehiiv', label: 'Beehiiv'},
85+
{value: 'wordpress', label: 'Wordpress'},
8386
{value: 'newspack', label: 'Newspack'},
87+
{value: 'medium', label: 'Medium'},
8488
{value: 'squarespace', label: 'Squarespace'},
85-
{value: 'patreon', label: 'Patreon'},
8689
{value: 'memberful', label: 'Memberful'},
8790
{value: 'other', label: 'Other'}
8891
];
@@ -94,73 +97,123 @@ const TestimonialsModal = NiceModal.create(() => {
9497
}}
9598
cancelLabel=''
9699
footer={false}
97-
// height={645}
98100
padding={false}
99101
testId='explore-testimonials-modal'
100102
title=''
101103
topRightContent='close'
102-
width={520}
104+
width={920}
103105
>
104-
<div className='h-[140px] bg-gradient-to-tr from-[rgba(142,66,255,0.07)] to-[rgba(142,66,255,0.02)]'></div>
105-
<div className='mx-6 mt-[-30px] flex size-[60px] items-center justify-center rounded-full bg-gradient-to-t from-[#CFB0FF] to-[#B27EFF]'>
106-
<svg fill="none" height="26" viewBox="0 0 32 26" width="32" xmlns="http://www.w3.org/2000/svg">
107-
<path d="M19.9967 26L18.1562 21.4542C23.4268 19.1176 25.7693 16.5261 26.2712 11.7255C25.5183 12.3627 24.5562 12.7026 23.3431 12.7026C20.1641 12.7026 17.4869 10.1536 17.4869 6.41503C17.4869 2.80392 20.3732 9.72736e-07 24.2634 6.32644e-07C28.2791 2.81581e-07 31.5 3.10131 31.5 8.96405C31.5 17.5458 27.4425 23.0261 19.9967 26ZM3.0098 26L1.12745 21.4543C6.43987 19.1176 8.78235 16.5261 9.24248 11.7255C8.53137 12.3627 7.56928 12.7026 6.35621 12.7026C3.17712 12.7026 0.499999 10.1536 0.499998 6.41504C0.499998 2.80393 3.38627 2.45778e-06 7.23464 2.12135e-06C11.2503 1.77028e-06 14.5131 3.10131 14.5131 8.96405C14.5131 17.5458 10.4556 23.0261 3.0098 26Z" fill="white"/>
108-
</svg>
109-
</div>
110-
<Form>
111-
<div className='-mb-1 px-7 pt-3'>
112-
<div>
113-
<div className='text-xl font-semibold tracking-tight'>Send a testimonial</div>
114-
<div className='mt-1 text-pretty'>
115-
We&apos;re featuring sites that share a testimonial more prominently on Ghost Explore.
106+
<Form className='!mb-0'>
107+
<div className='flex items-stretch'>
108+
<div className='hidden w-full flex-col justify-between bg-gradient-to-tl from-grey-100/50 to-grey-100/80 p-8 dark:from-grey-900/40 dark:to-grey-900/60 [@media(min-width:905px)]:!visible [@media(min-width:905px)]:!flex'>
109+
<div className='pr-6'>
110+
<div className='relative rounded-xl bg-white px-3 py-2.5 italic text-grey-700 shadow-lg before:absolute before:-bottom-1.5 before:left-5 before:block before:size-3 before:rotate-45 before:bg-white dark:bg-black dark:text-grey-300 before:dark:bg-black'>
111+
Moving to Ghost has proven to be one of the best business decisions we’ve made as an independent media outlet.
112+
</div>
113+
<div className='ml-2 mt-[14px] flex items-center gap-2 text-sm'>
114+
<div className='size-9 rounded-full bg-white bg-cover bg-center opacity-90 grayscale' style={{
115+
backgroundImage: `url(${JoelWarner})`
116+
}}></div>
117+
<div>
118+
<div className='font-medium text-black dark:text-white'>Joel Warner</div>
119+
<div className='-mt-0.5 text-grey-700'>Lever News</div>
120+
</div>
121+
</div>
116122
</div>
117-
</div>
118-
<div className='mt-5'>
119-
<TextArea
120-
error={Boolean(errors.content)}
121-
hint={errors.content}
122-
placeholder='Share your love for Ghost'
123-
rows={5}
124-
value={formState.content}
125-
autoFocus
126-
onChange={e => updateForm(state => ({...state, content: e.target.value}))}
127-
onKeyDown={() => clearError('content')}
128-
/>
129-
</div>
130-
<div className='ml-0.5 mt-4'>
131-
<div className='flex items-center gap-2'>
132-
<Avatar bgColor={generateAvatarColor((staffUserName ? staffUserName : staffUserEmail))} image={staffUserProfileImage ?? undefined} label={getInitials(staffUserName)} labelColor='white' size='lg' />
133-
<div className='flex flex-col'>
134-
<span className='text-sm font-medium'>By {staffUserName ? staffUserName : staffUserEmail}</span>
135-
<span className='text-xs text-grey-700'>{staffUserRole}{siteTitle}</span>
123+
124+
<div className='ml-6 mt-8'>
125+
<div className='relative rounded-xl bg-white px-3 py-2.5 italic text-grey-700 shadow-lg before:absolute before:-bottom-1.5 before:right-5 before:block before:size-3 before:rotate-45 before:bg-white dark:bg-black dark:text-grey-300 before:dark:bg-black'>
126+
It has now been one year since I quit my full-time job to go all in on Tangle. Today, we have 50,000+ paying subscribers. That’s roughly $5M in gross yearly revenue ... it’s the best paying job I’ve ever had.
127+
</div>
128+
<div className='mr-2 mt-[14px] flex items-center justify-end gap-2 text-sm'>
129+
<div className='flex flex-col items-end'>
130+
<div className='font-medium text-black dark:text-white'>Isaac Saul</div>
131+
<div className='-mt-0.5 text-grey-700'>Tangle</div>
132+
</div>
133+
<div className='size-9 rounded-full bg-white bg-cover bg-center opacity-90 grayscale' style={{
134+
backgroundImage: `url(${IsaacSaul})`
135+
}}></div>
136+
</div>
137+
</div>
138+
139+
<div className='mt-8 hidden pr-6 [@media(min-width:940px)]:!visible [@media(min-width:940px)]:!block'>
140+
<div className='relative rounded-xl bg-white px-3 py-2.5 italic text-grey-700 shadow-lg before:absolute before:-bottom-1.5 before:left-5 before:block before:size-3 before:rotate-45 before:bg-white dark:bg-black dark:text-grey-300 before:dark:bg-black'>
141+
You should be using Ghost because it’s absolutely amazing and I love it. It’s what I’ve been using for all my sites since 2016.
142+
</div>
143+
<div className='ml-2 mt-[14px] flex items-center gap-2 text-sm'>
144+
<div className='size-9 rounded-full bg-white bg-cover bg-center opacity-90 grayscale' style={{
145+
backgroundImage: `url(${AliAbdaal})`
146+
}}></div>
147+
<div>
148+
<div className='font-medium text-black dark:text-white'>Ali Abdaal</div>
149+
<div className='-mt-0.5 text-grey-700'>YouTuber</div>
150+
</div>
136151
</div>
137152
</div>
138153
</div>
139-
<div className='mt-10 flex items-center gap-4'>
140-
<div className='grow'>
141-
<Select
142-
error={Boolean(errors.prev_platform)}
143-
hint={errors.prev_platform}
144-
options={migratedFromOptions}
145-
placeholder='Which platform did you switch from?'
146-
selectedOption={migratedFromOptions.find(option => option.value === formState.prev_platform)}
147-
testId='migrated-from'
148-
onSelect={(option) => {
149-
updateForm(state => ({...state, prev_platform: option?.value || ''}));
150-
clearError('prev_platform');
151-
}}
152-
/>
154+
<div className='flex grow flex-col justify-between gap-6 p-8 [@media(min-width:905px)]:min-w-[520px] [@media(min-width:940px)]:min-w-[460px]'>
155+
<div>
156+
<div className='flex size-[60px] items-center justify-center rounded-full bg-gradient-to-t from-[#CFB0FF] to-[#B27EFF]'>
157+
<svg fill="none" height="26" viewBox="0 0 32 26" width="32" xmlns="http://www.w3.org/2000/svg">
158+
<path d="M19.9967 26L18.1562 21.4542C23.4268 19.1176 25.7693 16.5261 26.2712 11.7255C25.5183 12.3627 24.5562 12.7026 23.3431 12.7026C20.1641 12.7026 17.4869 10.1536 17.4869 6.41503C17.4869 2.80392 20.3732 9.72736e-07 24.2634 6.32644e-07C28.2791 2.81581e-07 31.5 3.10131 31.5 8.96405C31.5 17.5458 27.4425 23.0261 19.9967 26ZM3.0098 26L1.12745 21.4543C6.43987 19.1176 8.78235 16.5261 9.24248 11.7255C8.53137 12.3627 7.56928 12.7026 6.35621 12.7026C3.17712 12.7026 0.499999 10.1536 0.499998 6.41504C0.499998 2.80393 3.38627 2.45778e-06 7.23464 2.12135e-06C11.2503 1.77028e-06 14.5131 3.10131 14.5131 8.96405C14.5131 17.5458 10.4556 23.0261 3.0098 26Z" fill="white"/>
159+
</svg>
160+
</div>
161+
<div className='mt-6'>
162+
<div className='text-2xl font-semibold tracking-tight'>A quote about Ghost</div>
163+
<div className='mt-2 text-pretty'>
164+
We feature quotes from publishers to showcase their work, including a logo and a link! If you&apos;d like to be included, share a quote we can use to highlight you.
165+
</div>
166+
</div>
167+
<div className='mt-8'>
168+
<TextArea
169+
error={Boolean(errors.content)}
170+
hint={errors.content}
171+
placeholder='What changed for the better since you switched to Ghost?'
172+
rows={7}
173+
value={formState.content}
174+
autoFocus
175+
onChange={e => updateForm(state => ({...state, content: e.target.value}))}
176+
onKeyDown={() => clearError('content')}
177+
/>
178+
</div>
179+
<div className='ml-0.5 mt-4'>
180+
<div className='flex items-center gap-2'>
181+
<Avatar bgColor={generateAvatarColor((staffUserName ? staffUserName : staffUserEmail))} image={staffUserProfileImage ?? undefined} label={getInitials(staffUserName)} labelColor='white' size='md' />
182+
<div className='flex flex-col'>
183+
<span className='text-sm font-medium'>By {staffUserName ? staffUserName : staffUserEmail}</span>
184+
<span className='text-xs text-grey-700'>{staffUserRole}{siteTitle}</span>
185+
</div>
186+
</div>
187+
</div>
188+
</div>
189+
<div>
190+
<div className='mt-2 flex items-center gap-4'>
191+
<div className='grow'>
192+
<Select
193+
error={Boolean(errors.prev_platform)}
194+
hint={errors.prev_platform}
195+
options={migratedFromOptions}
196+
placeholder='Previous platform'
197+
selectedOption={migratedFromOptions.find(option => option.value === formState.prev_platform)}
198+
testId='migrated-from'
199+
onSelect={(option) => {
200+
updateForm(state => ({...state, prev_platform: option?.value || ''}));
201+
clearError('prev_platform');
202+
}}
203+
/>
204+
</div>
205+
<Button
206+
className='!h-[38px] rounded-lg'
207+
color="black"
208+
disabled={saveState === 'saving'}
209+
label="Send testimonial"
210+
loading={saveState === 'saving'}
211+
onClick={async () => {
212+
await handleSave();
213+
}}
214+
/>
215+
</div>
153216
</div>
154-
<Button
155-
className='!h-[38px] rounded-lg'
156-
color="black"
157-
disabled={saveState === 'saving'}
158-
label="Send testimonial"
159-
loading={saveState === 'saving'}
160-
onClick={async () => {
161-
await handleSave();
162-
}}
163-
/>
164217
</div>
165218
</div>
166219
</Form>

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,12 @@ test.describe('Ghost Explore', () => {
197197
await expect(section).toBeVisible();
198198

199199
// Click on the testimonials link/button to open the modal
200-
await section.getByText('Send a testimonial').click();
200+
await section.getByText('Send testimonial').click();
201201

202202
// Wait for modal to open
203203
const modal = page.getByTestId('explore-testimonials-modal');
204204
await expect(modal).toBeVisible();
205-
await expect(modal.getByText('Send a testimonial')).toBeVisible();
205+
await expect(modal.getByText('Send testimonial')).toBeVisible();
206206

207207
// Check that the staff user name, staff user role and site title are rendered
208208
await expect(modal.getByText('By Owner User')).toBeVisible();
@@ -216,7 +216,7 @@ test.describe('Ghost Explore', () => {
216216
await expect(modal.getByText('This field is required')).toBeVisible();
217217

218218
// Fill in the testimonial content
219-
const contentTextarea = modal.getByPlaceholder('Share your love for Ghost');
219+
const contentTextarea = modal.getByPlaceholder('What changed for the better since you switched to Ghost?');
220220
await contentTextarea.fill('I love Ghost!');
221221

222222
// Select a platform from the dropdown

0 commit comments

Comments
 (0)