0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Added custom fonts feature check (#21738)

ref DES-1011

- previously, we were hiding the old font settings from the official themes without checking if they support the custom fonts or not
- now we use the gscan warning info to check this — only when there's support, we hide the settings which means users with the older version will get the old settings back
- also added two new tests for this procedure
This commit is contained in:
Sodbileg Gansukh 2024-12-04 13:42:44 +08:00 committed by GitHub
parent 7c1d7080e9
commit ea0e598bf2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 242 additions and 2 deletions

View file

@ -52,6 +52,11 @@ export const useBrowseThemes = createQuery<ThemesResponseType>({
path: '/themes/'
});
export const useActiveTheme = createQuery<ThemesInstallResponseType>({
dataType,
path: '/themes/active/'
});
export const useActivateTheme = createMutation<ThemesResponseType, string>({
method: 'PUT',
path: name => `/themes/${name}/activate/`,

View file

@ -1,5 +1,6 @@
import React from 'react';
import ThemeSetting from './ThemeSetting';
import useCustomFonts from '../../../../hooks/useCustomFonts';
import useFeatureFlag from '../../../../hooks/useFeatureFlag';
import {CustomThemeSetting} from '@tryghost/admin-x-framework/api/customThemeSettings';
import {Form} from '@tryghost/admin-x-design-system';
@ -46,6 +47,7 @@ const ThemeSettings: React.FC<ThemeSettingsProps> = ({sections, updateSetting})
const activeThemeName = activeTheme?.package.name?.toLowerCase() || '';
const activeThemeAuthor = activeTheme?.package.author?.name || '';
const hasCustomFonts = useFeatureFlag('customFonts');
const {supportsCustomFonts} = useCustomFonts();
return (
<>
@ -70,7 +72,7 @@ const ThemeSettings: React.FC<ThemeSettingsProps> = ({sections, updateSetting})
// should be removed once we remove the settings from the themes in 6.0
if (hasCustomFonts) {
const hidingSettings = themeSettingsMap[activeThemeName];
if (hidingSettings && hidingSettings.includes(setting.key) && activeThemeAuthor === 'Ghost Foundation') {
if (hidingSettings && hidingSettings.includes(setting.key) && activeThemeAuthor === 'Ghost Foundation' && supportsCustomFonts) {
spaceClass += ' hidden';
}
}

View file

@ -1,6 +1,7 @@
import InvalidThemeModal, {FatalErrors} from './InvalidThemeModal';
import NiceModal from '@ebay/nice-modal-react';
import React from 'react';
import useCustomFonts from '../../../../hooks/useCustomFonts';
import {Button, ButtonProps, ConfirmationModal, List, ListItem, Menu, ModalPage, showToast} from '@tryghost/admin-x-design-system';
import {JSONError} from '@tryghost/admin-x-framework/errors';
import {Theme, isActiveTheme, isDefaultTheme, isDeletableTheme, isLegacyTheme, useActivateTheme, useDeleteTheme} from '@tryghost/admin-x-framework/api/themes';
@ -48,11 +49,13 @@ const ThemeActions: React.FC<ThemeActionProps> = ({
}) => {
const {mutateAsync: activateTheme} = useActivateTheme();
const {mutateAsync: deleteTheme} = useDeleteTheme();
const {refreshActiveThemeData} = useCustomFonts();
const handleError = useHandleError();
const handleActivate = async () => {
try {
await activateTheme(theme.name);
refreshActiveThemeData();
showToast({
title: 'Theme activated',
type: 'success',

View file

@ -1,5 +1,6 @@
import NiceModal from '@ebay/nice-modal-react';
import React, {ReactNode, useState} from 'react';
import useCustomFonts from '../../../../hooks/useCustomFonts';
import {Button, ConfirmationModalContent, Heading, List, ListItem, showToast} from '@tryghost/admin-x-design-system';
import {InstalledTheme, ThemeProblem, useActivateTheme} from '@tryghost/admin-x-framework/api/themes';
import {useHandleError} from '@tryghost/admin-x-framework/hooks';
@ -42,6 +43,7 @@ const ThemeInstalledModal: React.FC<{
onActivate?: () => void;
}> = ({title, prompt, installedTheme, onActivate}) => {
const {mutateAsync: activateTheme} = useActivateTheme();
const {refreshActiveThemeData} = useCustomFonts();
const handleError = useHandleError();
let errorPrompt = null;
@ -85,6 +87,7 @@ const ThemeInstalledModal: React.FC<{
try {
const resData = await activateTheme(installedTheme.name);
const updatedTheme = resData.themes[0];
refreshActiveThemeData();
showToast({
title: 'Theme activated',

View file

@ -0,0 +1,15 @@
import {useActiveTheme} from '@tryghost/admin-x-framework/api/themes';
import {useCallback} from 'react';
const useCustomFonts = () => {
const activeThemes = useActiveTheme();
const activeTheme = activeThemes.data?.themes[0];
const supportsCustomFonts = !activeTheme?.warnings?.some(warning => warning.code === 'GS051-CUSTOM-FONTS');
const refreshActiveThemeData = useCallback(() => {
activeThemes.refetch();
}, [activeThemes]);
return {supportsCustomFonts, refreshActiveThemeData};
};
export default useCustomFonts;

View file

@ -399,4 +399,204 @@ test.describe('Design settings', async () => {
expect(matchingHeader).toBeDefined();
// expect(lastRequest.previewHeader).toMatch(new RegExp(`&${expectedEncoded.replace(/\+/g, '\\+')}`));
});
test('Old font settings are hidden with custom fonts support', async ({page}) => {
toggleLabsFlag('customFonts', true);
const {lastApiRequests} = await mockApi({page, requests: {
...globalDataRequests,
browseThemes: {method: 'GET', path: '/themes/', response: responseFixtures.themes},
installTheme: {method: 'POST', path: /^\/themes\/install\/\?/, response: {
themes: [{
name: 'headline',
package: {},
active: false,
templates: []
}]
}},
activateTheme: {method: 'PUT', path: '/themes/headline/activate/', response: {
themes: [{
name: 'headline',
package: {
name: 'headline',
author: {
name: 'Ghost Foundation'
}
},
active: true,
templates: []
}]
}},
browseCustomThemeSettings: {method: 'GET', path: '/custom_theme_settings/', response: {
custom_theme_settings: [
{
type: 'select',
options: [
'Modern sans-serif',
'Elegant serif'
],
default: 'Modern sans-serif',
value: 'Modern sans-serif',
key: 'title_font'
},
{
type: 'select',
options: [
'Modern sans-serif',
'Elegant serif'
],
default: 'Elegant serif',
value: 'Elegant serif',
key: 'body_font'
}
]
}},
activeTheme: {
method: 'GET',
path: '/themes/active/',
response: {
themes: [{
name: 'casper',
package: {},
active: true,
templates: []
}]
}
}
}});
await page.goto('/');
const themeSection = page.getByTestId('theme');
await themeSection.getByRole('button', {name: 'Change theme'}).click();
const modal = page.getByTestId('theme-modal');
await modal.getByRole('button', {name: /Headline/}).click();
await modal.getByRole('button', {name: 'Install Headline'}).click();
await expect(page.getByTestId('confirmation-modal')).toHaveText(/installed/);
await page.getByRole('button', {name: 'Activate'}).click();
await expect(page.getByTestId('toast-success')).toHaveText(/headline is now your active theme/);
expect(lastApiRequests.installTheme?.url).toMatch(/\?source=github&ref=TryGhost%2FHeadline/);
await modal.getByRole('button', {name: 'Change theme'}).click();
await modal.getByRole('button', {name: 'Close'}).click();
const designSection = page.getByTestId('design');
await designSection.getByRole('button', {name: 'Customize'}).click();
const designModal = page.getByTestId('design-modal');
await designModal.getByRole('tab', {name: 'Theme'}).click();
const titleFontCustomThemeSetting = designModal.getByLabel('Title font');
await expect(titleFontCustomThemeSetting).not.toBeVisible();
const bodyFontCustomThemeSetting = designModal.getByLabel('Body font');
await expect(bodyFontCustomThemeSetting).not.toBeVisible();
});
test('Old font settings are visible with no custom fonts support', async ({page}) => {
toggleLabsFlag('customFonts', true);
await mockApi({page, requests: {
...globalDataRequests,
browseThemes: {method: 'GET', path: '/themes/', response: responseFixtures.themes},
activateTheme: {method: 'PUT', path: '/themes/casper/activate/', response: {
themes: [{
name: 'casper',
package: {},
active: true,
templates: []
}]
}},
browseCustomThemeSettings: {method: 'GET', path: '/custom_theme_settings/', response: {
custom_theme_settings: [
{
type: 'select',
options: [
'Modern sans-serif',
'Elegant serif'
],
default: 'Modern sans-serif',
value: 'Modern sans-serif',
key: 'title_font'
},
{
type: 'select',
options: [
'Modern sans-serif',
'Elegant serif'
],
default: 'Elegant serif',
value: 'Elegant serif',
key: 'body_font'
}
]
}},
activeTheme: {
method: 'GET',
path: '/themes/active/',
response: {
themes: [{
name: 'casper',
package: {},
active: true,
templates: [],
warnings: [{
fatal: false,
level: 'warning',
rule: 'Missing support for custom fonts',
details: 'CSS variables for Ghost font settings are not present: <code>--gh-font-heading</code>, <code>--gh-font-body</code>',
regex: {},
failures: [
{
ref: 'styles'
}
],
code: 'GS051-CUSTOM-FONTS'
}]
}]
}
}
}});
await page.goto('/');
const themeSection = page.getByTestId('theme');
await themeSection.getByRole('button', {name: 'Change theme'}).click();
const modal = page.getByTestId('theme-modal');
await modal.getByRole('button', {name: /Casper/}).click();
await expect(modal.getByRole('button', {name: 'Activate Casper'})).toBeVisible();
await expect(page.locator('iframe[title="Theme preview"]')).toHaveAttribute('src', 'https://demo.ghost.io/');
await modal.getByRole('button', {name: 'Change theme'}).click();
await modal.getByRole('button', {name: 'Close'}).click();
const designSection = page.getByTestId('design');
await designSection.getByRole('button', {name: 'Customize'}).click();
const designModal = page.getByTestId('design-modal');
await designModal.getByRole('tab', {name: 'Theme'}).click();
const titleFontCustomThemeSetting = designModal.getByLabel('Title font');
await expect(titleFontCustomThemeSetting).toBeVisible();
const bodyFontCustomThemeSetting = designModal.getByLabel('Body font');
await expect(bodyFontCustomThemeSetting).toBeVisible();
});
});

View file

@ -22,7 +22,19 @@ test.describe('Theme settings', async () => {
active: true,
templates: []
}]
}}
}},
activeTheme: {
method: 'GET',
path: '/themes/active/',
response: {
themes: [{
name: 'casper',
package: {},
active: true,
templates: []
}]
}
}
}});
await page.goto('/');