- [Overview](#overview)
/plugin marketplace add secondsky/sap-skills/plugin install sap-sac-custom-widget@sap-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
README.mdreferences/advanced-topics.mdreferences/best-practices-guide.mdreferences/echarts-integration.mdreferences/integration-and-migration.mdreferences/json-schema-reference.mdreferences/script-api-reference.mdreferences/widget-addon-guide.mdreferences/widget-templates.mdtemplates/basic-widget.jstemplates/data-bound-chart.jstemplates/styling-panel.jstemplates/widget.json-completetemplates/widget.json-minimalThis skill enables development of custom widgets for SAP Analytics Cloud (SAC). Custom widgets are Web Components that extend SAC stories and applications with custom visualizations, interactive elements, and specialized functionality.
Use this skill when:
Requirements:
This plugin provides specialized agents, commands, and validation hooks for comprehensive widget development support.
| Agent | Color | Purpose | Trigger Examples |
|---|---|---|---|
| widget-architect | Blue | Design widget structure, metadata, and integration patterns | "design custom widget", "plan widget architecture" |
| widget-debugger | Yellow | Troubleshoot loading, data binding, CORS, and runtime issues | "widget won't load", "CORS error", "data not binding" |
| widget-api-assistant | Green | Write JavaScript widget code, lifecycle functions, API integrations | "write widget code", "implement lifecycle functions" |
| Command | Usage | Description |
|---|---|---|
/widget-validate | /widget-validate [file] | Validate widget.json schema and widget.js structure |
/widget-generate | /widget-generate | Interactively generate widget scaffold with JSON + JS |
/widget-lint | /widget-lint [file] | Performance, security, and best practices analysis |
Automatic quality checks triggered on Write/Edit operations:
Ready-to-use scaffolds in templates/ directory:
basic-widget.js - Minimal Web Component with all lifecycle functionsdata-bound-chart.js - ECharts widget with data bindingstyling-panel.js - Runtime customization panelwidget.json-minimal - Bare-minimum metadatawidget.json-complete - Full-featured metadata with all optionsA custom widget requires two files:
1. widget.json (Metadata)
{
"id": "com.company.mywidget",
"version": "1.0.0",
"name": "My Custom Widget",
"description": "A simple custom widget",
"vendor": "Company Name",
"license": "MIT",
"icon": "",
"webcomponents": [
{
"kind": "main",
"tag": "my-custom-widget",
"url": "[https://your-host.com/widget.js",](https://your-host.com/widget.js",)
"integrity": "",
"ignoreIntegrity": true
}
],
"properties": {
"title": {
"type": "string",
"default": "My Widget"
}
},
"methods": {},
"events": {}
}
2. widget.js (Web Component)
(function() {
const template = document.createElement("template");
template.innerHTML = `
<style>
:host {
display: block;
width: 100%;
height: 100%;
}
.container {
padding: 16px;
font-family: Arial, sans-serif;
}
</style>
<div class="container">
<h3 id="title">My Widget</h3>
<div id="content"></div>
</div>
`;
class MyCustomWidget extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({ mode: "open" });
this._shadowRoot.appendChild(template.content.cloneNode(true));
this._props = {};
}
connectedCallback() {
// Called when element is added to DOM
}
onCustomWidgetBeforeUpdate(changedProperties) {
// Called BEFORE properties are updated
this._props = { ...this._props, ...changedProperties };
}
onCustomWidgetAfterUpdate(changedProperties) {
// Called AFTER properties are updated - render here
if (changedProperties.title !== undefined) {
this._shadowRoot.getElementById("title").textContent = changedProperties.title;
}
}
onCustomWidgetResize() {
// Called when widget is resized
}
onCustomWidgetDestroy() {
// Cleanup when widget is removed
}
// Property getter/setter (required for SAC framework)
get title() {
return this._props.title;
}
set title(value) {
this._props.title = value;
this.dispatchEvent(new CustomEvent("propertiesChanged", {
detail: { properties: { title: value } }
}));
}
}
customElements.define("my-custom-widget", MyCustomWidget);
})();
⚠️ Production Note: The ignoreIntegrity: true setting above is development only. For production deployments, generate a SHA256 integrity hash and set ignoreIntegrity: false.
SAP provides 15+ ready-to-use custom widget samples:
Repository: SAP-samples/SAC_Custom_Widgets
| Category | Widgets |
|---|---|
| Charts | Funnel, Pareto, Sankey, Sunburst, Tree, Line, UI5 Gantt |
| KPI/Gauge | KPI Ring, Gauge Grade, Half Donut, Nested Pie, Custom Pie |
| Utilities | File Upload, Word Cloud, Bar Gradient, Widget Add-on Sample |
Requirements: Optimized View Mode (OVM) enabled, data binding support
Note: Check third-party library licenses before production use.
Essential functions called by SAC framework:
onCustomWidgetBeforeUpdate(changedProperties) - Pre-update hookonCustomWidgetAfterUpdate(changedProperties) - Post-update (render here)onCustomWidgetResize() - Handle resize eventsonCustomWidgetDestroy() - Cleanup resourcesConfigure in widget.json to receive SAC model data:
{
"dataBindings": {
"myDataBinding": {
"feeds": [
{
"id": "dimensions",
"description": "Dimensions",
"type": "dimension"
},
{
"id": "measures",
"description": "Measures",
"type": "mainStructureMember"
}
]
}
}
}
Access data in JavaScript:
// Get data binding
const dataBinding = this.dataBindings.getDataBinding("myDataBinding");
// Access result set
const data = this.myDataBinding.data;
const metadata = this.myDataBinding.metadata;
// Iterate over rows
this.myDataBinding.data.forEach(row => {
const dimensionValue = row.dimensions_0.label;
const measureValue = row.measures_0.raw;
});
1. SAC-Hosted (Recommended, QRC Q2 2023+)
"/path/to/widget.js""integrity": "" and "ignoreIntegrity": true2. GitHub Pages
[https://username.github.io/repo/widget.js](https://username.github.io/repo/widget.js`)3. External Web Server
Access-Control-Allow-Origin: *For production, generate SHA256 hash:
# Generate hash
openssl dgst -sha256 -binary widget.js | openssl base64 -A
# Update JSON
"integrity": "sha256-abc123...",
"ignoreIntegrity": false
| Error | Cause | Solution |
|---|---|---|
| "The system couldn't load the custom widget" | Incorrect URL or hosting issue | Verify URL is accessible, check CORS |
| "Integrity check failed" | Hash mismatch | Regenerate hash after JS changes |
| Widget not appearing | Missing connectedCallback render | Call render in onCustomWidgetAfterUpdate |
| Properties not updating | Missing propertiesChanged dispatch | Use dispatchEvent with propertiesChanged |
| Data not displaying | Data binding misconfigured | Verify feeds in JSON match usage |
onCustomWidgetAfterUpdate(changedProperties) {
console.log("Widget updated:", changedProperties);
console.log("Current props:", this._props);
console.log("Data binding:", this.myDataBinding?.data);
this._render();
}
Widget Add-Ons extend built-in SAC widgets without building from scratch.
Use Cases:
Supported Charts: Bar/Column, Stacked Bar/Column, Line, Stacked Area, Numeric Point
Key Differences:
main and builder components (no styling)tooltip, plotArea, numericPoint)See references/widget-addon-guide.md for complete implementation.
templates/basic-widget.js - Minimal Web Component scaffold (~60 lines)templates/data-bound-chart.js - ECharts widget with SAC data binding (~120 lines)templates/styling-panel.js - Styling panel for runtime customization (~150 lines)templates/widget.json-minimal - Bare-minimum metadata (~25 lines)templates/widget.json-complete - Full-featured metadata (~100 lines)references/json-schema-reference.md - Complete JSON schema documentationreferences/widget-templates.md - Additional widget template patterns (6 templates)references/echarts-integration.md - ECharts library integration guidereferences/widget-addon-guide.md - Widget Add-On development (QRC Q4 2023+)references/best-practices-guide.md - Performance, security, and development guidelinesreferences/advanced-topics.md - Custom types, script API types, installationreferences/integration-and-migration.md - Script integration, content transportreferences/script-api-reference.md - DataSource, Selection, MemberInfo APIsPrimary References (for skill updates):
Sample Widgets:
v2.0.0 (2025-12-27)
v1.2.0 (2025-11-26)
v1.1.0 (2025-11-22)
v1.0.0 (2025-11-22)
Last Verified: 2025-12-27 | SAC Version: 2025.21 | Skill Version: 2.0.0
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.