mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(mod queue): reset board filter on navigation (#1166)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { createElement } from 'react';
|
||||
import { createRoot, type Root } from 'react-dom/client';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { Link, MemoryRouter, Route, Routes, useNavigate } from 'react-router-dom';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import ModQueueView from '../mod-queue';
|
||||
|
||||
@@ -31,7 +31,6 @@ const testState = vi.hoisted(() => ({
|
||||
queuedCommentHistory: [] as TestComment[],
|
||||
rememberCommentsInQueueMock: vi.fn(),
|
||||
resetMock: vi.fn(),
|
||||
selectedBoardFilter: null as string | null,
|
||||
setResetFunctionMock: vi.fn(),
|
||||
viewMode: 'compact' as 'compact' | 'feed',
|
||||
}));
|
||||
@@ -42,8 +41,6 @@ const getModQueueState = () => ({
|
||||
getAlertThresholdSeconds: () => 6 * 60 * 60,
|
||||
queuedCommentHistory: testState.queuedCommentHistory,
|
||||
rememberCommentsInQueue: testState.rememberCommentsInQueueMock,
|
||||
selectedBoardFilter: testState.selectedBoardFilter,
|
||||
setSelectedBoardFilter: vi.fn(),
|
||||
setViewMode: vi.fn(),
|
||||
viewMode: testState.viewMode,
|
||||
});
|
||||
@@ -161,6 +158,13 @@ vi.mock('../../../hooks/use-current-time', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-directories', () => ({
|
||||
findDirectoryByAddress: (directories: typeof testState.directories, address: string) =>
|
||||
directories.find((directory) =>
|
||||
[directory.address, directory.directoryCode, directory.title].some(
|
||||
(value) => typeof value === 'string' && value.replace(/(\.bso|\.eth)$/, '') === address.replace(/(\.bso|\.eth)$/, ''),
|
||||
),
|
||||
),
|
||||
normalizeBoardAddress: (address: string) => address.replace(/(\.bso|\.eth)$/, ''),
|
||||
useDirectories: () => testState.directories,
|
||||
}));
|
||||
|
||||
@@ -176,17 +180,17 @@ vi.mock('../../../components/error-display/error-display', () => ({
|
||||
default: ({ error }: { error?: Error }) => createElement('div', { 'data-testid': 'error-display' }, error?.message || 'error'),
|
||||
}));
|
||||
|
||||
vi.mock('../../../components/footer', () => ({
|
||||
vi.mock('../../../components/footer/footer', () => ({
|
||||
PageFooterDesktop: ({ firstRow }: { firstRow: React.ReactNode }) => createElement('div', { 'data-testid': 'footer-desktop' }, firstRow),
|
||||
PageFooterMobile: ({ children }: { children: React.ReactNode }) => createElement('div', { 'data-testid': 'footer-mobile' }, children),
|
||||
StyleOnlyFooterFirstRow: () => createElement('div', { 'data-testid': 'style-footer-row' }),
|
||||
}));
|
||||
|
||||
vi.mock('../../../components/loading-ellipsis', () => ({
|
||||
vi.mock('../../../components/loading-ellipsis/loading-ellipsis', () => ({
|
||||
default: ({ string }: { string: string }) => createElement('div', { 'data-testid': 'loading-ellipsis' }, string),
|
||||
}));
|
||||
|
||||
vi.mock('../../../components/tooltip', () => ({
|
||||
vi.mock('../../../components/tooltip/tooltip', () => ({
|
||||
default: ({ children }: { children: React.ReactNode }) => createElement(React.Fragment, {}, children),
|
||||
}));
|
||||
|
||||
@@ -208,6 +212,16 @@ vi.mock('../../post/post', () => ({
|
||||
let container: HTMLDivElement;
|
||||
let root: Root;
|
||||
|
||||
const ModQueueWithLeaveButton = () => {
|
||||
const navigate = useNavigate();
|
||||
return createElement(
|
||||
React.Fragment,
|
||||
{},
|
||||
createElement('button', { type: 'button', 'data-testid': 'leave-route', onClick: () => navigate('/other') }, 'leave'),
|
||||
createElement(ModQueueView),
|
||||
);
|
||||
};
|
||||
|
||||
const renderModQueue = async () => {
|
||||
await act(async () => {
|
||||
root.render(
|
||||
@@ -220,15 +234,36 @@ const renderModQueue = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const renderModQueueWithOtherRoute = async () => {
|
||||
await act(async () => {
|
||||
root.render(
|
||||
createElement(
|
||||
MemoryRouter,
|
||||
{ initialEntries: ['/mod/queue'] },
|
||||
createElement(
|
||||
Routes,
|
||||
{},
|
||||
createElement(Route, { path: '/mod/queue', element: createElement(ModQueueWithLeaveButton) }),
|
||||
createElement(Route, {
|
||||
path: '/other',
|
||||
element: createElement('div', {}, createElement(Link, { to: '/mod/queue' }, 'queue'), createElement('span', {}, 'other route')),
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
describe('ModQueueView', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
testState.accountCommunityAddresses = ['music-posting.eth'];
|
||||
testState.directories = [{ address: 'music-posting.eth', directoryCode: 'mu', title: '/mu/ - Music' }];
|
||||
testState.dismissedCommentCids = [];
|
||||
testState.feed = [];
|
||||
testState.hasMore = false;
|
||||
testState.isMobile = false;
|
||||
testState.queuedCommentHistory = [];
|
||||
testState.selectedBoardFilter = null;
|
||||
testState.viewMode = 'compact';
|
||||
|
||||
container = document.createElement('div');
|
||||
@@ -265,6 +300,58 @@ describe('ModQueueView', () => {
|
||||
expect(text.indexOf('No.')).toBeLessThan(text.indexOf('queue_is_empty'));
|
||||
});
|
||||
|
||||
it('resets the board summary selection to all after leaving and returning to the route', async () => {
|
||||
testState.accountCommunityAddresses = ['music-posting.eth', 'sports-posting.eth'];
|
||||
testState.directories = [
|
||||
{ address: 'music-posting.eth', directoryCode: 'mu', title: '/mu/ - Music' },
|
||||
{ address: 'sports-posting.eth', directoryCode: 'sp', title: '/sp/ - Sports' },
|
||||
];
|
||||
testState.feed = [
|
||||
{
|
||||
cid: 'music-pending',
|
||||
communityAddress: 'music-posting.eth',
|
||||
content: 'music pending body',
|
||||
pendingApproval: true,
|
||||
timestamp: 90_000,
|
||||
},
|
||||
{
|
||||
cid: 'sports-pending',
|
||||
communityAddress: 'sports-posting.eth',
|
||||
content: 'sports pending body',
|
||||
pendingApproval: true,
|
||||
timestamp: 90_000,
|
||||
},
|
||||
];
|
||||
|
||||
await renderModQueueWithOtherRoute();
|
||||
|
||||
const musicButton = Array.from(container.querySelectorAll<HTMLButtonElement>('button')).find((button) => button.textContent?.includes('mu'));
|
||||
expect(musicButton).toBeTruthy();
|
||||
|
||||
await act(async () => {
|
||||
musicButton?.click();
|
||||
});
|
||||
|
||||
expect(Array.from(container.querySelectorAll<HTMLButtonElement>('button')).find((button) => button.textContent?.includes('mu'))?.className).toContain(
|
||||
'boardSummaryLinkSelected',
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
container.querySelector<HTMLButtonElement>('[data-testid="leave-route"]')?.click();
|
||||
});
|
||||
expect(container.textContent).toContain('other route');
|
||||
|
||||
await act(async () => {
|
||||
container.querySelector<HTMLAnchorElement>('a[href="/mod/queue"]')?.click();
|
||||
});
|
||||
|
||||
const allButton = Array.from(container.querySelectorAll<HTMLButtonElement>('button')).find((button) => button.textContent?.includes('all'));
|
||||
expect(allButton?.className).toContain('boardSummaryLinkSelected');
|
||||
expect(Array.from(container.querySelectorAll<HTMLButtonElement>('button')).find((button) => button.textContent?.includes('mu'))?.className).not.toContain(
|
||||
'boardSummaryLinkSelected',
|
||||
);
|
||||
});
|
||||
|
||||
it('opens a full floating post preview from a compact excerpt hover', async () => {
|
||||
testState.feed = [
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Virtuoso } from 'react-virtuoso';
|
||||
import styles from './mod-queue.module.css';
|
||||
import postStyles from '../post/post.module.css';
|
||||
import useModQueueStore from '../../stores/use-mod-queue-store';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis/loading-ellipsis';
|
||||
import ErrorDisplay from '../../components/error-display/error-display';
|
||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||
import { getCommunityAddress, getBoardPath, areSameBoardAddress } from '../../lib/utils/route-utils';
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
getVisibleQueuedCommentHistory,
|
||||
shouldKeepQueuedCommentHistory,
|
||||
} from '../../lib/utils/mod-queue-utils';
|
||||
import Tooltip from '../../components/tooltip';
|
||||
import Tooltip from '../../components/tooltip/tooltip';
|
||||
import { useCommunityIdentifier, useCommunityIdentifiers } from '../../hooks/use-community-identifiers';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import { useCurrentTime } from '../../hooks/use-current-time';
|
||||
@@ -48,7 +48,7 @@ import { Post } from '../post/post';
|
||||
import { canAccessBoardModQueue, hasModQueueAccessRole } from '../../lib/utils/mod-access';
|
||||
import capitalize from 'lodash/capitalize';
|
||||
import lowerCase from 'lodash/lowerCase';
|
||||
import { PageFooterDesktop, PageFooterMobile, StyleOnlyFooterFirstRow } from '../../components/footer';
|
||||
import { PageFooterDesktop, PageFooterMobile, StyleOnlyFooterFirstRow } from '../../components/footer/footer';
|
||||
import footerStyles from '../../components/footer/footer.module.css';
|
||||
import { useModeratedCommunityAddresses } from '../../hooks/use-moderated-community-addresses';
|
||||
|
||||
@@ -263,15 +263,7 @@ const ModQueueExcerptPreviewLink = ({ comment, excerpt, postUrl, postUrlState }:
|
||||
{excerpt}
|
||||
</Link>
|
||||
) : (
|
||||
<span
|
||||
title={excerpt}
|
||||
ref={setReferenceNode}
|
||||
tabIndex={0}
|
||||
onMouseEnter={openPreview}
|
||||
onFocus={openPreview}
|
||||
onMouseLeave={closePreview}
|
||||
onBlur={closePreview}
|
||||
>
|
||||
<span title={excerpt} ref={setReferenceNode} tabIndex={0} onMouseEnter={openPreview} onFocus={openPreview} onMouseLeave={closePreview} onBlur={closePreview}>
|
||||
{excerpt}
|
||||
</span>
|
||||
)}
|
||||
@@ -687,6 +679,8 @@ interface ModQueueBoardSummaryProps {
|
||||
feed: Comment[];
|
||||
directories: DirectoryCommunity[];
|
||||
accountCommunityAddresses: string[];
|
||||
selectedBoardFilter: string | null;
|
||||
setSelectedBoardFilter: (boardAddress: string | null) => void;
|
||||
}
|
||||
|
||||
const ModQueueBoardCount = ({ normal, urgent }: { normal: number; urgent: number }) => {
|
||||
@@ -710,10 +704,8 @@ const ModQueueBoardCount = ({ normal, urgent }: { normal: number; urgent: number
|
||||
);
|
||||
};
|
||||
|
||||
const ModQueueBoardSummary = ({ feed, directories, accountCommunityAddresses }: ModQueueBoardSummaryProps) => {
|
||||
const ModQueueBoardSummary = ({ feed, directories, accountCommunityAddresses, selectedBoardFilter, setSelectedBoardFilter }: ModQueueBoardSummaryProps) => {
|
||||
const { t } = useTranslation();
|
||||
const selectedBoardFilter = useModQueueStore((state) => state.selectedBoardFilter);
|
||||
const setSelectedBoardFilter = useModQueueStore((state) => state.setSelectedBoardFilter);
|
||||
const getAlertThresholdSeconds = useModQueueStore((state) => state.getAlertThresholdSeconds);
|
||||
const currentTime = useCurrentTime();
|
||||
const alertThresholdSeconds = getAlertThresholdSeconds();
|
||||
@@ -936,7 +928,7 @@ export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProp
|
||||
const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProps) => {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
const selectedBoardFilter = useModQueueStore((state) => state.selectedBoardFilter);
|
||||
const [selectedBoardFilter, setSelectedBoardFilter] = useState<string | null>(null);
|
||||
const viewMode = useModQueueStore((state) => state.viewMode);
|
||||
const dismissedCommentCids = useModQueueStore((state) => state.dismissedCommentCids);
|
||||
const queuedCommentHistory = useModQueueStore((state) => state.queuedCommentHistory);
|
||||
@@ -1098,7 +1090,13 @@ const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProp
|
||||
{!resolvedAddress && (
|
||||
<div className={styles.controls}>
|
||||
<div className={styles.controlsLeft}>
|
||||
<ModQueueBoardSummary feed={feed} directories={directories} accountCommunityAddresses={accountCommunityAddresses} />
|
||||
<ModQueueBoardSummary
|
||||
feed={feed}
|
||||
directories={directories}
|
||||
accountCommunityAddresses={accountCommunityAddresses}
|
||||
selectedBoardFilter={selectedBoardFilter}
|
||||
setSelectedBoardFilter={setSelectedBoardFilter}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user