Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/skills/writing-docs/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
name: writing-docs
description: Write documentation. Use when asked to write documentation, update docs, or build plugin docs.
---

## Documenting Functions

For plugins that support it (indicated by the presence of a `make_docs.py` file, e.g. `plugins/ui/make_docs.py` or `plugins/plotly-express/make_docs.py`), document functions using the `dhautofunction` directive rather than building any table or description manually.

### Example

````markdown
## API Reference

```{eval-rst}
.. dhautofunction:: deephaven.ui.use_navigate
```
````

Use the fully qualified Python path to the function as the argument to `dhautofunction`. This automatically generates the function signature, parameters, return type, and description from the source docstring.

## Document Structure

Follow this consistent structure when writing docs for components or hooks:

1. **H1 title** — short name of the component or hook
2. **Brief description** — one or two sentences explaining what it does and when to use it
3. **`## Example`** — a minimal, runnable code example
4. **Screenshot** (components only) — `![Alt text](../_assets/component_name.png)`
5. **`## UI recommendations`** (components) or **`## Recommendations`** (hooks) — numbered list of best practices and usage guidance
6. **Additional sections** — more examples showing advanced usage, data sources, variants, etc.
7. **`## API Reference`** — always last, using `dhautofunction`

For plotly-express chart docs, use `## What are X useful for?` with bullet points in place of a recommendations section.

## File Placement

- Component docs: `plugins/ui/docs/components/<component_name>.md`
- Hook docs: `plugins/ui/docs/hooks/<hook_name>.md`
- Plot docs: `plugins/plotly-express/docs/<chart_name>.md`

## Code Block Annotations

Docs use MyST Markdown (`.md` files with embedded RST directives). Python code blocks support two special annotations:

- `order=var1,var2,...` — controls which variables are shown in Deephaven and in what order. Variables prefixed with `_` are hidden (useful for intermediate tables or setup code that shouldn't be displayed).
- `skip-test` — excludes the code block from automated testing (use sparingly, e.g. for pseudocode or illustrative snippets).

Example:

````markdown
```python order=line_plot,my_table
import deephaven.plot.express as dx
my_table = dx.data.stocks()
line_plot = dx.line(my_table, x="Timestamp", y="Price")
```
````

## Cross-References

Link to other docs using relative markdown paths:

```markdown
Consider using [`action_button`](./action_button.md) for task-based actions.
```

## Screenshots

Component screenshots are stored in `plugins/ui/docs/_assets/` and named descriptively (e.g. `button_basic.png`). Reference them with a relative path from the component doc:

```markdown
![Button Basic Example](../_assets/button_basic.png)
```
36 changes: 35 additions & 1 deletion plugins/ui/docs/components/link.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Link

Links allow users to navigate to a specified location.
Links allow users to navigate to a specified location. Use `href` for external URLs or full page reloads, and `to` for single-page application (SPA) navigation within Deephaven.

## Example

Expand Down Expand Up @@ -86,6 +86,40 @@ my_link_is_quiet_example = ui.text(
)
```

## SPA Navigation with `to`

The `to` prop enables single-page application (SPA) navigation within Deephaven. It is mutually exclusive with `href` (which triggers a full page reload but can navigate to any URL).

`to` accepts either a string (parsed for path, query params, and fragment) or a `NavigationTarget` dict for explicit control.

> [!NOTE]
> Deephaven and all custom components share the path. Avoid using routers, the path, path parameters, and navigation in shared components to prevent conflicts. Do not use the route segment `/-/` in your application path as it is reserved for internal use by Deephaven.

```python
from deephaven import ui


@ui.component
def nav():
return ui.flex(
# Simple string to the widget homepage
ui.link("Home", to="/"),
# Simple string to a custom path
ui.link("Search", to="/search?q=hello#results"),
# Dict form for explicit control
ui.link("Users", to={"path": "/users", "query_params": {"sort": "name"}}),
direction="column",
)


my_nav = nav()
```

## Navigation Recommendations

1. Use `to` for navigation within a Deephaven widget and `href` for external URLs. Do not use both on the same link.
2. For programmatic navigation triggered by events or side effects, use [`use_navigate`](../hooks/use_navigate.md) instead of a link.

## API Reference

```{eval-rst}
Expand Down
150 changes: 150 additions & 0 deletions plugins/ui/docs/components/router.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Router

`ui.router` is a component that matches the current URL path against provided routes and renders the matching route's element. Use it with [`route`](#route) to define hierarchical navigation structures.

> [!NOTE]
> Deephaven and all custom components share the path. Avoid using routers, the path, path parameters, and navigation in shared components to prevent conflicts. Do not use the route segment `/-/` in your application path as it is reserved for internal use by Deephaven.

## Example

```python order=app
from deephaven import ui


@ui.component
def home_page():
return ui.text("Home page")

@ui.component
def app():
# Index routes match the parent path exactly, so this renders home_page at the root URL
return ui.router(
ui.route(index=True, element=home_page),
)


app = app()
```

## Router Options

Build a simple app with a router, nested routes, route parameters, and a fallback "not found" page.

```python order=app
from deephaven import ui


@ui.component
def nav_links():
# Reuse navigation across pages for convenience
navigate = ui.use_navigate()
return ui.button_group(
ui.action_button("Home", on_press=lambda: navigate("/")),
ui.action_button("All Users", on_press=lambda: navigate("/users")),
ui.action_button("User 1", on_press=lambda: navigate("/users/1")),
ui.action_button("User 2", on_press=lambda: navigate("/users/2")),
)


@ui.component
def user_page():
# The use_params hook gives access to route parameters defined in the path
params = ui.use_params()
# user_id is optional due to the ? in the route path, so provide a default value
user_id = params.get("user_id", "unknown")
return ui.flex(
nav_links(),
ui.text(f"User profile for user {user_id}"),
direction="column",
)


@ui.component
def dashboard():
return ui.flex(
nav_links(),
ui.text("Dashboard home"),
direction="column",
)


@ui.component
def not_found():
return ui.flex(
nav_links(),
ui.text("Page not found"),
direction="column",
)


@ui.component
def app():
return ui.router(
# Nest routes for hierarchical paths
ui.route(
# Match /users/{user_id} and extract user_id as an optional param
ui.route(
path="{user_id?}",
element=user_page,
),
path="users",
),
# An index route matches the path exactly, so this matches the root path /
ui.route(index=True, element=dashboard),
# Match any unmatched path with a wildcard route
ui.route(path="*", element=not_found),
)


app = app()
```

This produces the following route table:

| URL Path | Matched Element | Params |
| ---------------- | --------------- | ------------------------ |
| `/` | `dashboard` | `{}` |
| `/users` | `user_page` | `{}` |
| `/users/42` | `user_page` | `{"user_id": "42"}` |
| `/anything-else` | `not_found` | `{"*": "anything-else"}` |

## Recommendations

1. Include a wildcard route (`path="*"`) as a fallback so unmatched paths render a meaningful "not found" message instead of an error.
2. Use an `index=True` route to define what renders at the exact parent path (such as a landing page at `/`).
3. Use [`use_params`](../hooks/use_params.md) inside routed components to access route parameters, and [`use_path`](../hooks/use_path.md) for the current path.
4. Use [`use_navigate`](../hooks/use_navigate.md) or [`link`](./link.md) with `to` to navigate between routes.

## API Reference

### Router

```{eval-rst}
.. dhautofunction:: deephaven.ui.router
```

#### Matching behavior

1. Static segments are preferred over parameterized segments.
2. Longer matches (more segments) are preferred over shorter ones.
3. Wildcard routes (`*`) have the lowest priority.
4. Optional segments are matched greedily.
5. Index routes match only the exact parent path.
6. If no route matches, the router renders an error.

### Route

```{eval-rst}
.. dhautofunction:: deephaven.ui.route
```

#### Path patterns

- `{var_name}`: Required dynamic segment
- `{var_name?}`: Optional dynamic segment (matches zero or one segment)
- `*`: Wildcard, matches any remaining path segments
- Static text: Exact match

See [use_params](../hooks/use_params.md) for more details on route parameters.

Child paths are appended to parent paths. `ui.route(ui.route(path="{user_id}"), path="users")` produces `/users/{user_id}`.
80 changes: 80 additions & 0 deletions plugins/ui/docs/hooks/use_navigate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# use_navigate

`use_navigate` is a hook that returns a function to trigger single page application (SPA) navigation within Deephaven. For declarative navigation, consider using [`link`](../components/link.md) with the `to` prop instead.

> [!NOTE]
> Deephaven and all custom components share the path. Avoid using routers, the path, path parameters, and navigation in shared components to prevent conflicts. Do not use the route segment `/-/` in your application path as it is reserved for internal use by Deephaven.

## Example

```python order=app
from deephaven import ui


@ui.component
def app():
# Navigate to the settings page
navigate = ui.use_navigate()
return ui.action_button(
"Go to settings", on_press=lambda: navigate("/settings")
)


app = app()
```

## Navigation Options

Use `use_navigate` together with `use_path` and `use_query_params` to build a simple navigation system that updates the path and displays query parameters.

```python order=app
from deephaven import ui


@ui.component
def navigation_demo():
path = ui.use_path()
query_params = ui.use_query_params()
navigate = ui.use_navigate()

def go_dashboard():
# Navigate to a page with a query parameter
navigate("/dashboard", query_params={"welcome": "true"})

def go_settings():
# Use replace=False to push a new history entry instead of replacing the current one
navigate("/settings", replace=False)

def scroll_to_section():
# Jump to a fragment on the current page
navigate(fragment="section-2")

def filter_by_tags():
# Update query parameters on the current page
navigate(query_params={"tag": ["python", "java"]})

return ui.flex(
ui.text(f"Current path: {path}"),
ui.text(f"Query params: {query_params}"),
ui.action_button("Dashboard", on_press=go_dashboard),
ui.action_button("Settings (push)", on_press=go_settings),
ui.action_button("Jump to section", on_press=scroll_to_section),
ui.action_button("Filter by tags", on_press=filter_by_tags),
direction="column",
)


app = navigation_demo()
```

## Recommendations

1. Prefer [`link`](../components/link.md) with `to` for user-clickable navigation. Reserve `use_navigate` for programmatic navigation triggered by events or side effects.
2. Use `replace=True` (the default) when navigating in response to a state change to avoid polluting the browser history.
3. Pair with [`router`](../components/router.md) and [`route`](../components/router.md) to define the route structure that `use_navigate` targets.

## API Reference

```{eval-rst}
.. dhautofunction:: deephaven.ui.use_navigate
```
Loading
Loading