A single custom widget for SAP Analytics Cloud that renders the “Traditional
Snap Report” dashboard: a high-level daily view of Annual Enrollment (AE)
progress. Built the same way as the Hello World
POC — a widget.json manifest plus a
web-component JS file — but with real dashboard content instead of a single
label, and three real data bindings instead of none.
Before wiring real data, read
DATASPHERE_VIEW_SPEC.md — it defines exactly
what Datasphere view(s) this widget expects, tracing every field back to the
already-approved SAP CDS views it reuses (no new business logic invented).
It also documents two open items that are not yet resolved (church
membership YoY count source, Synod/Region data access) — the widget itself
surfaces these as a standing notice until they’re settled.
widget.json — manifest: properties (width, height, asOfLabel),
three data bindings (employerStatus, dailyCounts, yoyComparison),
one exposed scripting method (refresh), and a reference to the hosted
main.js.main.js — defines the <com-porticobenefits-aesnapreport> custom
element. Parses the three data bindings and renders: Employer Selection
KPI tiles (Total Set Up / Completed / % Complete / Non-Completed /
Defaulted) with per-tile progress bars, an Of-Complete election-type
breakdown, a Synod/Region breakdown, a YoY change-rate breakdown, and a
hand-rolled inline SVG timeline bar chart (10/1–10/14, no external chart
library — SAC widget iframes are CSP-strict). Light theme only (see Known
limitations below — a dark/light toggle was tried and dropped), and a
“Mock Data — Preview” badge that flips to “Live” once real data is bound.
No in-widget filter controls — see Known limitations; filtering is
meant to happen via a native SAC Input Control instead. Falls back to
built-in mock data when no data binding is bound, so the whole layout is
reviewable standalone.icon.svg — icon shown in the SAC widget panel.preview.html — standalone local test harness; loads main.js and drives
the widget through the same onCustomWidgetBeforeUpdate /
onCustomWidgetAfterUpdate lifecycle hooks SAC uses, so verifying it here
is a faithful test of the real update path.DATASPHERE_VIEW_SPEC.md — the data design this widget is built against:
which Datasphere views feed which data binding, and the two open items
still pending confirmation.preview.html).DATASPHERE_VIEW_SPEC.md is a
handoff spec; the actual views need SAP Datasphere Web IDE access (see
spec’s “Next steps” section).Confirmed by testing directly in SAC (Optimized story): the widget’s
internal <button>/<select> controls receive click/change events normally
in Edit mode, but View mode delivers none of them — confirmed via a
completely silent DevTools console on click (no error, no log, nothing),
ruling out a JS bug. This isn’t something fixable from the widget’s own code
— SAC’s Optimized-story View mode apparently doesn’t forward internal DOM
events into a custom widget’s shadow DOM, at least not in this
configuration.
Consequence, resolved 2026-08-28: both the dark/light theme toggle and
the Status/Synod-Region filter dropdowns were removed from the widget
entirely, rather than ship controls that only work in Edit mode. The widget
now renders light theme only, with no in-widget filter UI at all — it’s a
pure display component that renders whatever data currently arrives through
its three data bindings via onCustomWidgetAfterUpdate (confirmed working
correctly end-to-end, independent of this issue).
Filtering now happens entirely in SAC, not in the widget: place a native Input Control on the Story, wire it to the underlying data source(s), and SAC handles re-querying and pushing the filtered result set into the widget’s bindings — no custom-widget-side code needed for that, and it isn’t subject to the View-mode limitation above since it’s SAP’s own supported control, not something drawn inside our shadow DOM. Worth raising the underlying platform behavior with SAP support/community separately (see the issue writeup shared alongside this doc), but no need to block on that to move forward.
Same reasoning applies to the employer-detail download feature (see
BUILD_PLAN_VWEMPLOYERSAVES.md, “Download experience”): it’s a native SAC
Table widget with SAC’s own built-in export, placed on the Story alongside
this widget — not a button inside it. Nothing in this repo (main.js,
widget.json) changes to support it.
python -m http.server 8420 --directory sac-ae-snap-report-widget
Then open http://localhost:8420/preview.html. You should see the full
dashboard rendered from the mock data baked into main.js.
main.js and icon.svgSame pattern as the Hello World POC — host this folder on GitHub Pages
(Settings > Pages, source: branch main, folder /root). widget.json
already points at:
https://bboehm1986.github.io/sac-ae-snap-report-widget/main.jshttps://bboehm1986.github.io/sac-ae-snap-report-widget/icon.svgIf main.js changes, recompute the integrity hash in widget.json:
openssl dgst -sha384 -binary main.js | openssl base64 -A
widget.json.Follow DATASPHERE_VIEW_SPEC.md end to end — do not skip its two open
items. Once DS_AE_SNAP_REPORT (or the source-aligned views individually)
exist in Datasphere:
DATASPHERE_VIEW_SPEC.md section 2 (employer status, daily counts, YoY).employerStatus, dailyCounts,
and yoyComparison to the three queries from step 4.DATASPHERE_VIEW_SPEC.md section 4 — start with scheduled refresh).ZVHCM_AE_004Q and ZVHCM_AE_005Q in the Data Product
Catalogue (they’re not there yet — see DATASPHERE_VIEW_SPEC.md).