Skip to content

feat(bookTree): add Redux state handlers for Drupal book trees

Liam O'Toole requested to merge feature/add-book-state-handlers into master

MR: feat(RecursiveMenu): add book tree support with isBookMenu prop

Branch: feature/add-book-state-handlers | Base: master | Generated: 2026-04-15 09:45


Summary

Add Drupal book tree support to RecursiveMenu via new isBookMenu and bookId optional props. When active, the component swaps the menu data provider (<Menu>) for a new <BookMenu> that reads from the book tree Redux state instead. The rendering pipeline remains identical — all existing menu functionality (collapsible, max menu, breadcrumb mode) works seamlessly with books. Fully backwards compatible.

Changes

  • Types: RecursiveMenuProps now uses a discriminated union (RecursiveMenuStandardProps | RecursiveMenuBookProps) to enforce type safety — menuId is required when isBookMenu is absent; bookId is required when isBookMenu: true. RecursiveMenuMaxMenuProps updated to use type intersection.
  • RecursiveMenuRenderer: Destructures isBookMenu, menuId, bookId at top level; adds runtime guard for missing bookId when isBookMenu=true; uses in_active_trail from Drupal API for book menu trail detection (vs. URL prefix matching for standard menus); conditional render of <BookMenu bookId={...}> or <Menu id={...}>.
  • BookMenu component: New functional component (src/Drupal/Entity/BookMenu/index.tsx) dispatches bookGetCurrentTree(bookId) on mount; reads state.bookTree.trees[bookId] and error state; implements render-prop pattern matching Menu contract.
  • Exports: BookMenu added to src/index.ts as a public component.
  • Stories: Added BookMenu story with mock Redux store and Drupal book tree test data.
  • Book state (prior commits): Redux actions (bookGetTree, bookGetCurrentTree) and reducer already in place; types expanded with BookTreeLink, BookTreeRouteParameters, BookTreeMetaData.
  • Project config: CLAUDE.md created documenting ecosystem, patterns, and TypeScript rules.

How to Test

  • ESLint: yarn eslint src/Components/RecursiveMenu/ — 0 errors on the component files
  • Standard menu (unchanged): Open Storybook (yarn start) → "Molecule/RecursiveMenu" → verify all existing stories (Default, WithIcon, CollapsibleMenu, MaxMenu variants) render and behave identically - Does not pass due to outstanding issues which are not in the scope of this MR
  • Book menu rendering: In Storybook, navigate to "Molecule/RecursiveMenu" → "BookMenu" story → verify the menu tree renders with correct hierarchy (root + 2 leaf nodes)
  • Type safety: Try passing both menuId and bookId in TypeScript — should error. Try isBookMenu={true} without bookId — should error.
  • Active trail: On the BookMenu story, the tree should use in_active_trail from the API (mocked to false in story data) for ancestor highlighting, not URL matching.

Review Guide

Suggested reading order:

  1. src/Components/RecursiveMenu/types.ts — Type changes: discriminated union definition (clearest expression of the new API contract)
  2. src/Components/RecursiveMenu/index.tsx — Renderer: prop destructuring, conditional data provider, trail logic
  3. src/Drupal/Entity/BookMenu/index.tsx — New component: data fetching pattern and render props
  4. src/index.ts — Export addition (straightforward)
  5. src/Components/RecursiveMenu/stories.jsx — Story: mock Redux setup and usage example

Key implementation detail: Active trail detection switches on isBookMenu — book menus use in_active_trail from the API, standard menus derive it from URL prefix matching.

Edited by Liam O'Toole

Merge request reports

Loading