Skip to content

Commit 5f67820

Browse files
authored
feat(Integration Cards): Improve generation of Configuration Editor (#230)
1 parent 4a52d77 commit 5f67820

36 files changed

Lines changed: 1252 additions & 56 deletions

resources/integration_cards_guidelines.md

Lines changed: 260 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
### 1.3 Analytical Cards
4040
- **ALWAYS** follow [6. Analytical Cards Coding Guidelines](#6-analytical-cards-coding-guidelines) when developing Analytical cards.
4141

42+
### 1.4 Configuration Editor
43+
- **ALWAYS** follow [5. Configuration Editor](#5-configuration-editor) guidelines when creating or modifying Configuration Editors for Integration Cards.
44+
4245
## 2. Validation
4346
- **ALWAYS** ensure that `manifest.json` file is valid JSON.
4447
- **ALWAYS** ensure that in `manifest.json` file the property `sap.app/type` is set to `"card"`.
@@ -50,13 +53,268 @@
5053
- 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
5154

5255
## 4. Preview Instructions
53-
- **ALWAYS** check the card folder for an existing preview file and any accompanying instructions or scripts, and reuse them if available.
56+
- 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.
5457
* for example, in NodeJS-based projects, search the `package.json` file for `start` or similar script. If such is available, use it
5558
* also search in the `README.md` file.
5659
- 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.
5760

5861
## 5. Configuration Editor
59-
- When a Configuration Editor is available, make as many Integration Card fields editable as possible.
62+
Configuration Editor allows different personas to customize Integration Cards without modifying the manifest file directly.
63+
The following roles/personas are supported:
64+
- Administrator
65+
- Page/Content Administrator
66+
- Translator
67+
68+
The Configuration Editor is implemented through two key components:
69+
70+
1. **Definition file**: Create a `dt/Configuration.js` file that exports a Designtime definition object
71+
2. **Manifest reference**: Reference this definition in the `manifest.json` under the `sap.card/configuration/editor` property
72+
73+
The `dt/Configuration.js` file defines the Configuration Editor's structure by specifying:
74+
- Form layout and field definitions
75+
- Input controls and visualizations
76+
- Validation rules and field relationships
77+
- Grouping and organization of configuration options
78+
79+
When creating or modifying Integration Cards, follow these guidelines for Configuration Editors:
80+
- Assume the role of Administrator persona when designing the Configuration Editor.
81+
- **ALWAYS** ensure that the Configuration Editor reflects the current structure and fields of the `manifest.json`.
82+
- **ALWAYS** make the existing fields in the `manifest.json` configurable via the editor. For example manifest parameters, title, subtitle, icon of the header, etc.
83+
- **NEVER** add fields to the editor that do not exist in the `manifest.json`.
84+
- **ALWAYS** remove fields from the editor when removing them from the `manifest.json`.
85+
- **ALWAYS** add fields in the Configuration Editor when adding them to the `manifest.json`.
86+
87+
### 5.1 Example:
88+
`manifest.json` file:
89+
```json
90+
{
91+
"sap.app": {
92+
"id": "test.editor",
93+
"type": "card",
94+
"title": "Test Card",
95+
"applicationVersion": {
96+
"version": "1.0.0"
97+
}
98+
},
99+
"sap.ui": {
100+
"technology": "UI5"
101+
},
102+
"sap.card": {
103+
"type": "List",
104+
"configuration": {
105+
"editor": "./dt/Configuration",
106+
"parameters": {
107+
"cardTitle": {
108+
"value": "Customers"
109+
},
110+
"icon": {
111+
"value": "sap-icon://account"
112+
},
113+
"maxItems": {
114+
"value": 3
115+
},
116+
"showDescription": {
117+
"value": true
118+
},
119+
"dateContext": {
120+
"value": "2020-09-02"
121+
},
122+
"Customers": {
123+
"value": ["ALFKI"]
124+
},
125+
"northwindDestination": {
126+
"value": "northwind"
127+
}
128+
},
129+
"destinations": {
130+
"northwind": {
131+
"name": "Northwind_V4",
132+
"defaultUrl": "https://services.odata.org/V4/Northwind/Northwind.svc"
133+
}
134+
}
135+
},
136+
"data": {
137+
"request": {
138+
"url": "{{destinations.northwind}}/Customers",
139+
"parameters": {
140+
"$select": "CustomerID,CompanyName,ContactName",
141+
"$top": "{parameters>/maxItems/value}"
142+
}
143+
}
144+
},
145+
"header": {
146+
"title": "{parameters>/cardTitle/value}",
147+
"subtitle": "As of {parameters>/dateContext/value}",
148+
"icon": {
149+
"src": "{parameters>/icon/value}",
150+
"shape": "Circle"
151+
}
152+
},
153+
"content": {
154+
"data": {
155+
"path": "/value"
156+
},
157+
"item": {
158+
"title": "{CompanyName}",
159+
"description": "{= ${parameters>/showDescription/value} ? ${ContactName} : '' }"
160+
},
161+
"maxItems": "{parameters>/maxItems/value}"
162+
}
163+
}
164+
}
165+
```
166+
167+
`dt/Configuration.js` file:
168+
```javascript
169+
sap.ui.define(["sap/ui/integration/Designtime"], function (Designtime) {
170+
"use strict";
171+
172+
return function () {
173+
return new Designtime({
174+
form: {
175+
items: {
176+
177+
/* =======================
178+
General
179+
======================= */
180+
generalGroup: {
181+
type: "group",
182+
label: "General"
183+
},
184+
185+
cardTitle: {
186+
manifestpath: "/sap.card/configuration/parameters/cardTitle/value",
187+
type: "string",
188+
label: "Card Title",
189+
translatable: true,
190+
required: true,
191+
allowDynamicValues: true
192+
},
193+
194+
icon: {
195+
manifestpath: "/sap.card/header/icon/src",
196+
type: "string",
197+
label: "Icon",
198+
visualization: {
199+
type: "IconSelect",
200+
settings: {
201+
value: "{currentSettings>value}",
202+
editable: "{currentSettings>editable}"
203+
}
204+
}
205+
},
206+
207+
iconShape: {
208+
manifestpath: "/sap.card/header/icon/shape",
209+
type: "string",
210+
label: "Icon Shape",
211+
visualization: {
212+
type: "ShapeSelect",
213+
settings: {
214+
value: "{currentSettings>value}",
215+
editable: "{currentSettings>editable}"
216+
}
217+
},
218+
cols: 1
219+
},
220+
221+
iconBackground: {
222+
manifestpath: "/sap.card/header/icon/backgroundColor",
223+
type: "string",
224+
label: "Icon Background",
225+
visualization: {
226+
type: "ColorSelect",
227+
settings: {
228+
enumValue: "{currentSettings>value}",
229+
editable: "{currentSettings>editable}"
230+
}
231+
},
232+
cols: 1
233+
},
234+
235+
/* =======================
236+
Data & Behavior
237+
======================= */
238+
dataGroup: {
239+
type: "group",
240+
label: "Data & Behavior"
241+
},
242+
243+
maxItems: {
244+
manifestpath: "/sap.card/configuration/parameters/maxItems/value",
245+
type: "integer",
246+
label: "Maximum Items",
247+
visualization: {
248+
type: "Slider",
249+
settings: {
250+
value: "{currentSettings>value}",
251+
min: 1,
252+
max: 10,
253+
width: "100%",
254+
enabled: "{currentSettings>editable}"
255+
}
256+
}
257+
},
258+
259+
showDescription: {
260+
manifestpath: "/sap.card/configuration/parameters/showDescription/value",
261+
type: "boolean",
262+
label: "Show Contact Name",
263+
visualization: {
264+
type: "Switch",
265+
settings: {
266+
state: "{currentSettings>value}",
267+
customTextOn: "Show",
268+
customTextOff: "Hide",
269+
enabled: "{currentSettings>editable}"
270+
}
271+
}
272+
},
273+
274+
dateContext: {
275+
manifestpath: "/sap.card/configuration/parameters/dateContext/value",
276+
type: "date",
277+
label: "Date Context"
278+
},
279+
280+
/* =======================
281+
Filtering
282+
======================= */
283+
filterGroup: {
284+
type: "group",
285+
label: "Customer Filter"
286+
},
287+
288+
CustomerID: {
289+
manifestpath: "/sap.card/configuration/parameters/CustomerID/value",
290+
type: "string",
291+
label: "Customer ID",
292+
values: {
293+
data: {
294+
request: {
295+
url: "{{destinations.northwind}}/Customers",
296+
parameters: {
297+
"$select": "CustomerID,CompanyName"
298+
}
299+
},
300+
path: "/value"
301+
},
302+
item: {
303+
key: "{CustomerID}",
304+
text: "{CompanyName}"
305+
}
306+
}
307+
}
308+
}
309+
},
310+
preview: {
311+
modes: "None"
312+
}
313+
});
314+
};
315+
});
316+
317+
```
60318

61319
## 6. Analytical Cards Coding Guidelines
62320
- **ALWAYS** set `sap.card/content/chartType` property.

resources/template-card/card/manifest.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@
1919
"sap.card": {
2020
"type": "<%= cardType %>",
2121
"configuration": {
22+
<% if (destinations && destinations.length > 0) { %>
23+
"destinations": {
24+
<% destinations.forEach(function(destination, index) { %>
25+
"<%= destination.name %>": {
26+
"name": "<%= destination.name %>",
27+
"defaultUrl": "<%= destination.defaultUrl %>"
28+
}<%= index < destinations.length - 1 ? ',' : '' %>
29+
<% }); %>
30+
},
31+
<% } %>
2232
"editor": "./dt/Configuration"
2333
},
2434
<% if (cardType === "Analytical") { %>

resources/template-card/test/App.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@ sap.ui.define(["sap/ui/integration/Host"], async (Host) => {
77
const card = document.getElementById("card");
88
const applyChangesBtn = document.getElementById("applyChangesBtn");
99
const resetBtn = document.getElementById("resetBtn");
10+
const destinations = {
11+
<%_ if (destinations && destinations.length > 0) { -%>
12+
<%_ destinations.forEach(function(destination, index) { -%>
13+
"<%= destination.name %>": "<%= destination.defaultUrl %>"<%= index < destinations.length - 1 ? ',' : '' %>
14+
<%_ }); -%>
15+
<%_ } -%>
16+
};
1017
const host = new Host({
11-
resolveDestination: function(sDestinationName) {
12-
if (sDestinationName === "Northwind") {
13-
return "https://services.odata.org/V4/Northwind/Northwind.svc/";
18+
resolveDestination: function(destinationName) {
19+
if (destinations[destinationName]) {
20+
return destinations[destinationName];
1421
}
1522

16-
throw new Error("Destination " + sDestinationName + " not found!");
23+
return Promise.reject("Destination " + destinationName + " not found!");
1724
},
1825
actions: [
1926
{
@@ -27,6 +34,15 @@ sap.ui.define(["sap/ui/integration/Host"], async (Host) => {
2734
]
2835
});
2936

37+
// Called by the Configuration Editor to show a list of available destinations
38+
host.getDestinations = function() {
39+
return Promise.resolve(Object.entries(destinations).map(([name, url]) => {
40+
return {
41+
name,
42+
};
43+
}));
44+
};
45+
3046
card.host = host.getId();
3147
card.manifest = "../card/manifest.json";
3248

resources/template-card/test/index.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ body {
1717
flex-direction: column;
1818
}
1919

20+
#editorSection {
21+
--sapUiIntegrationEditorFormHeight: auto;
22+
}
23+
2024
.button-container {
2125
display: flex;
2226
justify-content: flex-end;

resources/template-card/test/index.html

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ <h1>Preview</h1>
2323
<div class="card-section">
2424
<h2>Integration Card</h2>
2525
<ui-integration-card
26-
id="card"
27-
width="300px"
28-
height="400px">
26+
id="card">
2927
</ui-integration-card>
3028
</div>
3129
<div id="editorSection"

server.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"environmentVariables": [
2020
{
2121
"default": "localhost, services.odata.org",
22-
"description": "A comma-separated list of domains that are allowed to be used in the 'oDataV4Url' parameter of the 'create_ui5_app' tool, for example: 'localhost, example.com, sub.example.com'. Set to an empty string to allow any domains. For wildcard subdomains, prefix the domain with a dot: '.example.com'. This will match 'www.example.com' but not 'example.com'.",
22+
"description": "A comma-separated list of domains that are allowed to be used in various tools, for example: 'localhost, example.com, sub.example.com'. Set to an empty string to allow any domains. For wildcard subdomains, prefix the domain with a dot: '.example.com'. This will match 'www.example.com' but not 'example.com'.",
2323
"isRequired": false,
2424
"format": "string",
2525
"isSecret": false,

0 commit comments

Comments
 (0)