Added "No search result" screen to Settings (#19672)
refs. https://linear.app/tryghost/issue/DES-21/empty-screen-is-missing-for-search-in-settings - Search is one of the most useful functions in Settings and currently the screen when there's no result for a searchterm is just a plain white screen. Very non user-friendly. - This update gives us an opportunity to improve the overall visual hierarchy and focus of Settings in general. --------- Co-authored-by: Ronald Langeveld <hi@ronaldlangeveld.com>
This commit is contained in:
parent
46866788dd
commit
3ef8b53fad
@ -76,13 +76,7 @@ const SettingGroup = forwardRef<HTMLDivElement, SettingGroupProps>(function Sett
|
||||
onSave?.();
|
||||
};
|
||||
|
||||
if (saveState === 'unsaved') {
|
||||
styles += ' border-green';
|
||||
} else if (isEditing){
|
||||
styles += ' border-grey-700 dark:border-grey-600';
|
||||
} else {
|
||||
styles += ' border-grey-300 dark:border-grey-800 hover:border-grey-500';
|
||||
}
|
||||
styles += ' border-grey-250 dark:border-grey-925';
|
||||
|
||||
const viewButtons: ButtonProps[] = [];
|
||||
|
||||
@ -149,11 +143,11 @@ const SettingGroup = forwardRef<HTMLDivElement, SettingGroupProps>(function Sett
|
||||
});
|
||||
|
||||
const containerClasses = clsx(
|
||||
'relative flex-col gap-6 rounded-lg transition-all',
|
||||
border && 'border p-5 md:p-7',
|
||||
'relative flex-col gap-6 rounded-xl transition-all hover:border-grey-200',
|
||||
border && 'border p-5 hover:shadow-sm md:p-7',
|
||||
isVisible ? 'flex' : 'hidden',
|
||||
(highlight && highlightOnModalClose) && 'before:pointer-events-none before:absolute before:inset-[1px] before:animate-setting-highlight-fade-out before:rounded before:shadow-[0_0_0_3px_rgba(48,207,67,0.45)]',
|
||||
!isEditing && 'is-not-editing group/setting-group',
|
||||
(highlight && highlightOnModalClose) && 'border-grey-200 shadow-sm',
|
||||
!isEditing ? 'is-not-editing group/setting-group' : 'border-grey-200 shadow-sm',
|
||||
styles
|
||||
);
|
||||
|
||||
|
@ -21,14 +21,14 @@ const SettingNavItem = forwardRef<HTMLLIElement, SettingNavItemProps>(function S
|
||||
...props
|
||||
}, ref) {
|
||||
const classNames = clsx(
|
||||
'w-100 flex h-8 cursor-pointer items-center rounded-md px-2 py-1 text-left text-sm transition-all hover:bg-grey-100 focus:bg-grey-100 dark:text-grey-300 dark:hover:bg-grey-925 dark:focus:bg-grey-900',
|
||||
isCurrent && 'bg-grey-200 dark:bg-grey-900',
|
||||
'w-100 mt-1 flex h-[38px] cursor-pointer items-center rounded-lg px-3 py-2 text-left text-[14px] font-medium transition-all hover:bg-grey-200 focus:bg-grey-100 dark:text-grey-600 dark:hover:bg-grey-950 dark:focus:bg-grey-925',
|
||||
isCurrent ? 'bg-grey-250 text-black dark:bg-grey-925 dark:text-white' : 'text-grey-800',
|
||||
!isVisible && 'hidden'
|
||||
);
|
||||
|
||||
return (
|
||||
<li ref={ref} {...props}><a className={classNames} id={navid} onClick={onClick}>
|
||||
{icon && <Icon className='mr-[7px]' name={icon} size='sm' />}
|
||||
{icon && <Icon className='mr-[7px] h-[18px] w-[18px]' name={icon} size='custom' />}
|
||||
{title}
|
||||
</a></li>
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Separator} from '..';
|
||||
|
||||
export interface SettingNavSectionProps {
|
||||
title?: string;
|
||||
@ -13,11 +14,14 @@ const SettingNavSection: React.FC<SettingNavSectionProps> = ({title, isVisible,
|
||||
|
||||
return (
|
||||
<>
|
||||
{title && <h2 className='mb-4 ml-2 text-[16px] tracking-tight'>{title}</h2>}
|
||||
{title && <h2 className='mb-4 ml-2 text-base font-semibold tracking-normal text-black dark:text-grey-400'>{title}</h2>}
|
||||
{children &&
|
||||
<ul className="mb-14 mt-[-8px]">
|
||||
<>
|
||||
<ul className="-mt-1 mb-7">
|
||||
{children}
|
||||
</ul>
|
||||
<Separator className='mx-2 mb-7 border-grey-300 dark:border-grey-950' />
|
||||
</>
|
||||
}
|
||||
</>
|
||||
);
|
||||
|
@ -10,7 +10,7 @@ export interface SettingSectionProps {
|
||||
|
||||
const SettingSection: React.FC<SettingSectionProps> = ({title, isVisible = true, children}) => {
|
||||
const containerClassNames = clsx(
|
||||
'mb-[16vh]',
|
||||
'mb-[10vh]',
|
||||
isVisible ? '' : 'hidden'
|
||||
);
|
||||
|
||||
@ -18,7 +18,7 @@ const SettingSection: React.FC<SettingSectionProps> = ({title, isVisible = true,
|
||||
<div className={containerClassNames}>
|
||||
{title && <SettingSectionHeader title={title} />}
|
||||
{children &&
|
||||
<div className="mb-[100px] flex flex-col gap-12">
|
||||
<div className="mb-10 flex flex-col gap-12">
|
||||
{children}
|
||||
</div>
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ export interface SettingSectionHeaderProps {
|
||||
|
||||
const SettingSectionHeader: React.FC<SettingSectionHeaderProps> = ({title, sticky = false}) => {
|
||||
const classNames = clsx(
|
||||
'z-20 mb-px pb-10 text-4xl font-bold tracking-tighter',
|
||||
'z-20 mb-px pb-10 text-3xl font-bold tracking-tighter',
|
||||
(sticky ? 'sticky top-0 mt-[calc(-8vmin-4px)] bg-gradient-to-t from-transparent via-white via-20% to-white pt-[calc(8vmin-4px)] dark:bg-black' : 'mt-[-5px]')
|
||||
);
|
||||
|
||||
|
@ -66,7 +66,7 @@
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 860px) {
|
||||
@media (max-width: 800px) {
|
||||
.admin-x-base {
|
||||
height: calc(100vh - 55px);
|
||||
}
|
||||
@ -96,4 +96,4 @@
|
||||
/* Prose classes are for formatting arbitrary HTML that comes from the API */
|
||||
.gh-prose-links a {
|
||||
color: #30CF43;
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ module.exports = {
|
||||
100: '#F4F5F6',
|
||||
150: '#F1F3F4',
|
||||
200: '#EBEEF0',
|
||||
250: '#E5E9ED',
|
||||
300: '#DDE1E5',
|
||||
400: '#CED4D9',
|
||||
500: '#AEB7C1',
|
||||
@ -35,7 +36,8 @@ module.exports = {
|
||||
800: '#626D79',
|
||||
900: '#394047',
|
||||
925: '#2E3338',
|
||||
950: '#222427'
|
||||
950: '#222427',
|
||||
975: '#191B1E'
|
||||
},
|
||||
green: {
|
||||
DEFAULT: '#30CF43',
|
||||
@ -92,8 +94,9 @@ module.exports = {
|
||||
},
|
||||
boxShadow: {
|
||||
DEFAULT: '0 0 1px rgba(0,0,0,.05), 0 5px 18px rgba(0,0,0,.08)',
|
||||
sm: '0 0 1px rgba(0,0,0,.12), 0 1px 6px rgba(0,0,0,0.03), 0 6px 10px -8px rgba(0,0,0,.1)',
|
||||
md: '0 0 1px rgba(0,0,0,.05), 0 8px 28px rgba(0,0,0,.12)',
|
||||
xs: '0 0 1px rgba(0,0,0,0.04), 0 1px 3px rgba(0,0,0,0.03), 0 8px 10px -12px rgba(0,0,0,.1)',
|
||||
sm: '0 0 1px rgba(0,0,0,.12), 0 1px 6px rgba(0,0,0,0.03), 0 8px 10px -8px rgba(0,0,0,.1)',
|
||||
md: '0 0 1px rgba(0,0,0,0.12), 0 1px 6px rgba(0,0,0,0.03), 0 8px 10px -8px rgba(0,0,0,0.05), 0px 24px 37px -21px rgba(0, 0, 0, 0.05)',
|
||||
lg: '0 0 7px rgba(0, 0, 0, 0.08), 0 2.1px 2.2px -5px rgba(0, 0, 0, 0.011), 0 5.1px 5.3px -5px rgba(0, 0, 0, 0.016), 0 9.5px 10px -5px rgba(0, 0, 0, 0.02), 0 17px 17.9px -5px rgba(0, 0, 0, 0.024), 0 31.8px 33.4px -5px rgba(0, 0, 0, 0.029), 0 76px 80px -5px rgba(0, 0, 0, 0.04)',
|
||||
xl: '0 2.8px 2.2px rgba(0, 0, 0, 0.02), 0 6.7px 5.3px rgba(0, 0, 0, 0.028), 0 12.5px 10px rgba(0, 0, 0, 0.035), 0 22.3px 17.9px rgba(0, 0, 0, 0.042), 0 41.8px 33.4px rgba(0, 0, 0, 0.05), 0 100px 80px rgba(0, 0, 0, 0.07)',
|
||||
inner: 'inset 0 0 4px 0 rgb(0 0 0 / 0.08)',
|
||||
@ -269,14 +272,14 @@ module.exports = {
|
||||
},
|
||||
fontSize: {
|
||||
'2xs': '1.0rem',
|
||||
base: '1.45rem',
|
||||
base: '1.4rem',
|
||||
xs: '1.2rem',
|
||||
sm: '1.35rem',
|
||||
md: '1.45rem',
|
||||
lg: '1.75rem',
|
||||
sm: '1.32rem',
|
||||
md: '1.40rem',
|
||||
lg: '1.65rem',
|
||||
xl: '2rem',
|
||||
'2xl': '2.4rem',
|
||||
'3xl': '3rem',
|
||||
'3xl': '3.2rem',
|
||||
'4xl': '3.6rem',
|
||||
'5xl': ['4.2rem', '1.15'],
|
||||
'6xl': ['6rem', '1'],
|
||||
|
@ -11,11 +11,10 @@ import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||
|
||||
const Page: React.FC<{children: ReactNode}> = ({children}) => {
|
||||
return <>
|
||||
<div className='sticky top-0 z-30 px-[5vmin] py-4 tablet:fixed tablet:px-6'>
|
||||
<div className='sticky top-0 z-30 bg-white px-[5vmin] py-4 dark:bg-grey-975 tablet:fixed tablet:bg-transparent tablet:px-6 dark:tablet:bg-transparent xl:p-12'>
|
||||
<ExitSettingsButton />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto flex max-w-[1080px] flex-col px-[5vmin] pb-[12vmin] tablet:flex-row tablet:items-start tablet:gap-x-10 tablet:py-[8vmin]" id="admin-x-settings-content">
|
||||
<div className="w-full dark:bg-grey-975 tablet:fixed tablet:left-0 tablet:top-0 tablet:flex tablet:h-full" id="admin-x-settings-content">
|
||||
{children}
|
||||
</div>
|
||||
</>;
|
||||
@ -43,8 +42,8 @@ const MainContent: React.FC = () => {
|
||||
if (isEditorUser(currentUser)) {
|
||||
return (
|
||||
<Page>
|
||||
<div className='w-full'>
|
||||
<Heading className='mb-10'>Settings</Heading>
|
||||
<div className='mx-auto w-full max-w-5xl px-[5vmin] tablet:mt-16 xl:mt-10' id="admin-x-settings-scroller">
|
||||
<Heading className='mb-[5vmin]'>Settings</Heading>
|
||||
<Users highlight={false} keywords={[]} />
|
||||
</div>
|
||||
</Page>
|
||||
@ -54,14 +53,12 @@ const MainContent: React.FC = () => {
|
||||
return (
|
||||
<Page>
|
||||
{loadingModal && <div className={`fixed inset-0 z-40 h-[calc(100vh-55px)] w-[100vw] tablet:h-[100vh] ${topLevelBackdropClasses}`} />}
|
||||
|
||||
{/* Sidebar */}
|
||||
<div className="sticky -top-px z-20 mt-[-55px] min-w-[260px] grow-0 bg-white pt-[52px] dark:bg-black tablet:fixed tablet:top-[8vmin] tablet:mt-0 tablet:basis-[260px] tablet:pt-0">
|
||||
<div className="relative w-full bg-white dark:bg-black">
|
||||
<div className="no-scrollbar fixed inset-x-0 top-[52px] z-[999] flex-1 basis-[320px] bg-white px-8 pb-8 dark:bg-grey-975 tablet:relative tablet:inset-x-auto tablet:top-auto tablet:h-full tablet:overflow-y-scroll tablet:bg-grey-50 tablet:pb-0 dark:tablet:bg-black" id="admin-x-settings-sidebar-scroller">
|
||||
<div className="relative w-full">
|
||||
<Sidebar />
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex-auto pt-[10vmin] tablet:ml-[330px] tablet:pt-0">
|
||||
<div className="relative h-full flex-1 overflow-y-scroll pt-11 tablet:basis-[800px]" id="admin-x-settings-scroller">
|
||||
<Settings />
|
||||
</div>
|
||||
</Page>
|
||||
|
@ -2,10 +2,10 @@ import {SettingSection, SettingSectionProps} from '@tryghost/admin-x-design-syst
|
||||
import {useSearch} from './providers/SettingsAppProvider';
|
||||
|
||||
const SearchableSection: React.FC<Omit<SettingSectionProps, 'isVisible'> & {keywords: string[]}> = ({keywords, ...props}) => {
|
||||
const {checkVisible} = useSearch();
|
||||
const {checkVisible, noResult} = useSearch();
|
||||
|
||||
return (
|
||||
<SettingSection isVisible={checkVisible(keywords)} {...props} />
|
||||
<SettingSection isVisible={checkVisible(keywords) || noResult} {...props} />
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,7 @@ import SiteSettings from './settings/site/SiteSettings';
|
||||
const Settings: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<div className='mb-[40vh]'>
|
||||
<div className='mb-[40vh] px-8 pt-8 tablet:max-w-[760px] tablet:px-14 xl:pt-0'>
|
||||
<GeneralSettings />
|
||||
<SiteSettings />
|
||||
<MembershipSettings />
|
||||
|
@ -31,7 +31,7 @@ const NavItem: React.FC<Omit<SettingNavItemProps, 'isVisible' | 'isCurrent'> & {
|
||||
};
|
||||
|
||||
const Sidebar: React.FC = () => {
|
||||
const {filter, setFilter, checkVisible} = useSearch();
|
||||
const {filter, setFilter, checkVisible, noResult, setNoResult} = useSearch();
|
||||
const {updateRoute} = useRouting();
|
||||
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const {isAnyTextFieldFocused} = useFocusContext();
|
||||
@ -65,6 +65,19 @@ const Sidebar: React.FC = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!checkVisible(Object.values(generalSearchKeywords).flat()) &&
|
||||
!checkVisible(Object.values(siteSearchKeywords).flat()) &&
|
||||
!checkVisible(Object.values(membershipSearchKeywords).flat()) &&
|
||||
!checkVisible(Object.values(growthSearchKeywords).flat()) &&
|
||||
!checkVisible(Object.values(emailSearchKeywords).flat()) &&
|
||||
!checkVisible(Object.values(advancedSearchKeywords).flat())) {
|
||||
setNoResult(true);
|
||||
} else {
|
||||
setNoResult(false);
|
||||
}
|
||||
}, [checkVisible, setNoResult, filter]);
|
||||
|
||||
const {settings, config} = useGlobalData();
|
||||
const [newslettersEnabled] = getSettingValues(settings, ['editor_default_email_recipients']) as [string];
|
||||
const hasStripeEnabled = checkStripeEnabled(settings || [], config || {});
|
||||
@ -72,6 +85,7 @@ const Sidebar: React.FC = () => {
|
||||
const handleSectionClick = (e?: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
if (e) {
|
||||
setFilter('');
|
||||
setNoResult(false);
|
||||
updateRoute(e.currentTarget.id);
|
||||
}
|
||||
};
|
||||
@ -88,32 +102,42 @@ const Sidebar: React.FC = () => {
|
||||
};
|
||||
|
||||
const navClasses = clsx(
|
||||
'no-scrollbar hidden pt-10 tablet:!visible tablet:!block tablet:h-[calc(100vh-8vmin-36px)] tablet:overflow-y-auto'
|
||||
'hidden pt-10 tablet:!visible tablet:!block'
|
||||
);
|
||||
|
||||
return (
|
||||
<div data-testid="sidebar">
|
||||
<div className='relative flex content-stretch items-end tablet:h-[36px]'>
|
||||
<Icon className='absolute left-2 top-[10px] z-10' colorClass='text-grey-500' name='magnifying-glass' size='sm' />
|
||||
<TextField
|
||||
autoComplete="off"
|
||||
className='-mx-1 flex h-9 w-[calc(100%+8px)] items-center rounded-full border border-transparent bg-grey-150 px-[33px] py-1.5 text-sm transition-all hover:bg-grey-100 focus:border-green focus:bg-white focus:shadow-[0_0_0_1px_rgba(48,207,67,1)] focus:outline-2 dark:bg-grey-900 dark:text-white dark:focus:bg-black'
|
||||
containerClassName='w-100'
|
||||
inputRef={searchInputRef}
|
||||
placeholder="Search settings"
|
||||
title="Search"
|
||||
value={filter}
|
||||
clearBg
|
||||
hideTitle
|
||||
unstyled
|
||||
onChange={updateSearch}
|
||||
/>
|
||||
{filter ? <Button className='absolute right-3 top-[10px] p-1' icon='close' iconColorClass='text-grey-700 !w-[10px] !h-[10px]' size='sm' unstyled onClick={() => {
|
||||
setFilter('');
|
||||
searchInputRef.current?.focus();
|
||||
}} /> : <div className='absolute right-0 top-[20px] hidden rounded border border-grey-400 bg-white px-1.5 py-0.5 text-2xs font-semibold uppercase tracking-wider text-grey-600 shadow-[0px_1px_#CED4D9] dark:bg-grey-800 dark:text-grey-500 tablet:!visible tablet:right-3 tablet:top-[7px] tablet:!block'>/</div>}
|
||||
<div className='ml-auto flex w-full flex-col pt-0 tablet:max-w-[240px]' data-testid="sidebar">
|
||||
<div className='sticky top-0 flex content-stretch items-end bg-grey-50 dark:bg-grey-975 tablet:h-28 dark:tablet:bg-black xl:h-20'>
|
||||
<div className='relative w-full'>
|
||||
<Icon className='absolute left-3 top-3 z-10' colorClass='text-grey-500' name='magnifying-glass' size='sm' />
|
||||
<TextField
|
||||
autoComplete="off"
|
||||
className='flex h-10 w-full items-center rounded-lg border border-grey-200 bg-white px-[33px] py-1.5 text-[14px] shadow-xs transition-all hover:shadow-sm focus:border-green focus:bg-white focus:shadow-[0_0_0_1px_rgba(48,207,67,1)] focus:outline-2 dark:border-grey-950 dark:bg-grey-950 dark:text-white dark:placeholder:text-grey-800 dark:focus:bg-black'
|
||||
containerClassName='w-100'
|
||||
inputRef={searchInputRef}
|
||||
placeholder="Search settings"
|
||||
title="Search"
|
||||
value={filter}
|
||||
clearBg
|
||||
hideTitle
|
||||
unstyled
|
||||
onChange={updateSearch}
|
||||
/>
|
||||
{filter ? <Button className='absolute right-3 top-3 p-1' icon='close' iconColorClass='text-grey-700 !w-[10px] !h-[10px]' size='sm' unstyled onClick={() => {
|
||||
setFilter('');
|
||||
searchInputRef.current?.focus();
|
||||
}} /> : <div className='absolute -right-1 top-[9px] hidden rounded border border-grey-400 bg-white px-1.5 py-0.5 text-2xs font-semibold uppercase tracking-wider text-grey-600 shadow-[0px_1px_#CED4D9] dark:border-grey-800 dark:bg-grey-900 dark:text-grey-500 dark:shadow-[0px_1px_#626D79] tablet:!visible tablet:right-3 tablet:!block'>/</div>}
|
||||
</div>
|
||||
</div>
|
||||
<div className={navClasses} id='admin-x-settings-sidebar'>
|
||||
<nav className={navClasses} id='admin-x-settings-sidebar'>
|
||||
{noResult &&
|
||||
<div className='ml-2 text-base text-grey-700'>
|
||||
<h2 className='mb-2 text-base font-semibold tracking-normal text-black dark:text-white'>No result</h2>
|
||||
<div>
|
||||
{`We couldn't find any setting matching '${filter}'`}.
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<SettingNavSection isVisible={checkVisible(Object.values(generalSearchKeywords).flat())} title="General settings">
|
||||
<NavItem icon='textfield' keywords={generalSearchKeywords.titleAndDescription} navid='general' title="Title & description" onClick={handleSectionClick} />
|
||||
<NavItem icon='world-clock' keywords={generalSearchKeywords.timeZone} navid='timezone' title="Timezone" onClick={handleSectionClick} />
|
||||
@ -166,14 +190,14 @@ const Sidebar: React.FC = () => {
|
||||
</SettingNavSection>
|
||||
|
||||
{!filter &&
|
||||
<a className='mb-10 ml-1 flex cursor-pointer items-center gap-1.5 pl-1 text-sm !font-normal' onClick={() => {
|
||||
<a className='w-100 mb-10 mt-1 flex h-[38px] cursor-pointer items-center rounded-lg px-3 py-2 text-left text-[14px] font-medium text-grey-800 transition-all hover:bg-grey-200 focus:bg-grey-100 dark:text-grey-600 dark:hover:bg-grey-950 dark:focus:bg-grey-925' onClick={() => {
|
||||
updateRoute('about');
|
||||
}}>
|
||||
<img alt='Ghost Logo' className='h-[18px] w-[18px]' src={GhostLogo} />
|
||||
<img alt='Ghost Logo' className='mr-[7px] h-[18px] w-[18px]' src={GhostLogo} />
|
||||
About Ghost
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ import {useScrollSection} from '../hooks/useScrollSection';
|
||||
import {useSearch} from './providers/SettingsAppProvider';
|
||||
|
||||
const TopLevelGroup: React.FC<Omit<SettingGroupProps, 'isVisible' | 'highlight'> & {keywords: string[]}> = ({keywords, navid, ...props}) => {
|
||||
const {checkVisible} = useSearch();
|
||||
const {checkVisible, noResult} = useSearch();
|
||||
const {route} = useRouting();
|
||||
const [highlight, setHighlight] = useState(false);
|
||||
const {ref} = useScrollSection(navid);
|
||||
@ -18,11 +18,11 @@ const TopLevelGroup: React.FC<Omit<SettingGroupProps, 'isVisible' | 'highlight'>
|
||||
if (highlight) {
|
||||
setTimeout(() => {
|
||||
setHighlight(false);
|
||||
}, 3000);
|
||||
}, 2000);
|
||||
}
|
||||
}, [highlight]);
|
||||
|
||||
return <Base ref={ref} highlight={highlight} isVisible={checkVisible(keywords)} navid={navid} {...props} />;
|
||||
return <Base ref={ref} highlight={highlight} isVisible={checkVisible(keywords) || noResult} navid={navid} {...props} />;
|
||||
};
|
||||
|
||||
export default TopLevelGroup;
|
||||
|
@ -43,7 +43,14 @@ interface SettingsAppContextType {
|
||||
const SettingsAppContext = createContext<SettingsAppContextType>({
|
||||
officialThemes: [],
|
||||
zapierTemplates: [],
|
||||
search: {filter: '', setFilter: () => {}, checkVisible: () => true, highlightKeywords: () => ''},
|
||||
search: {
|
||||
filter: '',
|
||||
setFilter: () => {},
|
||||
checkVisible: () => true,
|
||||
highlightKeywords: () => '',
|
||||
noResult: false,
|
||||
setNoResult: () => {}
|
||||
},
|
||||
sortingState: []
|
||||
});
|
||||
|
||||
|
@ -16,8 +16,8 @@ export const searchKeywords = {
|
||||
timeZone: ['general', 'time', 'date', 'site timezone', 'time zone'],
|
||||
publicationLanguage: ['general', 'publication language', 'locale'],
|
||||
metadata: ['general', 'metadata', 'title', 'description', 'search', 'engine', 'google', 'meta data'],
|
||||
twitter: ['general', 'twitter card', 'structured data', 'rich cards', 'x card'],
|
||||
facebook: ['general', 'facebook card', 'structured data', 'rich cards'],
|
||||
twitter: ['general', 'twitter card', 'structured data', 'rich cards', 'x card', 'social'],
|
||||
facebook: ['general', 'facebook card', 'structured data', 'rich cards', 'social'],
|
||||
socialAccounts: ['general', 'social accounts', 'facebook', 'twitter', 'structured data', 'rich cards'],
|
||||
lockSite: ['general', 'password protection', 'lock site', 'make this site private'],
|
||||
users: ['general', 'users and permissions', 'roles', 'staff', 'invite people', 'contributors', 'editors', 'authors', 'administrators']
|
||||
|
@ -21,7 +21,7 @@ export const useScrollSectionContext = () => useContext(ScrollSectionContext);
|
||||
const scrollMargin = 193;
|
||||
|
||||
const scrollToSection = (element: HTMLDivElement, doneInitialScroll: boolean) => {
|
||||
const root = document.querySelector('.admin-x-settings')!;
|
||||
const root = document.getElementById('admin-x-settings-scroller')!;
|
||||
const top = element.getBoundingClientRect().top + root.scrollTop;
|
||||
|
||||
root.scrollTo({
|
||||
@ -31,7 +31,8 @@ const scrollToSection = (element: HTMLDivElement, doneInitialScroll: boolean) =>
|
||||
};
|
||||
|
||||
const scrollSidebarNav = (navElement: HTMLLIElement, doneInitialScroll: boolean) => {
|
||||
const sidebar = document.getElementById('admin-x-settings-sidebar')!;
|
||||
// const sidebar = document.getElementById('admin-x-settings-sidebar')!;
|
||||
const sidebar = document.getElementById('admin-x-settings-sidebar-scroller')!;
|
||||
|
||||
const bounds = navElement.getBoundingClientRect();
|
||||
|
||||
|
@ -5,10 +5,13 @@ export interface SearchService {
|
||||
setFilter: (value: string) => void;
|
||||
checkVisible: (keywords: string[]) => boolean;
|
||||
highlightKeywords: (text: ReactNode) => ReactNode;
|
||||
noResult: boolean;
|
||||
setNoResult: (value: boolean) => void;
|
||||
}
|
||||
|
||||
const useSearchService = () => {
|
||||
const [filter, setFilter] = useState('');
|
||||
const [noResult, setNoResult] = useState(false);
|
||||
|
||||
const checkVisible = (keywords: string[]) => {
|
||||
if (!keywords.length) {
|
||||
@ -47,7 +50,9 @@ const useSearchService = () => {
|
||||
filter,
|
||||
setFilter,
|
||||
checkVisible,
|
||||
highlightKeywords
|
||||
highlightKeywords,
|
||||
noResult,
|
||||
setNoResult
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -171,7 +171,7 @@ test.describe('Recommendations', async () => {
|
||||
expect(confirmation).toContainText('Your recommendation Recommendation 1 title will no longer be visible to your audience.');
|
||||
|
||||
await confirmation.getByRole('button', {name: 'Delete'}).click();
|
||||
await expect(page.getByTestId('toast-success')).toContainText('deleted the recommendation');
|
||||
await expect(page.getByTestId('toast-success')).toContainText('Successfully deleted the recommendation');
|
||||
|
||||
expect(lastApiRequests.deleteRecommendation).toBeTruthy();
|
||||
});
|
||||
|
@ -1448,10 +1448,10 @@
|
||||
padding: 0;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 3.6rem;
|
||||
font-size: 3.2rem;
|
||||
line-height: 1.3em;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.021em;
|
||||
letter-spacing: -0.05em;
|
||||
/* match button height to avoid jump on navigation between screens*/
|
||||
min-height: 35px;
|
||||
color: var(--black);
|
||||
|
Loading…
Reference in New Issue
Block a user