Files
gh-secondsky-sap-skills-ski…/references/json-schema-reference.md
2025-11-30 08:55:27 +08:00

580 lines
14 KiB
Markdown

# SAP SAC Custom Widget JSON Schema Reference
Complete reference for the JSON metadata file that defines custom widgets.
**Source**: [SAP Custom Widget Developer Guide](https://help.sap.com/doc/c813a28922b54e50bd2a307b099787dc/release/en-US/CustomWidgetDevGuide_en.pdf) - Section 6.1
---
## Table of Contents
1. [Complete Schema Example](#complete-schema-example)
2. [Root Object](#root-object)
3. [Webcomponents Array](#webcomponents-array)
4. [Properties Object](#properties-object)
5. [Methods Object](#methods-object)
6. [Events Object](#events-object)
7. [DataBindings Object](#databindings-object)
8. [Custom Types](#custom-types)
---
## Complete Schema Example
```json
{
"id": "com.company.advancedwidget",
"version": "1.0.0",
"name": "Advanced Custom Widget",
"description": "A feature-rich custom widget with data binding",
"vendor": "Company Name",
"license": "MIT",
"icon": "[https://example.com/icon.png",](https://example.com/icon.png",)
"newInstancePrefix": "advWidget",
"webcomponents": [
{
"kind": "main",
"tag": "advanced-widget",
"url": "[https://host.com/widget.js",](https://host.com/widget.js",)
"integrity": "sha256-abc123...",
"ignoreIntegrity": false
},
{
"kind": "styling",
"tag": "advanced-widget-styling",
"url": "[https://host.com/styling.js",](https://host.com/styling.js",)
"integrity": "sha256-def456...",
"ignoreIntegrity": false
},
{
"kind": "builder",
"tag": "advanced-widget-builder",
"url": "[https://host.com/builder.js",](https://host.com/builder.js",)
"integrity": "sha256-ghi789...",
"ignoreIntegrity": false
}
],
"properties": {
"title": {
"type": "string",
"default": "Widget Title",
"description": "The widget title"
},
"value": {
"type": "number",
"default": 0,
"description": "Numeric value"
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Enable/disable widget"
},
"color": {
"type": "Color",
"default": "#336699",
"description": "Primary color"
},
"items": {
"type": "string[]",
"default": [],
"description": "List of items"
},
"config": {
"type": "Object<string>",
"default": {},
"description": "Configuration object"
}
},
"methods": {
"refresh": {
"description": "Refresh widget data",
"body": "this._refresh();"
},
"setValue": {
"description": "Set the widget value",
"parameters": [
{
"name": "newValue",
"type": "number",
"description": "The new value"
}
],
"body": "this._setValue(newValue);"
},
"getValue": {
"description": "Get the current value",
"returnType": "number",
"body": "return this._getValue();"
}
},
"events": {
"onSelect": {
"description": "Fired when an item is selected"
},
"onChange": {
"description": "Fired when value changes"
},
"onLoad": {
"description": "Fired when widget loads"
}
},
"dataBindings": {
"myData": {
"feeds": [
{
"id": "dimensions",
"description": "Dimensions",
"type": "dimension"
},
{
"id": "measures",
"description": "Measures",
"type": "mainStructureMember"
}
]
}
}
}
```
---
## Root Object
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `id` | string | **Yes** | Unique identifier using reverse domain notation (e.g., "com.company.widgetname") |
| `version` | string | **Yes** | Semantic version (e.g., "1.0.0", "2.1.3") |
| `name` | string | **Yes** | Display name shown in SAC widget panel |
| `description` | string | No | Description shown in widget panel |
| `vendor` | string | No | Developer or company name |
| `license` | string | No | License type (MIT, Apache-2.0, proprietary) |
| `icon` | string | No | URL to widget icon (recommended: 32x32 PNG) |
| `newInstancePrefix` | string | No | Prefix for auto-generated script variable names |
| `webcomponents` | array | **Yes** | Array of web component definitions |
| `properties` | object | No | Widget properties accessible via script |
| `methods` | object | No | Methods callable from script |
| `events` | object | No | Events the widget can fire |
| `dataBindings` | object | No | Data binding configuration |
### ID Best Practices
```
com.company.widgetname # Standard format
com.github.username.widget # GitHub-hosted
sap.sample.widget # SAP samples only
```
---
## Webcomponents Array
Each widget can have up to three web components:
### Main Component (Required)
```json
{
"kind": "main",
"tag": "my-widget",
"url": "[https://host.com/widget.js",](https://host.com/widget.js",)
"integrity": "sha256-abc123...",
"ignoreIntegrity": false
}
```
### Styling Panel (Optional)
```json
{
"kind": "styling",
"tag": "my-widget-styling",
"url": "[https://host.com/styling.js",](https://host.com/styling.js",)
"integrity": "sha256-def456...",
"ignoreIntegrity": false
}
```
### Builder Panel (Optional)
```json
{
"kind": "builder",
"tag": "my-widget-builder",
"url": "[https://host.com/builder.js",](https://host.com/builder.js",)
"integrity": "sha256-ghi789...",
"ignoreIntegrity": false
}
```
### Webcomponent Properties
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `kind` | string | **Yes** | "main", "styling", or "builder" |
| `tag` | string | **Yes** | Custom element tag name (lowercase, hyphenated, must contain hyphen) |
| `url` | string | **Yes** | URL to JavaScript file (HTTPS required for external hosting) |
| `integrity` | string | No | SHA256 hash for subresource integrity |
| `ignoreIntegrity` | boolean | No | Skip integrity check (development only, default: false) |
### Tag Naming Rules
- Must be lowercase
- Must contain at least one hyphen (-)
- Cannot start with a hyphen
- Cannot use reserved names (like HTML elements)
**Valid**: `my-widget`, `company-chart-v2`, `data-grid-component`
**Invalid**: `MyWidget`, `widget`, `my_widget`
---
## Properties Object
### Simple Types
```json
{
"stringProp": {
"type": "string",
"default": "default value",
"description": "A string property"
},
"numberProp": {
"type": "number",
"default": 3.14,
"description": "A floating-point number"
},
"integerProp": {
"type": "integer",
"default": 42,
"description": "An integer"
},
"booleanProp": {
"type": "boolean",
"default": true,
"description": "A boolean flag"
}
}
```
### Array Types
```json
{
"stringArray": {
"type": "string[]",
"default": ["item1", "item2"],
"description": "Array of strings"
},
"numberArray": {
"type": "number[]",
"default": [1, 2, 3],
"description": "Array of numbers"
}
}
```
### Object Types
```json
{
"objectProp": {
"type": "Object<string>",
"default": {},
"description": "Object with string values"
},
"numberObject": {
"type": "Object<number>",
"default": { "a": 1, "b": 2 },
"description": "Object with number values"
}
}
```
### Script API Types
```json
{
"colorProp": {
"type": "Color",
"default": "#336699",
"description": "Color value"
},
"selectionProp": {
"type": "Selection",
"default": {},
"description": "Selection object"
}
}
```
**Note**: For detailed information on `Color` and `Selection` types, including their JavaScript usage patterns and structure, see [Script API Data Types](advanced-topics.md#script-api-data-types) in Advanced Topics.
### Property Configuration
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | string | **Yes** | Data type |
| `default` | varies | **Yes** | Default value matching type |
| `description` | string | No | Description for documentation |
---
## Methods Object
Methods allow scripts to call functions on the widget.
### Method Without Parameters
```json
{
"refresh": {
"description": "Refresh the widget",
"body": "this._refresh();"
}
}
```
> **⚠️ Important**: The `body` must call an internal method (prefixed with `_`) to avoid infinite recursion. When SAC invokes `Widget.refresh()`, it executes the body code. If the body calls `this.refresh()`, it would recursively call itself. Always use `this._refresh()` pattern.
### Method With Parameters
```json
{
"setTitle": {
"description": "Set the widget title",
"parameters": [
{
"name": "newTitle",
"type": "string",
"description": "The new title text"
}
],
"body": "this._setTitle(newTitle);"
}
}
```
### Method With Return Value
```json
{
"getTotal": {
"description": "Get the total value",
"returnType": "number",
"body": "return this._getTotal();"
}
}
```
### Method With Multiple Parameters
```json
{
"configure": {
"description": "Configure the widget",
"parameters": [
{ "name": "width", "type": "integer", "description": "Width in pixels" },
{ "name": "height", "type": "integer", "description": "Height in pixels" },
{ "name": "title", "type": "string", "description": "Title text" }
],
"body": "this._configure(width, height, title);"
}
}
```
### Method Configuration
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `description` | string | No | Method description |
| `parameters` | array | No | Array of parameter definitions |
| `returnType` | string | No | Return type (if method returns value) |
| `body` | string | **Yes** | JavaScript code to execute |
---
## Events Object
Events allow the widget to notify scripts of user interactions or state changes.
### Basic Event
```json
{
"onSelect": {
"description": "Fired when an item is selected"
},
"onClick": {
"description": "Fired when widget is clicked"
},
"onDataChange": {
"description": "Fired when data changes"
}
}
```
### Firing Events in Web Component
```javascript
// Simple event
this.dispatchEvent(new Event("onSelect"));
// Event with data (accessible via getEventInfo in script)
this.dispatchEvent(new CustomEvent("onSelect", {
detail: {
selectedItem: "item1",
selectedIndex: 0
}
}));
```
### Script Event Handler
In Analytics Designer script:
```javascript
// Event handler
Widget_1.onSelect = function() {
console.log("Item selected");
// Access event data if provided
var eventInfo = Widget_1.getEventInfo();
};
```
---
## DataBindings Object
Enable widgets to receive data from SAC models.
### Basic Data Binding
```json
{
"dataBindings": {
"myBinding": {
"feeds": [
{
"id": "dimensions",
"description": "Dimensions",
"type": "dimension"
},
{
"id": "measures",
"description": "Measures",
"type": "mainStructureMember"
}
]
}
}
}
```
### Feed Types
| Type | Description | Use Case |
|------|-------------|----------|
| `dimension` | Dimension members | Categories, labels, hierarchies |
| `mainStructureMember` | Measures/KPIs | Numeric values, calculations |
### Feed Configuration
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | **Yes** | Unique identifier for the feed |
| `description` | string | **Yes** | Display name in Builder Panel |
| `type` | string | **Yes** | "dimension" or "mainStructureMember" |
### Multiple Feeds Example
```json
{
"dataBindings": {
"chartData": {
"feeds": [
{ "id": "category", "description": "Category", "type": "dimension" },
{ "id": "series", "description": "Series", "type": "dimension" },
{ "id": "value", "description": "Value", "type": "mainStructureMember" },
{ "id": "target", "description": "Target", "type": "mainStructureMember" }
]
}
}
}
```
### Accessing Data in JavaScript
```javascript
// Access via property
const data = this.chartData.data;
const metadata = this.chartData.metadata;
// Iterate rows
this.chartData.data.forEach(row => {
const category = row.category_0.label;
const value = row.value_0.raw;
console.log(`${category}: ${value}`);
});
// Via getDataBinding method
const binding = this.dataBindings.getDataBinding("chartData");
```
---
## Custom Types
Define reusable complex types for properties.
### Defining Custom Type
```json
{
"types": {
"ChartConfig": {
"properties": {
"chartType": { "type": "string", "default": "bar" },
"showLegend": { "type": "boolean", "default": true },
"colors": { "type": "string[]", "default": [] }
}
}
},
"properties": {
"chartConfiguration": {
"type": "ChartConfig",
"default": {
"chartType": "bar",
"showLegend": true,
"colors": ["#336699", "#669933"]
}
}
}
}
```
---
## Validation Checklist
Before deploying, verify your JSON:
- [ ] `id` follows reverse domain notation
- [ ] `version` is semantic version format
- [ ] `name` is concise and descriptive
- [ ] All `webcomponents` have valid `tag` names (lowercase, hyphenated)
- [ ] All URLs are HTTPS (for external hosting)
- [ ] All `properties` have `type` and `default`
- [ ] All `methods` have `body`
- [ ] `integrity` is set for production (or explicitly `ignoreIntegrity: true` for dev)
- [ ] `dataBindings` feeds have unique `id` values
---
**Source Documentation**:
- [SAP Custom Widget Developer Guide (PDF)](https://help.sap.com/doc/c813a28922b54e50bd2a307b099787dc/release/en-US/CustomWidgetDevGuide_en.pdf)
- [SAP Help Portal - Custom Widgets](https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/0ac8c6754ff84605a4372468d002f2bf/75311f67527c41638ceb89af9cd8af3e.html)
**Last Updated**: 2025-11-22