@openmfp/webcomponents 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/.github/workflows/pipeline.yaml +41 -0
  2. package/.storybook/main.ts +34 -0
  3. package/.storybook/preview.ts +29 -0
  4. package/.storybook/tsconfig.json +11 -0
  5. package/AGENTS.md +153 -0
  6. package/CODEOWNERS +6 -0
  7. package/CONTRIBUTING.md +95 -0
  8. package/LICENSE +201 -0
  9. package/LICENSES/Apache-2.0.txt +73 -0
  10. package/README.md +91 -0
  11. package/REUSE.toml +9 -0
  12. package/angular.json +157 -0
  13. package/docs/dashboard.md +358 -0
  14. package/docs/declarative-form.md +178 -0
  15. package/docs/declarative-table-card.md +235 -0
  16. package/docs/declarative-table.md +315 -0
  17. package/eslint.config.js +41 -0
  18. package/package.json +73 -0
  19. package/projects/ngx/cards/favorites/favorites.component.html +12 -0
  20. package/projects/ngx/cards/favorites/favorites.component.scss +50 -0
  21. package/projects/ngx/cards/favorites/favorites.component.ts +19 -0
  22. package/projects/ngx/cards/public-api.ts +4 -0
  23. package/projects/ngx/cards/service-status/service-status-card.component.html +15 -0
  24. package/projects/ngx/cards/service-status/service-status-card.component.scss +87 -0
  25. package/projects/ngx/cards/service-status/service-status-card.component.ts +36 -0
  26. package/projects/ngx/cards/stories/visited-service-card.stories.ts +149 -0
  27. package/projects/ngx/cards/visited-service-card/visited-service-card.component.html +17 -0
  28. package/projects/ngx/cards/visited-service-card/visited-service-card.component.scss +34 -0
  29. package/projects/ngx/cards/visited-service-card/visited-service-card.component.ts +22 -0
  30. package/projects/ngx/cards/whats-new/whats-new.component.html +10 -0
  31. package/projects/ngx/cards/whats-new/whats-new.component.scss +25 -0
  32. package/projects/ngx/cards/whats-new/whats-new.component.ts +46 -0
  33. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.html +28 -0
  34. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.scss +44 -0
  35. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.spec.ts +85 -0
  36. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.ts +58 -0
  37. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.html +29 -0
  38. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.scss +63 -0
  39. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.spec.ts +255 -0
  40. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.ts +75 -0
  41. package/projects/ngx/declarative-ui/dashboard/card/utils/dashboard-card-registry.spec.ts +76 -0
  42. package/projects/ngx/declarative-ui/dashboard/card/utils/dashboard-card-registry.ts +109 -0
  43. package/projects/ngx/declarative-ui/dashboard/card/utils/index.ts +4 -0
  44. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-angular-card.spec.ts +141 -0
  45. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-angular-card.ts +44 -0
  46. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-sap-card.spec.ts +142 -0
  47. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-sap-card.ts +52 -0
  48. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-wc-card.spec.ts +107 -0
  49. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-wc-card.ts +22 -0
  50. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.html +134 -0
  51. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.scss +88 -0
  52. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.spec.ts +354 -0
  53. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.ts +238 -0
  54. package/projects/ngx/declarative-ui/dashboard/dashboard/index.ts +1 -0
  55. package/projects/ngx/declarative-ui/dashboard/index.ts +5 -0
  56. package/projects/ngx/declarative-ui/dashboard/models/constants.ts +2 -0
  57. package/projects/ngx/declarative-ui/dashboard/models/dashboard.model.ts +50 -0
  58. package/projects/ngx/declarative-ui/dashboard/models/index.ts +1 -0
  59. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.html +28 -0
  60. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.scss +85 -0
  61. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.spec.ts +104 -0
  62. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.ts +23 -0
  63. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.html +62 -0
  64. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.scss +12 -0
  65. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.spec.ts +301 -0
  66. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.ts +166 -0
  67. package/projects/ngx/declarative-ui/form/declarative-form/index.ts +1 -0
  68. package/projects/ngx/declarative-ui/form/index.ts +2 -0
  69. package/projects/ngx/declarative-ui/form/models/form-field-definition.ts +15 -0
  70. package/projects/ngx/declarative-ui/form/models/index.ts +1 -0
  71. package/projects/ngx/declarative-ui/form/utils/set-property-by-path.ts +30 -0
  72. package/projects/ngx/declarative-ui/models/index.ts +2 -0
  73. package/projects/ngx/declarative-ui/models/resource.ts +5 -0
  74. package/projects/ngx/declarative-ui/models/ui-definition.ts +95 -0
  75. package/projects/ngx/declarative-ui/public-api.ts +4 -0
  76. package/projects/ngx/declarative-ui/stories/add-card-dialog.stories.ts +91 -0
  77. package/projects/ngx/declarative-ui/stories/background-lightblue.png +0 -0
  78. package/projects/ngx/declarative-ui/stories/dashboard.cards.ts +107 -0
  79. package/projects/ngx/declarative-ui/stories/dashboard.stories.ts +296 -0
  80. package/projects/ngx/declarative-ui/stories/declarative-form.stories.ts +149 -0
  81. package/projects/ngx/declarative-ui/stories/declarative-table-card.stories.ts +358 -0
  82. package/projects/ngx/declarative-ui/stories/declarative-table.stories.ts +363 -0
  83. package/projects/ngx/declarative-ui/stories/pods-table.config.ts +188 -0
  84. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.html +138 -0
  85. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.scss +21 -0
  86. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.spec.ts +345 -0
  87. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.ts +61 -0
  88. package/projects/ngx/declarative-ui/table/declarative-table/index.ts +1 -0
  89. package/projects/ngx/declarative-ui/table/index.ts +2 -0
  90. package/projects/ngx/declarative-ui/table/models/index.ts +14 -0
  91. package/projects/ngx/declarative-ui/table/models/table.model.ts +17 -0
  92. package/projects/ngx/declarative-ui/table/utils/cssRules.engine.spec.ts +146 -0
  93. package/projects/ngx/declarative-ui/table/utils/cssRules.engine.ts +69 -0
  94. package/projects/ngx/declarative-ui/table/utils/field-definition.utils.spec.ts +70 -0
  95. package/projects/ngx/declarative-ui/table/utils/field-definition.utils.ts +13 -0
  96. package/projects/ngx/declarative-ui/table/utils/proccess-fields.spec.ts +511 -0
  97. package/projects/ngx/declarative-ui/table/utils/proccess-fields.ts +71 -0
  98. package/projects/ngx/declarative-ui/table/utils/resource-field-by-path.spec.ts +372 -0
  99. package/projects/ngx/declarative-ui/table/utils/resource-field-by-path.ts +98 -0
  100. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-cell.constants.ts +5 -0
  101. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.html +1 -0
  102. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.scss +0 -0
  103. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.spec.ts +119 -0
  104. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.ts +35 -0
  105. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.html +7 -0
  106. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.scss +0 -0
  107. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.spec.ts +114 -0
  108. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.ts +19 -0
  109. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.html +7 -0
  110. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.scss +10 -0
  111. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.spec.ts +188 -0
  112. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.ts +16 -0
  113. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.html +59 -0
  114. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.scss +33 -0
  115. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.spec.ts +316 -0
  116. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.ts +115 -0
  117. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.html +156 -0
  118. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.scss +123 -0
  119. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.spec.ts +786 -0
  120. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.ts +286 -0
  121. package/projects/ngx/declarative-ui/table-card/index.ts +2 -0
  122. package/projects/ngx/declarative-ui/table-card/models/configs.ts +46 -0
  123. package/projects/ngx/declarative-ui/tsconfig.lib.json +11 -0
  124. package/projects/ngx/declarative-ui/tsconfig.lib.prod.json +9 -0
  125. package/projects/ngx/declarative-ui/tsconfig.spec.json +9 -0
  126. package/projects/ngx/ng-package.json +8 -0
  127. package/projects/ngx/package.json +22 -0
  128. package/projects/ngx/public-api.ts +2 -0
  129. package/projects/ngx/tsconfig.lib.json +11 -0
  130. package/projects/ngx/tsconfig.lib.prod.json +9 -0
  131. package/projects/webcomponents/main.ts +92 -0
  132. package/projects/webcomponents/tsconfig.app.json +9 -0
  133. package/projects/webcomponents-dashboard/main.ts +15 -0
  134. package/projects/webcomponents-dashboard/tsconfig.app.json +9 -0
  135. package/renovate.json +6 -0
  136. package/scripts/bundle-wc.mjs +79 -0
  137. package/tsconfig.json +37 -0
  138. package/tsconfig.spec.json +8 -0
  139. package/tsconfig.storybook.json +16 -0
  140. package/vitest.config.ts +26 -0
@@ -0,0 +1,166 @@
1
+ import {
2
+ FormFieldChangeEvent,
3
+ FormFieldDefinition,
4
+ FormFieldErrors,
5
+ } from '../models';
6
+ import { setPropertyByPath } from '../utils/set-property-by-path';
7
+ import {
8
+ Component,
9
+ ViewEncapsulation,
10
+ effect,
11
+ inject,
12
+ input,
13
+ output,
14
+ } from '@angular/core';
15
+ import {
16
+ FormBuilder,
17
+ FormControl,
18
+ FormGroup,
19
+ ReactiveFormsModule,
20
+ } from '@angular/forms';
21
+ import { Input } from '@fundamental-ngx/ui5-webcomponents/input';
22
+ import { Label } from '@fundamental-ngx/ui5-webcomponents/label';
23
+ import { Option } from '@fundamental-ngx/ui5-webcomponents/option';
24
+ import { Select } from '@fundamental-ngx/ui5-webcomponents/select';
25
+
26
+ @Component({
27
+ selector: 'mfp-declarative-form',
28
+ imports: [ReactiveFormsModule, Input, Label, Select, Option],
29
+ templateUrl: './declarative-form.component.html',
30
+ styleUrl: './declarative-form.component.scss',
31
+ encapsulation: ViewEncapsulation.ShadowDom,
32
+ })
33
+ export class DeclarativeForm {
34
+ readonly fields = input.required<FormFieldDefinition[]>();
35
+ readonly initialValues = input<Record<string, unknown>>({});
36
+ readonly fieldErrors = input<FormFieldErrors>({});
37
+
38
+ readonly fieldChange = output<FormFieldChangeEvent>();
39
+ readonly formSubmit = output<Record<string, unknown>>();
40
+
41
+ readonly form: FormGroup;
42
+
43
+ private readonly fb = inject(FormBuilder);
44
+
45
+ constructor() {
46
+ this.form = this.fb.group({});
47
+
48
+ effect(() => {
49
+ this.rebuildControls(this.fields());
50
+ });
51
+
52
+ effect(() => {
53
+ this.setInitialValues(this.initialValues());
54
+ });
55
+
56
+ effect(() => {
57
+ const fields = this.fields();
58
+ this.initialValues();
59
+
60
+ for (const field of fields) {
61
+ if (field.validation) {
62
+ this.fieldChange.emit({
63
+ fieldProperty: field.name,
64
+ value: this.form.controls[field.name]?.value ?? '',
65
+ });
66
+ }
67
+ }
68
+ });
69
+ }
70
+
71
+ setFormControlValue($event: Event, field: FormFieldDefinition): void {
72
+ const target = $event.target as
73
+ | HTMLInputElement
74
+ | HTMLTextAreaElement
75
+ | HTMLSelectElement;
76
+
77
+ const control = this.form.controls[field.name];
78
+ control.setValue(target.value);
79
+ control.markAsTouched();
80
+ control.markAsDirty();
81
+
82
+ if (field.validation === 'onChange') {
83
+ this.fieldChange.emit({
84
+ fieldProperty: field.name,
85
+ value: control.value,
86
+ });
87
+ }
88
+ }
89
+
90
+ getError(name: string): string | null {
91
+ const control = this.form.controls[name];
92
+ const error = this.fieldErrors()[name];
93
+
94
+ return error && (control.dirty || control.touched) ? error : null;
95
+ }
96
+
97
+ getValueState(name: string): 'None' | 'Negative' {
98
+ return this.getError(name) ? 'Negative' : 'None';
99
+ }
100
+
101
+ onFieldBlur(field: FormFieldDefinition): void {
102
+ this.form.controls[field.name]?.markAsTouched();
103
+
104
+ if (field.validation === 'onBlur') {
105
+ this.fieldChange.emit({
106
+ fieldProperty: field.name,
107
+ value: this.form.controls[field.name]?.value,
108
+ });
109
+ }
110
+ }
111
+
112
+ submit(): void {
113
+ this.formSubmit.emit(this.buildOutputValue());
114
+ }
115
+
116
+ clear(): void {
117
+ this.form.reset();
118
+ }
119
+
120
+ private rebuildControls(fields: FormFieldDefinition[]): void {
121
+ const existingControls = this.form.controls;
122
+ const nextFieldNames = new Set(fields.map((field) => field.name));
123
+
124
+ for (const field of fields) {
125
+ const existingControl = existingControls[field.name];
126
+
127
+ if (existingControl) {
128
+ existingControl.clearValidators();
129
+ existingControl.updateValueAndValidity({ emitEvent: false });
130
+ continue;
131
+ }
132
+
133
+ this.form.addControl(field.name, new FormControl(''));
134
+ }
135
+
136
+ for (const controlName of Object.keys(existingControls)) {
137
+ if (!nextFieldNames.has(controlName)) {
138
+ this.form.removeControl(controlName);
139
+ }
140
+ }
141
+
142
+ this.form.updateValueAndValidity({ emitEvent: false });
143
+ }
144
+
145
+ private setInitialValues(
146
+ initialValues: Record<string, unknown> | null | undefined,
147
+ ): void {
148
+ if (!initialValues) {
149
+ return;
150
+ }
151
+
152
+ this.form.patchValue(initialValues, { emitEvent: false });
153
+ this.form.markAsPristine({ emitEvent: false });
154
+ this.form.updateValueAndValidity({ emitEvent: false });
155
+ }
156
+
157
+ private buildOutputValue(): Record<string, unknown> {
158
+ const result: Record<string, unknown> = {};
159
+
160
+ for (const key of Object.keys(this.form.controls)) {
161
+ setPropertyByPath(result, key, this.form.controls[key].value);
162
+ }
163
+
164
+ return result;
165
+ }
166
+ }
@@ -0,0 +1 @@
1
+ export * from './declarative-form.component';
@@ -0,0 +1,2 @@
1
+ export * from './declarative-form';
2
+ export * from './models';
@@ -0,0 +1,15 @@
1
+ export interface FormFieldDefinition {
2
+ name: string;
3
+ label?: string;
4
+ required?: boolean;
5
+ values?: string[];
6
+ disabled?: boolean;
7
+ validation?: 'onBlur' | 'onChange';
8
+ }
9
+
10
+ export interface FormFieldChangeEvent {
11
+ fieldProperty: string;
12
+ value: unknown;
13
+ }
14
+
15
+ export type FormFieldErrors = Record<string, string | null>;
@@ -0,0 +1 @@
1
+ export * from './form-field-definition';
@@ -0,0 +1,30 @@
1
+ export function setPropertyByPath<T extends Record<string, unknown>>(
2
+ object: T,
3
+ path: string,
4
+ value: unknown,
5
+ ): T {
6
+ const segments = path.split('.').filter(Boolean);
7
+ if (segments.length === 0) {
8
+ return object;
9
+ }
10
+
11
+ let current: Record<string, unknown> = object;
12
+
13
+ for (let i = 0; i < segments.length; i += 1) {
14
+ const key = segments[i];
15
+ if (i === segments.length - 1) {
16
+ current[key] = value;
17
+ break;
18
+ }
19
+
20
+ const existing = current[key];
21
+
22
+ if (existing === undefined || existing === null || typeof existing !== 'object') {
23
+ current[key] = {};
24
+ }
25
+
26
+ current = current[key] as Record<string, unknown>;
27
+ }
28
+
29
+ return object;
30
+ }
@@ -0,0 +1,2 @@
1
+ export * from './resource';
2
+ export * from './ui-definition';
@@ -0,0 +1,5 @@
1
+ export interface GenericResource extends Record<string, unknown> {
2
+ id?: string;
3
+ isAvailable?: boolean;
4
+ accessibleName?: string;
5
+ }
@@ -0,0 +1,95 @@
1
+ import { GenericResource } from './resource';
2
+
3
+ export type TransformType =
4
+ | 'uppercase'
5
+ | 'lowercase'
6
+ | 'capitalize'
7
+ | 'decode'
8
+ | 'encode';
9
+
10
+ export interface PropertyField {
11
+ key: string;
12
+ transform?: TransformType[];
13
+ }
14
+
15
+ export interface UiSettings {
16
+ labelDisplay?: boolean;
17
+ displayAs?:
18
+ | 'secret'
19
+ | 'boolIcon'
20
+ | 'link'
21
+ | 'tooltip'
22
+ | 'alert'
23
+ | 'img'
24
+ | 'button';
25
+ buttonSettings?: ButtonSettings;
26
+ tooltipIcon?: string;
27
+ withCopyButton?: boolean;
28
+ cssCustomization?: Partial<CSSStyleDeclaration>;
29
+ cssRules?: CssRule[];
30
+ columnWidth?: string;
31
+ }
32
+
33
+ type KnownButtonActions = 'openInModal' | 'navigate' | 'edit' | 'delete';
34
+ type ButtonActions = KnownButtonActions | (string & {});
35
+
36
+ export interface ButtonSettings {
37
+ text?: string;
38
+ icon?: string;
39
+ endIcon?: string;
40
+ design?:
41
+ | 'Default'
42
+ | 'Positive'
43
+ | 'Negative'
44
+ | 'Transparent'
45
+ | 'Emphasized'
46
+ | 'Attention';
47
+ tooltip?: string;
48
+ action: ButtonActions;
49
+ modalSettings?: ModalSettings;
50
+ }
51
+
52
+ export interface ModalSettings {
53
+ title?: string;
54
+ size?: 'fullscreen' | 'l' | 'm' | 's'; // ze of the modal
55
+ width?: string; //updates the width of the modal. Allowed units are 'px', '%', 'rem', 'em', 'vh' and 'vw
56
+ height?: string; //updates the height of the modal. Allowed units are 'px', '%', 'rem', 'em', 'vh' and 'vw
57
+ }
58
+
59
+ export type CssRuleCondition =
60
+ | 'equals'
61
+ | 'notEquals'
62
+ | 'greaterThan'
63
+ | 'greaterThanOrEqual'
64
+ | 'lessThan'
65
+ | 'lessThanOrEqual'
66
+ | 'contains';
67
+
68
+ export interface CssRule {
69
+ if: { condition: CssRuleCondition; value: string };
70
+ styles: Partial<CSSStyleDeclaration>;
71
+ }
72
+
73
+ export interface ValueCellButtonClickEvent<T extends GenericResource> {
74
+ event: MouseEvent;
75
+ field: TableFieldDefinition;
76
+ resource: T | undefined;
77
+ }
78
+
79
+ export interface FieldDefinition {
80
+ label?: string;
81
+ property?: string | string[];
82
+ propertyField?: PropertyField;
83
+ jsonPathExpression?: string;
84
+ value?: string;
85
+ uiSettings?: UiSettings;
86
+ }
87
+
88
+ export interface TableFieldDefinition extends FieldDefinition {
89
+ group?: {
90
+ name: string;
91
+ label?: string;
92
+ delimiter?: string;
93
+ multiline?: boolean;
94
+ };
95
+ }
@@ -0,0 +1,4 @@
1
+ export * from './table';
2
+ export * from './form';
3
+ export * from './table-card';
4
+ export * from './dashboard';
@@ -0,0 +1,91 @@
1
+ import { AddCardDialog } from '../dashboard/add-card-dialog/add-card-dialog.component';
2
+ import type { CardConfig } from '../dashboard/models';
3
+ import { Component, Input } from '@angular/core';
4
+ import type { Meta, StoryObj } from '@storybook/angular';
5
+
6
+ const AVAILABLE_CARDS: CardConfig[] = [
7
+ {
8
+ id: 'whats-new',
9
+ component: 'mfp-whats-new',
10
+ label: "What's New",
11
+ w: 3,
12
+ h: 3,
13
+ },
14
+ {
15
+ id: 'recently-visited',
16
+ component: 'mfp-visited-service-card',
17
+ label: 'Recently Visited',
18
+ w: 4,
19
+ h: 1,
20
+ },
21
+ ];
22
+
23
+ @Component({
24
+ selector: 'add-card-dialog-story',
25
+ imports: [AddCardDialog],
26
+ template: `
27
+ <ui5-button (click)="open = true">Open Dialog</ui5-button>
28
+ <mfp-add-card-dialog
29
+ [addedCardsIds]="addedComponents"
30
+ [availableCards]="availableCards"
31
+ [open]="open"
32
+ (cancel)="open = false"
33
+ (confirm)="onConfirm($event)"
34
+ />
35
+ @if (lastAdded) {
36
+ <ui5-message-strip design="Positive" style="margin-top: 1rem;">
37
+ Added: {{ lastAdded }}
38
+ </ui5-message-strip>
39
+ }
40
+ `,
41
+ })
42
+ class AddCardDialogStory {
43
+ @Input() availableCards: CardConfig[] = AVAILABLE_CARDS;
44
+ @Input() addedComponents: Set<string> = new Set<string>();
45
+
46
+ open = false;
47
+ lastAdded = '';
48
+
49
+ onConfirm(cards: CardConfig[]): void {
50
+ this.open = false;
51
+ this.lastAdded = cards.map((c) => c.label || c.component).join(', ');
52
+ setTimeout(() => (this.lastAdded = ''), 3000);
53
+ }
54
+ }
55
+
56
+ const meta: Meta<AddCardDialogStory> = {
57
+ title: 'Declarative UI / AddCardDialog',
58
+ component: AddCardDialogStory,
59
+ tags: ['autodocs'],
60
+ parameters: {
61
+ layout: 'padded',
62
+ },
63
+ argTypes: {
64
+ availableCards: { control: 'object' },
65
+ addedComponents: { control: false },
66
+ },
67
+ };
68
+
69
+ export default meta;
70
+ type Story = StoryObj<AddCardDialogStory>;
71
+
72
+ export const NoneAdded: Story = {
73
+ args: {
74
+ availableCards: AVAILABLE_CARDS,
75
+ addedComponents: new Set<string>(),
76
+ },
77
+ };
78
+
79
+ export const SomeAdded: Story = {
80
+ args: {
81
+ availableCards: AVAILABLE_CARDS,
82
+ addedComponents: new Set(['whats-new']),
83
+ },
84
+ };
85
+
86
+ export const AllAdded: Story = {
87
+ args: {
88
+ availableCards: AVAILABLE_CARDS,
89
+ addedComponents: new Set(AVAILABLE_CARDS.map((c) => c.id)),
90
+ },
91
+ };
@@ -0,0 +1,107 @@
1
+ import { Favorites } from '../../cards/favorites/favorites.component';
2
+ import { ServiceStatusCard } from '../../cards/service-status/service-status-card.component';
3
+ import { VisitedServiceCard } from '../../cards/visited-service-card/visited-service-card.component';
4
+ import { WhatsNew } from '../../cards/whats-new/whats-new.component';
5
+ import { Dashboard } from '../dashboard/dashboard/dashboard.component';
6
+ import type { CardConfig, SectionConfig } from '../dashboard/models';
7
+ import { TABLE_CARD_CONFIG, TABLE_RESOURCES } from './pods-table.config';
8
+
9
+ Dashboard.registerAngularComponents([
10
+ Favorites,
11
+ VisitedServiceCard,
12
+ ServiceStatusCard,
13
+ WhatsNew,
14
+ ]);
15
+
16
+ export const SECTIONS: SectionConfig[] = [
17
+ { id: 'ras', title: 'Recently accessed services', editable: false, w: 12 },
18
+ ];
19
+
20
+ const RAS_CARD_TEMPLATES = [
21
+ {
22
+ serviceType: 'SAP HANA Cloud',
23
+ serviceName: 'olc-hana-db',
24
+ serviceIcon: 'database',
25
+ serviceDescription: 'My Subaccount 1/Space dev',
26
+ path: '/hana/olc-hana-db',
27
+ },
28
+ {
29
+ serviceType: 'Cloud Identity Service',
30
+ serviceName: 'Cloud Identity',
31
+ serviceIcon: 'customer',
32
+ serviceDescription: 'My Subaccount 1/Space dev',
33
+ path: '/identity/cloud-identity',
34
+ },
35
+ {
36
+ serviceType: 'SAP HANA Cloud',
37
+ serviceName: 'olc-hana-db-test',
38
+ serviceIcon: 'database',
39
+ serviceDescription: 'My Subaccount 1/Space dev',
40
+ path: '/hana/olc-hana-db-test',
41
+ },
42
+ {
43
+ serviceType: 'Application Autoscaler',
44
+ serviceName: 'applicationtest',
45
+ serviceIcon: 'accelerated',
46
+ serviceDescription: 'My Subaccount 2/Space prod',
47
+ path: '/autoscaler/applicationtest',
48
+ },
49
+ {
50
+ serviceType: 'Cloud Identity Service',
51
+ serviceName: 'Cloud Identity 2',
52
+ serviceIcon: 'customer',
53
+ serviceDescription: 'Long text Subaccount 1/Space',
54
+ path: '/identity/cloud-identity-2',
55
+ },
56
+ {
57
+ serviceType: 'Audit Log Service',
58
+ serviceName: 'auditlog-name',
59
+ serviceIcon: 'log',
60
+ serviceDescription: 'My Subaccount 4/Space dev',
61
+ path: '/auditlog/auditlog-name',
62
+ },
63
+ ];
64
+
65
+ export const CARDS: CardConfig[] = [
66
+ ...RAS_CARD_TEMPLATES.map((t, i) => ({
67
+ id: `ras-card-${i}`,
68
+ w: 4,
69
+ h: 10,
70
+ sectionId: 'ras',
71
+ component: 'mfp-visited-service-card',
72
+ componentInputs: { ...t },
73
+ })),
74
+ {
75
+ id: 'table-pods',
76
+ w: 12,
77
+ h: 50,
78
+ component: 'mfp-wc-declarative-table-card',
79
+ componentInputs: {
80
+ config: TABLE_CARD_CONFIG,
81
+ header: 'Pods',
82
+ headerTooltip: 'This table lists all pods running in the cluster.',
83
+ resources: TABLE_RESOURCES,
84
+ },
85
+ },
86
+ {
87
+ id: 'whats-new',
88
+ label: "What's New",
89
+ w: 5,
90
+ h: 57,
91
+ component: 'mfp-whats-new',
92
+ },
93
+ {
94
+ id: 'favorites',
95
+ label: 'Favorites',
96
+ w: 4,
97
+ h: 21,
98
+ component: 'mfp-favorites',
99
+ },
100
+ {
101
+ id: 'service-status',
102
+ label: 'Service Status',
103
+ w: 4,
104
+ h: 30,
105
+ component: 'mfp-service-status-card',
106
+ },
107
+ ];