diff --git a/plugins/ui5/skills/integration-cards/SKILL.md b/plugins/ui5/skills/integration-cards/SKILL.md new file mode 100644 index 0000000..8e60d6c --- /dev/null +++ b/plugins/ui5/skills/integration-cards/SKILL.md @@ -0,0 +1,103 @@ +--- +name: integration-cards +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 + +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 + +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"] } +] +``` + +## 7. Card Explorer (reference) + +- Schema docs and live samples: +- Sample sources: 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..86e59f3 --- /dev/null +++ b/plugins/ui5/skills/integration-cards/references/analytical_chart_types.md @@ -0,0 +1,1732 @@ +# 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 + * Note: Stacking requires a second dimension fed to `color`; without it the chart renders as a plain bar + * Example: + ```json + { + "measures": [ + { + "name": "Revenue", + "value": "{revenueField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + }, + { + "name": "Product", + "value": "{productField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Revenue"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Product"] + } + ] + } + ``` + +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 + { + "measures": [ + { + "name": "Market Share", + "value": "{marketShareField}" + } + ], + "dimensions": [ + { + "name": "Sector", + "value": "{sectorField}" + }, + { + "name": "Product", + "value": "{productField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Sector"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Market Share"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Product"] + } + ] + } + ``` + +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 + * Note: Stacking requires a second dimension fed to `color`; without it the chart renders as a plain bar + * Example: + ```json + { + "measures": [ + { + "name": "Costs", + "value": "{costsField}" + } + ], + "dimensions": [ + { + "name": "Region", + "value": "{regionField}" + }, + { + "name": "Category", + "value": "{categoryField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Region"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Costs"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Category"] + } + ] + } + ``` + +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 + { + "measures": [ + { + "name": "Market Share", + "value": "{marketShareField}" + } + ], + "dimensions": [ + { + "name": "Product", + "value": "{productField}" + }, + { + "name": "Region", + "value": "{regionField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Product"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Market Share"] + }, + { + "type": "Dimension", + "uid": "color", + "values": ["Region"] + } + ] + } + ``` + +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}", + "dataType": "date" + }, + { + "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 + * Note: `targetValues` expects a measure (target value), not a dimension + * Example: + ```json + { + "measures": [ + { + "name": "Achievement", + "value": "{achievementField}" + }, + { + "name": "Target", + "value": "{targetField}" + } + ], + "dimensions": [ + { + "name": "KPI Name", + "value": "{kpiNameField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["KPI Name"] + }, + { + "type": "Measure", + "uid": "actualValues", + "values": ["Achievement"] + }, + { + "type": "Measure", + "uid": "targetValues", + "values": ["Target"] + } + ] + } + ``` + +30. bullet + * UIDs: categoryAxis, color, actualValues, additionalValues, targetValues, forecastValues + * Note: `targetValues` expects a measure (target value), not a dimension + * Example: + ```json + { + "measures": [ + { + "name": "Achievement", + "value": "{achievementField}" + }, + { + "name": "Target", + "value": "{targetField}" + } + ], + "dimensions": [ + { + "name": "KPI Name", + "value": "{kpiNameField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["KPI Name"] + }, + { + "type": "Measure", + "uid": "actualValues", + "values": ["Achievement"] + }, + { + "type": "Measure", + "uid": "targetValues", + "values": ["Target"] + } + ] + } + ``` + +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 + * Note: `waterfallType` is optional but recommended; it distinguishes total bars from running positive/negative changes + * Example: + ```json + { + "measures": [ + { + "name": "Change", + "value": "{changeField}" + } + ], + "dimensions": [ + { + "name": "Phase", + "value": "{phaseField}" + }, + { + "name": "Type", + "value": "{typeField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Phase"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Change"] + }, + { + "type": "Dimension", + "uid": "waterfallType", + "values": ["Type"] + } + ] + } + ``` + +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 + * Note: `waterfallType` is optional but recommended; it distinguishes total bars from running positive/negative changes + * Example: + ```json + { + "measures": [ + { + "name": "Growth", + "value": "{growthField}" + } + ], + "dimensions": [ + { + "name": "Milestone", + "value": "{milestoneField}" + }, + { + "name": "Type", + "value": "{typeField}" + } + ], + "feeds": [ + { + "type": "Dimension", + "uid": "categoryAxis", + "values": ["Milestone"] + }, + { + "type": "Measure", + "uid": "valueAxis", + "values": ["Growth"] + }, + { + "type": "Dimension", + "uid": "waterfallType", + "values": ["Type"] + } + ] + } + ``` + +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"] + } + ] + } + ``` 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..57b8994 --- /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" + }, + "Customer": { + "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/configuration/parameters/icon/value", + 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" + }, + + Customer: { + manifestpath: "/sap.card/configuration/parameters/Customer/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" + } + }); + }; +}); +```