All skills
secondsky avatar

/sap-sac-custom-widget

@4d87d99
by Eddiesecondsky/sap-skills456 stars
120

SAP Analytics Cloud (SAC) Custom Widget development. Use when building custom visualizations, extending SAC with Web Components, or creating Widget Add-Ons. Covers JSON metadata, JavaScript Web Components, lifecycle functions, data binding with feeds, styling/builder panels, property/event/method definitions, third-party library integration, hosting, security, performance, and debugging. Includes Widget Add-On feature (QRC Q4 2023+) and templates for widgets, charts, and KPI cards.

Use this Skill: https://skilld.dev/gh/secondsky/sap-skills/sap-sac-custom-widget

This session only. Nothing lands on disk.

referencesproperty-lifecycle.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

SAC Custom Widget Property Lifecycle

Use this reference when a widget has several manifest properties, builder or styling panels, script methods, import/export, or state that can be written by more than one surface.

The rules below are implementation guidance. Tenant observations are marked explicitly and must not be presented as universal SAP guarantees without a current tenant check.

The lifecycle model

Design as if SAC can:

  1. deliver properties one at a time, in any order;
  2. send the widget's own propertiesChanged values back as later updates; and
  3. provide a value in a different representation than the one originally written.

Render from the merged state after each update. Do not assume that the last property in a batch has arrived, and do not consume a one-shot adoption flag on an empty first render from a builder panel. Keep the flag armed until there is a real value to adopt.

If a setter uses reference equality for an object, in-place mutation can leave the widget stale. Either treat compound values as immutable or compare a canonical representation before deciding that nothing changed.

One canonical read path

Use one normalizer for runtime rendering, builder and styling panel display, and export. It should handle the representations the manifest and tenant can produce, including string-form numbers, boolean-like strings, localized text objects, blank values, and invisible whitespace. Invalid input must resolve to the declared default.

Do not write this:

var columns = Number(value) || 1;

It turns a valid zero into the fallback. Prefer an explicit finite-value check:

function finiteOrDefault(value, fallback) {
  var number = Number(value);
  return Number.isFinite(number) ? number : fallback;
}

Normalize incoming properties before parsing a serialized compound property that contains values from other properties. Otherwise the panel, runtime, and backup can each see a different state.

Generate manifest declarations, panel fields, normalizer entries, and export enumeration from one property specification where practical. At minimum, add a check that every manifest property is read by runtime code. A property that is only stored, shown in a panel, or exported is not wired.

Script methods are a narrow contract

methods[].body is Analytics Designer script, not arbitrary JavaScript. It should only read or assign properties declared in the same manifest. Put component logic in the Web Component and use a declared property as the bridge:

{
  "properties": {
    "refreshToken": { "type": "integer", "default": 0 }
  },
  "methods": {
    "refresh": {
      "body": "this.refreshToken = this.refreshToken + 1;"
    }
  }
}

Do not call private component methods from a manifest method body. A getter method should return a declared property and its returnType must match that property. Keep array-like results as a documented string format unless the target tenant and schema support a stronger type.

Editors and echoed writes

Builder and styling panels are stateful editors, not passive forms.

  • Preserve input value, focus, caret, selection, search text, and collapse state during text edits.
  • Use targeted DOM updates for keystrokes. Reserve full renders for structural changes.
  • Use composedPath() for outside-click logic when rerenders can detach the clicked node.
  • Derive document-level listeners from ownerDocument, and remove them from that same document.
  • Keep editor-only state out of persisted widget configuration.
  • When an external configuration replacement removes the selected item, clear stale drafts and pending targets instead of acting on an arbitrary replacement.
  • If a control is disabled by a missing prerequisite, show why it is disabled. Do not silently accept a no-op.
  • When a pending staged record is committed, clear it only if the slot still contains that exact record. A newer staged edit must not be discarded by an older commit.
  • A single user action should produce one property dispatch and one undo step. Include adoption or normalization in that transaction, rather than dispatching a hidden follow-up from rendering.

When a panel writes a value and SAC echoes it back, avoid treating the echo as a new user edit or starting a render loop. Verify the exact echo shape in a tenant before depending on identity checks.

For scheduled or expiring content, own one boundary timer: clear before arming, cap the delay at 2^31 - 1 milliseconds, stop it on destroy and detach, and re-arm when a property changes the boundary. Re-render only when the visible result changes. Test the old configuration path so a new timer does not change behavior when the feature is unused.

Defaults, overrides, and persistence

Distinguish these contracts:

  • a new manifest default can change the appearance of a newly created widget;
  • an existing stored configuration should remain compatible unless a migration is explicit; and
  • a per-item setting can be unset even when the widget-level default is non-empty.

Represent the unset state consistently in the sanitizer, editor control, runtime resolver, and every serializer. If an override replaces a built-in matcher, it replaces it. Do not silently widen the match or retarget a displaced stored value. Show a visible warning and require the author to repair the value.

When two editors write the same configuration, preserve keys unknown to the editor. Use optional replace-when-present sections for evolving backup formats, and prove an unmodified export to import to export cycle is a fixed point.

If a property carries script-set, permission, or reader-specific state, review its includeInBookmarks setting and export behavior together. The documented default and tenant restore order must be verified for the target SAC experience before making a security claim.

Import and export data

For widgets that import or export structured content:

  • Preserve stable identity keys, or disclose exactly what is lost when ids are reminted.
  • Apply validation to every language slot, not only the default language.
  • Treat an authored empty string as meaningful. Do not use absence and explicit empty as the same signal when seeding defaults unless that is the stated format contract.
  • Deep-copy nested templates before editing them. A shallow copy can mutate the preset and poison later instances or undo history.
  • Use Object.create(null) for maps keyed by user-authored names and test a __proto__ key.
  • Never key UI state or membership checks on display labels alone. Centralize case-insensitive name matching when the product contract is case-insensitive.
  • If a value is hidden from rendering but must remain searchable, enumerate every rendering surface, including attributes and accessible names, and test mouse and keyboard activation separately.

For derived numbers or dates, define the precision, timezone owner, rollover, and no-input behavior. If a feature expires at local end of day, compute the next local midnight minus one millisecond and test around daylight-saving transitions. A widget has no shared server clock, so a local-time rule is reader-local unless the product deliberately provides another clock.

Platform boundaries

A custom widget cannot reliably discover SAC user identity, SAC UI language, or a story-wide keyboard event. Pass role, user-facing text, and locale-dependent values through declared properties or story script. navigator.language is the browser language, not proof of the SAC UI language. Give every keyboard entry point a visible control and keep builder chrome language decisions separate from viewer-content localization.

Sources and evidence

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides a comprehensive environment and templates for developing SAP Analytics Cloud (SAC) Custom Widgets. It promotes robust security best practices, including subresource integrity (SRI), XSS prevention via input sanitization, and proper Shadow DOM encapsulation. It includes local tools for scaffold generation and iteration that operate on the user's machine without requiring external packages or services.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    3/16 files flagged

  • ZeroLeaks5mo

    2 findings · Score: 80/100

Signed by skilld at 4d87d99. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated last month
What it can do
Reads files Runs commands Network
All 3 allowed tools
ReadBashWebFetch
Other metadata
metadata
{
  "maintainer": "Eduard Jiglau",
  "maintainer_email": "hello@sap-ai-skills.com",
  "website": "https://sap-ai-skills.com",
  "version": "2.4.1",
  "last_verified": "2026-06-12",
  "sac_version": "2026.8",
  "errors_prevented": "40+",
  "official_docs": [
    "https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/0ac8c6754ff84605a4372468d002f2bf/75311f67527c41638ceb89af9cd8af3e.html",
    "https://help.sap.com/doc/c813a28922b54e50bd2a307b099787dc/release/en-US/CustomWidgetDevGuide_en.pdf"
  ],
  "samples_repo": "https://github.com/SAP-samples/analytics-cloud-datasphere-community-content/tree/main/SAC_Custom_Widgets",
  "keywords": [
    "sap analytics cloud",
    "sac custom widget",
    "web component sac",
    "json metadata widget",
    "widget lifecycle functions",
    "onCustomWidgetBeforeUpdate",
    "onCustomWidgetAfterUpdate",
    "onCustomWidgetResize",
    "onCustomWidgetDestroy",
    "sac data binding",
    "dataBindings feeds",
    "styling panel widget",
    "builder panel widget",
    "sac echarts integration",
    "sac d3js integration",
    "third party library sac",
    "widget hosting sac",
    "integrity hash widget",
    "sha256 integrity",
    "widget security cors",
    "sac widget debugging",
    "sac analytics designer widget",
    "optimized story experience widget",
    "sac widget api",
    "widget add-on",
    "sac script api widget",
    "shadow dom web component",
    "sac tooltip customization",
    "plot area addon",
    "sac resource zip upload",
    "root relative widget url",
    "resource file upload",
    "builder focus collapse state",
    "self contained component js",
    "resource zip artifact naming",
    "chat download artifacts"
  ]
}

README badge

README badge for secondsky/sap-skills/sap-sac-custom-widget