From b49005742925ec500bdaaf06a09970ac82afc431 Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Mon, 2 Oct 2023 15:14:46 +0300 Subject: [PATCH] Refactor user detail modal in AdminX (#18414) refs. https://github.com/TryGhost/Product/issues/3949 - the User detail modal class structure was way overcomplicated - the top part of the outline highlight of a setting group which is first in a section was cut - toggle label style was inconsisten in Newsletter settings - Audience feedback was not enabled by default when creating a Newsletter - the whole UI was using Twitter, instead of "X" --- .../src/admin-x-ds/assets/images/x-logo.svg | 1 + .../src/admin-x-ds/global/Heading.tsx | 2 +- .../settings/SettingSectionHeader.tsx | 2 +- .../src/components/Sidebar.tsx | 2 +- .../email/newsletters/AddNewsletterModal.tsx | 3 +- .../newsletters/NewsletterDetailModal.tsx | 1 - .../settings/general/GeneralSettings.tsx | 2 +- .../settings/general/SocialAccounts.tsx | 4 +- .../components/settings/general/Twitter.tsx | 10 +- .../settings/general/UserDetailModal.tsx | 150 +++++++++--------- .../test/e2e/general/socialAccounts.test.ts | 4 +- .../test/e2e/general/twitter.test.ts | 4 +- 12 files changed, 92 insertions(+), 93 deletions(-) create mode 100644 apps/admin-x-settings/src/admin-x-ds/assets/images/x-logo.svg diff --git a/apps/admin-x-settings/src/admin-x-ds/assets/images/x-logo.svg b/apps/admin-x-settings/src/admin-x-ds/assets/images/x-logo.svg new file mode 100644 index 0000000000..3044ed3e76 --- /dev/null +++ b/apps/admin-x-settings/src/admin-x-ds/assets/images/x-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/admin-x-settings/src/admin-x-ds/global/Heading.tsx b/apps/admin-x-settings/src/admin-x-ds/global/Heading.tsx index a44341ebcd..74209a925c 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/Heading.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/Heading.tsx @@ -57,7 +57,7 @@ const Heading: React.FC = if (!useLabelTag) { switch (level) { case 1: - styles += ' md:text-5xl leading-tight'; + styles += ' md:text-5xl leading-tighter'; break; case 2: styles += ' md:text-3xl'; diff --git a/apps/admin-x-settings/src/admin-x-ds/settings/SettingSectionHeader.tsx b/apps/admin-x-settings/src/admin-x-ds/settings/SettingSectionHeader.tsx index dedfa37f72..0d8dd05b2c 100644 --- a/apps/admin-x-settings/src/admin-x-ds/settings/SettingSectionHeader.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/settings/SettingSectionHeader.tsx @@ -6,7 +6,7 @@ interface Props { } const SettingSectionHeader: React.FC = ({title, sticky = false}) => { - let styles = 'pb-[10px] text-2xs font-semibold uppercase tracking-wider text-grey-700 z-20 '; + let styles = 'pb-[9px] mb-px text-2xs font-semibold uppercase tracking-wider text-grey-700 z-20 '; if (sticky) { styles += ' sticky top-0 -mt-4 pt-4 bg-white dark:bg-black'; } diff --git a/apps/admin-x-settings/src/components/Sidebar.tsx b/apps/admin-x-settings/src/components/Sidebar.tsx index b584e078bb..69acdcfae5 100644 --- a/apps/admin-x-settings/src/components/Sidebar.tsx +++ b/apps/admin-x-settings/src/components/Sidebar.tsx @@ -79,7 +79,7 @@ const Sidebar: React.FC = () => { - + diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/AddNewsletterModal.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/AddNewsletterModal.tsx index c288f67cce..e403e46929 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/AddNewsletterModal.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/AddNewsletterModal.tsx @@ -36,7 +36,8 @@ const AddNewsletterModal: React.FC = () => { const response = await addNewsletter({ name: formState.name, description: formState.description, - opt_in_existing: formState.optInExistingSubscribers + opt_in_existing: formState.optInExistingSubscribers, + feedback_enabled: true }); updateRoute({route: `newsletters/show/${response.newsletters[0].id}`}); diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx index 3a42387d6d..c53674d514 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx @@ -373,7 +373,6 @@ const Sidebar: React.FC<{ checked={newsletter.show_feature_image} direction="rtl" label='Feature image' - labelStyle='heading' onChange={e => updateNewsletter({show_feature_image: e.target.checked})} /> diff --git a/apps/admin-x-settings/src/components/settings/general/GeneralSettings.tsx b/apps/admin-x-settings/src/components/settings/general/GeneralSettings.tsx index 398bcd9737..e36ed67ef5 100644 --- a/apps/admin-x-settings/src/components/settings/general/GeneralSettings.tsx +++ b/apps/admin-x-settings/src/components/settings/general/GeneralSettings.tsx @@ -16,7 +16,7 @@ export const searchKeywords = { timeZone: ['time', 'date', 'site timezone', 'time zone'], publicationLanguage: ['publication language', 'locale'], metadata: ['metadata', 'title', 'description', 'search', 'engine', 'google'], - twitter: ['twitter card', 'structured data', 'rich cards'], + twitter: ['twitter card', 'structured data', 'rich cards', 'x'], facebook: ['facebook card', 'structured data', 'rich cards'], socialAccounts: ['social accounts', 'facebook', 'twitter', 'structured data', 'rich cards'], lockSite: ['private', 'password', 'lock site'], diff --git a/apps/admin-x-settings/src/components/settings/general/SocialAccounts.tsx b/apps/admin-x-settings/src/components/settings/general/SocialAccounts.tsx index 1c5442281a..5f4bbfcf47 100644 --- a/apps/admin-x-settings/src/components/settings/general/SocialAccounts.tsx +++ b/apps/admin-x-settings/src/components/settings/general/SocialAccounts.tsx @@ -103,7 +103,7 @@ const SocialAccounts: React.FC<{ keywords: string[] }> = ({keywords}) => { hideEmptyValue: true }, { - heading: 'URL of your TWITTER PROFILE', + heading: 'URL of your X (formerly Twitter) profile', key: 'twitter', value: twitterUrl, hideEmptyValue: true @@ -139,7 +139,7 @@ const SocialAccounts: React.FC<{ keywords: string[] }> = ({keywords}) => { hint={errors.twitter} inputRef={twitterInputRef} placeholder="https://twitter.com/ghost" - title="URL of your Twitter profile" + title="URL of your X (formerly Twitter) profile" value={twitterUrl} onBlur={(e) => { try { diff --git a/apps/admin-x-settings/src/components/settings/general/Twitter.tsx b/apps/admin-x-settings/src/components/settings/general/Twitter.tsx index b426b06863..aa94c725fe 100644 --- a/apps/admin-x-settings/src/components/settings/general/Twitter.tsx +++ b/apps/admin-x-settings/src/components/settings/general/Twitter.tsx @@ -6,7 +6,7 @@ import TextField from '../../../admin-x-ds/global/form/TextField'; import useHandleError from '../../../utils/api/handleError'; import usePinturaEditor from '../../../hooks/usePinturaEditor'; import useSettingGroup from '../../../hooks/useSettingGroup'; -import {ReactComponent as TwitterLogo} from '../../../admin-x-ds/assets/images/twitter-logo.svg'; +import {ReactComponent as TwitterLogo} from '../../../admin-x-ds/assets/images/x-logo.svg'; import {getImageUrl, useUploadImage} from '../../../api/images'; import {getSettingValues} from '../../../api/settings'; import {withErrorBoundary} from '../../../admin-x-ds/global/ErrorBoundary'; @@ -105,14 +105,14 @@ const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => { clearBg={true} inputRef={focusRef} placeholder={siteTitle} - title="Twitter title" + title="X title" value={twitterTitle} onChange={handleTitleChange} /> @@ -124,13 +124,13 @@ const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => { return ( = ({user}) => { okLabel = 'Saved'; } - const coverButtonContainerClassName = clsx( - showMenu ? ( - userData.cover_image ? 'relative ml-10 mr-[106px] flex translate-y-[-80px] gap-3 md:ml-0 md:justify-end' : 'relative -mb-8 ml-10 mr-[106px] flex translate-y-[358px] md:ml-0 md:translate-y-[268px] md:justify-end' - ) : ( - userData.cover_image ? 'relative ml-10 flex max-w-4xl translate-y-[-80px] gap-3 md:mx-auto md:justify-end' : 'relative -mb-8 ml-10 flex max-w-4xl translate-y-[358px] md:mx-auto md:translate-y-[268px] md:justify-end' - ) - ); - - const coverEditButtonBaseClasses = 'bg-[rgba(0,0,0,0.75)] rounded text-sm text-white flex items-center justify-center px-3 h-8 opacity-80 hover:opacity-100 transition-all cursor-pointer font-medium'; + const coverEditButtonBaseClasses = 'bg-[rgba(0,0,0,0.75)] rounded text-sm text-white flex items-center justify-center px-3 h-8 opacity-80 hover:opacity-100 transition-all cursor-pointer font-medium nowrap'; const fileUploadButtonClasses = clsx( coverEditButtonBaseClasses @@ -637,75 +629,81 @@ const UserDetailModalContent: React.FC<{user: User}> = ({user}) => { }} >
-
- editor.openEditor({ - image: userData.cover_image || '', - handleSave: async (file:File) => { - handleImageUpload('cover_image', file); - } - }) - } - } - unstyled={true} - onDelete={() => { - handleImageDelete('cover_image'); - }} - onUpload={(file: File) => { - handleImageUpload('cover_image', file); - }} - >Upload cover image - {showMenu &&
- }> -
} -
- } - editButtonClassName='md:invisible absolute right-[22px] -top-2 flex h-8 w-8 cursor-pointer items-center justify-center text-white group-hover:!visible z-20' - fileUploadClassName='rounded-full bg-black flex items-center justify-center opacity-80 transition hover:opacity-100 -ml-2 cursor-pointer h-[80px] w-[80px]' - id='avatar' - imageClassName='w-full h-full object-cover rounded-full shrink-0' - imageContainerClassName='relative group bg-cover bg-center -ml-2 h-[80px] w-[80px] shrink-0' - imageURL={userData.profile_image} - pintura={ - { - isEnabled: editor.isEnabled, - openEditor: async () => editor.openEditor({ - image: userData.profile_image || '', - handleSave: async (file:File) => { - handleImageUpload('profile_image', file); +
+
+
+
+ } + editButtonClassName='md:invisible absolute right-[22px] -top-2 flex h-8 w-8 cursor-pointer items-center justify-center text-white group-hover:!visible z-20' + fileUploadClassName='rounded-full bg-black flex items-center justify-center opacity-80 transition hover:opacity-100 -ml-2 cursor-pointer h-[80px] w-[80px]' + id='avatar' + imageClassName='w-full h-full object-cover rounded-full shrink-0' + imageContainerClassName='relative group bg-cover bg-center -ml-2 h-[80px] w-[80px] shrink-0' + imageURL={userData.profile_image} + pintura={ + { + isEnabled: editor.isEnabled, + openEditor: async () => editor.openEditor({ + image: userData.profile_image || '', + handleSave: async (file:File) => { + handleImageUpload('profile_image', file); + } + }) } - }) + } + unstyled={true} + width='80px' + onDelete={() => { + handleImageDelete('profile_image'); + }} + onUpload={(file: File) => { + handleImageUpload('profile_image', file); + }} + > + + +
+
+ {user.name}{suspendedText} + {user.roles[0].name.toLowerCase()} +
+
+
+ editor.openEditor({ + image: userData.cover_image || '', + handleSave: async (file:File) => { + handleImageUpload('cover_image', file); + } + }) + } } - } - unstyled={true} - width='80px' - onDelete={() => { - handleImageDelete('profile_image'); - }} - onUpload={(file: File) => { - handleImageUpload('profile_image', file); - }} - > - - -
- {user.name}{suspendedText} - {user.roles[0].name.toLowerCase()} + unstyled + onDelete={() => { + handleImageDelete('cover_image'); + }} + onUpload={(file: File) => { + handleImageUpload('cover_image', file); + }} + >Upload cover image + {showMenu &&
+ }> +
}
diff --git a/apps/admin-x-settings/test/e2e/general/socialAccounts.test.ts b/apps/admin-x-settings/test/e2e/general/socialAccounts.test.ts index 706458fcea..7651a47aec 100644 --- a/apps/admin-x-settings/test/e2e/general/socialAccounts.test.ts +++ b/apps/admin-x-settings/test/e2e/general/socialAccounts.test.ts @@ -21,11 +21,11 @@ test.describe('Social account settings', async () => { await section.getByRole('button', {name: 'Edit'}).click(); await section.getByLabel(`URL of your publication's Facebook Page`).fill('https://www.facebook.com/fb'); - await section.getByLabel('URL of your Twitter profile').fill('https://twitter.com/tw'); + await section.getByLabel('URL of your X (formerly Twitter) profile').fill('https://twitter.com/tw'); await section.getByRole('button', {name: 'Save'}).click(); - await expect(section.getByLabel('URL of your Twitter profile')).toHaveCount(0); + await expect(section.getByLabel('URL of your X (formerly Twitter) profile')).toHaveCount(0); await expect(section.getByText('https://www.facebook.com/fb')).toHaveCount(1); await expect(section.getByText('https://twitter.com/tw')).toHaveCount(1); diff --git a/apps/admin-x-settings/test/e2e/general/twitter.test.ts b/apps/admin-x-settings/test/e2e/general/twitter.test.ts index 9ba0244509..1be657ef7b 100644 --- a/apps/admin-x-settings/test/e2e/general/twitter.test.ts +++ b/apps/admin-x-settings/test/e2e/general/twitter.test.ts @@ -24,8 +24,8 @@ test.describe('Twitter settings', async () => { await expect(section.getByRole('img')).toBeVisible(); - await section.getByLabel('Twitter title').fill('Twititle'); - await section.getByLabel('Twitter description').fill('Twitscription'); + await section.getByLabel('X title').fill('Twititle'); + await section.getByLabel('X description').fill('Twitscription'); await section.getByRole('button', {name: 'Save'}).click();