Merge branch 'codex/feature/submit-board-button'

This commit is contained in:
Tommaso Casaburi
2026-06-07 22:47:46 +07:00
38 changed files with 171 additions and 75 deletions
@@ -44,6 +44,7 @@ vi.mock('react-i18next', () => ({
if (key === 'directory_status_online') return 'online';
if (key === 'directory_status_offline') return 'offline';
if (key === 'directory_heading') return `${values?.boardIdentifier} directory`;
if (key === 'directory_submit_board') return 'Submit Your Board';
if (key === 'view') return 'View';
return key;
},
@@ -75,18 +76,18 @@ vi.mock('../../../components/board-buttons/board-buttons', () => ({
TopButton: () => createElement('button', { type: 'button' }, 'top'),
}));
vi.mock('../../../components/footer', () => ({
vi.mock('../../../components/footer/footer', () => ({
PageFooterDesktop: ({ firstRow, styleRow }: { firstRow: React.ReactNode; styleRow: React.ReactNode }) =>
createElement('footer', { 'data-testid': 'desktop-footer' }, firstRow, styleRow),
PageFooterMobile: ({ children }: { children: React.ReactNode }) => createElement('footer', { 'data-testid': 'mobile-footer' }, children),
ThreadFooterStyleRow: () => createElement('div', null, 'style'),
}));
vi.mock('../../../components/loading-ellipsis', () => ({
vi.mock('../../../components/loading-ellipsis/loading-ellipsis', () => ({
default: ({ string }: { string: string }) => createElement('span', null, string),
}));
vi.mock('../../../components/tooltip', () => ({
vi.mock('../../../components/tooltip/tooltip', () => ({
default: ({ content, children }: { content: React.ReactNode; children: React.ReactNode }) =>
createElement('span', { title: typeof content === 'string' ? content : undefined }, children),
}));
@@ -167,6 +168,7 @@ const createCommunity = (address: string, updatedAt = testState.nowSeconds - 60)
});
const getDirectoryRow = (address = 'anime-and-manga.bso') => Array.from(container.querySelectorAll('tbody tr')).find((row) => row.textContent?.includes(address));
const getSubmitBoardLinks = () => Array.from(container.querySelectorAll<HTMLAnchorElement>('a')).filter((link) => link.textContent === 'Submit Your Board');
describe('Directory', () => {
beforeEach(() => {
@@ -230,6 +232,20 @@ describe('Directory', () => {
});
});
it('links the submit-board controls to the GitHub directory edit flow', async () => {
await renderDirectory();
const submitLinks = getSubmitBoardLinks();
expect(submitLinks).toHaveLength(4);
for (const link of submitLinks) {
expect(link.getAttribute('href')).toBe('https://github.com/bitsocialnet/lists/edit/master/5chan-directories/5chan-a-directory.json');
expect(link.getAttribute('target')).toBe('_blank');
expect(link.getAttribute('rel')).toContain('noreferrer');
expect(link.getAttribute('rel')).toContain('noopener');
}
});
it('shows loading status while the listed board status is loading', async () => {
testState.communities = {};
testState.offlineHookValue = {
+23 -8
View File
@@ -18,12 +18,24 @@
.desktopNavLinks {
display: flex;
align-items: center;
gap: 4px;
justify-content: space-between;
gap: 8px;
width: 100%;
text-align: left;
font-size: 13px;
text-transform: capitalize;
}
.navButtonGroup {
display: flex;
align-items: center;
gap: 4px;
}
.submitBoardControl {
white-space: nowrap;
}
.desktopNavLinks a:not([class~='button']),
.desktopFooterButtons a:not([class~='button']) {
all: unset;
@@ -36,11 +48,18 @@
margin-top: 5px;
}
.mobileNavLinks button {
.mobileNavLinks button,
.mobileNavLinks a[class~='button'],
.mobileFooterButtons button,
.mobileFooterButtons a[class~='button'] {
text-transform: capitalize;
margin: 5px 2px;
}
.mobileSubmitRow {
margin-top: -5px;
}
.mobileNavLinks a:not([class~='button']),
.mobileFooterButtons a:not([class~='button']) {
all: unset;
@@ -49,7 +68,8 @@
.desktopFooterButtons {
display: flex;
align-items: center;
gap: 4px;
justify-content: space-between;
gap: 8px;
width: 100%;
text-transform: capitalize;
}
@@ -59,11 +79,6 @@
text-transform: capitalize;
}
.mobileFooterButtons button {
text-transform: capitalize;
margin: 5px 2px;
}
.directorySummary {
margin: 0 0 12px;
text-align: center;
+59 -29
View File
@@ -4,9 +4,9 @@ import { Trans, useTranslation } from 'react-i18next';
import { useCommunity } from '@bitsocial/bitsocial-react-hooks';
import { shouldShowSnow } from '../../lib/snow';
import { BottomButton, BracketedCatalogButton, CatalogButton, ReturnButton, TopButton } from '../../components/board-buttons/board-buttons';
import { PageFooterDesktop, PageFooterMobile, ThreadFooterStyleRow } from '../../components/footer';
import LoadingEllipsis from '../../components/loading-ellipsis';
import Tooltip from '../../components/tooltip';
import { PageFooterDesktop, PageFooterMobile, ThreadFooterStyleRow } from '../../components/footer/footer';
import LoadingEllipsis from '../../components/loading-ellipsis/loading-ellipsis';
import Tooltip from '../../components/tooltip/tooltip';
import { useDirectories } from '../../hooks/use-directories';
import { useCommunityIdentifier } from '../../hooks/use-community-identifiers';
import { useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
@@ -44,43 +44,73 @@ const computeBoardStatus = (
const PASS_LINK = '/pass';
const DirectoryDesktopTopControls = ({ communityAddress }: { communityAddress: string | undefined }) => (
const DirectorySubmitBoardLink = ({ href }: { href: string }) => {
const { t } = useTranslation();
return (
<a className='button' href={href} target='_blank' rel='noreferrer noopener'>
{t('directory_submit_board')}
</a>
);
};
const DirectoryDesktopTopControls = ({ communityAddress, submitHref }: { communityAddress: string | undefined; submitHref: string }) => (
<div className={styles.desktopNavLinks}>
<span>
[<ReturnButton address={communityAddress} />]
</span>
<BracketedCatalogButton address={communityAddress} />
<span>
[<BottomButton />]
<div className={styles.navButtonGroup}>
<span>
[<ReturnButton address={communityAddress} />]
</span>
<BracketedCatalogButton address={communityAddress} />
<span>
[<BottomButton />]
</span>
</div>
<span className={styles.submitBoardControl}>
[<DirectorySubmitBoardLink href={submitHref} />]
</span>
</div>
);
const DirectoryDesktopFooterControls = ({ communityAddress }: { communityAddress: string | undefined }) => (
const DirectoryDesktopFooterControls = ({ communityAddress, submitHref }: { communityAddress: string | undefined; submitHref: string }) => (
<div className={styles.desktopFooterButtons}>
<span>
[<ReturnButton address={communityAddress} />]
</span>
<BracketedCatalogButton address={communityAddress} />
<span>
[<TopButton />]
<div className={styles.navButtonGroup}>
<span>
[<ReturnButton address={communityAddress} />]
</span>
<BracketedCatalogButton address={communityAddress} />
<span>
[<TopButton />]
</span>
</div>
<span className={styles.submitBoardControl}>
[<DirectorySubmitBoardLink href={submitHref} />]
</span>
</div>
);
const DirectoryMobileTopControls = ({ communityAddress }: { communityAddress: string | undefined }) => (
const DirectoryMobileTopControls = ({ communityAddress, submitHref }: { communityAddress: string | undefined; submitHref: string }) => (
<div className={styles.mobileNavLinks}>
<ReturnButton address={communityAddress} />
<CatalogButton address={communityAddress} />
<BottomButton />
<div>
<ReturnButton address={communityAddress} />
<CatalogButton address={communityAddress} />
<BottomButton />
</div>
<div className={styles.mobileSubmitRow}>
<DirectorySubmitBoardLink href={submitHref} />
</div>
</div>
);
const DirectoryMobileFooterControls = ({ communityAddress }: { communityAddress: string | undefined }) => (
const DirectoryMobileFooterControls = ({ communityAddress, submitHref }: { communityAddress: string | undefined; submitHref: string }) => (
<div className={styles.mobileFooterButtons}>
<ReturnButton address={communityAddress} />
<CatalogButton address={communityAddress} />
<TopButton />
<div>
<ReturnButton address={communityAddress} />
<CatalogButton address={communityAddress} />
<TopButton />
</div>
<div className={styles.mobileSubmitRow}>
<DirectorySubmitBoardLink href={submitHref} />
</div>
</div>
);
@@ -199,9 +229,9 @@ const Directory = () => {
return (
<div id='top' className={`${styles.page} ${shouldShowSnow() ? styles.garland : ''}`}>
<DirectoryMobileTopControls communityAddress={communityAddress} />
<DirectoryMobileTopControls communityAddress={communityAddress} submitHref={repoEditUrl} />
<hr className={styles.desktopDivider} />
<DirectoryDesktopTopControls communityAddress={communityAddress} />
<DirectoryDesktopTopControls communityAddress={communityAddress} submitHref={repoEditUrl} />
<hr className={styles.divider} />
{isLoadingShell ? (
<h4 className={styles.directorySummary}>
@@ -257,9 +287,9 @@ const Directory = () => {
</>
)}
<PageFooterDesktop firstRow={<DirectoryDesktopFooterControls communityAddress={communityAddress} />} styleRow={<ThreadFooterStyleRow />} />
<PageFooterDesktop firstRow={<DirectoryDesktopFooterControls communityAddress={communityAddress} submitHref={repoEditUrl} />} styleRow={<ThreadFooterStyleRow />} />
<PageFooterMobile>
<DirectoryMobileFooterControls communityAddress={communityAddress} />
<DirectoryMobileFooterControls communityAddress={communityAddress} submitHref={repoEditUrl} />
</PageFooterMobile>
</div>
);