mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Made the tabs sticky in design and newsletter settings (#21477)
ref DES-927, DES-928
This commit is contained in:
parent
87e24f6403
commit
e01b952ed2
4 changed files with 23 additions and 18 deletions
|
@ -50,7 +50,7 @@ export const TabButton: React.FC<TabButtonProps> = ({
|
||||||
>
|
>
|
||||||
{icon && <Icon className='mb-0.5 mr-1.5 inline' name={icon} size='sm' />}
|
{icon && <Icon className='mb-0.5 mr-1.5 inline' name={icon} size='sm' />}
|
||||||
{title}
|
{title}
|
||||||
{(typeof counter === 'number') &&
|
{(typeof counter === 'number') &&
|
||||||
<span className='ml-1.5 rounded-full bg-grey-200 px-1.5 py-[2px] text-xs font-medium text-grey-800 dark:bg-grey-900 dark:text-grey-300'>
|
<span className='ml-1.5 rounded-full bg-grey-200 px-1.5 py-[2px] text-xs font-medium text-grey-800 dark:bg-grey-900 dark:text-grey-300'>
|
||||||
{new Intl.NumberFormat().format(counter)}
|
{new Intl.NumberFormat().format(counter)}
|
||||||
</span>
|
</span>
|
||||||
|
@ -66,7 +66,8 @@ export interface TabListProps<ID = string> {
|
||||||
border: boolean;
|
border: boolean;
|
||||||
buttonBorder?: boolean;
|
buttonBorder?: boolean;
|
||||||
selectedTab?: ID,
|
selectedTab?: ID,
|
||||||
topRightContent?: React.ReactNode
|
topRightContent?: React.ReactNode,
|
||||||
|
stickyHeader?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TabList: React.FC<TabListProps> = ({
|
export const TabList: React.FC<TabListProps> = ({
|
||||||
|
@ -75,7 +76,8 @@ export const TabList: React.FC<TabListProps> = ({
|
||||||
handleTabChange,
|
handleTabChange,
|
||||||
border,
|
border,
|
||||||
buttonBorder,
|
buttonBorder,
|
||||||
topRightContent
|
topRightContent,
|
||||||
|
stickyHeader
|
||||||
}) => {
|
}) => {
|
||||||
const containerClasses = clsx(
|
const containerClasses = clsx(
|
||||||
'no-scrollbar mb-px flex w-full overflow-x-auto',
|
'no-scrollbar mb-px flex w-full overflow-x-auto',
|
||||||
|
@ -85,7 +87,7 @@ export const TabList: React.FC<TabListProps> = ({
|
||||||
border && 'border-b border-grey-300 dark:border-grey-900'
|
border && 'border-b border-grey-300 dark:border-grey-900'
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<TabsPrimitive.List>
|
<TabsPrimitive.List className={`${stickyHeader ? 'sticky top-0 z-50 bg-white dark:bg-black' : ''}`}>
|
||||||
<div className={containerClasses} role='tablist'>
|
<div className={containerClasses} role='tablist'>
|
||||||
{tabs.map(tab => (
|
{tabs.map(tab => (
|
||||||
<div>
|
<div>
|
||||||
|
@ -117,6 +119,7 @@ export interface TabViewProps<ID = string> {
|
||||||
width?: TabWidth;
|
width?: TabWidth;
|
||||||
containerClassName?: string;
|
containerClassName?: string;
|
||||||
topRightContent?: React.ReactNode;
|
topRightContent?: React.ReactNode;
|
||||||
|
stickyHeader?: boolean;
|
||||||
testId?: string;
|
testId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +132,8 @@ function TabView<ID extends string = string>({
|
||||||
buttonBorder = border,
|
buttonBorder = border,
|
||||||
width = 'normal',
|
width = 'normal',
|
||||||
containerClassName,
|
containerClassName,
|
||||||
topRightContent
|
topRightContent,
|
||||||
|
stickyHeader
|
||||||
}: TabViewProps<ID>) {
|
}: TabViewProps<ID>) {
|
||||||
if (tabs.length !== 0 && selectedTab === undefined) {
|
if (tabs.length !== 0 && selectedTab === undefined) {
|
||||||
selectedTab = tabs[0].id;
|
selectedTab = tabs[0].id;
|
||||||
|
@ -151,6 +155,7 @@ function TabView<ID extends string = string>({
|
||||||
buttonBorder={buttonBorder}
|
buttonBorder={buttonBorder}
|
||||||
handleTabChange={handleTabChange}
|
handleTabChange={handleTabChange}
|
||||||
selectedTab={selectedTab}
|
selectedTab={selectedTab}
|
||||||
|
stickyHeader={stickyHeader}
|
||||||
tabs={tabs}
|
tabs={tabs}
|
||||||
topRightContent={topRightContent}
|
topRightContent={topRightContent}
|
||||||
width={width}
|
width={width}
|
||||||
|
|
|
@ -514,8 +514,8 @@ const Sidebar: React.FC<{
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col'>
|
<div className='flex flex-col'>
|
||||||
<div className='px-7 pb-7 pt-5'>
|
<div className='px-7 pb-7 pt-0'>
|
||||||
<TabView selectedTab={selectedTab} tabs={tabs} onTabChange={handleTabChange} />
|
<TabView selectedTab={selectedTab} stickyHeader={true} tabs={tabs} onTabChange={handleTabChange} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,7 +31,7 @@ const Sidebar: React.FC<{
|
||||||
const tabs: Tab[] = [
|
const tabs: Tab[] = [
|
||||||
{
|
{
|
||||||
id: 'global',
|
id: 'global',
|
||||||
title: 'Global',
|
title: 'Brand',
|
||||||
contents: <GlobalSettings updateSetting={updateGlobalSetting} values={globalSettings} />
|
contents: <GlobalSettings updateSetting={updateGlobalSetting} values={globalSettings} />
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -39,7 +39,7 @@ const Sidebar: React.FC<{
|
||||||
if (themeSettingSections.length > 0) {
|
if (themeSettingSections.length > 0) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
id: 'theme-settings',
|
id: 'theme-settings',
|
||||||
title: 'Theme settings',
|
title: 'Theme',
|
||||||
contents: <ThemeSettings sections={themeSettingSections} updateSetting={updateThemeSetting} />
|
contents: <ThemeSettings sections={themeSettingSections} updateSetting={updateThemeSetting} />
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,9 @@ const Sidebar: React.FC<{
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex h-full flex-col justify-between'>
|
<div className='flex h-full flex-col justify-between'>
|
||||||
<div className='grow p-7 pt-2' data-testid="design-setting-tabs">
|
<div className='grow p-7 pt-0' data-testid="design-setting-tabs">
|
||||||
{tabs.length > 1 ?
|
{tabs.length > 1 ?
|
||||||
<TabView selectedTab={selectedTab} tabs={tabs} onTabChange={handleTabChange} />
|
<TabView selectedTab={selectedTab} stickyHeader={true} tabs={tabs} onTabChange={handleTabChange} />
|
||||||
:
|
:
|
||||||
<GlobalSettings updateSetting={updateGlobalSetting} values={globalSettings} />
|
<GlobalSettings updateSetting={updateGlobalSetting} values={globalSettings} />
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ test.describe('Design settings', async () => {
|
||||||
|
|
||||||
await section.getByRole('button', {name: 'Customize'}).click();
|
await section.getByRole('button', {name: 'Customize'}).click();
|
||||||
|
|
||||||
await modal.getByTestId('design-setting-tabs').getByRole('tab', {name: 'Theme settings'}).click();
|
await modal.getByTestId('design-setting-tabs').getByRole('tab', {name: 'Theme'}).click();
|
||||||
|
|
||||||
await modal.getByLabel('Email signup text').fill('test');
|
await modal.getByLabel('Email signup text').fill('test');
|
||||||
|
|
||||||
|
@ -182,14 +182,14 @@ test.describe('Design settings', async () => {
|
||||||
|
|
||||||
const modal = page.getByTestId('design-modal');
|
const modal = page.getByTestId('design-modal');
|
||||||
|
|
||||||
await modal.getByRole('tab', {name: 'Theme settings'}).click();
|
await modal.getByRole('tab', {name: 'Theme'}).click();
|
||||||
await chooseOptionInSelect(modal.getByTestId('setting-select-navigation_layout'), 'Logo in the middle');
|
await chooseOptionInSelect(modal.getByTestId('setting-select-navigation_layout'), 'Logo in the middle');
|
||||||
const expectedSettings = {navigation_layout: 'Logo in the middle'};
|
const expectedSettings = {navigation_layout: 'Logo in the middle'};
|
||||||
const expectedEncoded = new URLSearchParams([['custom', JSON.stringify(expectedSettings)]]).toString();
|
const expectedEncoded = new URLSearchParams([['custom', JSON.stringify(expectedSettings)]]).toString();
|
||||||
|
|
||||||
const matchingHeader = previewRequests.find(header => new RegExp(`&${expectedEncoded.replace(/\+/g, '\\+')}`).test(header));
|
const matchingHeader = previewRequests.find(header => new RegExp(`&${expectedEncoded.replace(/\+/g, '\\+')}`).test(header));
|
||||||
|
|
||||||
expect(matchingHeader).toBeDefined();
|
expect(matchingHeader).toBeDefined();
|
||||||
|
|
||||||
await modal.getByRole('button', {name: 'Save'}).click();
|
await modal.getByRole('button', {name: 'Save'}).click();
|
||||||
expect(lastApiRequests.editCustomThemeSettings?.body).toMatchObject({
|
expect(lastApiRequests.editCustomThemeSettings?.body).toMatchObject({
|
||||||
|
@ -230,8 +230,8 @@ test.describe('Design settings', async () => {
|
||||||
|
|
||||||
const designSettingTabs = modal.getByTestId('design-setting-tabs');
|
const designSettingTabs = modal.getByTestId('design-setting-tabs');
|
||||||
|
|
||||||
await expect(designSettingTabs.getByRole('tab', {name: 'Global'})).toBeHidden();
|
await expect(designSettingTabs.getByRole('tab', {name: 'Brand'})).toBeHidden();
|
||||||
await expect(designSettingTabs.getByRole('tab', {name: 'Theme settings'})).toBeHidden();
|
await expect(designSettingTabs.getByRole('tab', {name: 'Theme'})).toBeHidden();
|
||||||
|
|
||||||
await expect(designSettingTabs.getByTestId('accent-color-picker')).toBeVisible();
|
await expect(designSettingTabs.getByTestId('accent-color-picker')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
@ -277,7 +277,7 @@ test.describe('Design settings', async () => {
|
||||||
|
|
||||||
const modal = page.getByTestId('design-modal');
|
const modal = page.getByTestId('design-modal');
|
||||||
|
|
||||||
await modal.getByRole('tab', {name: 'Theme settings'}).click();
|
await modal.getByRole('tab', {name: 'Theme'}).click();
|
||||||
|
|
||||||
const showFeaturedPostsCustomThemeSetting = modal.getByLabel('Show featured posts');
|
const showFeaturedPostsCustomThemeSetting = modal.getByLabel('Show featured posts');
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue