На связи техредакция RuStore. В прошлой статье мы рассказали о миграции документации RuStore. Мы продолжаем исследовать возможности Docusaurus и знакомить вас с ними. Сегодня обсудим настройку навигационного меню в Docusaurus. С ростом объёма документации вручную контролировать навигацию становится сложно, поэтому мы решили перейти на автоматическое меню. В статье расскажем о настройке автоматического навигационного меню.

С чего начали

С самого начала документация была разделена на четыре больших раздела. Мы пишем в MDX, один файл — одна страница. Каждый из этих разделов имеет своё навигационное меню в левой части сайта.

Навигационное меню раздела SDK
Навигационное меню раздела SDK

Навигационное меню настраивается в системном файле движка документации — оно может быть собрано вручную или автоматически. Изначально было решено формировать навигационное меню вручную из абстрактного предположения, что это позволит лучше контролировать содержимое этого самого меню. То есть, в простейшем случае процедура добавления страниц и обновления навигационного меню выглядела следующим образом.

  1. Писатель добавляет страницу (MD/MDX-файл) в структуру документации и заполняет её нужным содержимым.

  2. Добавив страницу, писатель указывает ссылку на эту страницу в файле навигационного меню.

Строка добавленной страницы выглядит так: 'sdk/rustore-geo/general/intro',

После работы над страницей нужно сделать ещё одно простое действие: добавить страницу в меню навигации. Тем более, если при добавлении была допущена ошибка в синтаксисе или неверно указан путь/имя файла, движок не позволит собрать документацию или хотя бы запустить её локально в режиме разработчика (то есть, в режиме моментального отображения вносимых изменений). Автоматический предохранитель. Отладка на лету, а главное — меню навигации легко контролируется.

И, конечно же, едва ли объёмная документация может ограничиться одноуровневым списком страниц — так или иначе есть необходимость в категориях. (Здесь и далее под категориями подразумеваются раскрывающиеся списки в меню навигации.) Эти категории содержат ссылки на страницы (например: описания для конкретных версий SDK) и могут содержать подкатегории. Понятно, что на практике вряд ли кто-то будет без особой необходимости делать навигацию глубже второго, реже — третьего, уровня, но бывает всякое.

В зависимости от настроек при выборе категории может отображаться явно указанная кастомная страница (из MD/MDX-файла исходника) или динамически созданная страница, включающая общие сведения о содержимом категории (вложенных страницах и подкатегориях). В служебном файле меню навигации, куда нужно вносить изменения вручную, два этих типа категорий оформляются с незначительными различиями.

Код для кастомной страницы в ручном меню навигации:

type: 'category',
label: 'Начало работы',
link: {
  type: 'doc',
  id: 'users/start',
},
Кастомная страница категории
Кастомная страница категории

Код динамической страницы категории в ручном меню навигации:

type: 'category',
label: 'Как установить сборку',
link: {
  type: 'generated-index',
  slug: 'users/start/install-on-tv',
},
Динамическая страница категории
Динамическая страница категории

Синтаксис для создания категории легко запоминается, также можно сохранить структуру в отдельном файле или встроить в какой-нибудь макрос и повесить на сочетание клавиш. Новый сотрудник легко сориентируется на уже существующих примерах, как создать тот или иной тип категории (нет).

Всё-таки автомат

Долго ли, коротко ли размер ручной и хорошо контролируемой навигации вырос до 1364 (тысячи трёхсот шестидесяти четырёх) строк. Конечно, в это число входили строки, скрытые комментированием — а вдруг пригодится. Закомментированы могли быть также ссылки на страницы, находящиеся в разработке — чтобы впоследствии добавить их в меню разом, убрав синтаксис комментария. Главное, не забыть это сделать. Но основной прирост был за счёт пополнения документации по задачам от продуктологов и разработки. Необходимость изменения где-нибудь в середине списка навигации требовала от писателя поупражняться в скроллинге, цепко вглядываясь в штабеля строк, чтобы не прокрутить мимо нужного раздела.

Итого:

  • писатель после работы над страницей должен не забыть указать её в файле навигации вручную;

  • если создаётся категория, писатель должен оформить нужный тип этой категории и наполнить её содержимым;

  • всегда нужно помнить про пути и синтаксис — любая ошибка блокирует сборку всей документации (движок подсказывает, где ошибка, но это всё равно какое-то время на исправление);

  • писатель должен более или менее ориентироваться в более чем тысяче строк файла навигации;

  • то есть, помимо собственно работы с содержимым страницы, которое будет читать конечный пользователь документации, писатель также должен учитывать необходимость добавления этой страницы в меню.

  • разработчики также пополняют документы — визу на merge ставят, конечно, писатели, но это дополнительное время на проверку.

По совокупности причин было принято решение формировать меню навигации автоматически. Для этих целей была использована «хулиганская» ветка в репозитории, созданная ранее для экспериментов и получения ответов на постоянно возникающие вопросы, а что будет, если. (Напомню, документация недавно переехала, и движок для нас был новый.)

Что нужно сделать

Сначала мы просто заменили ручное оглавление автоматическим — получилось вот что.

Автоматическое меню навигации без дополнительных настроек
Автоматическое меню навигации без дополнительных настроек

То есть, автоматическая навигация повторяет файловую структуру раздела, при этом:

  • категории не имеют собственных страниц;

  • в качестве названия категорий отображается имя папки в файловой системе движка;

  • категории и страницы отображаются в алфавитном порядке, при этом:

    • за основу берётся название папки или файла в файловой системе движка;

    • символы верхнего регистра идут раньше символов нижнего регистра;

    • порядок отображения не зависит от типа элемента меню — категория или страница;

  • названия страниц в меню берётся из заголовка страницы.

Навели резкость — проблема предстала в чётком разрешении, разобраться нужно было в следующем:

  • как сделать, чтобы категории имели собственные страницы;

  • как контролировать порядок отображения пунктов меню навигации (категорий и отдельных страниц);

  • как задавать названия элементов навигации, в частности — категорий (про страницы было более или менее понятно).

Всё это нужно, чтобы сторонний наблюдатель не заметил изменения после перехода на автоматическое меню навигации. Чтобы сделать «как было», избавившись при этом от недостатков навигации, собранной вручную.

Теория и практика

Мы принялись подробно раскуривать мануал движка документации.

Документация движка устроена мозаично, и нужные сведения рассыпаны по разделам зачастую без очевидной связи. Кроме того, заметна кропотливая работа тамошнего SEO: различные формулировки запросов в поисковике приводили на одни и те же много раз просмотренные страницы, которые не давали никаких новых сведений, но, видимо, исправно накручивали счётчик просмотров. Конечно, вопрос настройки меню навигации можно считать частным кейсом, но в любом случае весь набор сведений, доступный в документации, был неполным.

Поэтому, как говорил классик: практика — критерий истины. И хорошо, когда есть кошки, на которых можно тренироваться. Это про ветку в репозитории для экспериментов. Сведения из документации нужно было проверять, а лакуны заполнять обоснованными интуицией и здравым смыслом догадками, которые тоже проверять. Так что опытным путём мы выявили интересующие нас закономерности и способы решения упомянутых практических задач. Об этом по порядку ниже.

Как создавать страницы категорий

Напомню, в ручном меню навигации страницы категорий были двух типов:

  • динамические страницы категорий — отображают сведения о содержимом категории (страницы, подкатегории);

  • кастомные страницы категорий — для создания кастомных страниц используются конкретные MD/MDX-файлы документации со своим содержимым.

Страницы категорий генерируются движком на основе метаданных категории. Метаданные указываются в файле _category_.json, который нужно разместить в папке категории. Например, у нас есть следующая файловая структура документации:

/
├── tower
│   ├── genie.mdx
│   ├── giant.mdx
│   └── naga.mdx
└── nomad.mdx

В приведённом примере есть одна папка (категория) tower, содержащая страницы, и файл nomad.mdx, который будет отображаться в меню навигации как отдельный пункт на одном уровне с категорией tower.

Итак, чтобы добавить метаданные для tower, нужно в эту папку добавить файл _category_.json:

/
├── tower
│   ├── category.json
│   ├── genie.mdx
│   ├── naga.mdx
│   └── giant.mdx
└── nomad.mdx

Файл _category_.json в зависимости от желаемого типа категории должен иметь следующие минимально необходимые настройки.

Динамическая страница категории

"type": "generated-index" определяет, что будет сформирована динамическая страница.

Кастомная страница категории

{
  "link": {
	"type": "doc",
	"id": "autogenerated/tower/naga"
  }
}
  • "type": "doc" указывает, что в для создания страницы категории будет использован конкретный файл.

  • "id" — идентификатор файла, его можно указывать различными способами, здесь: путь от корня сайта с указанием имени файла без расширения.

При этом:

  • в качестве кастомной страницы можно указать вообще любой файл из файловой структуры документации, даже находящийся не в этой категории;

  • если указать файл, находящийся в этой категории, он будет отображаться на уровне категории (в данном случае на первом уровне меню навигации) и не будет отображаться в раскрывающемся списке как подпункт, то есть не будет дублирования элемента меню.

Как задавать названия элементов навигации

Со страницами категорий разобрались — пришёл черёд отображаемых названий элементов навигационного меню. Тут всё зависит от того, какой элемент отображается:

  • отдельная страница;

  • динамическая страница категории;

  • кастомная страница категории.

Отдельная страница

Как мы поняли выше при первом включении автоматической навигации, в качестве названия элемента этой самой навигации отображается заголовок страницы. В Markdown заголовок задаётся решёткой (#). Однако MD/MDX-исходник страницы также может содержать метаданные (front matter), в которых можно указать параметры:

  • sidebar_label — имя, отображаемое в меню навигации;

  • title — заголовок страницы;

  • id — идентификатор страницы, который переопределяет имя файла-исходника в ссылках.

Кроме того, страница может вообще не иметь заголовка. Возьмём для примера исходный файл nomad.mdx со следующим содержимым:

---
sidebar_label: Nomad (sidebar label)
title: Nomad (title)
id: Nomad Id
---

# Nomad (heading lvl 1)

Lorem ipsum dolor sit amet...

## Heading lvl 2

Lorem ipsum dolor sit amet...

Движок для отображения элемента меню навигации использует следующий приоритет (от высшего к низшему).

Источник отображаемого имени

Расположение

Как отображается в меню навигации

sidebar_label

Метаданные страницы

Nomad (sidebar label)

title

Метаданные страницы

Nomad (title)

Заголовок первого уровня #

Тело страницы

Nomad (heading lvl 1)

id

Метаданные страницы

Nomad Id

Имя файла без расширения .md/.mdx

Файловая система

nomad

Заголовки Markdown второго и уровня и глубже не учитываются.

Динамическая страница категории

Тут значительно проще. Для отображения названия категории используется значение параметра label в метаданных категории (файл _category_.json):

{ 
  "label": Tower (category label),
    "link": {
      "type": "generated-index"
    }
}

Если этот параметр не указан, используется название папки в файловой структуре документации. То есть приоритет следующий:

Источник отображаемого имени

Расположение

Как отображается в меню навигации

label

метаданные категории

Tower (label)

Имя папки

файловая система

tower

Кастомная страница категории

Как можно предположить, в этом случае имя категории отображается на основе комбинации двух предыдущих правил. Метаданные категории, расположенной в папке tower:

{
  "label": Naga (category label),
  "link": {
    "type": "doc",
      "id": "Naga Id"
  }
}

Содержимое страницы naga.mdx, которая используется в качестве страницы категории:

---
sidebar_label: Naga (sidebar label)
title: Naga (title)
id: Naga Id
---

# Naga (heading lvl 1)

# Naga

Lorem ipsum dolor sit amet...

Обратите внимание! В параметре id метаданных категории (_category_.json) указано значение, заданное в id метаданных файла naga.mdx.

Ниже представлена таблица приоритетов источника отображаемого имени категории.

Источник отображаемого имени

Расположение

Как отображается в меню навигации

label

метаданные категории

Naga (category label)

sidebar_label

метаданные страницы

Naga (sidebar label)

title

метаданные страницы

Naga (title)

Заголовок первого уровня #

тело страницы

Naga (heading lvl 1)

id

метаданные страницы/категории

Naga Id

Имя файла без расширения .md/.mdx.

файловая система

naga

Как контролировать порядок отображения элементов навигации

Как упомянуто ранее, по умолчанию без дополнительных настроек движок располагает страницы и категории в алфавитном порядке имён файлов и папок, при этом символы в верхнем регистре сортируются раньше символов в нижнем регистре. То есть все файлы/папки, имена которых начинается с символа в верхнем регистре в меню отображаются выше всех файлов/папок, имена которых начинается с символа в нижнем регистре. Страницы сортируются наравне с категориями (см. ниже).

Файловая структура:

/
├── fortress
│   ├── basilisk.mdx
│   ├── gorgon.mdx
│   └── Hydra.mdx
├── Stronghold
│   ├── behemoth.mdx
│   ├── Goblin.mdx
│   └── Ogre.mdx
├── nomad.mdx
├── sharpshooter.mdx
└── Troll.mdx

Порядок отображения в навигационном меню:

  • Stronghold

    • Goblin

    • Ogre

    • behemoth

  • Troll

  • fortress

    • Hydra

    • basilisk

    • gorgon

  • nomad

  • sharpshooter

Так как же контролировать порядок отображения пунктов? В движке предусмотрена знакомая многим система «развесовки» элементов меню навигации. Так, страница/категория с «весом» 1 располагается в меню выше страницы/категории с «весом» 2 — легко усваиваемый принцип. И хотя, конечно, к моменту перевода в автоматический режим навигационное меню устаканилось, всё же нужно было предусмотреть вероятность, что в середину меню нужно будет вклинить новый пункт. Поэтому после небольшого мозгового штурма было принято решение использовать «веса» 10, 20, 30 и так далее.

Так, чтобы в конец навигационного меню новый пункт, нужно добавить +10 «веса» от предыдущего. Если нужно вставить пункт посередине (скажем, между 40 и 50) — следует использовать значение 45, между 40 и 45 — использовать 43 и т. п.

А что если появится необходимость расширить меню ещё больше? Или повесить пункт выше первого? В документации движка явно не указано, но движок позволяет использовать:

  • ноль;

  • отрицательные значения;

  • десятичные дроби с точкой в качестве разделителя.

Забегая вперёд, скажу, что отрицательные значения и дроби потом пригодятся.

Таким образом, при использовании смешанной сортировки применяется следующая приоритизация:

  • сортировка по явно указанным «весам» элемента меню навигации;

  • алфавитная сортировка по именам файлов/папок — символы верхнего регистра;

  • алфавитная сортировка — символы нижнего регистра.

Таким образом, любой элемент меню навигации с явно указанным «весом» будет выше любого элемента, где этот «вес» не задан. Параметр «веса» зависит от того, для чего он выставляется: для страницы или для категории:

  • в метаданных страницы используется параметр sidebar_position;

  • в метаданных категории используется параметр position.

Подробнее см. ниже.

Отдельная страница

«Вес» страницы указывается в её метаданных:

---
sidebar_position: 10
---

Ниже представлен приоритет сортировки:

Критерий

Расположение

sidebar_position

метаданные страницы

Имя файла, начинающееся с символа в верхнем регистре.

файловая система

Имя файла, начинающееся с символа в нижнем регистре

файловая система

Динамическая страница категории

Пример метаданных категории (_category_.json):

{
  position: 20,
  "label": Tower (category label),
  "link": {
     "type": "generated-index"
     }
}

Ниже представлен приоритет сортировки:

Критерий

Расположение

position

метаданные категории

Имя папки категории, начинающееся с символа в верхнем регистре.

файловая система

Имя папки категории, начинающееся с символа в нижнем регистре.

файловая система

Кастомная страница категории

Здесь, как и в случае с наименованием страницы, позиция может определяться в метаданных категории или в метаданных страницы. Пример метаданных категории (_category_.json):

{
  position: 30,
  "label": Naga (category label),
  "link": {
    "type": "doc",
    "id": "Naga Id"
    }
}

Пример метаданных кастомной страницы категории:

---
sidebar_position: 0
id: Naga Id
---

Ниже представлен приоритет сортировки:

Критерий

Расположение

position

метаданные категории

sidebar_position

метаданные страницы

Имя папки категории, начинающееся с символа в верхнем регистре.

файловая система

Имя папки категории, начинающееся с символа в нижнем регистре

файловая система

Обратите внимание!

  • Если удалить position из метаданных категории (_category_.json), то эта категория с третьего места встанет выше всех категорий, где указан положительный «вес» — т. к. 0 предшествует значениям 10 и 20.

  • Если убрать «вес» и в категории, и на странице, то для алфавитной сортировки учитывается имя папки категории — имя файла страницы не учитывается.

Что в итоге вышло

Итак, вроде бы, всё настроено: названия элементов меню отображаются как положено и расположены в нужном порядке. Категории имеют свои страницы — «как было». По идее, можно аккуратно подменить ручное меню автоматическим и жить счастливо. Однако, как это часто бывает, потребовались дополнительные действия. Об этом, а также общие соображения касательно автоматической навигации — в следующей части статьи.

Комментарии (1)


  1. VLDCORP
    07.08.2024 18:35

    Здравствуйте, а не хотите сделать доклад на TechWriter Days?