♻️(frontend) search on all docs if no children
When searching for documents, if no children are found, the search will now include all documents instead of just those with children.
This commit is contained in:
@@ -16,6 +16,7 @@ and this project adheres to
|
||||
|
||||
### Changed
|
||||
|
||||
- ♻️(frontend) search on all docs if no children #1184
|
||||
- ♻️(frontend) redirect to doc after duplicate #1175
|
||||
- 🔧(project) change env.d system by using local files #1200
|
||||
- ⚡️(frontend) improve tree stability #1207
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { createDoc, randomName, verifyDocName } from './utils-common';
|
||||
import { createRootSubPage } from './utils-sub-pages';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
@@ -98,39 +99,46 @@ test.describe('Document search', () => {
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
test("it checks we don't see filters in search modal", async ({ page }) => {
|
||||
const searchButton = page
|
||||
.getByTestId('left-panel-desktop')
|
||||
.getByRole('button', { name: 'search' });
|
||||
|
||||
await expect(searchButton).toBeVisible();
|
||||
await page.getByRole('button', { name: 'search', exact: true }).click();
|
||||
await expect(
|
||||
page.getByRole('combobox', { name: 'Quick search input' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId('doc-search-filters')).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Sub page search', () => {
|
||||
test('it check the presence of filters in search modal', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
const [doc1Title] = await createDoc(
|
||||
page,
|
||||
'My sub page search',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
await verifyDocName(page, doc1Title);
|
||||
// Doc grid filters are not visible
|
||||
const searchButton = page
|
||||
.getByTestId('left-panel-desktop')
|
||||
.getByRole('button', { name: 'search' });
|
||||
await searchButton.click();
|
||||
.getByRole('button', { name: 'search', exact: true });
|
||||
|
||||
const filters = page.getByTestId('doc-search-filters');
|
||||
|
||||
await searchButton.click();
|
||||
await expect(
|
||||
page.getByRole('combobox', { name: 'Quick search input' }),
|
||||
).toBeVisible();
|
||||
await expect(filters).toBeHidden();
|
||||
|
||||
await page.getByRole('button', { name: 'close' }).click();
|
||||
|
||||
// Create a doc without children for the moment
|
||||
// and check that filters are not visible
|
||||
const [doc1Title] = await createDoc(page, 'My page search', browserName, 1);
|
||||
await verifyDocName(page, doc1Title);
|
||||
|
||||
await searchButton.click();
|
||||
await expect(
|
||||
page.getByRole('combobox', { name: 'Quick search input' }),
|
||||
).toBeVisible();
|
||||
await expect(filters).toBeHidden();
|
||||
|
||||
await page.getByRole('button', { name: 'close' }).click();
|
||||
|
||||
// Create a sub page
|
||||
// and check that filters are visible
|
||||
await createRootSubPage(page, browserName, 'My sub page search');
|
||||
|
||||
await searchButton.click();
|
||||
|
||||
await expect(filters).toBeVisible();
|
||||
|
||||
await filters.click();
|
||||
await filters.getByRole('button', { name: 'Current doc' }).click();
|
||||
await expect(
|
||||
@@ -139,43 +147,70 @@ test.describe('Sub page search', () => {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Current doc' }),
|
||||
).toBeVisible();
|
||||
await page.getByRole('menuitem', { name: 'Current doc' }).click();
|
||||
await page.getByRole('menuitem', { name: 'All docs' }).click();
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Reset' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('it searches sub pages', async ({ page, browserName }) => {
|
||||
await page.goto('/');
|
||||
|
||||
const [doc1Title] = await createDoc(
|
||||
// First doc
|
||||
const [firstDocTitle] = await createDoc(
|
||||
page,
|
||||
'My sub page search',
|
||||
'My first sub page search',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
await verifyDocName(page, doc1Title);
|
||||
await verifyDocName(page, firstDocTitle);
|
||||
|
||||
// Create a new doc - for the moment without children
|
||||
await page.getByRole('button', { name: 'New doc' }).click();
|
||||
await verifyDocName(page, '');
|
||||
await page.getByRole('textbox', { name: 'doc title input' }).click();
|
||||
await page
|
||||
.getByRole('textbox', { name: 'doc title input' })
|
||||
.press('ControlOrMeta+a');
|
||||
const [randomDocName] = randomName('doc-sub-page', browserName, 1);
|
||||
const [secondDocTitle] = randomName(
|
||||
'My second sub page search',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
await page
|
||||
.getByRole('textbox', { name: 'doc title input' })
|
||||
.fill(randomDocName);
|
||||
.fill(secondDocTitle);
|
||||
|
||||
const searchButton = page
|
||||
.getByTestId('left-panel-desktop')
|
||||
.getByRole('button', { name: 'search' });
|
||||
|
||||
await searchButton.click();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Current doc' }),
|
||||
).toBeVisible();
|
||||
await page.getByRole('combobox', { name: 'Quick search input' }).click();
|
||||
await page
|
||||
.getByRole('combobox', { name: 'Quick search input' })
|
||||
.fill('sub');
|
||||
await expect(page.getByLabel(randomDocName)).toBeVisible();
|
||||
.fill('sub page search');
|
||||
|
||||
// Expect to find the first doc
|
||||
await expect(
|
||||
page.getByRole('presentation').getByLabel(firstDocTitle),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('presentation').getByLabel(secondDocTitle),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'close' }).click();
|
||||
|
||||
// Create a sub page
|
||||
await createRootSubPage(page, browserName, secondDocTitle);
|
||||
await searchButton.click();
|
||||
await page
|
||||
.getByRole('combobox', { name: 'Quick search input' })
|
||||
.fill('sub page search');
|
||||
|
||||
// Now there is a sub page - expect to have the focus on the current doc
|
||||
await expect(
|
||||
page.getByRole('presentation').getByLabel(secondDocTitle),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('presentation').getByLabel(firstDocTitle),
|
||||
).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './useCollaboration';
|
||||
export * from './useCopyDocLink';
|
||||
export * from './useDocUtils';
|
||||
export * from './useIsCollaborativeEditable';
|
||||
export * from './useTrans';
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Doc } from '@/docs/doc-management';
|
||||
|
||||
export const useTreeUtils = (doc: Doc) => {
|
||||
export const useDocUtils = (doc: Doc) => {
|
||||
return {
|
||||
isTopRoot: doc.depth === 1,
|
||||
isChild: doc.depth > 1,
|
||||
hasChildren: doc.numchild > 0,
|
||||
isDesynchronized: !!(
|
||||
doc.ancestors_link_reach &&
|
||||
(doc.computed_link_reach !== doc.ancestors_link_reach ||
|
||||
@@ -7,9 +7,9 @@ import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { QuickSearch } from '@/components/quick-search';
|
||||
import { Doc, useDocUtils } from '@/docs/doc-management';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { Doc } from '../../doc-management';
|
||||
import EmptySearchIcon from '../assets/illustration-docs-empty.png';
|
||||
|
||||
import { DocSearchContent } from './DocSearchContent';
|
||||
@@ -20,18 +20,18 @@ import {
|
||||
} from './DocSearchFilters';
|
||||
import { DocSearchSubPageContent } from './DocSearchSubPageContent';
|
||||
|
||||
type DocSearchModalProps = {
|
||||
type DocSearchModalGlobalProps = {
|
||||
onClose: () => void;
|
||||
isOpen: boolean;
|
||||
showFilters?: boolean;
|
||||
defaultFilters?: DocSearchFiltersValues;
|
||||
};
|
||||
|
||||
export const DocSearchModal = ({
|
||||
const DocSearchModalGlobal = ({
|
||||
showFilters = false,
|
||||
defaultFilters,
|
||||
...modalProps
|
||||
}: DocSearchModalProps) => {
|
||||
}: DocSearchModalGlobalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -126,3 +126,42 @@ export const DocSearchModal = ({
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
type DocSearchModalDetailProps = DocSearchModalGlobalProps & {
|
||||
doc: Doc;
|
||||
};
|
||||
|
||||
const DocSearchModalDetail = ({
|
||||
doc,
|
||||
...modalProps
|
||||
}: DocSearchModalDetailProps) => {
|
||||
const { hasChildren, isChild } = useDocUtils(doc);
|
||||
const isWithChildren = isChild || hasChildren;
|
||||
|
||||
let defaultFilters = DocSearchTarget.ALL;
|
||||
let showFilters = false;
|
||||
if (isWithChildren) {
|
||||
defaultFilters = DocSearchTarget.CURRENT;
|
||||
showFilters = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<DocSearchModalGlobal
|
||||
{...modalProps}
|
||||
showFilters={showFilters}
|
||||
defaultFilters={{ target: defaultFilters }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type DocSearchModalProps = DocSearchModalGlobalProps & {
|
||||
doc?: Doc;
|
||||
};
|
||||
|
||||
export const DocSearchModal = ({ doc, ...modalProps }: DocSearchModalProps) => {
|
||||
if (doc) {
|
||||
return <DocSearchModalDetail doc={doc} {...modalProps} />;
|
||||
}
|
||||
|
||||
return <DocSearchModalGlobal {...modalProps} />;
|
||||
};
|
||||
|
||||
@@ -17,9 +17,9 @@ import {
|
||||
LinkReach,
|
||||
LinkRole,
|
||||
getDocLinkReach,
|
||||
useDocUtils,
|
||||
useUpdateDocLink,
|
||||
} from '@/docs/doc-management';
|
||||
import { useTreeUtils } from '@/docs/doc-tree';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { useTranslatedShareSettings } from '../hooks/';
|
||||
@@ -37,7 +37,7 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
|
||||
const canManage = doc.abilities.accesses_manage;
|
||||
const docLinkReach = getDocLinkReach(doc);
|
||||
const docLinkRole = doc.computed_link_role ?? LinkRole.READER;
|
||||
const { isDesynchronized } = useTreeUtils(doc);
|
||||
const { isDesynchronized } = useDocUtils(doc);
|
||||
const { linkModeTranslations, linkReachChoices, linkReachTranslations } =
|
||||
useTranslatedShareSettings();
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './useTreeUtils';
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './api';
|
||||
export * from './components';
|
||||
export * from './hooks';
|
||||
export * from './utils';
|
||||
|
||||
@@ -3,7 +3,8 @@ import { useRouter } from 'next/router';
|
||||
import { PropsWithChildren, useCallback, useState } from 'react';
|
||||
|
||||
import { Box, Icon, SeparatedSection } from '@/components';
|
||||
import { DocSearchModal, DocSearchTarget } from '@/docs/doc-search/';
|
||||
import { useDocStore } from '@/docs/doc-management';
|
||||
import { DocSearchModal } from '@/docs/doc-search/';
|
||||
import { useAuth } from '@/features/auth';
|
||||
import { useCmdK } from '@/hook/useCmdK';
|
||||
|
||||
@@ -12,10 +13,9 @@ import { useLeftPanelStore } from '../stores';
|
||||
import { LeftPanelHeaderButton } from './LeftPanelHeaderButton';
|
||||
|
||||
export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
|
||||
const { currentDoc } = useDocStore();
|
||||
const router = useRouter();
|
||||
const { authenticated } = useAuth();
|
||||
const isDoc = router.pathname === '/docs/[id]';
|
||||
|
||||
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false);
|
||||
|
||||
const openSearchModal = useCallback(() => {
|
||||
@@ -81,10 +81,7 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
|
||||
<DocSearchModal
|
||||
onClose={closeSearchModal}
|
||||
isOpen={isSearchModalOpen}
|
||||
showFilters={isDoc}
|
||||
defaultFilters={{
|
||||
target: isDoc ? DocSearchTarget.CURRENT : undefined,
|
||||
}}
|
||||
doc={currentDoc}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user