Templates and Pages
Templates structure free-floating molecules and organisms into a context: a specific page.
Navigation
Users should always know where they are and how to get back where they came from.
     
  
Standard navigation
These navigation elements should be present on almost every page. The following assumes that the page descends from hqwebapp/bootstrap3/base_section.html, which virtually all pages should.
- Top navigation
- 
        The top navigation should always be visible to users.
        Individual tabs are defined in
        tabclasses.py. Which tab is highlighted depends on the
        url_prefix_formatsdefined for each tab. Which items appear in a tab's dropdown is determined bydropdown_items.
- Side navigation
- 
  The side navigation appears almost everywhere. Major exceptions are the dashboard, which has no navigation, and app manager, which has a custom sidebar corresponding to the app's structure. Other pages should almost certainly have a sidebar. The sidebar is also defined in tabclasses.py, controlled by the sidebar_itemsfunctions.Temporary sub-items that appear only when on a specific page, such as the "Create Form Data Export" item that appears under the main "Export Form Data" item only when you're creating a new export, can be added using the subpagesproperty.Dynamic item names, such as displaying the username when editing a web user, can be enabled by passing a function to the menu item's title.
- Breadcrumbs
- 
    Breadcrumbs are defined by the parent_pagesin the template context (see below for how to populate). Standard pages should have a breadcrumb for the top nav section (e.g., "Data"), for the left sidebar section if there is one (e.g., "Edit Data"), and for the page itself. Multi-step workflows such as case import should also be reflected in breadcrumbs. All items in the breadcrumbs should be links, except for the last (the current page).A few pages override the page_breadcrumbsblock defined in hqwebapp/bootstrap3/base_section.html. This should only be done in exceptional circumstances.
Page headers
A page header at the top of the page's main content helps the user stay oriented, and this is a good place to briefly introduce the user to the page and link to documentation.
    This is an area where the technical implementation isn't well-standardized.
    When possible, set page_title in the template's context.
    Some pages do create the header by hand.
  
Page header
In-page navigation
If a page has multiple sections, you have a few options to visually separate them:
- Split into multiple pages.
- 
      Enclose each section in a panel.
      Note that app manager has its own panel styling, .panel-appmanager.
- Set each section up as a tab. Avoid using pills for navigation, because they look similar to the toggles used in forms.
Panels
Tabs for in-page navigation
Class-Based Views
    Ultimately all views are based on django's TemplateView class,
    if the view has a user-facing UI.
  
    If not, then inheriting from a simple
    View will probably be enough.
  
The BaseDomainView
  
    If your view has something to do with the domain, then
    it will probably inherit from this view.
  
What benefits do you have from using this view?
- The permission login_and_domain_requiredis already in effect established.
- 
      self.domain(the name of the domain) is already available as an instance variable for your class and is automatically inserted into the template's context.
- 
      self.domain_objectis already available as an instance variable (theDomaininstance with the domain name given byself.domain.
- 
      The self.page_urlproperty is already avaiable, provided that your page doesn't require other args beyonddomain. If it does (for instance if your url looks like/a/[domain]/my/page/[unique-id], then you'll have to re-implementself.page_urlwith the required arguments.
    Here is an example of a class inheriting from the BaseDomainView.
  
class MyView(BaseDomainView):
    urlname = 'my_view'
  
    (Required) urlname will be the string referenced when you do the url mapping for
    this view in it's application's urls.py.
(e.g.
    url(r'^myview/$', MyView.as_view(), name=MyView.urlname).
  
   template_name = 'all_the_views/my_view.html'
    page_title = gettext_lazy("My View")
    section_title = gettext_lazy("All the Views")
    @property
    def section_url(self):
        return reverse(MySectionPageRoot.urlnale, args=[self.domain])
  
    (Required) section_title and section_url are the title and url
    for the "tab" or major content area that the view is inside. For instance, Settings,
    Users, Data, Reports, etc. (See
    Creating a Base View for my Section for best
    practices on abstracting this out so that every View in the same Section doesn't have to
    define this each time).
  
    Both the section_url and page_title are used in rendering
    the page's breadcrumbs (it's the first crumb).
  
    @property
    def parent_pages(self):
        return [
            {
                'title': MyParentPageView.page_title,
                'url': reverse(MyParentPageView.urlname, args=[self.domain]),
            }
        ]
  (Optional) This is what shows up as the hierarchy in the Breadcrumbs for the page, and is what's in between the first crumb (the section) and the last crumb (the current page).
    @property
    def page_name(self):
        return "{domain}'s {page_title}".format(
            domain=self.domain,
            page_title=self.page_title,
        )
  
    (Optional) page_name by default returns page_title. It's a
    way of adding request-specific context to the page_title, but still having
    access to the page_title from a class level for general referencing of the page.
  
    @property
    def page_context(self):
        return {
            'form': MyForm(),
        }
  
    (Optional) While page_context is optional, it's very likely you'll implement it,
    as that's where you return all the variables specific to that page. If you are starting to create
    a hierarchy where you're then adding to the page_context via.
  
    # note, this is an example of what NOT to do
    @property
    def page_context(self):
        context = super(MyView, self).page_context
        context.update({
            'form': MyForm(),
        })
        return context
  
    Then, first, ask why you need that specific hierarchy. Perhaps you want to just inherit from
    a Base Section view? Second, this should never happen with page_context. That's what
    main_context is for, as that covers the context for more than one page (or in the
    next example, a whole section).
  
Creating a Base View for My Section
    The best practice is to create a Base Section View for all of the views
    that will be part of that section (or tab) so that section_title
    section_url and any section-specific context can be added to the
    main_context.
  
class BaseMySectionView(BaseDomainView):
    section_name = "My Section"
    @property
    def section_url(self):
        return reverse(TheRootOfMySection.urlname, args=[self.domain]
    @property
    def main_context(self):
        context = super(BaseMySectionView, self).main_context
        context.update({
            'my_section_emoji': "bears",
        })
        return context
  
    main_context here is optional but a good place to insert any section-specific
    template contexts in.
  
    def dispatch(self, request, *args, **kwargs):
        return super(MyView, self).dispatch(request, *args, **kwargs)
Functional Views
When using functional views, "fake" the same context that class-based views generate.
Manually mimic the context provided by BasePageView.main_context and rendered by hqwebapp/bootstrap3/base_section.html. This is an area where we could use better standards / shared code.
context.update({
    'current_page': {
        'title': page_name,
        'page_name': page_name,
        'parents': [
            {
                'title': grandparent_title,
                'page_name': grandparent_name,
                'url': grandparent_url,
            },
            {
                'title': parent_title,
                'page_name': parent_name,
                'url': parent_url,
            },
       ],
    },
    'section': {
        'page_name': MySection.section_name,
        'url': reverse("section_url_name"),
    },
});