@openmfp/webcomponents 0.6.1 → 0.6.8

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 (142) hide show
  1. package/mfp-wc-dashboard.js +153 -0
  2. package/mfp-webcomponents.js +573 -0
  3. package/package.json +14 -68
  4. package/.github/workflows/pipeline.yaml +0 -41
  5. package/.storybook/main.ts +0 -34
  6. package/.storybook/preview.ts +0 -29
  7. package/.storybook/tsconfig.json +0 -11
  8. package/AGENTS.md +0 -153
  9. package/CODEOWNERS +0 -6
  10. package/CONTRIBUTING.md +0 -95
  11. package/LICENSE +0 -201
  12. package/LICENSES/Apache-2.0.txt +0 -73
  13. package/README.md +0 -91
  14. package/REUSE.toml +0 -9
  15. package/angular.json +0 -157
  16. package/docs/dashboard.md +0 -358
  17. package/docs/declarative-form.md +0 -178
  18. package/docs/declarative-table-card.md +0 -235
  19. package/docs/declarative-table.md +0 -315
  20. package/eslint.config.js +0 -41
  21. package/projects/ngx/cards/favorites/favorites.component.html +0 -12
  22. package/projects/ngx/cards/favorites/favorites.component.scss +0 -50
  23. package/projects/ngx/cards/favorites/favorites.component.ts +0 -19
  24. package/projects/ngx/cards/public-api.ts +0 -4
  25. package/projects/ngx/cards/service-status/service-status-card.component.html +0 -15
  26. package/projects/ngx/cards/service-status/service-status-card.component.scss +0 -87
  27. package/projects/ngx/cards/service-status/service-status-card.component.ts +0 -36
  28. package/projects/ngx/cards/stories/visited-service-card.stories.ts +0 -149
  29. package/projects/ngx/cards/visited-service-card/visited-service-card.component.html +0 -17
  30. package/projects/ngx/cards/visited-service-card/visited-service-card.component.scss +0 -34
  31. package/projects/ngx/cards/visited-service-card/visited-service-card.component.ts +0 -22
  32. package/projects/ngx/cards/whats-new/whats-new.component.html +0 -10
  33. package/projects/ngx/cards/whats-new/whats-new.component.scss +0 -25
  34. package/projects/ngx/cards/whats-new/whats-new.component.ts +0 -46
  35. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.html +0 -28
  36. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.scss +0 -44
  37. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.spec.ts +0 -85
  38. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.ts +0 -58
  39. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.html +0 -29
  40. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.scss +0 -63
  41. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.spec.ts +0 -255
  42. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.ts +0 -75
  43. package/projects/ngx/declarative-ui/dashboard/card/utils/dashboard-card-registry.spec.ts +0 -76
  44. package/projects/ngx/declarative-ui/dashboard/card/utils/dashboard-card-registry.ts +0 -109
  45. package/projects/ngx/declarative-ui/dashboard/card/utils/index.ts +0 -4
  46. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-angular-card.spec.ts +0 -141
  47. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-angular-card.ts +0 -44
  48. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-sap-card.spec.ts +0 -142
  49. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-sap-card.ts +0 -52
  50. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-wc-card.spec.ts +0 -107
  51. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-wc-card.ts +0 -22
  52. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.html +0 -134
  53. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.scss +0 -88
  54. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.spec.ts +0 -354
  55. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.ts +0 -238
  56. package/projects/ngx/declarative-ui/dashboard/dashboard/index.ts +0 -1
  57. package/projects/ngx/declarative-ui/dashboard/index.ts +0 -5
  58. package/projects/ngx/declarative-ui/dashboard/models/constants.ts +0 -2
  59. package/projects/ngx/declarative-ui/dashboard/models/dashboard.model.ts +0 -50
  60. package/projects/ngx/declarative-ui/dashboard/models/index.ts +0 -1
  61. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.html +0 -28
  62. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.scss +0 -85
  63. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.spec.ts +0 -104
  64. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.ts +0 -23
  65. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.html +0 -62
  66. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.scss +0 -12
  67. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.spec.ts +0 -301
  68. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.ts +0 -166
  69. package/projects/ngx/declarative-ui/form/declarative-form/index.ts +0 -1
  70. package/projects/ngx/declarative-ui/form/index.ts +0 -2
  71. package/projects/ngx/declarative-ui/form/models/form-field-definition.ts +0 -15
  72. package/projects/ngx/declarative-ui/form/models/index.ts +0 -1
  73. package/projects/ngx/declarative-ui/form/utils/set-property-by-path.ts +0 -30
  74. package/projects/ngx/declarative-ui/models/index.ts +0 -2
  75. package/projects/ngx/declarative-ui/models/resource.ts +0 -5
  76. package/projects/ngx/declarative-ui/models/ui-definition.ts +0 -95
  77. package/projects/ngx/declarative-ui/public-api.ts +0 -4
  78. package/projects/ngx/declarative-ui/stories/add-card-dialog.stories.ts +0 -91
  79. package/projects/ngx/declarative-ui/stories/background-lightblue.png +0 -0
  80. package/projects/ngx/declarative-ui/stories/dashboard.cards.ts +0 -107
  81. package/projects/ngx/declarative-ui/stories/dashboard.stories.ts +0 -296
  82. package/projects/ngx/declarative-ui/stories/declarative-form.stories.ts +0 -149
  83. package/projects/ngx/declarative-ui/stories/declarative-table-card.stories.ts +0 -358
  84. package/projects/ngx/declarative-ui/stories/declarative-table.stories.ts +0 -363
  85. package/projects/ngx/declarative-ui/stories/pods-table.config.ts +0 -188
  86. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.html +0 -138
  87. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.scss +0 -21
  88. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.spec.ts +0 -345
  89. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.ts +0 -61
  90. package/projects/ngx/declarative-ui/table/declarative-table/index.ts +0 -1
  91. package/projects/ngx/declarative-ui/table/index.ts +0 -2
  92. package/projects/ngx/declarative-ui/table/models/index.ts +0 -14
  93. package/projects/ngx/declarative-ui/table/models/table.model.ts +0 -17
  94. package/projects/ngx/declarative-ui/table/utils/cssRules.engine.spec.ts +0 -146
  95. package/projects/ngx/declarative-ui/table/utils/cssRules.engine.ts +0 -69
  96. package/projects/ngx/declarative-ui/table/utils/field-definition.utils.spec.ts +0 -70
  97. package/projects/ngx/declarative-ui/table/utils/field-definition.utils.ts +0 -13
  98. package/projects/ngx/declarative-ui/table/utils/proccess-fields.spec.ts +0 -511
  99. package/projects/ngx/declarative-ui/table/utils/proccess-fields.ts +0 -71
  100. package/projects/ngx/declarative-ui/table/utils/resource-field-by-path.spec.ts +0 -372
  101. package/projects/ngx/declarative-ui/table/utils/resource-field-by-path.ts +0 -98
  102. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-cell.constants.ts +0 -5
  103. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.html +0 -1
  104. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.scss +0 -0
  105. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.spec.ts +0 -119
  106. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.ts +0 -35
  107. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.html +0 -7
  108. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.scss +0 -0
  109. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.spec.ts +0 -114
  110. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.ts +0 -19
  111. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.html +0 -7
  112. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.scss +0 -10
  113. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.spec.ts +0 -188
  114. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.ts +0 -16
  115. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.html +0 -59
  116. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.scss +0 -33
  117. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.spec.ts +0 -316
  118. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.ts +0 -115
  119. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.html +0 -156
  120. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.scss +0 -123
  121. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.spec.ts +0 -786
  122. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.ts +0 -286
  123. package/projects/ngx/declarative-ui/table-card/index.ts +0 -2
  124. package/projects/ngx/declarative-ui/table-card/models/configs.ts +0 -46
  125. package/projects/ngx/declarative-ui/tsconfig.lib.json +0 -11
  126. package/projects/ngx/declarative-ui/tsconfig.lib.prod.json +0 -9
  127. package/projects/ngx/declarative-ui/tsconfig.spec.json +0 -9
  128. package/projects/ngx/ng-package.json +0 -8
  129. package/projects/ngx/package.json +0 -22
  130. package/projects/ngx/public-api.ts +0 -2
  131. package/projects/ngx/tsconfig.lib.json +0 -11
  132. package/projects/ngx/tsconfig.lib.prod.json +0 -9
  133. package/projects/webcomponents/main.ts +0 -92
  134. package/projects/webcomponents/tsconfig.app.json +0 -9
  135. package/projects/webcomponents-dashboard/main.ts +0 -15
  136. package/projects/webcomponents-dashboard/tsconfig.app.json +0 -9
  137. package/renovate.json +0 -6
  138. package/scripts/bundle-wc.mjs +0 -79
  139. package/tsconfig.json +0 -37
  140. package/tsconfig.spec.json +0 -8
  141. package/tsconfig.storybook.json +0 -16
  142. package/vitest.config.ts +0 -26
@@ -1,63 +0,0 @@
1
- :host {
2
- display: block;
3
- min-width: 0;
4
- min-height: 0;
5
- overflow: visible;
6
- }
7
-
8
- .card__remove {
9
- position: absolute;
10
- top: 0;
11
- right: -0.5rem;
12
- z-index: 10;
13
- border-radius: 50% !important;
14
- width: 1.25rem !important;
15
- height: 1.25rem !important;
16
- min-width: unset !important;
17
- padding: 0 !important;
18
- font-size: 0.625rem !important;
19
- }
20
-
21
- .remove-wrapper {
22
- position: sticky;
23
- height: 0.5rem;
24
- top: 0;
25
- right: 0;
26
- z-index: 10;
27
- }
28
-
29
- .component-card {
30
- position: relative;
31
- height: 100%;
32
- overflow: visible;
33
- isolation: isolate;
34
- padding: 0 0.5rem;
35
- }
36
-
37
- .component-host {
38
- height: 100%;
39
- }
40
-
41
- .card {
42
- position: relative;
43
- display: flex;
44
- flex-direction: column;
45
- height: 100%;
46
- border: 1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);
47
- border-radius: 0.5rem;
48
- background: var(--sapBackgroundColor, #fff);
49
- overflow: visible;
50
-
51
- &__header {
52
- padding: 0.75rem 1rem;
53
- font-weight: 600;
54
- border-bottom: 1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);
55
- background: var(--sapGroup_TitleBackground, #f5f5f5);
56
- }
57
-
58
- &__body {
59
- flex: 1;
60
- padding: 1rem;
61
- overflow: auto;
62
- }
63
- }
@@ -1,255 +0,0 @@
1
- import { DashboardCard } from './dashboard-card.component';
2
- import { ComponentFixture, TestBed } from '@angular/core/testing';
3
-
4
- type Fixture = ComponentFixture<DashboardCard>;
5
-
6
- function setup(): { fixture: Fixture; component: DashboardCard } {
7
- const fixture = TestBed.createComponent(DashboardCard);
8
- const component = fixture.componentInstance;
9
- return { fixture, component };
10
- }
11
-
12
- function root(fixture: Fixture): ShadowRoot | HTMLElement {
13
- return fixture.nativeElement.shadowRoot ?? fixture.nativeElement;
14
- }
15
-
16
- describe('DashboardCard', () => {
17
- beforeEach(async () => {
18
- await TestBed.configureTestingModule({
19
- imports: [DashboardCard],
20
- }).compileComponents();
21
- });
22
-
23
- it('uses w and h for grid placement on the host element', () => {
24
- const { fixture } = setup();
25
-
26
- fixture.componentRef.setInput('card', {
27
- id: 'card-1',
28
- component: 'demo-widget',
29
- w: 3,
30
- h: 2,
31
- });
32
- fixture.detectChanges();
33
-
34
- expect(fixture.nativeElement.style.gridColumn).toBe('span 3');
35
- expect(fixture.nativeElement.style.gridRow).toBe('span 2');
36
- });
37
-
38
- it('uses x and y as zero-based grid start coordinates when provided', () => {
39
- const { fixture } = setup();
40
-
41
- fixture.componentRef.setInput('card', {
42
- id: 'card-1',
43
- component: 'demo-widget',
44
- w: 3,
45
- h: 2,
46
- x: 0,
47
- y: 4,
48
- });
49
- fixture.detectChanges();
50
-
51
- expect(fixture.nativeElement.style.gridColumn).toBe('1 / span 3');
52
- expect(fixture.nativeElement.style.gridRow).toBe('5 / span 2');
53
- });
54
-
55
- it('renders a dynamic component and applies component inputs', () => {
56
- const { fixture } = setup();
57
-
58
- fixture.componentRef.setInput('card', {
59
- id: 'card-1',
60
- component: 'demo-widget',
61
- componentInputs: { title: 'Pods', count: 3 },
62
- });
63
- fixture.detectChanges();
64
-
65
- const element = root(fixture).querySelector('demo-widget') as
66
- | (HTMLElement & { title?: string; count?: number })
67
- | null;
68
-
69
- expect(element).not.toBeNull();
70
- expect(element?.title).toBe('Pods');
71
- expect(element?.count).toBe(3);
72
- });
73
-
74
- it('replaces the rendered dynamic component when the card definition changes', () => {
75
- const { fixture } = setup();
76
-
77
- fixture.componentRef.setInput('card', {
78
- id: 'card-1',
79
- component: 'demo-widget',
80
- componentInputs: { title: 'Pods' },
81
- });
82
- fixture.detectChanges();
83
- fixture.componentRef.setInput('card', {
84
- id: 'card-1',
85
- component: 'next-widget',
86
- componentInputs: { title: 'Services' },
87
- });
88
- fixture.detectChanges();
89
-
90
- const current = root(fixture).querySelector('next-widget') as
91
- | (HTMLElement & { title?: string })
92
- | null;
93
-
94
- expect(root(fixture).querySelector('demo-widget')).toBeNull();
95
- expect(current?.title).toBe('Services');
96
- });
97
-
98
- it('shows a remove action in edit mode and emits when it is clicked', () => {
99
- const { fixture, component } = setup();
100
- let emitted = 0;
101
-
102
- component.removeCard.subscribe(() => emitted++);
103
- fixture.componentRef.setInput('card', {
104
- id: 'card-1',
105
- component: 'demo-widget',
106
- });
107
- fixture.componentRef.setInput('editMode', true);
108
- fixture.detectChanges();
109
-
110
- const button = root(fixture).querySelector('.card__remove');
111
- button?.dispatchEvent(new Event('click'));
112
-
113
- expect(button).not.toBeNull();
114
- expect(emitted).toBe(1);
115
- });
116
-
117
- it('renders the fallback card shell when no dynamic component tag is provided', () => {
118
- const { fixture } = setup();
119
-
120
- fixture.componentRef.setInput('card', {
121
- id: 'card-1',
122
- component: '',
123
- });
124
- fixture.detectChanges();
125
-
126
- expect(root(fixture).querySelector('.card__body')).not.toBeNull();
127
- expect(root(fixture).querySelector('.component-card')).toBeNull();
128
- });
129
-
130
- describe('sap-ui type', () => {
131
- let placeAt: ReturnType<typeof vi.fn>;
132
- let destroy: ReturnType<typeof vi.fn>;
133
- let sapRequire: ReturnType<typeof vi.fn>;
134
- let ComponentContainer: ReturnType<typeof vi.fn>;
135
-
136
- beforeEach(() => {
137
- placeAt = vi.fn();
138
- destroy = vi.fn();
139
- ComponentContainer = vi.fn(function (this: Record<string, unknown>) {
140
- this['placeAt'] = placeAt;
141
- this['destroy'] = destroy;
142
- });
143
- sapRequire = vi
144
- .fn()
145
- .mockImplementation((_deps: unknown, cb: (ctor: unknown) => void) => {
146
- cb(ComponentContainer);
147
- });
148
-
149
- (window as unknown as Record<string, unknown>)['sap'] = {
150
- ui: { require: sapRequire },
151
- };
152
- });
153
-
154
- afterEach(() => {
155
- delete (window as unknown as Record<string, unknown>)['sap'];
156
- });
157
-
158
- it('calls sap.ui.require with ComponentContainer dependency', () => {
159
- const { fixture } = setup();
160
-
161
- fixture.componentRef.setInput('card', {
162
- id: 'card-1',
163
- component: 'my.sap.App',
164
- type: 'sap-ui',
165
- });
166
- fixture.detectChanges();
167
-
168
- expect(sapRequire).toHaveBeenCalledWith(
169
- ['sap/ui/core/ComponentContainer'],
170
- expect.any(Function),
171
- );
172
- });
173
-
174
- it('mounts the SAP component with correct config', () => {
175
- const { fixture } = setup();
176
-
177
- fixture.componentRef.setInput('card', {
178
- id: 'card-1',
179
- component: 'my.sap.App',
180
- type: 'sap-ui',
181
- componentInputs: { env: 'prod' },
182
- });
183
- fixture.detectChanges();
184
-
185
- expect(placeAt).toHaveBeenCalledTimes(1);
186
- expect(placeAt.mock.calls[0][0]).toBeInstanceOf(HTMLElement);
187
- });
188
-
189
- it('destroys the SAP container when the card definition changes', () => {
190
- const { fixture } = setup();
191
-
192
- fixture.componentRef.setInput('card', {
193
- id: 'card-1',
194
- component: 'my.sap.App',
195
- type: 'sap-ui',
196
- });
197
- fixture.detectChanges();
198
-
199
- fixture.componentRef.setInput('card', {
200
- id: 'card-1',
201
- component: 'my.sap.Other',
202
- type: 'sap-ui',
203
- });
204
- fixture.detectChanges();
205
-
206
- expect(destroy).toHaveBeenCalledTimes(1);
207
- });
208
-
209
- it('does not mount a SAP container after the card is cleaned up', () => {
210
- const requireResolver: { current?: (ctor: unknown) => void } = {};
211
- sapRequire.mockImplementationOnce(
212
- (_deps: unknown, cb: (ctor: unknown) => void) => {
213
- requireResolver.current = cb;
214
- },
215
- );
216
- const { fixture } = setup();
217
-
218
- fixture.componentRef.setInput('card', {
219
- id: 'card-1',
220
- component: 'my.sap.App',
221
- type: 'sap-ui',
222
- });
223
- fixture.detectChanges();
224
-
225
- fixture.componentRef.setInput('card', {
226
- id: 'card-1',
227
- component: 'demo-widget',
228
- });
229
- fixture.detectChanges();
230
-
231
- requireResolver.current?.(ComponentContainer);
232
-
233
- expect(ComponentContainer).not.toHaveBeenCalled();
234
- expect(placeAt).not.toHaveBeenCalled();
235
- });
236
-
237
- it('logs an error when window.sap is not available', () => {
238
- delete (window as unknown as Record<string, unknown>)['sap'];
239
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(vi.fn());
240
- const { fixture } = setup();
241
-
242
- fixture.componentRef.setInput('card', {
243
- id: 'card-1',
244
- component: 'my.sap.App',
245
- type: 'sap-ui',
246
- });
247
- fixture.detectChanges();
248
-
249
- expect(consoleSpy).toHaveBeenCalledWith(
250
- '[DashboardCard] SAP UI5 is not available on window.sap',
251
- );
252
- consoleSpy.mockRestore();
253
- });
254
- });
255
- });
@@ -1,75 +0,0 @@
1
- import { CARD_TYPES, CardConfig } from '../models';
2
- import {
3
- Component,
4
- Renderer2,
5
- ViewContainerRef,
6
- ViewEncapsulation,
7
- computed,
8
- effect,
9
- inject,
10
- input,
11
- output,
12
- viewChild,
13
- } from '@angular/core';
14
- import { Button } from '@fundamental-ngx/ui5-webcomponents/button';
15
- import { mountSapCard, mountAngularCard, mountWcCard } from './utils';
16
-
17
- @Component({
18
- selector: 'mfp-dashboard-card',
19
- imports: [Button],
20
- templateUrl: './dashboard-card.component.html',
21
- styleUrl: './dashboard-card.component.scss',
22
- encapsulation: ViewEncapsulation.Emulated,
23
- host: {
24
- '[style.grid-column]': 'gridColumn()',
25
- '[style.grid-row]': 'gridRow()',
26
- },
27
- })
28
- export class DashboardCard {
29
- card = input.required<CardConfig>();
30
- editMode = input<boolean>(false);
31
- readonly removeCard = output<void>();
32
- protected readonly gridColumn = computed(() => {
33
- const width = this.card().w ?? 12;
34
- return this.createGridTrack(this.card().x, width);
35
- });
36
- protected readonly gridRow = computed(() => {
37
- const height = this.card().h ?? 100;
38
- return this.createGridTrack(this.card().y, height);
39
- });
40
-
41
- private host = viewChild('elementHost', { read: ViewContainerRef });
42
- private renderer = inject(Renderer2);
43
-
44
- constructor() {
45
- effect((onCleanup) => {
46
- const host = this.host();
47
- const cfg = this.card();
48
- if (!host || !cfg.component) return;
49
-
50
- host.clear();
51
- host.element.nativeElement.innerHTML = '';
52
-
53
- switch (cfg.type) {
54
- case CARD_TYPES.SAP_UI:
55
- mountSapCard(cfg, host, onCleanup);
56
- break;
57
- case CARD_TYPES.ANGULAR:
58
- mountAngularCard(cfg, host, onCleanup);
59
- break;
60
- case CARD_TYPES.WC:
61
- default:
62
- mountWcCard(cfg, host, onCleanup, this.renderer);
63
- break;
64
- }
65
- });
66
- }
67
-
68
- private createGridTrack(start: number | undefined, span: number): string {
69
- if (start === undefined) {
70
- return `span ${span}`;
71
- }
72
-
73
- return `${start + 1} / span ${span}`;
74
- }
75
- }
@@ -1,76 +0,0 @@
1
- import {
2
- addComponentToRegistry,
3
- resetDashboardCardRegistry,
4
- } from './dashboard-card-registry';
5
- import { Component } from '@angular/core';
6
-
7
- @Component({
8
- selector: 'dashboard-test-card',
9
- standalone: true,
10
- template: 'dashboard test card',
11
- })
12
- class DashboardTestCard {}
13
-
14
- @Component({
15
- selector: '[dashboard-test-card]',
16
- standalone: true,
17
- template: 'dashboard attr card',
18
- })
19
- class DashboardAttrCard {}
20
-
21
- @Component({
22
- selector: 'dashboard-test-card',
23
- standalone: true,
24
- host: {
25
- 'data-test-duplicate': 'true',
26
- },
27
- template: 'dashboard duplicate card',
28
- })
29
- class DashboardDuplicateCard {}
30
-
31
- @Component({
32
- selector: 'dashboard-non-standalone-card',
33
- standalone: false,
34
- template: 'dashboard non-standalone card',
35
- })
36
- class DashboardNonStandaloneCard {}
37
-
38
- describe('dashboard card registry', () => {
39
- beforeEach(() => {
40
- resetDashboardCardRegistry();
41
- });
42
-
43
- it('registers standalone Angular components by selector', () => {
44
- expect(() => addComponentToRegistry([DashboardTestCard])).not.toThrow();
45
- });
46
-
47
- it('rejects non-component registrations', () => {
48
- class NotAComponent {}
49
-
50
- expect(() => addComponentToRegistry([NotAComponent])).toThrowError(
51
- 'Dashboard card registration failed: "NotAComponent" is not an Angular component.',
52
- );
53
- });
54
-
55
- it('rejects selectors that are not a single element selector', () => {
56
- expect(() => addComponentToRegistry([DashboardAttrCard])).toThrowError(
57
- /must use a single element selector\. Received "\[dashboard-test-card\]"./,
58
- );
59
- });
60
-
61
- it('rejects non-standalone Angular components', () => {
62
- expect(() =>
63
- addComponentToRegistry([DashboardNonStandaloneCard]),
64
- ).toThrowError(
65
- 'Dashboard card registration failed: "dashboard-non-standalone-card" must be a standalone Angular component.',
66
- );
67
- });
68
-
69
- it('rejects duplicate selector registrations for different component types', () => {
70
- addComponentToRegistry([DashboardTestCard]);
71
-
72
- expect(() => addComponentToRegistry([DashboardDuplicateCard])).toThrowError(
73
- 'Dashboard card registration failed: selector "dashboard-test-card" is already registered.',
74
- );
75
- });
76
- });
@@ -1,109 +0,0 @@
1
- import {
2
- Type,
3
- isDevMode,
4
- isStandalone,
5
- reflectComponentType,
6
- } from '@angular/core';
7
-
8
-
9
- export interface RegisteredDashboardCardComponent {
10
- componentType: Type<unknown>;
11
- selector: string;
12
- inputs: ReadonlyMap<string, string>;
13
- }
14
-
15
- const ELEMENT_SELECTOR_PATTERN = /^[a-z](?:[a-z0-9-]*)$/;
16
-
17
- const dashboardCardRegistry = new Map<
18
- string,
19
- RegisteredDashboardCardComponent
20
- >();
21
-
22
- export function addComponentToRegistry(componentTypes: Type<unknown>[]): void {
23
- for (const componentType of componentTypes) {
24
- const mirror = reflectComponentType(componentType);
25
-
26
- if (!mirror) {
27
- throw new Error(
28
- `Dashboard card registration failed: "${getTypeName(componentType)}" is not an Angular component.`,
29
- );
30
- }
31
-
32
- if (!isStandalone(componentType)) {
33
- throw new Error(
34
- `Dashboard card registration failed: "${mirror.selector}" must be a standalone Angular component.`,
35
- );
36
- }
37
-
38
- const selector = toElementSelector(mirror.selector, componentType);
39
- const existing = dashboardCardRegistry.get(selector);
40
-
41
- if (existing && existing.componentType !== componentType) {
42
- throw new Error(
43
- `Dashboard card registration failed: selector "${selector}" is already registered.`,
44
- );
45
- }
46
-
47
- dashboardCardRegistry.set(selector, {
48
- componentType,
49
- selector,
50
- inputs: createInputMap(mirror.inputs),
51
- });
52
- }
53
- }
54
-
55
- export function getRegisteredDashboardCardComponent(
56
- selector: string,
57
- ): RegisteredDashboardCardComponent | undefined {
58
- return dashboardCardRegistry.get(selector);
59
- }
60
-
61
- export function resetDashboardCardRegistry(): void {
62
- dashboardCardRegistry.clear();
63
- }
64
-
65
- export function warnForUnknownDashboardCardInput(
66
- selector: string,
67
- inputName: string,
68
- ): void {
69
- if (!isDevMode()) return;
70
-
71
- console.warn(
72
- `Dashboard card "${selector}" ignores unknown Angular input "${inputName}".`,
73
- );
74
- }
75
-
76
- function createInputMap(
77
- inputs: readonly {
78
- readonly propName: string;
79
- readonly templateName: string;
80
- }[],
81
- ): ReadonlyMap<string, string> {
82
- const bindings = new Map<string, string>();
83
-
84
- for (const input of inputs) {
85
- bindings.set(input.templateName, input.templateName);
86
- bindings.set(input.propName, input.templateName);
87
- }
88
-
89
- return bindings;
90
- }
91
-
92
- function toElementSelector(
93
- selector: string,
94
- componentType: Type<unknown>,
95
- ): string {
96
- const normalized = selector.trim();
97
-
98
- if (!ELEMENT_SELECTOR_PATTERN.test(normalized)) {
99
- throw new Error(
100
- `Dashboard card registration failed: "${getTypeName(componentType)}" must use a single element selector. Received "${selector}".`,
101
- );
102
- }
103
-
104
- return normalized;
105
- }
106
-
107
- function getTypeName(componentType: Type<unknown>): string {
108
- return componentType.name.replace(/^_+/, '') || 'AnonymousComponent';
109
- }
@@ -1,4 +0,0 @@
1
- export * from './dashboard-card-registry';
2
- export * from './mount-angular-card';
3
- export * from './mount-sap-card';
4
- export * from './mount-wc-card';