# Container-Level Visibility

Container-level visibility lets operators control, on a per-organization and per-term basis, whether each individual container on a report page renders its data, shows a placeholder message, or is hidden entirely from the navigation. End users see only what is appropriate for their organization and the current reporting term — hidden sections disappear from the side navigation automatically, and a notification guides users to the nearest available section when their current selection is no longer visible.

## Table of Contents

* [Overview](#overview)
* [Who This Affects](#who-this-affects)
* [How Containers Are Filtered by Organization Type](#how-containers-are-filtered-by-organization-type)
* [The Five Render States](#the-five-render-states)
  * [Normal](#normal)
  * [No-Data Notice](#no-data-notice)
  * [Suppressed with Message](#suppressed-with-message)
  * [Suppressed Silently](#suppressed-silently)
  * [Configuration Error](#configuration-error)
* [Hidden-Wins Precedence](#hidden-wins-precedence)
* [Side-Navigation Cascade](#side-navigation-cascade)
* [Organization-Switch Behavior and Toast Notification](#organization-switch-behavior-and-toast-notification)
  * [Auto-Navigation After a Cascade](#auto-navigation-after-a-cascade)
  * [URL Pre-Selection on First Load](#url-pre-selection-on-first-load)
* [Operator Configuration](#operator-configuration)
  * [Visibility Fields Reference](#visibility-fields-reference)
  * [Precedence Summary](#precedence-summary)
  * [Missing Rows](#missing-rows)
  * [Additional Message Banner](#additional-message-banner)
* [Accessibility](#accessibility)
* [Related Guides](#related-guides)

***

## Overview

Report pages in the Container Viewer are divided into sections (report groups) that each contain one or more containers — the individual panels that display charts, tables, and data summaries. Container-level visibility is the system that decides, at page load and on every organization change, which containers render and which do not.

The decision is driven by a configuration table (`AppReportGroupContainerVisibility`) that stores one row per organization, reporting term, and container. Operators populate this table to express exactly what each organization should see in a given term. Containers with no row are flagged as configuration errors so operators can catch missing configuration before rollout.

***

## Who This Affects

**Operators (report administrators)** are responsible for populating and maintaining the visibility configuration. They control which containers each organization can access and can supply custom messages for hidden or data-pending containers.

**End users** (state, district, and school staff, and public visitors on applicable deployments) experience the results: containers that are hidden do not appear at all in the page or side navigation, and containers with no data display the operator-supplied placeholder message in place of the chart or table.

***

## How Containers Are Filtered by Organization Type

Each container on a report page can be scoped to one or more organization types — state, district, school, or others. When a user selects an organization, the Container Viewer filters the container list to show only containers whose type scope includes the selected organization's type. Containers scoped to a different organization type are not shown and do not count toward the cascade calculations described below.

This filtering happens before visibility rows are evaluated, so a district-only container will never appear when a school is selected, regardless of that container's visibility row.

***

## The Five Render States

Every container that passes the organization-type filter is resolved to one of five states. The state determines what the user sees in the container's slot on the page.

### Normal

The container renders its full data view (charts, tables, or other content). This occurs when the visibility row has `IncludeFlag = 1` and `NoDataFlag = 0`.

### No-Data Notice

The container slot shows an operator-supplied HTML message in place of the data view. This occurs when `IncludeFlag = 1` and `NoDataFlag = 1`. The message is left-aligned and rendered at a readable body size. A default fallback message is shown if the operator left `NoDataMessage` blank.

Use this state to indicate that data is expected but not yet available for the current term — for example, "Data for this measure will be posted by April 15."

### Suppressed with Message

The container slot collapses and a brief notice appears in its place. This occurs when `IncludeFlag = 0` and `IncludeMessage` is not blank. The notice renders the operator-supplied message and the container counts as hidden for cascade purposes (see [Side-Navigation Cascade](#side-navigation-cascade)).

### Suppressed Silently

The container slot collapses with no visible notice. This occurs when `IncludeFlag = 0` and `IncludeMessage` is blank. The container is fully absent from the rendered page and counts as hidden for cascade purposes.

### Configuration Error

A wrench-icon notice appears in the container slot indicating the container is not yet configured for the selected organization and term. This occurs when no visibility row exists for the `(organization, term, container)` combination. The container stays listed in the side navigation — the parent group does not cascade out — so operators can identify and fix the gap.

This state should not be visible to end users in a properly configured deployment. It is a diagnostic signal for operators reviewing report pages before rollout.

***

## Hidden-Wins Precedence

When `IncludeFlag = 0` and `NoDataFlag = 1` are both set on the same row, the hide branch fires. The `NoDataMessage` is not shown. The container is treated as suppressed, and if `IncludeMessage` is present it is displayed; if not, the slot collapses silently.

In short: a hidden container is always hidden, regardless of the no-data flag.

***

## Side-Navigation Cascade

The side navigation on a report page shows only the section groups that have at least one visible container. A container is considered visible if its render state is Normal or No-Data (not Suppressed or Configuration Error, though the Configuration Error state does keep its parent group listed as noted above).

When all containers in a section group are suppressed, the group's tab or navigation entry is removed from the side navigation automatically. This cascade is recursive — if a parent group's only child groups all cascade out, the parent group also disappears.

The cascade recalculates whenever the selected organization or term changes, so the navigation always reflects what is available for the current selection.

For more detail on how the side navigation is structured, see [Menu & Navigation Management](/guides/menu-navigation.md).

***

## Organization-Switch Behavior and Toast Notification

### Auto-Navigation After a Cascade

When a user switches to a different organization and the section they were viewing cascades out (because all its containers are suppressed for the new organization), the portal automatically navigates to the nearest non-empty section. The selection logic follows this order:

1. Another section in the same parent group (sibling), chosen by position.
2. If no siblings are available, the nearest non-empty group at a higher level in the hierarchy.

A notification toast appears at the top of the page when this auto-navigation occurs. If the cascaded section had a suppression message on any of its containers, that message is included in the toast so the user understands why their previous section is no longer available. Otherwise a generic message is shown.

The notification is informational — users do not need to take any action. It disappears after a few seconds.

### URL Pre-Selection on First Load

When a report page is opened with an organization pre-selected in the URL (for example, via a deep link or a menu item with query parameters), the first load is always silent. Even if the pre-selected organization causes the default section to cascade out and the portal auto-navigates to another section, no toast notification is shown. This prevents an unexpected notification from appearing on initial page load when the URL already establishes the organization context.

Toast notifications are only triggered by user-initiated organization changes that happen after the page has fully loaded.

***

## Operator Configuration

Visibility is controlled by rows in the `AppReportGroupContainerVisibility` table. Each row applies to exactly one organization, one reporting term, and one container.

Operators create, update, or remove rows to control what each organization sees in each term. At present there is no dedicated administration UI for editing these rows — configuration is applied directly to the database table, typically as part of a pre-rollout data preparation step.

For the full column definitions, constraint details, and worked SQL examples, see the [ReportGroupContainerVisibility Schema Reference](https://github.com/otised-inc/OtisEd.Nimble/blob/master/docs/schema/ReportGroupContainerVisibility.md).

### Visibility Fields Reference

| Field               | What it controls                                                                                                                                                                                                         |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `IncludeFlag`       | `1` = container renders (Normal or No-Data state); `0` = container is hidden (Suppressed state).                                                                                                                         |
| `IncludeMessage`    | Optional message displayed in the container slot when `IncludeFlag = 0`. Leave blank for a silent collapse. Maximum 1,000 characters.                                                                                    |
| `NoDataFlag`        | `1` = show a no-data placeholder instead of the data view (when `IncludeFlag = 1`). `0` = render normally.                                                                                                               |
| `NoDataMessage`     | The placeholder text or HTML shown when `NoDataFlag = 1`. Leave blank to display the system default. Maximum 1,000 characters.                                                                                           |
| `AdditionalMessage` | Optional contextual banner displayed ABOVE the container — independent of the include / no-data state. Leave blank for no banner. Maximum 1,000 characters. See [Additional Message Banner](#additional-message-banner). |

### Precedence Summary

| `IncludeFlag` | `NoDataFlag` | Result                                                              |
| ------------- | ------------ | ------------------------------------------------------------------- |
| 1             | 0            | Normal — container renders data                                     |
| 1             | 1            | No-Data Notice — operator message shown                             |
| 0             | 0            | Suppressed — slot collapses (message if `IncludeMessage` set)       |
| 0             | 1            | Hidden wins — treated as `IncludeFlag = 0`; `NoDataMessage` ignored |

### Missing Rows

If no row exists for a given `(organization, term, container)` combination, the container displays the configuration-error notice. This is intentional: the system uses an allow-list model where containers must be explicitly enabled for each organization and term. A missing row is not treated as "include by default."

Operators should ensure every container that should render for a given organization and term has a row with `IncludeFlag = 1` before publishing that page to users.

### Additional Message Banner

`AdditionalMessage` is an optional column that lets operators attach a contextual notice ABOVE a container without changing whether the container renders, hides, or shows a no-data placeholder. When set, the React side renders a Bootstrap `alert alert-info` (blue, neutral) banner directly above the container's content area. When the column is blank, null, or whitespace-only, no banner appears (the DOM contains no empty wrapper).

The banner is independent of the include / no-data matrix:

| Visibility state                              | AdditionalMessage behavior                                                            |
| --------------------------------------------- | ------------------------------------------------------------------------------------- |
| Configuration Error                           | Banner appears above the wrench notice (when a row exists with the message set).      |
| Suppressed With Message / Suppressed Silently | Banner is hidden — the container slot has collapsed and there is no slot to be above. |
| No-Data Notice                                | Banner appears above the no-data placeholder text.                                    |
| Normal                                        | Banner appears above the live container (chart, table, etc.).                         |

Operators typically use AdditionalMessage to communicate context that applies regardless of the data state — for example, "Reporting window closes Friday at 5 PM ET" or "Note: this year's figures are provisional pending DBE certification." If the goal is instead to explain that data is missing for a specific term, use `NoDataMessage` (which renders in place of the data view) — see [No-Data Notice](#no-data-notice).

**HTML support.** `AdditionalMessage` is stored and rendered as HTML, matching the existing posture for `NoDataMessage` and the Container Designer's HTML Block card. The React side does not run a sanitization layer because the table is populated only by trusted operators with direct DB write access. Authors should still keep markup simple — links (`<a>`), bold (`<b>`/`<strong>`), italic (`<i>`/`<em>`), line breaks (`<br>`), paragraphs (`<p>`), and lists (`<ul>`/`<ol>`/`<li>`) cover the expected use cases.

Avoid the following tags and attributes:

* `<script>`, `<iframe>` — script execution and cross-origin embedding
* inline `on*` event handlers (e.g. `onclick`, `onmouseover`) — script execution via attribute
* `<form>`, `<input>`, `<button>`, `<select>`, `<textarea>` — even when the inner `<form>` is ignored by HTML5 nesting rules, a `<button type="submit">` child can submit the OUTER page-level form (filter controls or card actions) to whatever browser-default applies, navigating users away unexpectedly. The same caveat applies to `NoDataMessage` and `IncludeMessage`.

This trust-ops posture depends on `AppReportGroupContainerVisibility` having no app-layer write path. If an admin UI for editing visibility rows is ever introduced, the implementer must add either server-side sanitization at the write boundary or client-side DOMPurify at the render boundary for all three message columns — otherwise the surface becomes a stored-XSS vector reachable by anonymous public viewers.

**Maximum length:** 1,000 characters.

A runnable seed script with placeholder IDs and revert instructions is provided at `src/OtisEd.Nimble.EntityFrameworkCore/Scripts/Miscellaneous/Seed_AdditionalMessage_Sample.sql` for QA / pre-rollout testing.

***

## Accessibility

The Container Viewer's visibility notices are built to meet Section 508 and WCAG 2.1 Level AA requirements.

**No-data notice** — The notice element carries an ARIA live region attribute (`aria-live="polite"`) so screen readers announce the message when it is injected into the page after an organization switch. The notice is a native HTML element with standard document flow, so it is reachable by keyboard navigation without additional configuration.

**Toast notification** — The OENotification toast used for cascade auto-navigation announcements is rendered in a live region so assistive technology announces the message without requiring focus to move to the toast. The toast has sufficient color contrast against the portal background and does not rely on color alone to convey its meaning.

**Suppression notice** — When `IncludeMessage` is displayed, the notice is a focusable, visibly styled element within the normal tab order. It does not require mouse interaction to read.

**Configuration-error notice** — The wrench icon is accompanied by a visible text label; the icon itself is marked as decorative (`aria-hidden="true"`) so screen readers read the text label only.

**Additional Message banner** — The banner is a Bootstrap `alert alert-info` styled element with `role="status"` and `aria-live="polite"`, so screen readers announce the message when it appears or changes during filter switches. The blue background meets WCAG 2.1 Level AA contrast ratios with the default text color. Any link the operator embeds in the message receives a native focus indicator on keyboard `Tab` traversal. Operators authoring the message HTML should not introduce out-of-order heading tags — the banner is meant for inline content (links, emphasis) rather than structural headings.

***

## Related Guides

* [Menu & Navigation Management](/guides/menu-navigation.md) — how section groups appear in the portal side navigation and how menu permissions interact with report page access.
* [Container Designer & Dashboard Cards](/guides/container-designer.md) — how containers are authored, what card types are available, and how the responsive grid layout is configured.
* [ReportGroupContainerVisibility Schema Reference](https://github.com/otised-inc/OtisEd.Nimble/blob/master/docs/schema/ReportGroupContainerVisibility.md) — full column definitions, constraints, foreign keys, behavior rules, and SQL examples for the visibility configuration table.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://nimble.docs.otised.com/guides/container-level-visibility.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
