diff --git a/.github/workflows/pdoc.yaml b/.github/workflows/publish-docs.yaml similarity index 79% rename from .github/workflows/pdoc.yaml rename to .github/workflows/publish-docs.yaml index ae7d9c2..b56e4c2 100644 --- a/.github/workflows/pdoc.yaml +++ b/.github/workflows/publish-docs.yaml @@ -24,16 +24,12 @@ jobs: - uses: actions/setup-python@v5 with: python-version-file: ".python-version" - - # ADJUST THIS: install all dependencies (including pdoc) - run: uv sync --all-extras --dev - # ADJUST THIS: build your documentation into docs/. - # We use a custom build script for pdoc itself, ideally you just run `pdoc -o docs/ ...` here. - - run: uv run pdoc src/dash_builder -o docs/ + - run: uv run mkdocs build - uses: actions/upload-pages-artifact@v3 with: - path: docs/ + path: site/ # Deploy the artifact to GitHub pages. # This is a separate job so that only actions/deploy-pages has the necessary permissions. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b2a6e79 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,34 @@ +# Welcome to MkDocs + +For full documentation visit [mkdocs.org](https://www.mkdocs.org). + +## Commands + +* `mkdocs new [dir-name]` - Create a new project. +* `mkdocs serve` - Start the live-reloading docs server. +* `mkdocs build` - Build the documentation site. +* `mkdocs -h` - Print help message and exit. + +## Project layout + + mkdocs.yml # The configuration file. + docs/ + index.md # The documentation homepage. + ... # Other markdown pages, images and other files. + +## Termynal + + + +``` +$ uv -h + +... +``` + +::: dash_builder.dash_page.DashPage + options: + members: + - valid_layout + - layout + inherited_members: true diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..30109fa --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,6 @@ +site_name: My Docs +theme: + name: material +plugins: + - termynal + - mkdocstrings diff --git a/pyproject.toml b/pyproject.toml index 024dda1..c5a6948 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dash-builder" -version = "0.1.4" +version = "0.1.5" description = "A tool and framework to simplify construction of Python Dash applications." readme = "README.md" authors = [{ name = "robfs", email = "robfoxsimms@gmail.com" }] @@ -26,7 +26,15 @@ venvPath = "." venv = ".venv" [dependency-groups] -dev = ["pdoc>=15.0.1", "pytest>=8.3.4", "pytest-cov>=6.0.0", "ruff>=0.9.7"] +dev = [ + "mkdocs>=1.6.1", + "mkdocs-material>=9.6.9", + "mkdocstrings-python>=1.16.7", + "pytest>=8.3.4", + "pytest-cov>=6.0.0", + "ruff>=0.9.7", + "termynal>=0.13.0", +] [tool.ruff] # Exclude a variety of commonly ignored directories. diff --git a/src/dash_builder/_dash_object.py b/src/dash_builder/_dash_object.py index 80227f4..1f149c8 100644 --- a/src/dash_builder/_dash_object.py +++ b/src/dash_builder/_dash_object.py @@ -4,6 +4,7 @@ import re import traceback +import dash_mantine_components as dmc from dash import html __all__ = ["DashObject"] @@ -54,7 +55,14 @@ def error_container(cls, message: str) -> html.Div: `dash.html.Div` container. """ - return html.Div(html.Pre(message)) + return dmc.Container( + dmc.Code( + message, + block=True, + color="var(--mantine-color-red-2)", + style={"whiteSpace": "pre-wrap"}, + ) + ) @classmethod @abc.abstractmethod diff --git a/src/dash_builder/cli.py b/src/dash_builder/cli.py index c046018..abc372a 100644 --- a/src/dash_builder/cli.py +++ b/src/dash_builder/cli.py @@ -169,7 +169,7 @@ def create_new_page_module(self, template: PageTemplate): def add_view_import_to_init_file(self, template: ViewTemplate): """Add a view import to the init file.""" init_file = template.file_path.parent / "__init__.py" - module_name = template.file_name.strip(".py") + module_name = template.file_name.replace(".py", "") if not init_file.exists(): init_file.write_text('"""Views module."""\n\n__all__ = []\n') @@ -213,7 +213,7 @@ def add_view(self, view_name: str): f"[bold green]CREATED[/bold green] {template.class_name} view." ) - def add_page(self, page_name: str, url_path: str): + def add_page(self, page_name: str, url_path: str | None = None): """Add a new page to the project.""" page_path = self.project / "pages" template = PageTemplate(page_name, page_path, url_path) @@ -253,7 +253,9 @@ def build( @app.command("view") def create_view( - view_name: Annotated[str, typer.Argument(help="The name of the view to add.")], + view_names: Annotated[ + list[str], typer.Argument(help="The name of the view(s) to add.") + ], location: Annotated[ str, typer.Option(help="The destination directory for the project.") ] = "", @@ -261,25 +263,47 @@ def create_view( """Add a new view to the project. Args: - view_name: the name of the view to add. + view_names: the name(s) of the view(s) to add. location: the destination directory for the project. """ project = Project.detect(location=location) - project.add_view(view_name) + + for view_name in view_names: + project.add_view(view_name) @app.command("page") def create_page( - page_name: Annotated[str, typer.Argument(help="The name of the page to add.")], - url_path: Annotated[str, typer.Option(help="The URL path of the page.")] = None, + page_names: Annotated[ + list[str], typer.Argument(help="The name of the page(s) to add.") + ], + location: Annotated[ + str, typer.Option(help="The destination directory for the project.") + ] = "", + url_path: Annotated[ + str, + typer.Option( + help="The URL path of the page. Only available for single page creation." + ), + ] = None, ): """Add a new page to the project. Args: - page_name: the name of the page to add. - url_path: the URL path of the page. + page_names: the name of the page to add. + location: the destination directory for the project. + url_path: the URL path of the page. Only available for single page creation. """ - project = Project.detect() - project.add_page(page_name, url_path) + project = Project.detect(location=location) + if len(page_names) > 1: + if url_path: + project.console.print( + "[bold red]ERROR[/bold red] URL path is only available for single page creation." + ) + return + for page_name in page_names: + project.add_page(page_name) + else: + project.add_page(page_names[0], url_path) diff --git a/src/dash_builder/examples/basic-mantine/app.py b/src/dash_builder/examples/basic-mantine/app.py index 4a751e0..7cde43a 100644 --- a/src/dash_builder/examples/basic-mantine/app.py +++ b/src/dash_builder/examples/basic-mantine/app.py @@ -4,10 +4,11 @@ import dash import dash_mantine_components as dmc +from dash import dcc from typing_extensions import override -from views import FooterView, HeaderView, SidebarView from dash_builder import DashPage +from views import FooterView, HeaderView, SidebarView dash._dash_renderer._set_react_version("18.2.0") @@ -293,6 +294,7 @@ def valid_layout(cls, **kwargs): SidebarView.layout("sidebar"), dmc.AppShellMain(dash.page_container), FooterView.layout("footer"), + dcc.Location(id="url"), ], header={"height": 60}, footer={"height": 30}, diff --git a/tests/test_dash_page.py b/tests/test_dash_page.py index 3c2fb06..4bb9878 100644 --- a/tests/test_dash_page.py +++ b/tests/test_dash_page.py @@ -2,6 +2,7 @@ import pytest from dash import html +import dash_mantine_components as dmc from src.dash_builder import DashPage @@ -10,8 +11,8 @@ def test_page_error(test_page): container = test_page.error_container("test message") inner = container.children inner_text = inner.children - assert isinstance(container, html.Div) - assert isinstance(inner, html.Pre) + assert isinstance(container, dmc.Container) + assert isinstance(inner, dmc.Code) assert inner_text == "test message" @@ -19,8 +20,8 @@ def test_page_on_error(error_page): container = error_page.layout() inner = container.children inner_text = inner.children - assert isinstance(container, html.Div) - assert isinstance(inner, html.Pre) + assert isinstance(container, dmc.Container) + assert isinstance(inner, dmc.Code) assert "ZeroDivisionError: division by zero" in inner_text