Initial commit
This commit is contained in:
579
references/json-schema-reference.md
Normal file
579
references/json-schema-reference.md
Normal file
@@ -0,0 +1,579 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user