Skip to content

Commit d9c4034

Browse files
committed
feat: Add support for clickable top-level links with submenus
Allow dropdown items to have both a clickable URL and a submenu. Previously, items with submenus could not be clicked directly. New behavior: - With url + submenu: Parent is clickable, hover shows submenu - With submenu only: Parent not clickable, hover shows submenu - With url only: Simple clickable link (existing behavior) Examples: # Clickable parent with submenu - text: "All Docs" url: "/docs/" submenu: - text: "Guide" url: "/docs/guide/" # Non-clickable parent with submenu (existing behavior) - text: "Documentation" submenu: - text: "Guide" url: "/guide/" Changes: - Updated template to check for url on submenu items - Updated README.md with new examples - Updated USAGE.md with detailed documentation - Added DOCKER_SETUP.md for Docker integration docs
1 parent 1547a07 commit d9c4034

File tree

4 files changed

+267
-10
lines changed

4 files changed

+267
-10
lines changed

DOCKER_SETUP.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Docker Setup for mkdocs-header-dropdown
2+
3+
This document explains how the plugin is integrated into Docker images for documentation builds.
4+
5+
## Overview
6+
7+
The plugin is available in two ways:
8+
1. **PyPI package** - `mkdocs-header-dropdown` (published at https://pypi.org/project/mkdocs-header-dropdown/)
9+
2. **Docker image** - Pre-installed in the `ghcr.io/cms-cat/mkdocs-material` image
10+
11+
## Docker Image Integration
12+
13+
### Repository Structure
14+
15+
The Docker image is maintained in: `/home/anovak/code/mkdocs-material`
16+
17+
Key files:
18+
- `Dockerfile` - Builds the image with mkdocs-material and plugins
19+
- `requirements.txt` - Contains `mkdocs-material==9.7.0`
20+
- `requirements-plugins.txt` - Contains all MkDocs plugins including `mkdocs-header-dropdown==0.1.0`
21+
22+
### Plugin Installation in Docker
23+
24+
The plugin is installed via `requirements-plugins.txt`:
25+
26+
```txt
27+
mkdocs-header-dropdown==0.1.0
28+
```
29+
30+
When the Docker image is built:
31+
1. Installs mkdocs-material from `requirements.txt`
32+
2. Installs all plugins from `requirements-plugins.txt` (if `WITH_PLUGINS=true`)
33+
3. The plugin is installed from PyPI automatically
34+
35+
## Using the Docker Image
36+
37+
### Production Use (Remote Image)
38+
39+
The published Docker image is available at `ghcr.io/cms-cat/mkdocs-material`.
40+
41+
Use `serve.sh` in the cat-docs repository:
42+
43+
```bash
44+
./serve.sh [port] [host]
45+
```
46+
47+
This pulls and runs the latest published image.
48+
49+
### Development Use (Local Image)
50+
51+
For testing changes before publishing the Docker image:
52+
53+
1. **Build the local image:**
54+
```bash
55+
cd /home/anovak/code/mkdocs-material
56+
docker build -t mkdocs-material-local:latest .
57+
```
58+
59+
2. **Use the local image:**
60+
```bash
61+
cd /home/anovak/code/cat-docs
62+
./serve-local-docker.sh [port] [host]
63+
```
64+
65+
### Serve Scripts Comparison
66+
67+
| Script | Image Used | Purpose |
68+
|--------|-----------|---------|
69+
| `serve.sh` | `ghcr.io/cms-cat/mkdocs-material` (remote) | Production use with published image |
70+
| `serve-local-docker.sh` | `mkdocs-material-local:latest` (local) | Testing with locally-built image |
71+
| `serve-local.sh` | None (uses local Python) | Development without Docker |
72+
73+
## Updating the Plugin in Docker
74+
75+
When a new version of the plugin is released:
76+
77+
### 1. Update requirements-plugins.txt
78+
79+
```bash
80+
cd /home/anovak/code/mkdocs-material
81+
# Edit requirements-plugins.txt to update version
82+
vim requirements-plugins.txt
83+
# Change: mkdocs-header-dropdown==0.1.0
84+
# To: mkdocs-header-dropdown==0.2.0
85+
```
86+
87+
### 2. Commit the change
88+
89+
```bash
90+
git add requirements-plugins.txt
91+
git commit -m "chore: Update mkdocs-header-dropdown to 0.2.0"
92+
git push
93+
```
94+
95+
### 3. Rebuild and publish the Docker image
96+
97+
This is typically automated via CI/CD when you push to the repository.
98+
99+
If building manually:
100+
```bash
101+
docker build -t ghcr.io/cms-cat/mkdocs-material:latest .
102+
docker push ghcr.io/cms-cat/mkdocs-material:latest
103+
```
104+
105+
### 4. Update cat-docs requirements.txt
106+
107+
```bash
108+
cd /home/anovak/code/cat-docs
109+
# Edit requirements.txt
110+
vim requirements.txt
111+
# Change: mkdocs-header-dropdown~=0.1.0
112+
# To: mkdocs-header-dropdown~=0.2.0
113+
```
114+
115+
## Verification
116+
117+
After building the Docker image, verify the plugin is installed:
118+
119+
```bash
120+
docker run --rm mkdocs-material-local:latest pip list | grep mkdocs-header-dropdown
121+
```
122+
123+
Expected output:
124+
```
125+
mkdocs-header-dropdown 0.1.0
126+
```
127+
128+
## Troubleshooting
129+
130+
### Plugin not found error
131+
132+
If you see:
133+
```
134+
ERROR - Config value 'plugins': The "header-dropdown" plugin is not installed
135+
```
136+
137+
**Solution:** Rebuild the Docker image with the plugin in requirements-plugins.txt
138+
139+
```bash
140+
cd /home/anovak/code/mkdocs-material
141+
docker build -t mkdocs-material-local:latest .
142+
```
143+
144+
### Version mismatch
145+
146+
If the wrong version is installed, check:
147+
1. `requirements-plugins.txt` has the correct version
148+
2. Docker build cache - try `docker build --no-cache`
149+
3. You're using the correct image (local vs remote)
150+
151+
## Related Files
152+
153+
In `mkdocs-material` repository:
154+
- `Dockerfile` - Docker image definition
155+
- `requirements.txt` - MkDocs Material version
156+
- `requirements-plugins.txt` - All plugins including mkdocs-header-dropdown
157+
158+
In `cat-docs` repository:
159+
- `requirements.txt` - Python dependencies (for local development)
160+
- `serve.sh` - Uses remote Docker image
161+
- `serve-local-docker.sh` - Uses local Docker image
162+
- `serve-local.sh` - Uses local Python (no Docker)
163+
- `mkdocs.yml` - MkDocs configuration with plugin settings
164+
165+
## Resources
166+
167+
- Plugin PyPI page: https://pypi.org/project/mkdocs-header-dropdown/
168+
- Plugin GitHub: https://github.com/cms-cat/mkdocs-header-dropdown
169+
- Docker image: ghcr.io/cms-cat/mkdocs-material

README.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ plugins:
148148

149149
## Example: Nested Dropdowns
150150

151-
Create submenus by using `submenu` instead of `url`:
151+
Create submenus by using `submenu`:
152152

153153
```yaml
154154
plugins:
@@ -158,7 +158,7 @@ plugins:
158158
links:
159159
- text: "GitHub"
160160
url: "https://github.com/example"
161-
- text: "Documentation" # This will show an arrow
161+
- text: "Documentation" # Not clickable, shows submenu only
162162
submenu:
163163
- text: "User Guide"
164164
url: "/guide/"
@@ -169,9 +169,30 @@ plugins:
169169
url: "/tutorials/"
170170
```
171171

172-
Nested dropdowns:
172+
### Clickable Top-Level Link with Submenu
173+
174+
You can also make the top-level item clickable by adding a `url`:
175+
176+
```yaml
177+
plugins:
178+
- header-dropdown:
179+
dropdowns:
180+
- title: "Documentation"
181+
links:
182+
- text: "All Docs"
183+
url: "/docs/" # Clicking goes here
184+
target: "_blank"
185+
submenu: # Hovering shows submenu
186+
- text: "User Guide"
187+
url: "/docs/guide/"
188+
- text: "API Reference"
189+
url: "/docs/api/"
190+
```
191+
192+
Nested dropdown features:
173193
- Show an arrow indicator (▶) automatically
174194
- Appear to the right on hover
195+
- Top-level can be clickable (with `url`) or non-clickable (without `url`)
175196
- Support multiple levels of nesting
176197
- Work with keyboard navigation
177198
```

USAGE.md

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,9 @@ Each link in the `links` list supports:
106106
| Field | Type | Required | Description |
107107
|-------|------|----------|-------------|
108108
| `text` | string | Yes | The link text |
109-
| `url` | string | Yes | The target URL (can be relative or absolute) |
109+
| `url` | string | Conditional | The target URL (can be relative or absolute). Required unless `submenu` is provided. Optional when using `submenu` to make the parent clickable. |
110110
| `target` | string | No | HTML target attribute (e.g., `_blank` for new tab) |
111+
| `submenu` | list | No | List of nested links (creates a submenu). See Nested Dropdowns below. |
111112

112113
## Advanced Examples
113114

@@ -171,6 +172,57 @@ plugins:
171172
url: "/api/"
172173
```
173174

175+
### Nested Dropdowns (Submenus)
176+
177+
Create multi-level dropdown menus using the `submenu` field:
178+
179+
```yaml
180+
plugins:
181+
- header-dropdown:
182+
dropdowns:
183+
- title: "Resources"
184+
links:
185+
- text: "GitHub"
186+
url: "https://github.com/example"
187+
- text: "Documentation" # Not clickable, shows submenu on hover
188+
submenu:
189+
- text: "User Guide"
190+
url: "/guide/"
191+
- text: "API Reference"
192+
url: "/api/"
193+
- text: "Tutorials"
194+
url: "/tutorials/"
195+
```
196+
197+
#### Clickable Parent with Submenu
198+
199+
You can make the parent item clickable by adding both `url` and `submenu`:
200+
201+
```yaml
202+
plugins:
203+
- header-dropdown:
204+
dropdowns:
205+
- title: "Documentation"
206+
links:
207+
- text: "All Docs"
208+
url: "/docs/" # Click to go to main docs page
209+
target: "_blank"
210+
submenu: # Hover to see submenu
211+
- text: "User Guide"
212+
url: "/docs/guide/"
213+
- text: "API Reference"
214+
url: "/docs/api/"
215+
- text: "Tutorials"
216+
url: "/docs/tutorials/"
217+
```
218+
219+
Nested dropdown features:
220+
- Arrow indicator (▶) shows automatically for items with submenus
221+
- Submenus appear to the right on hover
222+
- Parent can be clickable (with `url`) or non-clickable (without `url`)
223+
- Supports multiple levels of nesting
224+
- Works with keyboard navigation
225+
174226
## Sharing the Plugin Across Projects
175227

176228
### Method 1: Git Repository

mkdocs_header_dropdown/templates/partials/header-dropdown.html

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,29 @@
2424
style="position: relative;"
2525
onmouseenter="showNestedDropdown(this)"
2626
onmouseleave="hideNestedDropdown(this)">
27-
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 1rem; color: var(--md-default-fg-color); font-size: 0.7rem; cursor: pointer;"
27+
{% if link.url %}
28+
{# Top-level link with submenu - clickable link with arrow #}
29+
<a href="{{ link.url }}"
30+
{% if link.target %}target="{{ link.target }}"{% endif %}
31+
style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 1rem; color: var(--md-default-fg-color); text-decoration: none; font-size: 0.7rem;"
2832
onmouseover="this.style.backgroundColor='var(--md-default-fg-color--lightest)'"
2933
onmouseout="this.style.backgroundColor='transparent'">
30-
<span>{{ link.text }}</span>
31-
<svg style="width: 0.8rem; height: 0.8rem; margin-left: 0.5rem;" viewBox="0 0 24 24" fill="currentColor">
32-
<path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/>
33-
</svg>
34-
</div>
34+
<span>{{ link.text }}</span>
35+
<svg style="width: 0.8rem; height: 0.8rem; margin-left: 0.5rem;" viewBox="0 0 24 24" fill="currentColor">
36+
<path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/>
37+
</svg>
38+
</a>
39+
{% else %}
40+
{# Top-level item with submenu only - not clickable #}
41+
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 1rem; color: var(--md-default-fg-color); font-size: 0.7rem; cursor: pointer;"
42+
onmouseover="this.style.backgroundColor='var(--md-default-fg-color--lightest)'"
43+
onmouseout="this.style.backgroundColor='transparent'">
44+
<span>{{ link.text }}</span>
45+
<svg style="width: 0.8rem; height: 0.8rem; margin-left: 0.5rem;" viewBox="0 0 24 24" fill="currentColor">
46+
<path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/>
47+
</svg>
48+
</div>
49+
{% endif %}
3550
<div class="md-header__dropdown-submenu"
3651
style="display: none; position: absolute; left: 100%; top: 0; background: var(--md-default-bg-color); border: 1px solid var(--md-default-fg-color--lightest); border-radius: 0.1rem; box-shadow: var(--md-shadow-z2); min-width: 200px; z-index: 1001;">
3752
{% for sublink in link.submenu %}

0 commit comments

Comments
 (0)