Skip to content

Introduction

The following release introduces two minor, backward-compatible changes. The first change allows users to add attributes to page wrapper div:

<div data-zenaura="72e7e09f" hidden=""></div>

Adding Attributes to Pages

The updated implementation now allows adding class names for styling purposes:

<div data-zenaura="72e7e09f" hidden="" class="flex"></div>

Users can specify class names for the pages when configuring the router:

home_page = Page([intro_section], attributes={"class": "flex"}) # Optional class name

Layout

Support for higher-order components will remain unchanged. However, this release introduces a custom app hydration method called hydrate_app_layou, for the hydration of the app and pages.

For example, consider this layout: nav → page content → footer. Using higher-order components and pages, this results in the following structure, using @Reuseable on nav component, footer and passing new instance for each page will yield the expected behavior but with html redundancy :

<div>
    <div data-zenaura="72e7e09f" hidden="">
        <nav></nav>
        <div>content</div>
        <footer></footer>
    </div>
    <div data-zenaura="72e7e09f" hidden="">
        <nav></nav>
        <div>content</div>
        <footer></footer>
    </div>
    <div data-zenaura="72e7e09f" hidden="">
        <nav></nav>
        <div>content</div>
        <footer></footer>
    </div>
</div>

Solution

Instead of users passing the router class to the build process, they now use ZenauraServer.hydrate_app_layout, which allows them to pass multiple components and a list of pages in the order they want them to be rendered. Here is an example using higher-order components:

from zenaura.client.app import Route, App
from zenaura.client.page import Page
from public.routes import ClientRoutes
from public.components.header import Header
from public.components.intro import IntroSection
from public.components.footer import Footer
from public.components.examples import Example

nav_bar_header = Header(router)
intro_section = IntroSection()
footer = Footer()
example = Example()

# Higher-order component
def AppLayout(page_children):
    return [
        nav_bar_header,
        *page_children,
        footer,
    ]

# App and routing
home_page = Page(AppLayout([intro_section]))

router.add_route(Route(
    title="components",
    path=ClientRoutes.components.value,
    page=components_page
))

This approach is now replaced with:

from zenaura.client.app import Route, App
from zenaura.client.page import Page
from zenaura.client.layout import Layout  # Change
from public.routes import ClientRoutes
from public.components.header import Header
from public.components.intro import IntroSection
from public.components.footer import Footer
from public.components.examples import Example

nav_bar_header = Header(router)
intro_section = IntroSection()
footer = Footer()
example = Example()
# No higher-order component needed

# App and routing
home_page = Page([intro_section])
example_page = Page([example])

router.add_route(Route(
    title="Developer-Focused | Zenaura",
    path=ClientRoutes.home.value,
    page=home_page
))

router.add_route(Route(
    title="example",
    path=ClientRoutes.examples.value,
    page=example_page
))

# Adding app layout
my_app_layout = Layout(
    top= [nav_bar_header], # first child component to root div
    routes=app.routes,  # Pages stored here
    bottom=[footer] # last child components to root div
)

Updating build.py:

from public.main import my_app_layout
ZenauraServer.hydrate_app_layout(my_app_layout, scripts=[
    '<link rel="stylesheet" href="public/gigavolt.min.css">',
    '<link rel="stylesheet" href="public/output.css">',
    '<script src="public/highlight.min.js"></script>',
    '<script src="public/python.min.js"></script>',
    '<script>hljs.highlightAll();</script>',
    """
    <script>
        const ws = new WebSocket("ws://localhost:5000/refresh");
        ws.onmessage = () => {
        console.log("Reloading...");
        location.reload();
        };
    </script>
    """
])

This change ensures that global components are included within the root div, not within every page:

<div id="root">
    <nav></nav> <!-- Global top of pages nav component -->
    <div id="page1"></div>
    <div id="page2" hidden></div>
    <div id="page3" hidden></div>
    <div id="page4" hidden></div>
    <footer></footer> <!-- Global bottom of pages footer component -->
</div>