From d777dd64fecc192b939480a77b7064ad68f82d89 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 14 May 2026 14:45:29 +0300 Subject: [PATCH 01/13] feat(ui5): Add integration cards skill --- plugins/ui5/skills/integration-cards/SKILL.md | 1943 +++++++++++++++++ 1 file changed, 1943 insertions(+) create mode 100644 plugins/ui5/skills/integration-cards/SKILL.md diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md new file mode 100644 index 0000000..a5e79c8 --- /dev/null +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -0,0 +1,1943 @@ +--- +name: integration-cards +description: Guidelines and best practices for developing UI Integration Cards (also called UI5 Integration Cards). This skill MUST be loaded before working on any Integration Card related task. +--- + +# UI Integration Cards Development Guidelines + +> *This document outlines the fundamental rules and best practices an AI agent must follow when developing or modifying Integration Cards. Adherence to these guidelines is critical for creating modern, maintainable, and performant UI Integration Cards.* +## 1. Coding Guidelines +- **ALWAYS** strive to create declarative Integration Card, such as "Calendar", "List", "Table", "Timeline", "Object" or "Analytical". + - create an Integration Card Extension only in exceptional cases. +- **ALWAYS** create links using the `actions` property. +- **ALWAYS** refer to parameters using correct syntax - `{parameters>/parameterKey/value}`. +- **ALWAYS** perform validation of the integration card as described in [2. Validation](#2-validation). +- **ALWAYS** show a preview of the generated card following the [4. Preview Instructions](#4-preview-instructions). +- **ALWAYS** generate new declarative integration cards using the `create_integration_card` tool. + +### 1.1 Data +- **NEVER** modify the provided data under any circumstances. +- **ALWAYS** include the service URL directly in the card manifest when one is supplied. +- **ALWAYS** reference destinations by name when available. Configure the destination in the `sap.card/configuration/destinations/` and reuse it with binding syntax like `{{destinations.destinationName}}`. +- **NEVER** replace destination name with its URL. +- **ALWAYS** place data configuration in: `"sap.card"/data/` +- **NEVER** place data configuration in: + - `"sap.card"/content/data/` + - `"sap.card"/header/data/` +- Data can be provided via: + 1. Inline JSON object + 2. Network request (HTTP/HTTPS/Destination) + 3. Extension method call +- **ALWAYS** verify these paths are correctly set: + - `"sap.card"/data/path` (Primary data path) + - `"sap.card"/content/data/path` (Content-specific path. It overrides the primary data path) + - `"sap.card"/header/data/path` (Header-specific path. It overrides the primary data path) + +#### 1.1.1 Data Errors Detection +- Symptom: "No data to display" message appears. +- Cause: Incorrect data configuration or data path in the content incorrectly overrides the primary data path. +- Solution: Verify all rules in [1.1 Data](#11-data) are properly followed. + +### 1.2 Internationalization +- **ALWAYS** bind properties that are not bound to the data to the `i18n` model. + +### 1.3 Analytical Cards +- **ALWAYS** follow [6. Analytical Cards Coding Guidelines](#6-analytical-cards-coding-guidelines) when developing Analytical cards. + +### 1.4 Configuration Editor +- **ALWAYS** follow [5. Configuration Editor](#5-configuration-editor) guidelines when creating or modifying Configuration Editors for Integration Cards. + +## 2. Validation +- **ALWAYS** ensure that `manifest.json` file is valid JSON. +- **ALWAYS** ensure that in `manifest.json` file the property `sap.app/type` is set to `"card"`. +- **ALWAYS** validate the `manifest.json` against the UI5 Manifest schema. Use the `run_manifest_validation` tool to do this. +- **ALWAYS** avoid using deprecated properties in `manifest.json` and elsewhere. +- **NEVER** treat Integration Cards' project as UI5 project, except for cards of type "Component". + +## 3. Card Explorer +- The Card Explorer provides detailed documentation for the Integration Cards schema, including descriptions of every property, guidance for integrating cards into hosting environments, configuration editor documentation with examples, and broader best practices. It is available at: https://ui5.sap.com/test-resources/sap/ui/integration/demokit/cardExplorer/webapp/index.html + +## 4. Preview Instructions +- If preview of the card must be shown, **ALWAYS** check the card folder for an existing preview file and any accompanying instructions or scripts, and reuse them if available. + * for example, in NodeJS-based projects, search the `package.json` file for `start` or similar script. If such is available, use it + * also search in the `README.md` file. +- If preview instructions are not available, you have to create an HTML page that contains a `ui-integration` card element which references the card manifest. Then serve the HTML page using `http` server. + +## 5. Configuration Editor +Configuration Editor allows different personas to customize Integration Cards without modifying the manifest file directly. +The following roles/personas are supported: +- Administrator +- Page/Content Administrator +- Translator + +The Configuration Editor is implemented through two key components: + +1. **Definition file**: Create a `dt/Configuration.js` file that exports a Designtime definition object +2. **Manifest reference**: Reference this definition in the `manifest.json` under the `sap.card/configuration/editor` property + +The `dt/Configuration.js` file defines the Configuration Editor's structure by specifying: +- Form layout and field definitions +- Input controls and visualizations +- Validation rules and field relationships +- Grouping and organization of configuration options + +When creating or modifying Integration Cards, follow these guidelines for Configuration Editors: +- Assume the role of Administrator persona when designing the Configuration Editor. +- **ALWAYS** ensure that the Configuration Editor reflects the current structure and fields of the `manifest.json`. +- **ALWAYS** make the existing fields in the `manifest.json` configurable via the editor. For example manifest parameters, title, subtitle, icon of the header, etc. +- **NEVER** add fields to the editor that do not exist in the `manifest.json`. +- **ALWAYS** remove fields from the editor when removing them from the `manifest.json`. +- **ALWAYS** add fields in the Configuration Editor when adding them to the `manifest.json`. + +### 5.1 Example: +`manifest.json` file: +```json +{ + "sap.app": { + "id": "test.editor", + "type": "card", + "title": "Test Card", + "applicationVersion": { + "version": "1.0.0" + } + }, + "sap.ui": { + "technology": "UI5" + }, + "sap.card": { + "type": "List", + "configuration": { + "editor": "./dt/Configuration", + "parameters": { + "cardTitle": { + "value": "Customers" + }, + "icon": { + "value": "sap-icon://account" + }, + "maxItems": { + "value": 3 + }, + "showDescription": { + "value": true + }, + "dateContext": { + "value": "2020-09-02" + }, + "Customers": { + "value": ["ALFKI"] + }, + "northwindDestination": { + "value": "northwind" + } + }, + "destinations": { + "northwind": { + "name": "Northwind_V4", + "defaultUrl": "https://services.odata.org/V4/Northwind/Northwind.svc" + } + } + }, + "data": { + "request": { + "url": "{{destinations.northwind}}/Customers", + "parameters": { + "$select": "CustomerID,CompanyName,ContactName", + "$top": "{parameters>/maxItems/value}" + } + } + }, + "header": { + "title": "{parameters>/cardTitle/value}", + "subtitle": "As of {parameters>/dateContext/value}", + "icon": { + "src": "{parameters>/icon/value}", + "shape": "Circle" + } + }, + "content": { + "data": { + "path": "/value" + }, + "item": { + "title": "{CompanyName}", + "description": "{= ${parameters>/showDescription/value} ? ${ContactName} : '' }" + }, + "maxItems": "{parameters>/maxItems/value}" + } + } +} +``` + +`dt/Configuration.js` file: +```javascript +sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { + "use strict"; + + return function () { + return new Designtime({ + form: { + items: { + + /* ======================= + General + ======================= */ + generalGroup: { + type: "group", + label: "General" + }, + + cardTitle: { + manifestpath: "/sap.card/configuration/parameters/cardTitle/value", + type: "string", + label: "Card Title", + translatable: true, + required: true, + allowDynamicValues: true + }, + + icon: { + manifestpath: "/sap.card/header/icon/src", + type: "string", + label: "Icon", + visualization: { + type: "IconSelect", + settings: { + value: "{currentSettings>value}", + editable: "{currentSettings>editable}" + } + } + }, + + iconShape: { + manifestpath: "/sap.card/header/icon/shape", + type: "string", + label: "Icon Shape", + visualization: { + type: "ShapeSelect", + settings: { + value: "{currentSettings>value}", + editable: "{currentSettings>editable}" + } + }, + cols: 1 + }, + + iconBackground: { + manifestpath: "/sap.card/header/icon/backgroundColor", + type: "string", + label: "Icon Background", + visualization: { + type: "ColorSelect", + settings: { + enumValue: "{currentSettings>value}", + editable: "{currentSettings>editable}" + } + }, + cols: 1 + }, + + /* ======================= + Data & Behavior + ======================= */ + dataGroup: { + type: "group", + label: "Data & Behavior" + }, + + maxItems: { + manifestpath: "/sap.card/configuration/parameters/maxItems/value", + type: "integer", + label: "Maximum Items", + visualization: { + type: "Slider", + settings: { + value: "{currentSettings>value}", + min: 1, + max: 10, + width: "100%", + enabled: "{currentSettings>editable}" + } + } + }, + + showDescription: { + manifestpath: "/sap.card/configuration/parameters/showDescription/value", + type: "boolean", + label: "Show Contact Name", + visualization: { + type: "Switch", + settings: { + state: "{currentSettings>value}", + customTextOn: "Show", + customTextOff: "Hide", + enabled: "{currentSettings>editable}" + } + } + }, + + dateContext: { + manifestpath: "/sap.card/configuration/parameters/dateContext/value", + type: "date", + label: "Date Context" + }, + + /* ======================= + Filtering + ======================= */ + filterGroup: { + type: "group", + label: "Customer Filter" + }, + + CustomerID: { + manifestpath: "/sap.card/configuration/parameters/CustomerID/value", + type: "string", + label: "Customer ID", + values: { + data: { + request: { + url: "{{destinations.northwind}}/Customers", + parameters: { + "$select": "CustomerID,CompanyName" + } + }, + path: "/value" + }, + item: { + key: "{CustomerID}", + text: "{CompanyName}" + } + } + } + } + }, + preview: { + modes: "None" + } + }); + }; +}); + +``` + +## 6. Analytical Cards Coding Guidelines +- **ALWAYS** set `sap.card/content/chartType` property. +- **ALWAYS** adjust `sap.card/content/measures`, `sap.card/content/dimensions` and `sap.card/content/feeds` to match the `sap.card/content/chartType` property and data structure. This is critical for proper data display. +- **ALWAYS** use `sap.card/content/chartProperties` to adjust labels, colors, the legend, and other chart aspects. +- **ALWAYS** define each feed with its type (Dimension or Measure), its unique identifier (uid), and the associated values using defined measures and dimensions. Example: +```json +"feeds": [ + { + "type": "Dimension", + "uid": "color", + "values": [ + "Store Name" + ] + }, + { + "type": "Measure", + "uid": "size", + "values": [ + "Revenue" + ] + } +] +``` +- **ALWAYS** ensure the `uid` in `feeds` exactly matches the UID required for the selected chartType (e.g., color, size, dataFrame). + +### 6.1 Comprehensive List of All Chart Types, UIDs and Examples + +1. donut/pie + * UIDs: size, color, dataFrame + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueDataField}" + } + ], + "dimensions": [ + { + "name": "Product Category", + "value": "{productCategoryField}" + } + ], + "feeds": [ + { + "type": "Measure", + "uid": "size", + "values": ["Revenue"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Product Category"] + } + ] + } + ``` + +2. heatmap + * UIDs: categoryAxis, categoryAxis2, color + * Example: + ```json + { + "measures": [ + { + "name": "Temperature", + "value": "{temperatureField}" + } + ], + "dimensions": [ + { + "name": "Location", + "value": "{locationField}" + }, + { + "name": "Product", + "value": "{productField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Location"] + }, + { + "type": "Dimension", + "uid": "categoryAxis2", + "values": ["Product"] + }, + { + "type": "Measure", + "uid": "color", + "values": ["Temperature"] + } + ] + } + ``` + +3. treemap + * UIDs: title, color, weight + * Example: + ```json + { + "measures": [ + { + "name": "Profit", + "value": "{profitField}" + }, + { + "name": "Budget", + "value": "{budgetField}" + } + ], + "dimensions": [ + { + "name": "Department", + "value": "{departmentField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "title", + "values": ["Department"] + }, + { + "type": "Measure", + "uid": "color", + "values": ["Profit"] + }, + { + "type": "Measure", + "uid": "weight", + "values": ["Budget"] + } + ] + } + ``` + +4. bar + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + } + ], + "dimensions": [ + { + "name": "Month", + "value": "{monthField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Month"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales"] + } + ] + } + ``` + +5. dual_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Expenses", + "value": "{expensesField}" + } + ], + "dimensions": [ + { + "name": "Quarter", + "value": "{quarterField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Quarter"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Expenses"] + } + ] + } + ``` + +6. column + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + } + ], + "dimensions": [ + { + "name": "Month", + "value": "{monthField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Month"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + } + ] + } + ``` + +7. timeseries_column + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Traffic", + "value": "{trafficField}" + } + ], + "dimensions": [ + { + "name": "Date", + "value": "{dateField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Date"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Traffic"] + } + ] + } + ``` + +8. dual_column + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Costs", + "value": "{costsField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Costs"] + } + ] + } + ``` + +9. stacked_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + } + ] + } + ``` + +10. stacked_column + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Market Share", + "value": "{marketShareField}" + } + ], + "dimensions": [ + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Sector"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Market Share"] + } + ] + } + ``` + +11. timeseries_stacked_column + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Investment", + "value": "{investmentField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Investment"] + } + ] + } + ``` + +12. 100_stacked_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Costs", + "value": "{costsField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Costs"] + } + ] + } + ``` + +13. 100_stacked_column + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Market Share", + "value": "{marketShareField}" + } + ], + "dimensions": [ + { + "name": "Product", + "value": "{productField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Product"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Market Share"] + } + ] + } + ``` + +14. timeseries_100_stacked_column + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Investment", + "value": "{investmentField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Investment"] + } + ] + } + ``` + +15. dual_stacked_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Profit", + "value": "{profitField}" + } + ], + "dimensions": [ + { + "name": "Brand", + "value": "{brandField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Brand"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Profit"] + } + ] + } + ``` + +16. dual_stacked_column + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Growth", + "value": "{growthField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" + } + ], + "dimensions": [ + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Sector"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Growth"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Revenue"] + } + ] + } + ``` + +17. 100_dual_stacked_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + }, + { + "name": "Growth", + "value": "{growthField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Growth"] + } + ] + } + ``` + +18. 100_dual_stacked_column + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + }, + { + "name": "Growth", + "value": "{growthField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Growth"] + } + ] + } + ``` + +19. line + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Price", + "value": "{priceField}" + } + ], + "dimensions": [ + { + "name": "Time", + "value": "{timeField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Time"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Price"] + } + ] + } + ``` + +20. dual_line + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Price", + "value": "{priceField}" + }, + { + "name": "Volume", + "value": "{volumeField}" + } + ], + "dimensions": [ + { + "name": "Time", + "value": "{timeField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Time"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Price"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Volume"] + } + ] + } + ``` + +21. timeseries_line + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Temperature", + "value": "{temperatureField}" + } + ], + "dimensions": [ + { + "name": "Date", + "value": "{dateField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Date"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Temperature"] + } + ] + } + ``` + +22. bubble + * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth + * Example: + ```json + { + "measures": [ + { + "name": "Expansion", + "value": "{expansionField}" + }, + { + "name": "Size", + "value": "{sizeField}" + } + ], + "dimensions": [ + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Measure", + "uid": "bubbleWidth", + "values": ["Size"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Sector"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Expansion"] + } + ] + } + ``` + +23. time_bubble + * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth + * Example: + ```json + { + "measures": [ + { + "name": "Expansion", + "value": "{expansionField}" + }, + { + "name": "Size", + "value": "{sizeField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}" + }, + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "bubbleWidth", + "values": ["Size"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Sector"] + } + ] + } + ``` + +24. timeseries_bubble + * UIDs: color, shape, valueAxis, timeAxis, bubbleWidth + * Example: + ```json + { + "measures": [ + { + "name": "Size", + "value": "{sizeField}" + }, + { + "name": "Performance", + "value": "{performanceField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + }, + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "bubbleWidth", + "values": ["Size"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Sector"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Performance"] + } + ] + } + ``` + +25. scatter + * UIDs: dataFrame, color, shape, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Efficiency", + "value": "{efficiencyField}" + }, + { + "name": "Cost", + "value": "{costField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Efficiency"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Cost"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Region"] + } + ] + } + ``` + +26. timeseries_scatter + * UIDs: color, shape, valueAxis, timeAxis + * Example: + ```json + { + "measures": [ + { + "name": "Performance", + "value": "{performanceField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Performance"] + } + ] + } + ``` + +27. area + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Score", + "value": "{scoreField}" + } + ], + "dimensions": [ + { + "name": "Competency", + "value": "{competencyField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Competency"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Score"] + } + ] + } + ``` + +28. radar + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Proficiency Level", + "value": "{proficiencyField}" + } + ], + "dimensions": [ + { + "name": "Skill", + "value": "{skillField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Skill"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Proficiency Level"] + } + ] + } + ``` + +29. vertical_bullet + * UIDs: categoryAxis, color, actualValues, additionalValues, targetValues, forecastValues + * Example: + ```json + { + "measures": [ + { + "name": "Achievement", + "value": "{achievementField}" + } + ], + "dimensions": [ + { + "name": "Target", + "value": "{targetField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Target"] + }, + { + "type": "Measure", + "uid": "actualValues", + "values": ["Achievement"] + } + ] + } + ``` + +30. bullet + * UIDs: categoryAxis, color, actualValues, additionalValues, targetValues, forecastValues + * Example: + ```json + { + "measures": [ + { + "name": "Achievement", + "value": "{achievementField}" + } + ], + "dimensions": [ + { + "name": "Target", + "value": "{targetField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Target"] + }, + { + "type": "Measure", + "uid": "actualValues", + "values": ["Achievement"] + } + ] + } + ``` + +31. timeseries_bullet + * UIDs: timeAxis, color, actualValues, additionalValues, targetValues + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + } + ], + "dimensions": [ + { + "name": "Date", + "value": "{dateField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Date"] + }, + { + "type": "Measure", + "uid": "actualValues", + "values": ["Sales"] + } + ] + } + ``` + +32. waterfall + * UIDs: categoryAxis, waterfallType, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Change", + "value": "{changeField}" + } + ], + "dimensions": [ + { + "name": "Phase", + "value": "{phaseField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Phase"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Change"] + } + ] + } + ``` + +33. timeseries_waterfall + * UIDs: timeAxis, valueAxis, color + * Example: + ```json + { + "measures": [ + { + "name": "Financial Change", + "value": "{financialChangeField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Financial Change"] + } + ] + } + ``` + +34. horizontal_waterfall + * UIDs: categoryAxis, waterfallType, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Growth", + "value": "{growthField}" + } + ], + "dimensions": [ + { + "name": "Milestone", + "value": "{milestoneField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Milestone"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Growth"] + } + ] + } + ``` + +35. combination + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Expense", + "value": "{expenseField}" + } + ], + "dimensions": [ + { + "name": "Period", + "value": "{periodField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Period"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Expense"] + } + ] + } + ``` + +36. stacked_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + } + ], + "dimensions": [ + { + "name": "Category", + "value": "{categoryField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Category"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + } + ] + } + ``` + +37. horizontal_stacked_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Growth", + "value": "{growthField}" + } + ], + "dimensions": [ + { + "name": "Product", + "value": "{productField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Product"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Growth"] + } + ] + } + ``` + +38. dual_stacked_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Costs", + "value": "{costsField}" + } + ], + "dimensions": [ + { + "name": "Time Period", + "value": "{timePeriodField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Time Period"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Costs"] + } + ] + } + ``` + +39. dual_horizontal_stacked_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + }, + { + "name": "Returns", + "value": "{returnsField}" + } + ], + "dimensions": [ + { + "name": "Brand", + "value": "{brandField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Brand"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Returns"] + } + ] + } + ``` + +40. dual_horizontal_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Engagement", + "value": "{engagementField}" + }, + { + "name": "Spend", + "value": "{spendField}" + } + ], + "dimensions": [ + { + "name": "Campaign", + "value": "{campaignField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Campaign"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Engagement"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Spend"] + } + ] + } + ``` + +41. dual_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Sales Revenue", + "value": "{salesRevenueField}" + }, + { + "name": "Operating Cost", + "value": "{operatingCostField}" + } + ], + "dimensions": [ + { + "name": "Time Frame", + "value": "{timeFrameField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Time Frame"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Operating Cost"] + } + ] + } + ``` + +42. timeseries_combination + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Earnings", + "value": "{earningsField}" + } + ], + "dimensions": [ + { + "name": "Month", + "value": "{monthField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Month"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Earnings"] + } + ] + } + ``` + +43. dual_timeseries_combination + * UIDs: timeAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Cost", + "value": "{costField}" + } + ], + "dimensions": [ + { + "name": "Month", + "value": "{monthField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Month"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Cost"] + } + ] + } + ``` + +44. timeseries_stacked_combination + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Performance", + "value": "{performanceField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Performance"] + } + ] + } + ``` From e2257370856a8042642956515869f425117a9d69 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Mon, 18 May 2026 09:52:42 +0300 Subject: [PATCH 02/13] docs: Correct samples for bubble and combination charts --- plugins/ui5/skills/integration-cards/SKILL.md | 77 +++++++++++++++---- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md index a5e79c8..565060a 100644 --- a/plugins/ui5/skills/integration-cards/SKILL.md +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -1107,6 +1107,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { 22. bubble * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth + * Note: Requires at least 3 measures (for valueAxis, valueAxis2, and bubbleWidth) * Example: ```json { @@ -1115,6 +1116,10 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { "name": "Expansion", "value": "{expansionField}" }, + { + "name": "Cost", + "value": "{costField}" + }, { "name": "Size", "value": "{sizeField}" @@ -1127,6 +1132,16 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { } ], "feeds": [ + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Expansion"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Cost"] + }, { "type": "Measure", "uid": "bubbleWidth", @@ -1136,18 +1151,14 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { "type": "Dimension", "uid": "color", "values": ["Sector"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Expansion"] } ] } ``` 23. time_bubble - * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth + * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth, timeAxis + * Note: Requires timeAxis dimension, at least 2 measures (for valueAxis and bubbleWidth), and a color dimension * Example: ```json { @@ -1156,6 +1167,10 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { "name": "Expansion", "value": "{expansionField}" }, + { + "name": "Growth", + "value": "{growthField}" + }, { "name": "Size", "value": "{sizeField}" @@ -1177,6 +1192,16 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { "uid": "timeAxis", "values": ["Year"] }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Expansion"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Growth"] + }, { "type": "Measure", "uid": "bubbleWidth", @@ -1193,6 +1218,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { 24. timeseries_bubble * UIDs: color, shape, valueAxis, timeAxis, bubbleWidth + * Note: Requires timeAxis dimension with dataType "date", bubbleWidth measure, and valueAxis measure * Example: ```json { @@ -1244,6 +1270,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { 25. scatter * UIDs: dataFrame, color, shape, valueAxis, valueAxis2 + * Note: Requires 2 measures for valueAxis and valueAxis2 * Example: ```json { @@ -1576,6 +1603,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { 35. combination * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1583,6 +1611,10 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "name": "Expense", "value": "{expenseField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" } ], "dimensions": [ @@ -1600,7 +1632,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "type": "Measure", "uid": "valueAxis", - "values": ["Expense"] + "values": ["Expense", "Revenue"] } ] } @@ -1608,6 +1640,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { 36. stacked_combination * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1615,6 +1648,10 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "name": "Revenue", "value": "{revenueField}" + }, + { + "name": "Sales", + "value": "{salesField}" } ], "dimensions": [ @@ -1632,7 +1669,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "type": "Measure", "uid": "valueAxis", - "values": ["Revenue"] + "values": ["Revenue", "Sales"] } ] } @@ -1640,6 +1677,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { 37. horizontal_stacked_combination * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1647,6 +1685,10 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "name": "Growth", "value": "{growthField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" } ], "dimensions": [ @@ -1664,7 +1706,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "type": "Measure", "uid": "valueAxis", - "values": ["Growth"] + "values": ["Growth", "Revenue"] } ] } @@ -1836,6 +1878,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { 42. timeseries_combination * UIDs: timeAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1843,6 +1886,10 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "name": "Earnings", "value": "{earningsField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" } ], "dimensions": [ @@ -1861,7 +1908,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "type": "Measure", "uid": "valueAxis", - "values": ["Earnings"] + "values": ["Earnings", "Revenue"] } ] } @@ -1911,6 +1958,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { 44. timeseries_stacked_combination * UIDs: timeAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1918,6 +1966,10 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "name": "Performance", "value": "{performanceField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" } ], "dimensions": [ @@ -1936,8 +1988,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { { "type": "Measure", "uid": "valueAxis", - "values": ["Performance"] + "values": ["Performance", "Revenue"] } ] - } - ``` + } \ No newline at end of file From d6677fe93532bac64243f483fb94e230b76e2ca0 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Mon, 18 May 2026 13:22:18 +0300 Subject: [PATCH 03/13] refactor(ui5): Split integration cards skill into reference files --- plugins/ui5/skills/integration-cards/SKILL.md | 1883 +---------------- .../references/analytical_chart_types.md | 1599 ++++++++++++++ .../configuration_editor_example.md | 233 ++ 3 files changed, 1837 insertions(+), 1878 deletions(-) create mode 100644 plugins/ui5/skills/integration-cards/references/analytical_chart_types.md create mode 100644 plugins/ui5/skills/integration-cards/references/configuration_editor_example.md diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md index 565060a..ceffc78 100644 --- a/plugins/ui5/skills/integration-cards/SKILL.md +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -6,6 +6,7 @@ description: Guidelines and best practices for developing UI Integration Cards ( # UI Integration Cards Development Guidelines > *This document outlines the fundamental rules and best practices an AI agent must follow when developing or modifying Integration Cards. Adherence to these guidelines is critical for creating modern, maintainable, and performant UI Integration Cards.* + ## 1. Coding Guidelines - **ALWAYS** strive to create declarative Integration Card, such as "Calendar", "List", "Table", "Timeline", "Object" or "Analytical". - create an Integration Card Extension only in exceptional cases. @@ -46,6 +47,7 @@ description: Guidelines and best practices for developing UI Integration Cards ( ### 1.4 Configuration Editor - **ALWAYS** follow [5. Configuration Editor](#5-configuration-editor) guidelines when creating or modifying Configuration Editors for Integration Cards. +- **ALWAYS** load [references/configuration_editor_example.md](references/configuration_editor_example.md) whenever a `dt/Configuration.js` file is present in the project, is being created, or is being modified — even if the user did not explicitly mention the Configuration Editor. ## 2. Validation - **ALWAYS** ensure that `manifest.json` file is valid JSON. @@ -77,7 +79,7 @@ The Configuration Editor is implemented through two key components: The `dt/Configuration.js` file defines the Configuration Editor's structure by specifying: - Form layout and field definitions -- Input controls and visualizations +- Input controls and visualizations - Validation rules and field relationships - Grouping and organization of configuration options @@ -89,237 +91,7 @@ When creating or modifying Integration Cards, follow these guidelines for Config - **ALWAYS** remove fields from the editor when removing them from the `manifest.json`. - **ALWAYS** add fields in the Configuration Editor when adding them to the `manifest.json`. -### 5.1 Example: -`manifest.json` file: -```json -{ - "sap.app": { - "id": "test.editor", - "type": "card", - "title": "Test Card", - "applicationVersion": { - "version": "1.0.0" - } - }, - "sap.ui": { - "technology": "UI5" - }, - "sap.card": { - "type": "List", - "configuration": { - "editor": "./dt/Configuration", - "parameters": { - "cardTitle": { - "value": "Customers" - }, - "icon": { - "value": "sap-icon://account" - }, - "maxItems": { - "value": 3 - }, - "showDescription": { - "value": true - }, - "dateContext": { - "value": "2020-09-02" - }, - "Customers": { - "value": ["ALFKI"] - }, - "northwindDestination": { - "value": "northwind" - } - }, - "destinations": { - "northwind": { - "name": "Northwind_V4", - "defaultUrl": "https://services.odata.org/V4/Northwind/Northwind.svc" - } - } - }, - "data": { - "request": { - "url": "{{destinations.northwind}}/Customers", - "parameters": { - "$select": "CustomerID,CompanyName,ContactName", - "$top": "{parameters>/maxItems/value}" - } - } - }, - "header": { - "title": "{parameters>/cardTitle/value}", - "subtitle": "As of {parameters>/dateContext/value}", - "icon": { - "src": "{parameters>/icon/value}", - "shape": "Circle" - } - }, - "content": { - "data": { - "path": "/value" - }, - "item": { - "title": "{CompanyName}", - "description": "{= ${parameters>/showDescription/value} ? ${ContactName} : '' }" - }, - "maxItems": "{parameters>/maxItems/value}" - } - } -} -``` - -`dt/Configuration.js` file: -```javascript -sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { - "use strict"; - - return function () { - return new Designtime({ - form: { - items: { - - /* ======================= - General - ======================= */ - generalGroup: { - type: "group", - label: "General" - }, - - cardTitle: { - manifestpath: "/sap.card/configuration/parameters/cardTitle/value", - type: "string", - label: "Card Title", - translatable: true, - required: true, - allowDynamicValues: true - }, - - icon: { - manifestpath: "/sap.card/header/icon/src", - type: "string", - label: "Icon", - visualization: { - type: "IconSelect", - settings: { - value: "{currentSettings>value}", - editable: "{currentSettings>editable}" - } - } - }, - - iconShape: { - manifestpath: "/sap.card/header/icon/shape", - type: "string", - label: "Icon Shape", - visualization: { - type: "ShapeSelect", - settings: { - value: "{currentSettings>value}", - editable: "{currentSettings>editable}" - } - }, - cols: 1 - }, - - iconBackground: { - manifestpath: "/sap.card/header/icon/backgroundColor", - type: "string", - label: "Icon Background", - visualization: { - type: "ColorSelect", - settings: { - enumValue: "{currentSettings>value}", - editable: "{currentSettings>editable}" - } - }, - cols: 1 - }, - - /* ======================= - Data & Behavior - ======================= */ - dataGroup: { - type: "group", - label: "Data & Behavior" - }, - - maxItems: { - manifestpath: "/sap.card/configuration/parameters/maxItems/value", - type: "integer", - label: "Maximum Items", - visualization: { - type: "Slider", - settings: { - value: "{currentSettings>value}", - min: 1, - max: 10, - width: "100%", - enabled: "{currentSettings>editable}" - } - } - }, - - showDescription: { - manifestpath: "/sap.card/configuration/parameters/showDescription/value", - type: "boolean", - label: "Show Contact Name", - visualization: { - type: "Switch", - settings: { - state: "{currentSettings>value}", - customTextOn: "Show", - customTextOff: "Hide", - enabled: "{currentSettings>editable}" - } - } - }, - - dateContext: { - manifestpath: "/sap.card/configuration/parameters/dateContext/value", - type: "date", - label: "Date Context" - }, - - /* ======================= - Filtering - ======================= */ - filterGroup: { - type: "group", - label: "Customer Filter" - }, - - CustomerID: { - manifestpath: "/sap.card/configuration/parameters/CustomerID/value", - type: "string", - label: "Customer ID", - values: { - data: { - request: { - url: "{{destinations.northwind}}/Customers", - parameters: { - "$select": "CustomerID,CompanyName" - } - }, - path: "/value" - }, - item: { - key: "{CustomerID}", - text: "{CompanyName}" - } - } - } - } - }, - preview: { - modes: "None" - } - }); - }; -}); - -``` +- **ALWAYS** load [references/configuration_editor_example.md](references/configuration_editor_example.md) before creating or modifying a Configuration Editor (i.e. a `dt/Configuration.js` file). It contains a complete example of a `manifest.json` paired with the corresponding `dt/Configuration.js` file. ## 6. Analytical Cards Coding Guidelines - **ALWAYS** set `sap.card/content/chartType` property. @@ -346,1649 +118,4 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { ``` - **ALWAYS** ensure the `uid` in `feeds` exactly matches the UID required for the selected chartType (e.g., color, size, dataFrame). -### 6.1 Comprehensive List of All Chart Types, UIDs and Examples - -1. donut/pie - * UIDs: size, color, dataFrame - * Example: - ```json - { - "measures": [ - { - "name": "Revenue", - "value": "{revenueDataField}" - } - ], - "dimensions": [ - { - "name": "Product Category", - "value": "{productCategoryField}" - } - ], - "feeds": [ - { - "type": "Measure", - "uid": "size", - "values": ["Revenue"] - }, - { - "type": "Dimension", - "uid": "color", - "values": ["Product Category"] - } - ] - } - ``` - -2. heatmap - * UIDs: categoryAxis, categoryAxis2, color - * Example: - ```json - { - "measures": [ - { - "name": "Temperature", - "value": "{temperatureField}" - } - ], - "dimensions": [ - { - "name": "Location", - "value": "{locationField}" - }, - { - "name": "Product", - "value": "{productField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Location"] - }, - { - "type": "Dimension", - "uid": "categoryAxis2", - "values": ["Product"] - }, - { - "type": "Measure", - "uid": "color", - "values": ["Temperature"] - } - ] - } - ``` - -3. treemap - * UIDs: title, color, weight - * Example: - ```json - { - "measures": [ - { - "name": "Profit", - "value": "{profitField}" - }, - { - "name": "Budget", - "value": "{budgetField}" - } - ], - "dimensions": [ - { - "name": "Department", - "value": "{departmentField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "title", - "values": ["Department"] - }, - { - "type": "Measure", - "uid": "color", - "values": ["Profit"] - }, - { - "type": "Measure", - "uid": "weight", - "values": ["Budget"] - } - ] - } - ``` - -4. bar - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Sales", - "value": "{salesField}" - } - ], - "dimensions": [ - { - "name": "Month", - "value": "{monthField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Month"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Sales"] - } - ] - } - ``` - -5. dual_bar - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Revenue", - "value": "{revenueField}" - }, - { - "name": "Expenses", - "value": "{expensesField}" - } - ], - "dimensions": [ - { - "name": "Quarter", - "value": "{quarterField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Quarter"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Revenue"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Expenses"] - } - ] - } - ``` - -6. column - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Revenue", - "value": "{revenueField}" - } - ], - "dimensions": [ - { - "name": "Month", - "value": "{monthField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Month"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Revenue"] - } - ] - } - ``` - -7. timeseries_column - * UIDs: timeAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Traffic", - "value": "{trafficField}" - } - ], - "dimensions": [ - { - "name": "Date", - "value": "{dateField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Date"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Traffic"] - } - ] - } - ``` - -8. dual_column - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Revenue", - "value": "{revenueField}" - }, - { - "name": "Costs", - "value": "{costsField}" - } - ], - "dimensions": [ - { - "name": "Region", - "value": "{regionField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Region"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Revenue"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Costs"] - } - ] - } - ``` - -9. stacked_bar - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Revenue", - "value": "{revenueField}" - } - ], - "dimensions": [ - { - "name": "Region", - "value": "{regionField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Region"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Revenue"] - } - ] - } - ``` - -10. stacked_column - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Market Share", - "value": "{marketShareField}" - } - ], - "dimensions": [ - { - "name": "Sector", - "value": "{sectorField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Sector"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Market Share"] - } - ] - } - ``` - -11. timeseries_stacked_column - * UIDs: timeAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Investment", - "value": "{investmentField}" - } - ], - "dimensions": [ - { - "name": "Year", - "value": "{yearField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Year"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Investment"] - } - ] - } - ``` - -12. 100_stacked_bar - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Costs", - "value": "{costsField}" - } - ], - "dimensions": [ - { - "name": "Region", - "value": "{regionField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Region"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Costs"] - } - ] - } - ``` - -13. 100_stacked_column - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Market Share", - "value": "{marketShareField}" - } - ], - "dimensions": [ - { - "name": "Product", - "value": "{productField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Product"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Market Share"] - } - ] - } - ``` - -14. timeseries_100_stacked_column - * UIDs: timeAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Investment", - "value": "{investmentField}" - } - ], - "dimensions": [ - { - "name": "Year", - "value": "{yearField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Year"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Investment"] - } - ] - } - ``` - -15. dual_stacked_bar - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Revenue", - "value": "{revenueField}" - }, - { - "name": "Profit", - "value": "{profitField}" - } - ], - "dimensions": [ - { - "name": "Brand", - "value": "{brandField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Brand"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Revenue"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Profit"] - } - ] - } - ``` - -16. dual_stacked_column - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Growth", - "value": "{growthField}" - }, - { - "name": "Revenue", - "value": "{revenueField}" - } - ], - "dimensions": [ - { - "name": "Sector", - "value": "{sectorField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Sector"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Growth"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Revenue"] - } - ] - } - ``` - -17. 100_dual_stacked_bar - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Sales", - "value": "{salesField}" - }, - { - "name": "Growth", - "value": "{growthField}" - } - ], - "dimensions": [ - { - "name": "Region", - "value": "{regionField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Region"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Sales"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Growth"] - } - ] - } - ``` - -18. 100_dual_stacked_column - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Sales", - "value": "{salesField}" - }, - { - "name": "Growth", - "value": "{growthField}" - } - ], - "dimensions": [ - { - "name": "Region", - "value": "{regionField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Region"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Sales"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Growth"] - } - ] - } - ``` - -19. line - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Price", - "value": "{priceField}" - } - ], - "dimensions": [ - { - "name": "Time", - "value": "{timeField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Time"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Price"] - } - ] - } - ``` - -20. dual_line - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Price", - "value": "{priceField}" - }, - { - "name": "Volume", - "value": "{volumeField}" - } - ], - "dimensions": [ - { - "name": "Time", - "value": "{timeField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Time"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Price"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Volume"] - } - ] - } - ``` - -21. timeseries_line - * UIDs: timeAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Temperature", - "value": "{temperatureField}" - } - ], - "dimensions": [ - { - "name": "Date", - "value": "{dateField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Date"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Temperature"] - } - ] - } - ``` - -22. bubble - * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth - * Note: Requires at least 3 measures (for valueAxis, valueAxis2, and bubbleWidth) - * Example: - ```json - { - "measures": [ - { - "name": "Expansion", - "value": "{expansionField}" - }, - { - "name": "Cost", - "value": "{costField}" - }, - { - "name": "Size", - "value": "{sizeField}" - } - ], - "dimensions": [ - { - "name": "Sector", - "value": "{sectorField}" - } - ], - "feeds": [ - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Expansion"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Cost"] - }, - { - "type": "Measure", - "uid": "bubbleWidth", - "values": ["Size"] - }, - { - "type": "Dimension", - "uid": "color", - "values": ["Sector"] - } - ] - } - ``` - -23. time_bubble - * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth, timeAxis - * Note: Requires timeAxis dimension, at least 2 measures (for valueAxis and bubbleWidth), and a color dimension - * Example: - ```json - { - "measures": [ - { - "name": "Expansion", - "value": "{expansionField}" - }, - { - "name": "Growth", - "value": "{growthField}" - }, - { - "name": "Size", - "value": "{sizeField}" - } - ], - "dimensions": [ - { - "name": "Year", - "value": "{yearField}" - }, - { - "name": "Sector", - "value": "{sectorField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Year"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Expansion"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Growth"] - }, - { - "type": "Measure", - "uid": "bubbleWidth", - "values": ["Size"] - }, - { - "type": "Dimension", - "uid": "color", - "values": ["Sector"] - } - ] - } - ``` - -24. timeseries_bubble - * UIDs: color, shape, valueAxis, timeAxis, bubbleWidth - * Note: Requires timeAxis dimension with dataType "date", bubbleWidth measure, and valueAxis measure - * Example: - ```json - { - "measures": [ - { - "name": "Size", - "value": "{sizeField}" - }, - { - "name": "Performance", - "value": "{performanceField}" - } - ], - "dimensions": [ - { - "name": "Year", - "value": "{yearField}", - "dataType": "date" - }, - { - "name": "Sector", - "value": "{sectorField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Year"] - }, - { - "type": "Measure", - "uid": "bubbleWidth", - "values": ["Size"] - }, - { - "type": "Dimension", - "uid": "color", - "values": ["Sector"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Performance"] - } - ] - } - ``` - -25. scatter - * UIDs: dataFrame, color, shape, valueAxis, valueAxis2 - * Note: Requires 2 measures for valueAxis and valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Efficiency", - "value": "{efficiencyField}" - }, - { - "name": "Cost", - "value": "{costField}" - } - ], - "dimensions": [ - { - "name": "Region", - "value": "{regionField}" - } - ], - "feeds": [ - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Efficiency"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Cost"] - }, - { - "type": "Dimension", - "uid": "color", - "values": ["Region"] - } - ] - } - ``` - -26. timeseries_scatter - * UIDs: color, shape, valueAxis, timeAxis - * Example: - ```json - { - "measures": [ - { - "name": "Performance", - "value": "{performanceField}" - } - ], - "dimensions": [ - { - "name": "Year", - "value": "{yearField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Year"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Performance"] - } - ] - } - ``` - -27. area - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Score", - "value": "{scoreField}" - } - ], - "dimensions": [ - { - "name": "Competency", - "value": "{competencyField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Competency"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Score"] - } - ] - } - ``` - -28. radar - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Proficiency Level", - "value": "{proficiencyField}" - } - ], - "dimensions": [ - { - "name": "Skill", - "value": "{skillField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Skill"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Proficiency Level"] - } - ] - } - ``` - -29. vertical_bullet - * UIDs: categoryAxis, color, actualValues, additionalValues, targetValues, forecastValues - * Example: - ```json - { - "measures": [ - { - "name": "Achievement", - "value": "{achievementField}" - } - ], - "dimensions": [ - { - "name": "Target", - "value": "{targetField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Target"] - }, - { - "type": "Measure", - "uid": "actualValues", - "values": ["Achievement"] - } - ] - } - ``` - -30. bullet - * UIDs: categoryAxis, color, actualValues, additionalValues, targetValues, forecastValues - * Example: - ```json - { - "measures": [ - { - "name": "Achievement", - "value": "{achievementField}" - } - ], - "dimensions": [ - { - "name": "Target", - "value": "{targetField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Target"] - }, - { - "type": "Measure", - "uid": "actualValues", - "values": ["Achievement"] - } - ] - } - ``` - -31. timeseries_bullet - * UIDs: timeAxis, color, actualValues, additionalValues, targetValues - * Example: - ```json - { - "measures": [ - { - "name": "Sales", - "value": "{salesField}" - } - ], - "dimensions": [ - { - "name": "Date", - "value": "{dateField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Date"] - }, - { - "type": "Measure", - "uid": "actualValues", - "values": ["Sales"] - } - ] - } - ``` - -32. waterfall - * UIDs: categoryAxis, waterfallType, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Change", - "value": "{changeField}" - } - ], - "dimensions": [ - { - "name": "Phase", - "value": "{phaseField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Phase"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Change"] - } - ] - } - ``` - -33. timeseries_waterfall - * UIDs: timeAxis, valueAxis, color - * Example: - ```json - { - "measures": [ - { - "name": "Financial Change", - "value": "{financialChangeField}" - } - ], - "dimensions": [ - { - "name": "Year", - "value": "{yearField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Year"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Financial Change"] - } - ] - } - ``` - -34. horizontal_waterfall - * UIDs: categoryAxis, waterfallType, valueAxis - * Example: - ```json - { - "measures": [ - { - "name": "Growth", - "value": "{growthField}" - } - ], - "dimensions": [ - { - "name": "Milestone", - "value": "{milestoneField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Milestone"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Growth"] - } - ] - } - ``` - -35. combination - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Note: Requires at least 2 measures in the valueAxis feed for proper rendering - * Example: - ```json - { - "measures": [ - { - "name": "Expense", - "value": "{expenseField}" - }, - { - "name": "Revenue", - "value": "{revenueField}" - } - ], - "dimensions": [ - { - "name": "Period", - "value": "{periodField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Period"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Expense", "Revenue"] - } - ] - } - ``` - -36. stacked_combination - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Note: Requires at least 2 measures in the valueAxis feed for proper rendering - * Example: - ```json - { - "measures": [ - { - "name": "Revenue", - "value": "{revenueField}" - }, - { - "name": "Sales", - "value": "{salesField}" - } - ], - "dimensions": [ - { - "name": "Category", - "value": "{categoryField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Category"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Revenue", "Sales"] - } - ] - } - ``` - -37. horizontal_stacked_combination - * UIDs: dataFrame, categoryAxis, color, valueAxis - * Note: Requires at least 2 measures in the valueAxis feed for proper rendering - * Example: - ```json - { - "measures": [ - { - "name": "Growth", - "value": "{growthField}" - }, - { - "name": "Revenue", - "value": "{revenueField}" - } - ], - "dimensions": [ - { - "name": "Product", - "value": "{productField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Product"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Growth", "Revenue"] - } - ] - } - ``` - -38. dual_stacked_combination - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Revenue", - "value": "{revenueField}" - }, - { - "name": "Costs", - "value": "{costsField}" - } - ], - "dimensions": [ - { - "name": "Time Period", - "value": "{timePeriodField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Time Period"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Revenue"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Costs"] - } - ] - } - ``` - -39. dual_horizontal_stacked_combination - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Sales", - "value": "{salesField}" - }, - { - "name": "Returns", - "value": "{returnsField}" - } - ], - "dimensions": [ - { - "name": "Brand", - "value": "{brandField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Brand"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Sales"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Returns"] - } - ] - } - ``` - -40. dual_horizontal_combination - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Engagement", - "value": "{engagementField}" - }, - { - "name": "Spend", - "value": "{spendField}" - } - ], - "dimensions": [ - { - "name": "Campaign", - "value": "{campaignField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Campaign"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Engagement"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Spend"] - } - ] - } - ``` - -41. dual_combination - * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Sales Revenue", - "value": "{salesRevenueField}" - }, - { - "name": "Operating Cost", - "value": "{operatingCostField}" - } - ], - "dimensions": [ - { - "name": "Time Frame", - "value": "{timeFrameField}" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "categoryAxis", - "values": ["Time Frame"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Sales Revenue"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Operating Cost"] - } - ] - } - ``` - -42. timeseries_combination - * UIDs: timeAxis, color, valueAxis - * Note: Requires at least 2 measures in the valueAxis feed for proper rendering - * Example: - ```json - { - "measures": [ - { - "name": "Earnings", - "value": "{earningsField}" - }, - { - "name": "Revenue", - "value": "{revenueField}" - } - ], - "dimensions": [ - { - "name": "Month", - "value": "{monthField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Month"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Earnings", "Revenue"] - } - ] - } - ``` - -43. dual_timeseries_combination - * UIDs: timeAxis, color, valueAxis, valueAxis2 - * Example: - ```json - { - "measures": [ - { - "name": "Revenue", - "value": "{revenueField}" - }, - { - "name": "Cost", - "value": "{costField}" - } - ], - "dimensions": [ - { - "name": "Month", - "value": "{monthField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Month"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Revenue"] - }, - { - "type": "Measure", - "uid": "valueAxis2", - "values": ["Cost"] - } - ] - } - ``` - -44. timeseries_stacked_combination - * UIDs: timeAxis, color, valueAxis - * Note: Requires at least 2 measures in the valueAxis feed for proper rendering - * Example: - ```json - { - "measures": [ - { - "name": "Performance", - "value": "{performanceField}" - }, - { - "name": "Revenue", - "value": "{revenueField}" - } - ], - "dimensions": [ - { - "name": "Year", - "value": "{yearField}", - "dataType": "date" - } - ], - "feeds": [ - { - "type": "Dimension", - "uid": "timeAxis", - "values": ["Year"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Performance", "Revenue"] - } - ] - } \ No newline at end of file +- **ALWAYS** load [references/analytical_chart_types.md](references/analytical_chart_types.md) before creating or modifying an Analytical card. It contains the comprehensive list of all supported chart types, their required UIDs, and example configurations. diff --git a/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md b/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md new file mode 100644 index 0000000..372ef37 --- /dev/null +++ b/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md @@ -0,0 +1,1599 @@ +# Analytical Cards - Chart Types Reference + +Comprehensive list of all supported chart types for Analytical Integration Cards, with their required UIDs and example configurations. + +For each chart type, the `feeds` array must use the listed UIDs to bind the corresponding measures and dimensions. + +1. donut/pie + * UIDs: size, color, dataFrame + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueDataField}" + } + ], + "dimensions": [ + { + "name": "Product Category", + "value": "{productCategoryField}" + } + ], + "feeds": [ + { + "type": "Measure", + "uid": "size", + "values": ["Revenue"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Product Category"] + } + ] + } + ``` + +2. heatmap + * UIDs: categoryAxis, categoryAxis2, color + * Example: + ```json + { + "measures": [ + { + "name": "Temperature", + "value": "{temperatureField}" + } + ], + "dimensions": [ + { + "name": "Location", + "value": "{locationField}" + }, + { + "name": "Product", + "value": "{productField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Location"] + }, + { + "type": "Dimension", + "uid": "categoryAxis2", + "values": ["Product"] + }, + { + "type": "Measure", + "uid": "color", + "values": ["Temperature"] + } + ] + } + ``` + +3. treemap + * UIDs: title, color, weight + * Example: + ```json + { + "measures": [ + { + "name": "Profit", + "value": "{profitField}" + }, + { + "name": "Budget", + "value": "{budgetField}" + } + ], + "dimensions": [ + { + "name": "Department", + "value": "{departmentField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "title", + "values": ["Department"] + }, + { + "type": "Measure", + "uid": "color", + "values": ["Profit"] + }, + { + "type": "Measure", + "uid": "weight", + "values": ["Budget"] + } + ] + } + ``` + +4. bar + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + } + ], + "dimensions": [ + { + "name": "Month", + "value": "{monthField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Month"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales"] + } + ] + } + ``` + +5. dual_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Expenses", + "value": "{expensesField}" + } + ], + "dimensions": [ + { + "name": "Quarter", + "value": "{quarterField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Quarter"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Expenses"] + } + ] + } + ``` + +6. column + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + } + ], + "dimensions": [ + { + "name": "Month", + "value": "{monthField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Month"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + } + ] + } + ``` + +7. timeseries_column + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Traffic", + "value": "{trafficField}" + } + ], + "dimensions": [ + { + "name": "Date", + "value": "{dateField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Date"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Traffic"] + } + ] + } + ``` + +8. dual_column + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Costs", + "value": "{costsField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Costs"] + } + ] + } + ``` + +9. stacked_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + } + ] + } + ``` + +10. stacked_column + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Market Share", + "value": "{marketShareField}" + } + ], + "dimensions": [ + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Sector"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Market Share"] + } + ] + } + ``` + +11. timeseries_stacked_column + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Investment", + "value": "{investmentField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Investment"] + } + ] + } + ``` + +12. 100_stacked_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Costs", + "value": "{costsField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Costs"] + } + ] + } + ``` + +13. 100_stacked_column + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Market Share", + "value": "{marketShareField}" + } + ], + "dimensions": [ + { + "name": "Product", + "value": "{productField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Product"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Market Share"] + } + ] + } + ``` + +14. timeseries_100_stacked_column + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Investment", + "value": "{investmentField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Investment"] + } + ] + } + ``` + +15. dual_stacked_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Profit", + "value": "{profitField}" + } + ], + "dimensions": [ + { + "name": "Brand", + "value": "{brandField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Brand"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Profit"] + } + ] + } + ``` + +16. dual_stacked_column + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Growth", + "value": "{growthField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" + } + ], + "dimensions": [ + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Sector"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Growth"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Revenue"] + } + ] + } + ``` + +17. 100_dual_stacked_bar + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + }, + { + "name": "Growth", + "value": "{growthField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Growth"] + } + ] + } + ``` + +18. 100_dual_stacked_column + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + }, + { + "name": "Growth", + "value": "{growthField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Growth"] + } + ] + } + ``` + +19. line + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Price", + "value": "{priceField}" + } + ], + "dimensions": [ + { + "name": "Time", + "value": "{timeField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Time"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Price"] + } + ] + } + ``` + +20. dual_line + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Price", + "value": "{priceField}" + }, + { + "name": "Volume", + "value": "{volumeField}" + } + ], + "dimensions": [ + { + "name": "Time", + "value": "{timeField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Time"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Price"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Volume"] + } + ] + } + ``` + +21. timeseries_line + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Temperature", + "value": "{temperatureField}" + } + ], + "dimensions": [ + { + "name": "Date", + "value": "{dateField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Date"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Temperature"] + } + ] + } + ``` + +22. bubble + * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth + * Example: + ```json + { + "measures": [ + { + "name": "Expansion", + "value": "{expansionField}" + }, + { + "name": "Size", + "value": "{sizeField}" + } + ], + "dimensions": [ + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Measure", + "uid": "bubbleWidth", + "values": ["Size"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Sector"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Expansion"] + } + ] + } + ``` + +23. time_bubble + * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth + * Example: + ```json + { + "measures": [ + { + "name": "Expansion", + "value": "{expansionField}" + }, + { + "name": "Size", + "value": "{sizeField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}" + }, + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "bubbleWidth", + "values": ["Size"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Sector"] + } + ] + } + ``` + +24. timeseries_bubble + * UIDs: color, shape, valueAxis, timeAxis, bubbleWidth + * Example: + ```json + { + "measures": [ + { + "name": "Size", + "value": "{sizeField}" + }, + { + "name": "Performance", + "value": "{performanceField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + }, + { + "name": "Sector", + "value": "{sectorField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "bubbleWidth", + "values": ["Size"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Sector"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Performance"] + } + ] + } + ``` + +25. scatter + * UIDs: dataFrame, color, shape, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Efficiency", + "value": "{efficiencyField}" + }, + { + "name": "Cost", + "value": "{costField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Efficiency"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Cost"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Region"] + } + ] + } + ``` + +26. timeseries_scatter + * UIDs: color, shape, valueAxis, timeAxis + * Example: + ```json + { + "measures": [ + { + "name": "Performance", + "value": "{performanceField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Performance"] + } + ] + } + ``` + +27. area + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Score", + "value": "{scoreField}" + } + ], + "dimensions": [ + { + "name": "Competency", + "value": "{competencyField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Competency"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Score"] + } + ] + } + ``` + +28. radar + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Proficiency Level", + "value": "{proficiencyField}" + } + ], + "dimensions": [ + { + "name": "Skill", + "value": "{skillField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Skill"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Proficiency Level"] + } + ] + } + ``` + +29. vertical_bullet + * UIDs: categoryAxis, color, actualValues, additionalValues, targetValues, forecastValues + * Example: + ```json + { + "measures": [ + { + "name": "Achievement", + "value": "{achievementField}" + } + ], + "dimensions": [ + { + "name": "Target", + "value": "{targetField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Target"] + }, + { + "type": "Measure", + "uid": "actualValues", + "values": ["Achievement"] + } + ] + } + ``` + +30. bullet + * UIDs: categoryAxis, color, actualValues, additionalValues, targetValues, forecastValues + * Example: + ```json + { + "measures": [ + { + "name": "Achievement", + "value": "{achievementField}" + } + ], + "dimensions": [ + { + "name": "Target", + "value": "{targetField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Target"] + }, + { + "type": "Measure", + "uid": "actualValues", + "values": ["Achievement"] + } + ] + } + ``` + +31. timeseries_bullet + * UIDs: timeAxis, color, actualValues, additionalValues, targetValues + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + } + ], + "dimensions": [ + { + "name": "Date", + "value": "{dateField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Date"] + }, + { + "type": "Measure", + "uid": "actualValues", + "values": ["Sales"] + } + ] + } + ``` + +32. waterfall + * UIDs: categoryAxis, waterfallType, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Change", + "value": "{changeField}" + } + ], + "dimensions": [ + { + "name": "Phase", + "value": "{phaseField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Phase"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Change"] + } + ] + } + ``` + +33. timeseries_waterfall + * UIDs: timeAxis, valueAxis, color + * Example: + ```json + { + "measures": [ + { + "name": "Financial Change", + "value": "{financialChangeField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Financial Change"] + } + ] + } + ``` + +34. horizontal_waterfall + * UIDs: categoryAxis, waterfallType, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Growth", + "value": "{growthField}" + } + ], + "dimensions": [ + { + "name": "Milestone", + "value": "{milestoneField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Milestone"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Growth"] + } + ] + } + ``` + +35. combination + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Expense", + "value": "{expenseField}" + } + ], + "dimensions": [ + { + "name": "Period", + "value": "{periodField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Period"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Expense"] + } + ] + } + ``` + +36. stacked_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + } + ], + "dimensions": [ + { + "name": "Category", + "value": "{categoryField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Category"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + } + ] + } + ``` + +37. horizontal_stacked_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Growth", + "value": "{growthField}" + } + ], + "dimensions": [ + { + "name": "Product", + "value": "{productField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Product"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Growth"] + } + ] + } + ``` + +38. dual_stacked_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Costs", + "value": "{costsField}" + } + ], + "dimensions": [ + { + "name": "Time Period", + "value": "{timePeriodField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Time Period"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Costs"] + } + ] + } + ``` + +39. dual_horizontal_stacked_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Sales", + "value": "{salesField}" + }, + { + "name": "Returns", + "value": "{returnsField}" + } + ], + "dimensions": [ + { + "name": "Brand", + "value": "{brandField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Brand"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Returns"] + } + ] + } + ``` + +40. dual_horizontal_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Engagement", + "value": "{engagementField}" + }, + { + "name": "Spend", + "value": "{spendField}" + } + ], + "dimensions": [ + { + "name": "Campaign", + "value": "{campaignField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Campaign"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Engagement"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Spend"] + } + ] + } + ``` + +41. dual_combination + * UIDs: dataFrame, categoryAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Sales Revenue", + "value": "{salesRevenueField}" + }, + { + "name": "Operating Cost", + "value": "{operatingCostField}" + } + ], + "dimensions": [ + { + "name": "Time Frame", + "value": "{timeFrameField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Time Frame"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Sales Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Operating Cost"] + } + ] + } + ``` + +42. timeseries_combination + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Earnings", + "value": "{earningsField}" + } + ], + "dimensions": [ + { + "name": "Month", + "value": "{monthField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Month"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Earnings"] + } + ] + } + ``` + +43. dual_timeseries_combination + * UIDs: timeAxis, color, valueAxis, valueAxis2 + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + }, + { + "name": "Cost", + "value": "{costField}" + } + ], + "dimensions": [ + { + "name": "Month", + "value": "{monthField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Month"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Cost"] + } + ] + } + ``` + +44. timeseries_stacked_combination + * UIDs: timeAxis, color, valueAxis + * Example: + ```json + { + "measures": [ + { + "name": "Performance", + "value": "{performanceField}" + } + ], + "dimensions": [ + { + "name": "Year", + "value": "{yearField}", + "dataType": "date" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "timeAxis", + "values": ["Year"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Performance"] + } + ] + } + ``` diff --git a/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md b/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md new file mode 100644 index 0000000..902dab6 --- /dev/null +++ b/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md @@ -0,0 +1,233 @@ +# Configuration Editor Example + +This reference shows a complete pairing of a `manifest.json` and the corresponding `dt/Configuration.js` file for an Integration Card with a Configuration Editor. + +`manifest.json` file: +```json +{ + "sap.app": { + "id": "test.editor", + "type": "card", + "title": "Test Card", + "applicationVersion": { + "version": "1.0.0" + } + }, + "sap.ui": { + "technology": "UI5" + }, + "sap.card": { + "type": "List", + "configuration": { + "editor": "./dt/Configuration", + "parameters": { + "cardTitle": { + "value": "Customers" + }, + "icon": { + "value": "sap-icon://account" + }, + "maxItems": { + "value": 3 + }, + "showDescription": { + "value": true + }, + "dateContext": { + "value": "2020-09-02" + }, + "Customers": { + "value": ["ALFKI"] + }, + "northwindDestination": { + "value": "northwind" + } + }, + "destinations": { + "northwind": { + "name": "Northwind_V4", + "defaultUrl": "https://services.odata.org/V4/Northwind/Northwind.svc" + } + } + }, + "data": { + "request": { + "url": "{{destinations.northwind}}/Customers", + "parameters": { + "$select": "CustomerID,CompanyName,ContactName", + "$top": "{parameters>/maxItems/value}" + } + } + }, + "header": { + "title": "{parameters>/cardTitle/value}", + "subtitle": "As of {parameters>/dateContext/value}", + "icon": { + "src": "{parameters>/icon/value}", + "shape": "Circle" + } + }, + "content": { + "data": { + "path": "/value" + }, + "item": { + "title": "{CompanyName}", + "description": "{= ${parameters>/showDescription/value} ? ${ContactName} : '' }" + }, + "maxItems": "{parameters>/maxItems/value}" + } + } +} +``` + +`dt/Configuration.js` file: +```javascript +sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { + "use strict"; + + return function () { + return new Designtime({ + form: { + items: { + + /* ======================= + General + ======================= */ + generalGroup: { + type: "group", + label: "General" + }, + + cardTitle: { + manifestpath: "/sap.card/configuration/parameters/cardTitle/value", + type: "string", + label: "Card Title", + translatable: true, + required: true, + allowDynamicValues: true + }, + + icon: { + manifestpath: "/sap.card/header/icon/src", + type: "string", + label: "Icon", + visualization: { + type: "IconSelect", + settings: { + value: "{currentSettings>value}", + editable: "{currentSettings>editable}" + } + } + }, + + iconShape: { + manifestpath: "/sap.card/header/icon/shape", + type: "string", + label: "Icon Shape", + visualization: { + type: "ShapeSelect", + settings: { + value: "{currentSettings>value}", + editable: "{currentSettings>editable}" + } + }, + cols: 1 + }, + + iconBackground: { + manifestpath: "/sap.card/header/icon/backgroundColor", + type: "string", + label: "Icon Background", + visualization: { + type: "ColorSelect", + settings: { + enumValue: "{currentSettings>value}", + editable: "{currentSettings>editable}" + } + }, + cols: 1 + }, + + /* ======================= + Data & Behavior + ======================= */ + dataGroup: { + type: "group", + label: "Data & Behavior" + }, + + maxItems: { + manifestpath: "/sap.card/configuration/parameters/maxItems/value", + type: "integer", + label: "Maximum Items", + visualization: { + type: "Slider", + settings: { + value: "{currentSettings>value}", + min: 1, + max: 10, + width: "100%", + enabled: "{currentSettings>editable}" + } + } + }, + + showDescription: { + manifestpath: "/sap.card/configuration/parameters/showDescription/value", + type: "boolean", + label: "Show Contact Name", + visualization: { + type: "Switch", + settings: { + state: "{currentSettings>value}", + customTextOn: "Show", + customTextOff: "Hide", + enabled: "{currentSettings>editable}" + } + } + }, + + dateContext: { + manifestpath: "/sap.card/configuration/parameters/dateContext/value", + type: "date", + label: "Date Context" + }, + + /* ======================= + Filtering + ======================= */ + filterGroup: { + type: "group", + label: "Customer Filter" + }, + + CustomerID: { + manifestpath: "/sap.card/configuration/parameters/CustomerID/value", + type: "string", + label: "Customer ID", + values: { + data: { + request: { + url: "{{destinations.northwind}}/Customers", + parameters: { + "$select": "CustomerID,CompanyName" + } + }, + path: "/value" + }, + item: { + key: "{CustomerID}", + text: "{CompanyName}" + } + } + } + } + }, + preview: { + modes: "None" + } + }); + }; +}); +``` From 19fb94c596ed6ab92c52821b7ac7d8b783f9afd5 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Tue, 19 May 2026 12:23:56 +0300 Subject: [PATCH 04/13] docs(ui5): Align Customer parameter key in editor sample Editor field referenced parameters/CustomerID/value but the manifest declared "Customers" as an array, so the binding never matched. Use a singular "Customer" string key in both places. --- .../references/configuration_editor_example.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md b/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md index 902dab6..e79c3aa 100644 --- a/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md +++ b/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md @@ -36,8 +36,8 @@ This reference shows a complete pairing of a `manifest.json` and the correspondi "dateContext": { "value": "2020-09-02" }, - "Customers": { - "value": ["ALFKI"] + "Customer": { + "value": "ALFKI" }, "northwindDestination": { "value": "northwind" @@ -202,8 +202,8 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { label: "Customer Filter" }, - CustomerID: { - manifestpath: "/sap.card/configuration/parameters/CustomerID/value", + Customer: { + manifestpath: "/sap.card/configuration/parameters/Customer/value", type: "string", label: "Customer ID", values: { From 68b8fb0e3f25e3e0826d3eece62c63a9d8c3f1d5 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Wed, 20 May 2026 14:22:01 +0300 Subject: [PATCH 05/13] docs(ui5): Address review feedback on integration cards skill - Fix analytical chart samples (bubble, time_bubble, stacked variants, bullet, waterfall, combinations) so feeds and measures match each chart type's requirements - Add GitHub samples link to the Card Explorer section - Require asking the user before making all manifest fields editable in the Configuration Editor - Clarify the donut/pie context on the feeds example in SKILL.md --- plugins/ui5/skills/integration-cards/SKILL.md | 4 +- .../references/analytical_chart_types.md | 169 ++++++++++++++++-- 2 files changed, 154 insertions(+), 19 deletions(-) diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md index ceffc78..de400cd 100644 --- a/plugins/ui5/skills/integration-cards/SKILL.md +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -58,6 +58,7 @@ description: Guidelines and best practices for developing UI Integration Cards ( ## 3. Card Explorer - The Card Explorer provides detailed documentation for the Integration Cards schema, including descriptions of every property, guidance for integrating cards into hosting environments, configuration editor documentation with examples, and broader best practices. It is available at: https://ui5.sap.com/test-resources/sap/ui/integration/demokit/cardExplorer/webapp/index.html +- The Card Explorer sample sources (manifest.json, data.json, etc.) are available in the openui5 GitHub repository: https://github.com/UI5/openui5/tree/master/src/sap.ui.integration/test/sap/ui/integration/demokit/cardExplorer/webapp/samples ## 4. Preview Instructions - If preview of the card must be shown, **ALWAYS** check the card folder for an existing preview file and any accompanying instructions or scripts, and reuse them if available. @@ -87,6 +88,7 @@ When creating or modifying Integration Cards, follow these guidelines for Config - Assume the role of Administrator persona when designing the Configuration Editor. - **ALWAYS** ensure that the Configuration Editor reflects the current structure and fields of the `manifest.json`. - **ALWAYS** make the existing fields in the `manifest.json` configurable via the editor. For example manifest parameters, title, subtitle, icon of the header, etc. +- **ALWAYS** ask the user if they agree to make all fields editable and what other fields should be added. - **NEVER** add fields to the editor that do not exist in the `manifest.json`. - **ALWAYS** remove fields from the editor when removing them from the `manifest.json`. - **ALWAYS** add fields in the Configuration Editor when adding them to the `manifest.json`. @@ -97,7 +99,7 @@ When creating or modifying Integration Cards, follow these guidelines for Config - **ALWAYS** set `sap.card/content/chartType` property. - **ALWAYS** adjust `sap.card/content/measures`, `sap.card/content/dimensions` and `sap.card/content/feeds` to match the `sap.card/content/chartType` property and data structure. This is critical for proper data display. - **ALWAYS** use `sap.card/content/chartProperties` to adjust labels, colors, the legend, and other chart aspects. -- **ALWAYS** define each feed with its type (Dimension or Measure), its unique identifier (uid), and the associated values using defined measures and dimensions. Example: +- **ALWAYS** define each feed with its type (Dimension or Measure), its unique identifier (uid), and the associated values using defined measures and dimensions. Example (for a donut/pie chart): ```json "feeds": [ { diff --git a/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md b/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md index 372ef37..86e59f3 100644 --- a/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md +++ b/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md @@ -299,6 +299,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 9. stacked_bar * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Stacking requires a second dimension fed to `color`; without it the chart renders as a plain bar * Example: ```json { @@ -312,6 +313,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Region", "value": "{regionField}" + }, + { + "name": "Product", + "value": "{productField}" } ], "feeds": [ @@ -324,6 +329,11 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "type": "Measure", "uid": "valueAxis", "values": ["Revenue"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Product"] } ] } @@ -331,6 +341,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 10. stacked_column * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Stacking requires a second dimension fed to `color`; without it the chart renders as a plain column * Example: ```json { @@ -344,6 +355,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Sector", "value": "{sectorField}" + }, + { + "name": "Product", + "value": "{productField}" } ], "feeds": [ @@ -356,6 +371,11 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "type": "Measure", "uid": "valueAxis", "values": ["Market Share"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Product"] } ] } @@ -396,6 +416,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 12. 100_stacked_bar * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Stacking requires a second dimension fed to `color`; without it the chart renders as a plain bar * Example: ```json { @@ -409,6 +430,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Region", "value": "{regionField}" + }, + { + "name": "Category", + "value": "{categoryField}" } ], "feeds": [ @@ -421,6 +446,11 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "type": "Measure", "uid": "valueAxis", "values": ["Costs"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Category"] } ] } @@ -428,6 +458,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 13. 100_stacked_column * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Stacking requires a second dimension fed to `color`; without it the chart renders as a plain column * Example: ```json { @@ -441,6 +472,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Product", "value": "{productField}" + }, + { + "name": "Region", + "value": "{regionField}" } ], "feeds": [ @@ -453,6 +488,11 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "type": "Measure", "uid": "valueAxis", "values": ["Market Share"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Region"] } ] } @@ -763,6 +803,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 22. bubble * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth + * Note: Requires at least 3 measures (for valueAxis, valueAxis2, and bubbleWidth) * Example: ```json { @@ -771,6 +812,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "name": "Expansion", "value": "{expansionField}" }, + { + "name": "Cost", + "value": "{costField}" + }, { "name": "Size", "value": "{sizeField}" @@ -783,6 +828,16 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr } ], "feeds": [ + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Expansion"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Cost"] + }, { "type": "Measure", "uid": "bubbleWidth", @@ -792,18 +847,14 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "type": "Dimension", "uid": "color", "values": ["Sector"] - }, - { - "type": "Measure", - "uid": "valueAxis", - "values": ["Expansion"] } ] } ``` 23. time_bubble - * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth + * UIDs: dataFrame, color, shape, valueAxis, valueAxis2, bubbleWidth, timeAxis + * Note: Requires timeAxis dimension, at least 2 measures (for valueAxis and bubbleWidth), and a color dimension * Example: ```json { @@ -812,6 +863,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "name": "Expansion", "value": "{expansionField}" }, + { + "name": "Growth", + "value": "{growthField}" + }, { "name": "Size", "value": "{sizeField}" @@ -820,7 +875,8 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "dimensions": [ { "name": "Year", - "value": "{yearField}" + "value": "{yearField}", + "dataType": "date" }, { "name": "Sector", @@ -833,6 +889,16 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "uid": "timeAxis", "values": ["Year"] }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Expansion"] + }, + { + "type": "Measure", + "uid": "valueAxis2", + "values": ["Growth"] + }, { "type": "Measure", "uid": "bubbleWidth", @@ -849,6 +915,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 24. timeseries_bubble * UIDs: color, shape, valueAxis, timeAxis, bubbleWidth + * Note: Requires timeAxis dimension with dataType "date", bubbleWidth measure, and valueAxis measure * Example: ```json { @@ -900,6 +967,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 25. scatter * UIDs: dataFrame, color, shape, valueAxis, valueAxis2 + * Note: Requires 2 measures for valueAxis and valueAxis2 * Example: ```json { @@ -1038,6 +1106,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 29. vertical_bullet * UIDs: categoryAxis, color, actualValues, additionalValues, targetValues, forecastValues + * Note: `targetValues` expects a measure (target value), not a dimension * Example: ```json { @@ -1045,24 +1114,33 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Achievement", "value": "{achievementField}" + }, + { + "name": "Target", + "value": "{targetField}" } ], "dimensions": [ { - "name": "Target", - "value": "{targetField}" + "name": "KPI Name", + "value": "{kpiNameField}" } ], "feeds": [ { "type": "Dimension", "uid": "categoryAxis", - "values": ["Target"] + "values": ["KPI Name"] }, { "type": "Measure", "uid": "actualValues", "values": ["Achievement"] + }, + { + "type": "Measure", + "uid": "targetValues", + "values": ["Target"] } ] } @@ -1070,6 +1148,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 30. bullet * UIDs: categoryAxis, color, actualValues, additionalValues, targetValues, forecastValues + * Note: `targetValues` expects a measure (target value), not a dimension * Example: ```json { @@ -1077,24 +1156,33 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Achievement", "value": "{achievementField}" + }, + { + "name": "Target", + "value": "{targetField}" } ], "dimensions": [ { - "name": "Target", - "value": "{targetField}" + "name": "KPI Name", + "value": "{kpiNameField}" } ], "feeds": [ { "type": "Dimension", "uid": "categoryAxis", - "values": ["Target"] + "values": ["KPI Name"] }, { "type": "Measure", "uid": "actualValues", "values": ["Achievement"] + }, + { + "type": "Measure", + "uid": "targetValues", + "values": ["Target"] } ] } @@ -1135,6 +1223,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 32. waterfall * UIDs: categoryAxis, waterfallType, valueAxis + * Note: `waterfallType` is optional but recommended; it distinguishes total bars from running positive/negative changes * Example: ```json { @@ -1148,6 +1237,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Phase", "value": "{phaseField}" + }, + { + "name": "Type", + "value": "{typeField}" } ], "feeds": [ @@ -1160,6 +1253,11 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "type": "Measure", "uid": "valueAxis", "values": ["Change"] + }, + { + "type": "Dimension", + "uid": "waterfallType", + "values": ["Type"] } ] } @@ -1200,6 +1298,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 34. horizontal_waterfall * UIDs: categoryAxis, waterfallType, valueAxis + * Note: `waterfallType` is optional but recommended; it distinguishes total bars from running positive/negative changes * Example: ```json { @@ -1213,6 +1312,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Milestone", "value": "{milestoneField}" + }, + { + "name": "Type", + "value": "{typeField}" } ], "feeds": [ @@ -1225,6 +1328,11 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "type": "Measure", "uid": "valueAxis", "values": ["Growth"] + }, + { + "type": "Dimension", + "uid": "waterfallType", + "values": ["Type"] } ] } @@ -1232,6 +1340,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 35. combination * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1239,6 +1348,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Expense", "value": "{expenseField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" } ], "dimensions": [ @@ -1256,7 +1369,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "type": "Measure", "uid": "valueAxis", - "values": ["Expense"] + "values": ["Expense", "Revenue"] } ] } @@ -1264,6 +1377,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 36. stacked_combination * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1271,6 +1385,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Revenue", "value": "{revenueField}" + }, + { + "name": "Sales", + "value": "{salesField}" } ], "dimensions": [ @@ -1288,7 +1406,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "type": "Measure", "uid": "valueAxis", - "values": ["Revenue"] + "values": ["Revenue", "Sales"] } ] } @@ -1296,6 +1414,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 37. horizontal_stacked_combination * UIDs: dataFrame, categoryAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1303,6 +1422,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Growth", "value": "{growthField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" } ], "dimensions": [ @@ -1320,7 +1443,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "type": "Measure", "uid": "valueAxis", - "values": ["Growth"] + "values": ["Growth", "Revenue"] } ] } @@ -1492,6 +1615,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 42. timeseries_combination * UIDs: timeAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1499,6 +1623,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Earnings", "value": "{earningsField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" } ], "dimensions": [ @@ -1517,7 +1645,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "type": "Measure", "uid": "valueAxis", - "values": ["Earnings"] + "values": ["Earnings", "Revenue"] } ] } @@ -1567,6 +1695,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 44. timeseries_stacked_combination * UIDs: timeAxis, color, valueAxis + * Note: Requires at least 2 measures in the valueAxis feed for proper rendering * Example: ```json { @@ -1574,6 +1703,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "name": "Performance", "value": "{performanceField}" + }, + { + "name": "Revenue", + "value": "{revenueField}" } ], "dimensions": [ @@ -1592,7 +1725,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr { "type": "Measure", "uid": "valueAxis", - "values": ["Performance"] + "values": ["Performance", "Revenue"] } ] } From e7e246972f06a803e81c4abdc1fa99129124eee1 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Wed, 20 May 2026 14:22:01 +0300 Subject: [PATCH 06/13] docs(ui5): Strengthen integration-cards skill auto-load signal Lead the skill description with the same imperative the UI5 MCP server's "Get Integration Cards Guidelines" tool uses, and enumerate the trigger surfaces (manifest.json, Configuration Editor, analytical charts), so Claude prefers loading the skill over calling the MCP tool. --- plugins/ui5/skills/integration-cards/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md index de400cd..9868987 100644 --- a/plugins/ui5/skills/integration-cards/SKILL.md +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -1,6 +1,6 @@ --- name: integration-cards -description: Guidelines and best practices for developing UI Integration Cards (also called UI5 Integration Cards). This skill MUST be loaded before working on any Integration Card related task. +description: MUST be loaded before any UI Integration Cards (also called UI5 Integration Cards) task — creating, modifying, validating, previewing, or reviewing a card, its `manifest.json`, its Configuration Editor (`dt/Configuration.js`), or any analytical chart configuration. Provides the official guidelines, validation rules, supported chart types, and Configuration Editor patterns. --- # UI Integration Cards Development Guidelines From 45c7c6a6c0f1deb792af6708c5c1436fd72385f0 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Wed, 20 May 2026 14:13:19 +0300 Subject: [PATCH 07/13] docs(ui5): Fix icon binding contradiction in Configuration Editor example The manifest defines header.icon.src as {parameters>/icon/value}, so the editor should bind to the parameter path instead of the header path. Otherwise the editor writes to a different manifest location than the binding reads from. --- .../references/configuration_editor_example.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md b/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md index e79c3aa..57b8994 100644 --- a/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md +++ b/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md @@ -109,7 +109,7 @@ sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) { }, icon: { - manifestpath: "/sap.card/header/icon/src", + manifestpath: "/sap.card/configuration/parameters/icon/value", type: "string", label: "Icon", visualization: { From c34d265b759b91d2f12901e7e0277a755868bd88 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Wed, 20 May 2026 14:13:25 +0300 Subject: [PATCH 08/13] docs(ui5): Restructure integration-cards skill for agent efficiency Reduce word count from ~1058 to ~740 by restructuring prose into compact rules tables. Hoist reference-loading triggers to the top so agents load analytical_chart_types.md and configuration_editor_example.md before working on those areas. Replace section symbols with markdown anchor links, demote the Card Explorer reference to the bottom, and add a "No data to display" symptom callout pointing at the data-placement rule (the most common cause). --- plugins/ui5/skills/integration-cards/SKILL.md | 198 ++++++++---------- 1 file changed, 89 insertions(+), 109 deletions(-) diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md index 9868987..8e60d6c 100644 --- a/plugins/ui5/skills/integration-cards/SKILL.md +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -5,119 +5,99 @@ description: MUST be loaded before any UI Integration Cards (also called UI5 Int # UI Integration Cards Development Guidelines -> *This document outlines the fundamental rules and best practices an AI agent must follow when developing or modifying Integration Cards. Adherence to these guidelines is critical for creating modern, maintainable, and performant UI Integration Cards.* - -## 1. Coding Guidelines -- **ALWAYS** strive to create declarative Integration Card, such as "Calendar", "List", "Table", "Timeline", "Object" or "Analytical". - - create an Integration Card Extension only in exceptional cases. -- **ALWAYS** create links using the `actions` property. -- **ALWAYS** refer to parameters using correct syntax - `{parameters>/parameterKey/value}`. -- **ALWAYS** perform validation of the integration card as described in [2. Validation](#2-validation). -- **ALWAYS** show a preview of the generated card following the [4. Preview Instructions](#4-preview-instructions). -- **ALWAYS** generate new declarative integration cards using the `create_integration_card` tool. - -### 1.1 Data -- **NEVER** modify the provided data under any circumstances. -- **ALWAYS** include the service URL directly in the card manifest when one is supplied. -- **ALWAYS** reference destinations by name when available. Configure the destination in the `sap.card/configuration/destinations/` and reuse it with binding syntax like `{{destinations.destinationName}}`. -- **NEVER** replace destination name with its URL. -- **ALWAYS** place data configuration in: `"sap.card"/data/` -- **NEVER** place data configuration in: - - `"sap.card"/content/data/` - - `"sap.card"/header/data/` -- Data can be provided via: - 1. Inline JSON object - 2. Network request (HTTP/HTTPS/Destination) - 3. Extension method call -- **ALWAYS** verify these paths are correctly set: - - `"sap.card"/data/path` (Primary data path) - - `"sap.card"/content/data/path` (Content-specific path. It overrides the primary data path) - - `"sap.card"/header/data/path` (Header-specific path. It overrides the primary data path) - -#### 1.1.1 Data Errors Detection -- Symptom: "No data to display" message appears. -- Cause: Incorrect data configuration or data path in the content incorrectly overrides the primary data path. -- Solution: Verify all rules in [1.1 Data](#11-data) are properly followed. - -### 1.2 Internationalization -- **ALWAYS** bind properties that are not bound to the data to the `i18n` model. - -### 1.3 Analytical Cards -- **ALWAYS** follow [6. Analytical Cards Coding Guidelines](#6-analytical-cards-coding-guidelines) when developing Analytical cards. - -### 1.4 Configuration Editor -- **ALWAYS** follow [5. Configuration Editor](#5-configuration-editor) guidelines when creating or modifying Configuration Editors for Integration Cards. -- **ALWAYS** load [references/configuration_editor_example.md](references/configuration_editor_example.md) whenever a `dt/Configuration.js` file is present in the project, is being created, or is being modified — even if the user did not explicitly mention the Configuration Editor. - -## 2. Validation -- **ALWAYS** ensure that `manifest.json` file is valid JSON. -- **ALWAYS** ensure that in `manifest.json` file the property `sap.app/type` is set to `"card"`. -- **ALWAYS** validate the `manifest.json` against the UI5 Manifest schema. Use the `run_manifest_validation` tool to do this. -- **ALWAYS** avoid using deprecated properties in `manifest.json` and elsewhere. -- **NEVER** treat Integration Cards' project as UI5 project, except for cards of type "Component". - -## 3. Card Explorer -- The Card Explorer provides detailed documentation for the Integration Cards schema, including descriptions of every property, guidance for integrating cards into hosting environments, configuration editor documentation with examples, and broader best practices. It is available at: https://ui5.sap.com/test-resources/sap/ui/integration/demokit/cardExplorer/webapp/index.html -- The Card Explorer sample sources (manifest.json, data.json, etc.) are available in the openui5 GitHub repository: https://github.com/UI5/openui5/tree/master/src/sap.ui.integration/test/sap/ui/integration/demokit/cardExplorer/webapp/samples - -## 4. Preview Instructions -- If preview of the card must be shown, **ALWAYS** check the card folder for an existing preview file and any accompanying instructions or scripts, and reuse them if available. - * for example, in NodeJS-based projects, search the `package.json` file for `start` or similar script. If such is available, use it - * also search in the `README.md` file. -- If preview instructions are not available, you have to create an HTML page that contains a `ui-integration` card element which references the card manifest. Then serve the HTML page using `http` server. +Rules an agent must follow when creating, modifying, validating, or previewing a UI Integration Card. Adherence is critical for working cards. + +## When to load each reference + +| Trigger | Load | +|---|---| +| Working on or planning an Analytical card | [`references/analytical_chart_types.md`](references/analytical_chart_types.md) | +| `dt/Configuration.js` exists, is being created, or is being modified | [`references/configuration_editor_example.md`](references/configuration_editor_example.md) | + +If the trigger applies, load before producing any output. Do not work from memory. + +## 1. Core rules + +| Rule | Detail | +|---|---| +| Prefer declarative cards | Types: List, Table, Calendar, Timeline, Object, Analytical. Create an Extension only in exceptional cases. | +| Use `create_integration_card` MCP tool | When creating a new declarative card. | +| Parameter binding syntax | `{parameters>/parameterKey/value}` — single braces, `>` separator, `value` suffix. | +| Destination binding syntax | `{{destinations.destinationName}}` — double braces, dot. Configure under `sap.card/configuration/destinations/`. Reference by name; never replace with raw URL. | +| i18n binding | Bind every non-data, user-visible string to the i18n model. | +| Links | Use the `actions` property; never inline `` or hand-rolled URL handlers. | +| Validate before declaring done | See [3. Validation](#3-validation). | +| Show preview when requested | See [4. Preview](#4-preview). | +| Don't modify provided data | Use it as supplied. | + +## 2. Data placement + +`sap.card/data/` is the only correct top-level location for the data request. + +| Path | Purpose | +|---|---| +| `sap.card/data/path` | Primary data path | +| `sap.card/content/data/path` | Content-specific path; **overrides** the primary path if set | +| `sap.card/header/data/path` | Header-specific path; **overrides** the primary path if set | + +Forbidden: putting the request itself under `sap.card/content/data/` or `sap.card/header/data/`. + +**Symptom — "No data to display":** typically caused by a `content/data` block that overrides the primary data path. Verify [2. Data placement](#2-data-placement) before debugging anything else. + +## 3. Validation + +| Rule | Detail | +|---|---| +| Valid JSON | `manifest.json` must parse. | +| `sap.app/type` | Must be `"card"`. | +| Schema validation | Use `run_manifest_validation` MCP tool. | +| No deprecated properties | In `manifest.json` or elsewhere. | +| Not a UI5 project | Except for `Component`-type cards. | + +## 4. Preview + +If asked to preview, first check the card folder for an existing preview entry point — `package.json` `start` script, `README.md`, or an existing HTML file. Reuse it if present. Otherwise create an HTML page with a `` element pointing at the manifest, and serve via an `http` server. ## 5. Configuration Editor -Configuration Editor allows different personas to customize Integration Cards without modifying the manifest file directly. -The following roles/personas are supported: -- Administrator -- Page/Content Administrator -- Translator - -The Configuration Editor is implemented through two key components: - -1. **Definition file**: Create a `dt/Configuration.js` file that exports a Designtime definition object -2. **Manifest reference**: Reference this definition in the `manifest.json` under the `sap.card/configuration/editor` property - -The `dt/Configuration.js` file defines the Configuration Editor's structure by specifying: -- Form layout and field definitions -- Input controls and visualizations -- Validation rules and field relationships -- Grouping and organization of configuration options - -When creating or modifying Integration Cards, follow these guidelines for Configuration Editors: -- Assume the role of Administrator persona when designing the Configuration Editor. -- **ALWAYS** ensure that the Configuration Editor reflects the current structure and fields of the `manifest.json`. -- **ALWAYS** make the existing fields in the `manifest.json` configurable via the editor. For example manifest parameters, title, subtitle, icon of the header, etc. -- **ALWAYS** ask the user if they agree to make all fields editable and what other fields should be added. -- **NEVER** add fields to the editor that do not exist in the `manifest.json`. -- **ALWAYS** remove fields from the editor when removing them from the `manifest.json`. -- **ALWAYS** add fields in the Configuration Editor when adding them to the `manifest.json`. - -- **ALWAYS** load [references/configuration_editor_example.md](references/configuration_editor_example.md) before creating or modifying a Configuration Editor (i.e. a `dt/Configuration.js` file). It contains a complete example of a `manifest.json` paired with the corresponding `dt/Configuration.js` file. - -## 6. Analytical Cards Coding Guidelines -- **ALWAYS** set `sap.card/content/chartType` property. -- **ALWAYS** adjust `sap.card/content/measures`, `sap.card/content/dimensions` and `sap.card/content/feeds` to match the `sap.card/content/chartType` property and data structure. This is critical for proper data display. -- **ALWAYS** use `sap.card/content/chartProperties` to adjust labels, colors, the legend, and other chart aspects. -- **ALWAYS** define each feed with its type (Dimension or Measure), its unique identifier (uid), and the associated values using defined measures and dimensions. Example (for a donut/pie chart): + +The editor lets the Administrator, Page/Content Administrator, and Translator personas customise a card without editing `manifest.json` directly. + +Two pieces: +1. `dt/Configuration.js` — exports a function that returns `new Designtime({ form: {...} })`. +2. `manifest.json` — references the file at `sap.card/configuration/editor`. + +Design as the **Administrator** persona. + +| Rule | Detail | +|---|---| +| Mirror the manifest | Editor reflects the current structure and parameters of `manifest.json` exactly. | +| All existing fields editable | Title, subtitle, header icon, parameters — make them configurable. | +| Ask the user | Before deciding which fields to expose, ask: "Make all manifest fields editable? Anything else to add?" | +| No invented fields | Never add an editor field that does not exist in `manifest.json`. | +| Keep in sync | Add to or remove from the editor when adding to or removing from `manifest.json`. | + +Load [`references/configuration_editor_example.md`](references/configuration_editor_example.md) for the canonical paired example. + +## 6. Analytical cards + +Load [`references/analytical_chart_types.md`](references/analytical_chart_types.md) for the full chart-type catalog (UIDs and per-type examples). + +| Rule | Detail | +|---|---| +| Set `chartType` | `sap.card/content/chartType` is required. | +| Match feeds to chart type | `measures`, `dimensions`, and `feeds` must match the UIDs the chart type expects. The reference file lists them per chart. | +| Each feed needs three keys | `type` (`Dimension`\|`Measure`), `uid`, `values`. | +| `chartProperties` | Use it for labels, colours, legend, etc. Do not invent keys. Omit entirely if defaults are fine. | + +Minimal feeds example (donut/pie): ```json "feeds": [ - { - "type": "Dimension", - "uid": "color", - "values": [ - "Store Name" - ] - }, - { - "type": "Measure", - "uid": "size", - "values": [ - "Revenue" - ] - } + { "type": "Dimension", "uid": "color", "values": ["Store Name"] }, + { "type": "Measure", "uid": "size", "values": ["Revenue"] } ] ``` -- **ALWAYS** ensure the `uid` in `feeds` exactly matches the UID required for the selected chartType (e.g., color, size, dataFrame). -- **ALWAYS** load [references/analytical_chart_types.md](references/analytical_chart_types.md) before creating or modifying an Analytical card. It contains the comprehensive list of all supported chart types, their required UIDs, and example configurations. +## 7. Card Explorer (reference) + +- Schema docs and live samples: +- Sample sources: From 28d7c4b0952e8d21468550f5fa78706976f12a4e Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 May 2026 12:40:28 +0300 Subject: [PATCH 09/13] docs(ui5): Polish integration cards skill from review feedback - Document that manifestpath can target any manifest path, not only configuration parameters, in the "Mirror the manifest" rule - Add color-feed Note and second dimension to timeseries_stacked_column and timeseries_100_stacked_column examples; without a color dimension these chart types render as plain timeseries_column - Fix indentation of the data block in the Configuration Editor example and add the missing header.icon.backgroundColor that the editor's iconBackground field references - Use American English spelling (customize, colors) --- plugins/ui5/skills/integration-cards/SKILL.md | 6 +++--- .../references/analytical_chart_types.md | 20 +++++++++++++++++++ .../configuration_editor_example.md | 17 ++++++++-------- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md index 8e60d6c..5801388 100644 --- a/plugins/ui5/skills/integration-cards/SKILL.md +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -60,7 +60,7 @@ If asked to preview, first check the card folder for an existing preview entry p ## 5. Configuration Editor -The editor lets the Administrator, Page/Content Administrator, and Translator personas customise a card without editing `manifest.json` directly. +The editor lets the Administrator, Page/Content Administrator, and Translator personas customize a card without editing `manifest.json` directly. Two pieces: 1. `dt/Configuration.js` — exports a function that returns `new Designtime({ form: {...} })`. @@ -70,7 +70,7 @@ Design as the **Administrator** persona. | Rule | Detail | |---|---| -| Mirror the manifest | Editor reflects the current structure and parameters of `manifest.json` exactly. | +| Mirror the manifest | Editor reflects the current structure and parameters of `manifest.json` exactly. `manifestpath` can target any existing path — a `configuration/parameters/*/value` for parameterized fields, or a direct path like `/sap.card/header/icon/shape` for static manifest properties. | | All existing fields editable | Title, subtitle, header icon, parameters — make them configurable. | | Ask the user | Before deciding which fields to expose, ask: "Make all manifest fields editable? Anything else to add?" | | No invented fields | Never add an editor field that does not exist in `manifest.json`. | @@ -87,7 +87,7 @@ Load [`references/analytical_chart_types.md`](references/analytical_chart_types. | Set `chartType` | `sap.card/content/chartType` is required. | | Match feeds to chart type | `measures`, `dimensions`, and `feeds` must match the UIDs the chart type expects. The reference file lists them per chart. | | Each feed needs three keys | `type` (`Dimension`\|`Measure`), `uid`, `values`. | -| `chartProperties` | Use it for labels, colours, legend, etc. Do not invent keys. Omit entirely if defaults are fine. | +| `chartProperties` | Use it for labels, colors, legend, etc. Do not invent keys. Omit entirely if defaults are fine. | Minimal feeds example (donut/pie): ```json diff --git a/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md b/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md index 86e59f3..a261f65 100644 --- a/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md +++ b/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md @@ -383,6 +383,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 11. timeseries_stacked_column * UIDs: timeAxis, color, valueAxis + * Note: Stacking requires a second dimension fed to `color`; without it the chart renders as a plain timeseries_column * Example: ```json { @@ -397,6 +398,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "name": "Year", "value": "{yearField}", "dataType": "date" + }, + { + "name": "Sector", + "value": "{sectorField}" } ], "feeds": [ @@ -409,6 +414,11 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "type": "Measure", "uid": "valueAxis", "values": ["Investment"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Sector"] } ] } @@ -500,6 +510,7 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr 14. timeseries_100_stacked_column * UIDs: timeAxis, color, valueAxis + * Note: Stacking requires a second dimension fed to `color`; without it the chart renders as a plain timeseries_column * Example: ```json { @@ -514,6 +525,10 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "name": "Year", "value": "{yearField}", "dataType": "date" + }, + { + "name": "Sector", + "value": "{sectorField}" } ], "feeds": [ @@ -526,6 +541,11 @@ For each chart type, the `feeds` array must use the listed UIDs to bind the corr "type": "Measure", "uid": "valueAxis", "values": ["Investment"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Sector"] } ] } diff --git a/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md b/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md index 57b8994..d1a4ca6 100644 --- a/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md +++ b/plugins/ui5/skills/integration-cards/references/configuration_editor_example.md @@ -51,20 +51,21 @@ This reference shows a complete pairing of a `manifest.json` and the correspondi } }, "data": { - "request": { - "url": "{{destinations.northwind}}/Customers", - "parameters": { - "$select": "CustomerID,CompanyName,ContactName", - "$top": "{parameters>/maxItems/value}" - } + "request": { + "url": "{{destinations.northwind}}/Customers", + "parameters": { + "$select": "CustomerID,CompanyName,ContactName", + "$top": "{parameters>/maxItems/value}" } - }, + } + }, "header": { "title": "{parameters>/cardTitle/value}", "subtitle": "As of {parameters>/dateContext/value}", "icon": { "src": "{parameters>/icon/value}", - "shape": "Circle" + "shape": "Circle", + "backgroundColor": "Transparent" } }, "content": { From a3f82a0ae3ea84b8c7eb59c94cf5dbabf91a24ba Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 May 2026 12:46:52 +0300 Subject: [PATCH 10/13] docs(ui5): Add integration-cards section to plugin README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit List the high-level capabilities the skill covers — declarative card types, binding syntax, data placement, validation, preview, the Configuration Editor, the Analytical cards catalog, and i18n/actions — matching the style of the ui5-best-practices entry. --- plugins/ui5/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/ui5/README.md b/plugins/ui5/README.md index fa34919..e896127 100644 --- a/plugins/ui5/README.md +++ b/plugins/ui5/README.md @@ -6,6 +6,17 @@ - Supports the developer to detect and fix UI5-specific errors - Provides additional UI5-specific information for Claude Code +### 📋 Skills: integration-cards + +Development guidelines for UI Integration Cards (also known as UI5 Integration Cards) — `manifest.json`, Configuration Editor, and Analytical cards: +- **Declarative card types** - List, Table, Calendar, Timeline, Object, Analytical +- **Parameter and destination binding** - `{parameters>/key/value}` and `{{destinations.name}}` syntax +- **Data placement rules** - When `sap.card/data` vs `content/data` vs `header/data` applies +- **Manifest validation** - JSON, schema, and deprecated-property checks before declaring done +- **Local preview workflow** - Reusing existing entry points or serving via a `` HTML page +- **Configuration Editor patterns** - `dt/Configuration.js` paired with `manifest.json`, mirroring fields and `manifestpath` targets +- **Analytical cards** - 44 chart types with required UIDs, feeds, and per-type examples +- **i18n and actions** - Bind user-visible strings; use `actions` for links instead of inline `` ## Installation From c29762b57d53d8f0e255962812721f484c68c714 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 May 2026 12:51:36 +0300 Subject: [PATCH 11/13] docs(ui5): Require JSON responses for integration card data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card's data layer parses JSON only — XML, CSV, and other response types fail silently or render no data. Note the OData $format=json workaround for services that default to XML. --- plugins/ui5/skills/integration-cards/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md index 5801388..fb0caa1 100644 --- a/plugins/ui5/skills/integration-cards/SKILL.md +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -29,6 +29,7 @@ If the trigger applies, load before producing any output. Do not work from memor | Validate before declaring done | See [3. Validation](#3-validation). | | Show preview when requested | See [4. Preview](#4-preview). | | Don't modify provided data | Use it as supplied. | +| JSON responses only | The endpoint behind `sap.card/data/request` must return JSON. For OData services, append `$format=json` to the request URL or parameters. | ## 2. Data placement From e26057d3dc3d5ca8e3d2ca078482381cd3230f0e Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 May 2026 19:04:05 +0300 Subject: [PATCH 12/13] docs(ui5): Recommend wrapping service URLs in destinations Add a Core rule next to the destination-binding-syntax row so agents default to declaring a destination per external endpoint instead of inlining raw URLs in the manifest. --- plugins/ui5/skills/integration-cards/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md index fb0caa1..29bc30d 100644 --- a/plugins/ui5/skills/integration-cards/SKILL.md +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -24,6 +24,7 @@ If the trigger applies, load before producing any output. Do not work from memor | Use `create_integration_card` MCP tool | When creating a new declarative card. | | Parameter binding syntax | `{parameters>/parameterKey/value}` — single braces, `>` separator, `value` suffix. | | Destination binding syntax | `{{destinations.destinationName}}` — double braces, dot. Configure under `sap.card/configuration/destinations/`. Reference by name; never replace with raw URL. | +| Use destinations for service URLs | Wrap every external service URL in a destination under `sap.card/configuration/destinations/` and reference it as `{{destinations.name}}`. | | i18n binding | Bind every non-data, user-visible string to the i18n model. | | Links | Use the `actions` property; never inline `` or hand-rolled URL handlers. | | Validate before declaring done | See [3. Validation](#3-validation). | From 6ca4849194d2be6fa335d0339212349bfa436d34 Mon Sep 17 00:00:00 2001 From: Petar Dimov Date: Thu, 21 May 2026 19:04:05 +0300 Subject: [PATCH 13/13] docs(ui5): Refine integration-cards bullets in plugin README Split i18n and actions into separate bullets, add a "Building a card" entry covering the manifest structure, and rephrase the data-related bullet to cover placement, destinations, and the JSON-only response requirement together. --- plugins/ui5/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/ui5/README.md b/plugins/ui5/README.md index e896127..27676ee 100644 --- a/plugins/ui5/README.md +++ b/plugins/ui5/README.md @@ -8,15 +8,17 @@ ### 📋 Skills: integration-cards -Development guidelines for UI Integration Cards (also known as UI5 Integration Cards) — `manifest.json`, Configuration Editor, and Analytical cards: +Development guidelines for UI Integration Cards (also known as UI5 Integration Cards): - **Declarative card types** - List, Table, Calendar, Timeline, Object, Analytical +- **Building a card** - Structure of the declarative `manifest.json` format for a UI Integration Card - **Parameter and destination binding** - `{parameters>/key/value}` and `{{destinations.name}}` syntax -- **Data placement rules** - When `sap.card/data` vs `content/data` vs `header/data` applies +- **Data rules** - Where the data block goes (`sap.card/data`/`content/data`/`header/data`), wrapping URLs in destinations, and requiring JSON responses - **Manifest validation** - JSON, schema, and deprecated-property checks before declaring done - **Local preview workflow** - Reusing existing entry points or serving via a `` HTML page - **Configuration Editor patterns** - `dt/Configuration.js` paired with `manifest.json`, mirroring fields and `manifestpath` targets - **Analytical cards** - 44 chart types with required UIDs, feeds, and per-type examples -- **i18n and actions** - Bind user-visible strings; use `actions` for links instead of inline `` +- **i18n** - Bind all user-facing strings to the i18n model; never hardcode +- **Actions** - Use the `actions` property for links and interactions; never inline `` tags or hand-roll URL handlers ## Installation