diff --git a/README.md b/README.md index 3e3eb72..985406f 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,15 @@ If you want to enable or disable collapsing of a single page, without applying t collapse: true ``` +If you want to force the section to collapse even when having multiple children, add the flag `force_collapse`: + +```yaml +collapse: true +force_collapse: true +``` + +Note that setting `collapse: false` will ignore `force_collapse` + ### Hide Directory Create a file named `.pages` in a directory and set the `hide` attribute to `true` to hide the directory, including all sub-pages and sub-sections, from the navigation: diff --git a/mkdocs_awesome_pages_plugin/meta.py b/mkdocs_awesome_pages_plugin/meta.py index 3acc1da..7cfac35 100644 --- a/mkdocs_awesome_pages_plugin/meta.py +++ b/mkdocs_awesome_pages_plugin/meta.py @@ -107,6 +107,7 @@ class Meta: ARRANGE_REST_TOKEN = "..." COLLAPSE_ATTRIBUTE = "collapse" COLLAPSE_SINGLE_PAGES_ATTRIBUTE = "collapse_single_pages" + FORCE_COLLAPSE_ATTRIBUTE = "force_collapse" HIDE_ATTRIBUTE = "hide" ORDER_ATTRIBUTE = "order" SORT_TYPE_ATTRIBUTE = "sort_type" @@ -125,9 +126,10 @@ def __init__( arrange: Optional[List[str]] = None, nav: Optional[List[MetaNavItem]] = None, path: Optional[str] = None, - collapse: bool = None, - collapse_single_pages: bool = None, - hide: bool = None, + collapse: Optional[bool] = None, + collapse_single_pages: Optional[bool] = None, + force_collapse: Optional[bool] = None, + hide: Optional[bool] = None, order: Optional[str] = None, sort_type: Optional[str] = None, order_by: Optional[str] = None, @@ -142,6 +144,7 @@ def __init__( self.path = path self.collapse = collapse self.collapse_single_pages = collapse_single_pages + self.force_collapse = force_collapse self.hide = hide self.order = order self.sort_type = sort_type @@ -172,6 +175,7 @@ def load_from(path: str) -> "Meta": nav = contents.get(Meta.NAV_ATTRIBUTE) collapse = contents.get(Meta.COLLAPSE_ATTRIBUTE) collapse_single_pages = contents.get(Meta.COLLAPSE_SINGLE_PAGES_ATTRIBUTE) + force_collapse = contents.get(Meta.FORCE_COLLAPSE_ATTRIBUTE) hide = contents.get(Meta.HIDE_ATTRIBUTE) order = contents.get(Meta.ORDER_ATTRIBUTE) sort_type = contents.get(Meta.SORT_TYPE_ATTRIBUTE) @@ -232,6 +236,17 @@ def load_from(path: str) -> "Meta": context=path, ) ) + + if force_collapse is not None: + if not isinstance(force_collapse, bool): + raise TypeError( + 'Expected "{attribute}" attribute to be a boolean - got {type} [{context}]'.format( + attribute=Meta.FORCE_COLLAPSE_ATTRIBUTE, + type=type(force_collapse), + context=path, + ) + ) + if hide is not None: if not isinstance(hide, bool): raise TypeError( @@ -277,6 +292,7 @@ def load_from(path: str) -> "Meta": path=path, collapse=collapse, collapse_single_pages=collapse_single_pages, + force_collapse=force_collapse, hide=hide, order=order, sort_type=sort_type, diff --git a/mkdocs_awesome_pages_plugin/navigation.py b/mkdocs_awesome_pages_plugin/navigation.py index ee4f716..39a1e88 100644 --- a/mkdocs_awesome_pages_plugin/navigation.py +++ b/mkdocs_awesome_pages_plugin/navigation.py @@ -77,12 +77,12 @@ def _process_child_sections(self, children: List[NavigationItem], collapse: bool for item in children: if isinstance(item, VirtualSection): item.children = self._process_child_sections(item.children, collapse) + result.append(item) elif isinstance(item, Section): - item = self._process_section(item, collapse) - if item is None: - continue - result.append(item) - + items = self._process_section(item, collapse) + result.extend(items) + else: + result.append(item) return result def _order(self, items: List[NavigationItem], meta: Meta): @@ -168,11 +168,11 @@ def _expand_rest_rec(result: List[Union[NavigationItem, MetaNavRestItem]]): return result - def _process_section(self, section: Section, collapse_recursive: bool) -> Optional[NavigationItem]: + def _process_section(self, section: Section, collapse_recursive: bool) -> List[NavigationItem]: meta = self.meta.sections[section] if meta.hide is True: - return None + return [] if meta.collapse_single_pages is not None: collapse_recursive = meta.collapse_single_pages @@ -182,12 +182,12 @@ def _process_section(self, section: Section, collapse_recursive: bool) -> Option section.children = self._process_children(section.children, collapse_recursive, meta) if section in self.explicit_sections: - return section + return [section] if not section.children: - return None + return [] - return self._collapse(section, meta.collapse, collapse_recursive) + return self._collapse(section, meta.collapse, collapse_recursive, force_collapse=meta.force_collapse) def _get_item_path(self, item: NavigationItem) -> Optional[str]: if isinstance(item, Section): @@ -241,13 +241,17 @@ def _set_title(section: Section, meta: Meta): section.title = meta.title @staticmethod - def _collapse(section: Section, collapse: Optional[bool], collapse_recursive: bool) -> NavigationItem: + def _collapse( + section: Section, collapse: Optional[bool], collapse_recursive: bool, force_collapse: bool = False + ) -> List[NavigationItem]: if collapse is None: collapse = collapse_recursive - if collapse and len(section.children) == 1: - return section.children[0] - return section + if collapse: + if force_collapse or len(section.children) == 1: + return section.children + + return [section] def to_mkdocs(self) -> MkDocsNavigation: pages = get_by_type(self.items, Page) diff --git a/mkdocs_awesome_pages_plugin/tests/e2e/base.py b/mkdocs_awesome_pages_plugin/tests/e2e/base.py index 065cf82..9c02d49 100644 --- a/mkdocs_awesome_pages_plugin/tests/e2e/base.py +++ b/mkdocs_awesome_pages_plugin/tests/e2e/base.py @@ -31,6 +31,7 @@ def pagesFile( nav: Optional[List[Union[str, dict]]] = None, collapse: bool = None, collapse_single_pages: bool = None, + force_collapse: bool = None, hide: bool = None, order: Optional[str] = None, sort_type: Optional[str] = None, @@ -43,6 +44,7 @@ def pagesFile( "nav": nav, "collapse": collapse, "collapse_single_pages": collapse_single_pages, + "force_collapse": force_collapse, "hide": hide, "order": order, "sort_type": sort_type, diff --git a/mkdocs_awesome_pages_plugin/tests/e2e/test_collapse.py b/mkdocs_awesome_pages_plugin/tests/e2e/test_collapse.py index 8e7a1f6..dac4d5e 100644 --- a/mkdocs_awesome_pages_plugin/tests/e2e/test_collapse.py +++ b/mkdocs_awesome_pages_plugin/tests/e2e/test_collapse.py @@ -15,6 +15,38 @@ def test_local(self): self.assertEqual(navigation, [("A", [("C", [("Page", "/a/b/c/page")])])]) + def test_local_multichildren(self): + navigation = self.mkdocs( + self.config, + [("a", [("b", [("c", ["page.md"]), ("d", ["page2.md"]), self.pagesFile(collapse=True)])])], + ) + + self.assertEqual( + navigation, [("A", [("B", [("C", [("Page", "/a/b/c/page")]), ("D", [("Page2", "/a/b/d/page2")])])])] + ) + + def test_local_multichildren_force_collapse(self): + navigation = self.mkdocs( + self.config, + [ + ( + "a", + [ + ( + "b", + [ + ("c", ["page.md"]), + ("d", ["page2.md"]), + self.pagesFile(collapse=True, force_collapse=True), + ], + ) + ], + ) + ], + ) + + self.assertEqual(navigation, [("A", [("C", [("Page", "/a/b/c/page")]), ("D", [("Page2", "/a/b/d/page2")])])]) + def test_local_recursively(self): navigation = self.mkdocs( self.config, @@ -79,6 +111,29 @@ def test_local_with_hide(self): self.assertEqual(navigation, [("A", [("C", [("1", "/a/b/c/1")])])]) + def test_local_with_hide_multichildren_force_collapse(self): + navigation = self.mkdocs( + self.config, + [ + ( + "a", + [ + ( + "b", + [ + ("c", ["1.md"]), + ("d", ["2.md", self.pagesFile(hide=True)]), + ("e", ["3.md"]), + self.pagesFile(collapse=True, force_collapse=True), + ], + ) + ], + ) + ], + ) + + self.assertEqual(navigation, [("A", [("C", [("1", "/a/b/c/1")]), ("E", [("3", "/a/b/e/3")])])]) + class TestCollapseGlobalEnabled(E2ETestCase): def setUp(self): @@ -102,6 +157,15 @@ def test_override_local(self): self.assertEqual(navigation, [("B", [("Page", "/a/b/c/page")])]) + def test_override_local_force_collapse(self): + # force_collapse is only effective if when collapse is active + navigation = self.mkdocs( + self.config, + [("a", [("b", [("c", ["page.md"]), self.pagesFile(collapse=False, force_collapse=True)])])], + ) + + self.assertEqual(navigation, [("B", [("Page", "/a/b/c/page")])]) + def test_override_local_recursively(self): navigation = self.mkdocs( self.config, diff --git a/mkdocs_awesome_pages_plugin/tests/navigation/test_static.py b/mkdocs_awesome_pages_plugin/tests/navigation/test_static.py index 675d3a6..574a168 100644 --- a/mkdocs_awesome_pages_plugin/tests/navigation/test_static.py +++ b/mkdocs_awesome_pages_plugin/tests/navigation/test_static.py @@ -31,45 +31,56 @@ def setUp(self): def test_default(self): self.assertEqual( AwesomeNavigation._collapse(self.parent, collapse=None, collapse_recursive=False), - self.parent, + [self.parent], ) def test_local_false(self): self.assertEqual( AwesomeNavigation._collapse(self.parent, collapse=False, collapse_recursive=False), - self.parent, + [self.parent], ) def test_explicit(self): self.assertEqual( AwesomeNavigation._collapse(self.parent, collapse=True, collapse_recursive=False), - self.child, + [self.child], ) def test_explicit_and_recursive(self): self.assertEqual( AwesomeNavigation._collapse(self.parent, collapse=True, collapse_recursive=True), - self.child, + [self.child], ) def test_recursive(self): self.assertEqual( AwesomeNavigation._collapse(self.parent, collapse=None, collapse_recursive=True), - self.child, + [self.child], ) def test_local_override_false(self): self.assertEqual( AwesomeNavigation._collapse(self.parent, collapse=False, collapse_recursive=True), - self.parent, + [self.parent], ) def test_multiple_children(self): section = Section("Parent", [Section("Child 1", []), Section("Child 2", [])]) - self.assertEqual(AwesomeNavigation._collapse(section, None, False), section) - self.assertEqual(AwesomeNavigation._collapse(section, None, True), section) - self.assertEqual(AwesomeNavigation._collapse(section, False, False), section) - self.assertEqual(AwesomeNavigation._collapse(section, True, False), section) - self.assertEqual(AwesomeNavigation._collapse(section, True, True), section) - self.assertEqual(AwesomeNavigation._collapse(section, False, True), section) + self.assertEqual(AwesomeNavigation._collapse(section, None, False), [section]) + self.assertEqual(AwesomeNavigation._collapse(section, None, True), [section]) + self.assertEqual(AwesomeNavigation._collapse(section, False, False), [section]) + self.assertEqual(AwesomeNavigation._collapse(section, True, False), [section]) + self.assertEqual(AwesomeNavigation._collapse(section, True, True), [section]) + self.assertEqual(AwesomeNavigation._collapse(section, False, True), [section]) + + def test_multiple_children_force_collapse(self): + children = [Section("Child 1", []), Section("Child 2", [])] + section = Section("Parent", children) + + self.assertEqual(AwesomeNavigation._collapse(section, None, False, True), [section]) + self.assertEqual(AwesomeNavigation._collapse(section, None, True, True), children) + self.assertEqual(AwesomeNavigation._collapse(section, False, False, True), [section]) + self.assertEqual(AwesomeNavigation._collapse(section, True, False, True), children) + self.assertEqual(AwesomeNavigation._collapse(section, True, True, True), children) + self.assertEqual(AwesomeNavigation._collapse(section, False, True, True), [section])