@openmfp/webcomponents 0.6.1 → 0.6.7

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,141 +0,0 @@
1
- import {
2
- ChangeDetectorRef,
3
- Component,
4
- EffectCleanupRegisterFn,
5
- ViewContainerRef,
6
- input,
7
- } from '@angular/core';
8
- import { mountAngularCard } from './mount-angular-card';
9
- import {
10
- addComponentToRegistry,
11
- resetDashboardCardRegistry,
12
- } from './dashboard-card-registry';
13
- import { CardConfig } from '../../models';
14
-
15
- @Component({
16
- selector: 'mfp-test-angular-card',
17
- standalone: true,
18
- template: '<span>angular card</span>',
19
- })
20
- class TestAngularCard {
21
- title = input<string>();
22
- count = input<number>();
23
- }
24
-
25
- function makeCleanup(): {
26
- onCleanup: EffectCleanupRegisterFn;
27
- runCleanup: () => void;
28
- } {
29
- let registered: (() => void) | undefined;
30
- return {
31
- onCleanup: (fn) => {
32
- registered = fn;
33
- },
34
- runCleanup: () => registered?.(),
35
- };
36
- }
37
-
38
- function makeMockVcr(): {
39
- vcr: ViewContainerRef;
40
- setInput: ReturnType<typeof vi.fn>;
41
- clear: ReturnType<typeof vi.fn>;
42
- } {
43
- const setInput = vi.fn();
44
- const detectChanges = vi.fn();
45
- const componentRef = {
46
- setInput,
47
- changeDetectorRef: { detectChanges } as unknown as ChangeDetectorRef,
48
- };
49
- const clear = vi.fn();
50
- const vcr = {
51
- createComponent: vi.fn(() => componentRef),
52
- clear,
53
- } as unknown as ViewContainerRef;
54
- return { vcr, setInput, clear };
55
- }
56
-
57
- function makeCfg(overrides: Partial<CardConfig> = {}): CardConfig {
58
- return {
59
- id: 'card-1',
60
- component: 'mfp-test-angular-card',
61
- type: 'angular',
62
- ...overrides,
63
- };
64
- }
65
-
66
- describe('mountAngularCard', () => {
67
- beforeEach(() => {
68
- resetDashboardCardRegistry();
69
- addComponentToRegistry([TestAngularCard]);
70
- });
71
-
72
- afterEach(() => {
73
- vi.restoreAllMocks();
74
- resetDashboardCardRegistry();
75
- });
76
-
77
- it('creates the registered Angular component in the host', () => {
78
- const { vcr } = makeMockVcr();
79
- const { onCleanup } = makeCleanup();
80
- mountAngularCard(makeCfg(), vcr, onCleanup);
81
-
82
- expect(
83
- (vcr.createComponent as ReturnType<typeof vi.fn>),
84
- ).toHaveBeenCalledWith(TestAngularCard);
85
- });
86
-
87
- it('calls setInput for each provided componentInput', () => {
88
- const { vcr, setInput } = makeMockVcr();
89
- const { onCleanup } = makeCleanup();
90
- mountAngularCard(
91
- makeCfg({ componentInputs: { title: 'Pods', count: 3 } }),
92
- vcr,
93
- onCleanup,
94
- );
95
-
96
- expect(setInput).toHaveBeenCalledWith('title', 'Pods');
97
- expect(setInput).toHaveBeenCalledWith('count', 3);
98
- });
99
-
100
- it('warns and skips unknown inputs in dev mode', () => {
101
- const warnSpy = vi.spyOn(console, 'warn').mockImplementation(vi.fn());
102
- const { vcr, setInput } = makeMockVcr();
103
- const { onCleanup } = makeCleanup();
104
-
105
- mountAngularCard(
106
- makeCfg({ componentInputs: { unknownProp: 'x' } }),
107
- vcr,
108
- onCleanup,
109
- );
110
-
111
- expect(warnSpy).toHaveBeenCalledWith(
112
- expect.stringContaining('unknownProp'),
113
- );
114
- expect(setInput).not.toHaveBeenCalled();
115
- });
116
-
117
- it('clears the host on cleanup', () => {
118
- const { vcr, clear } = makeMockVcr();
119
- const { onCleanup, runCleanup } = makeCleanup();
120
- mountAngularCard(makeCfg(), vcr, onCleanup);
121
-
122
- runCleanup();
123
-
124
- expect(clear).toHaveBeenCalledTimes(1);
125
- });
126
-
127
- it('warns and returns without creating a component when selector is not registered', () => {
128
- const warnSpy = vi.spyOn(console, 'warn').mockImplementation(vi.fn());
129
- const { vcr } = makeMockVcr();
130
- const { onCleanup } = makeCleanup();
131
-
132
- mountAngularCard(makeCfg({ component: 'unknown-card' }), vcr, onCleanup);
133
-
134
- expect(
135
- (vcr.createComponent as ReturnType<typeof vi.fn>),
136
- ).not.toHaveBeenCalled();
137
- expect(warnSpy).toHaveBeenCalledWith(
138
- expect.stringContaining('"unknown-card" is not registered'),
139
- );
140
- });
141
- });
@@ -1,44 +0,0 @@
1
- import { CardConfig } from '../../models';
2
- import {
3
- getRegisteredDashboardCardComponent,
4
- warnForUnknownDashboardCardInput,
5
- } from './dashboard-card-registry';
6
- import { EffectCleanupRegisterFn, ViewContainerRef } from '@angular/core';
7
-
8
- export function mountAngularCard(
9
- cfg: CardConfig,
10
- angularHost: ViewContainerRef,
11
- onCleanup: EffectCleanupRegisterFn,
12
- ): void {
13
- const registeredComponent = getRegisteredDashboardCardComponent(
14
- cfg.component,
15
- );
16
-
17
- if (!registeredComponent) {
18
- console.warn(
19
- `[DashboardCard] Angular component "${cfg.component}" is not registered`,
20
- );
21
- return;
22
- }
23
-
24
- const componentRef = angularHost.createComponent(
25
- registeredComponent.componentType,
26
- );
27
-
28
- for (const [inputName, value] of Object.entries(cfg.componentInputs ?? {})) {
29
- const templateName = registeredComponent.inputs.get(inputName);
30
-
31
- if (!templateName) {
32
- warnForUnknownDashboardCardInput(cfg.component, inputName);
33
- continue;
34
- }
35
-
36
- componentRef.setInput(templateName, value);
37
- }
38
-
39
- componentRef.changeDetectorRef.detectChanges();
40
-
41
- onCleanup(() => {
42
- angularHost.clear();
43
- });
44
- }
@@ -1,142 +0,0 @@
1
- import { EffectCleanupRegisterFn, ViewContainerRef } from '@angular/core';
2
- import { mountSapCard } from './mount-sap-card';
3
- import { CardConfig } from '../../models';
4
-
5
- function makeCleanup(): {
6
- onCleanup: EffectCleanupRegisterFn;
7
- runCleanup: () => void;
8
- } {
9
- let registered: (() => void) | undefined;
10
- return {
11
- onCleanup: (fn) => {
12
- registered = fn;
13
- },
14
- runCleanup: () => registered?.(),
15
- };
16
- }
17
-
18
- function makeContainer(): { container: ViewContainerRef; el: HTMLElement } {
19
- const el = document.createElement('div');
20
- document.body.appendChild(el);
21
- const container = { element: { nativeElement: el } } as unknown as ViewContainerRef;
22
- return { container, el };
23
- }
24
-
25
- function makeCfg(overrides: Partial<CardConfig> = {}): CardConfig {
26
- return { id: 'card-1', component: 'my.sap.App', type: 'sap-ui', ...overrides };
27
- }
28
-
29
- describe('mountSapCard', () => {
30
- let placeAt: ReturnType<typeof vi.fn>;
31
- let destroy: ReturnType<typeof vi.fn>;
32
- let ComponentContainer: ReturnType<typeof vi.fn>;
33
- let sapRequire: ReturnType<typeof vi.fn>;
34
-
35
- beforeEach(() => {
36
- placeAt = vi.fn();
37
- destroy = vi.fn();
38
- ComponentContainer = vi.fn(function (this: Record<string, unknown>) {
39
- this['placeAt'] = placeAt;
40
- this['destroy'] = destroy;
41
- });
42
- sapRequire = vi
43
- .fn()
44
- .mockImplementation((_deps: unknown, cb: (ctor: unknown) => void) => {
45
- cb(ComponentContainer);
46
- });
47
-
48
- (window as unknown as Record<string, unknown>)['sap'] = {
49
- ui: { require: sapRequire },
50
- };
51
- });
52
-
53
- afterEach(() => {
54
- delete (window as unknown as Record<string, unknown>)['sap'];
55
- vi.restoreAllMocks();
56
- });
57
-
58
- it('calls sap.ui.require with ComponentContainer dependency', () => {
59
- const { onCleanup } = makeCleanup();
60
- mountSapCard(makeCfg(), makeContainer().container, onCleanup);
61
-
62
- expect(sapRequire).toHaveBeenCalledWith(
63
- ['sap/ui/core/ComponentContainer'],
64
- expect.any(Function),
65
- );
66
- });
67
-
68
- it('instantiates ComponentContainer with the correct config', () => {
69
- const { onCleanup } = makeCleanup();
70
- mountSapCard(
71
- makeCfg({ componentInputs: { env: 'prod' } }),
72
- makeContainer().container,
73
- onCleanup,
74
- );
75
-
76
- expect(ComponentContainer).toHaveBeenCalledWith({
77
- name: 'my.sap.App',
78
- manifest: true,
79
- async: true,
80
- settings: { env: 'prod' },
81
- });
82
- });
83
-
84
- it('falls back to empty settings when componentInputs is omitted', () => {
85
- const { onCleanup } = makeCleanup();
86
- mountSapCard(makeCfg(), makeContainer().container, onCleanup);
87
-
88
- expect(ComponentContainer).toHaveBeenCalledWith(
89
- expect.objectContaining({ settings: {} }),
90
- );
91
- });
92
-
93
- it('calls placeAt with the host element', () => {
94
- const { onCleanup } = makeCleanup();
95
- const { container, el } = makeContainer();
96
- mountSapCard(makeCfg(), container, onCleanup);
97
-
98
- expect(placeAt).toHaveBeenCalledWith(el);
99
- });
100
-
101
- it('destroys the container and clears the host on cleanup', () => {
102
- const { onCleanup, runCleanup } = makeCleanup();
103
- const { container, el } = makeContainer();
104
- el.innerHTML = '<span>old</span>';
105
- mountSapCard(makeCfg(), container, onCleanup);
106
-
107
- runCleanup();
108
-
109
- expect(destroy).toHaveBeenCalledTimes(1);
110
- expect(el.innerHTML).toBe('');
111
- });
112
-
113
- it('does not mount the container when cleanup runs before the require callback fires', () => {
114
- let deferred: ((ctor: unknown) => void) | undefined;
115
- sapRequire.mockImplementationOnce(
116
- (_deps: unknown, cb: (ctor: unknown) => void) => {
117
- deferred = cb;
118
- },
119
- );
120
-
121
- const { onCleanup, runCleanup } = makeCleanup();
122
- mountSapCard(makeCfg(), makeContainer().container, onCleanup);
123
-
124
- runCleanup();
125
- deferred?.(ComponentContainer);
126
-
127
- expect(ComponentContainer).not.toHaveBeenCalled();
128
- expect(placeAt).not.toHaveBeenCalled();
129
- });
130
-
131
- it('logs an error when window.sap is not available', () => {
132
- delete (window as unknown as Record<string, unknown>)['sap'];
133
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(vi.fn());
134
- const { onCleanup } = makeCleanup();
135
-
136
- mountSapCard(makeCfg(), makeContainer().container, onCleanup);
137
-
138
- expect(consoleSpy).toHaveBeenCalledWith(
139
- '[DashboardCard] SAP UI5 is not available on window.sap',
140
- );
141
- });
142
- });
@@ -1,52 +0,0 @@
1
- import { CardConfig } from '../../models';
2
- import { EffectCleanupRegisterFn, ViewContainerRef } from '@angular/core';
3
-
4
- export type SapUiRequire = (
5
- deps: string[],
6
- cb: (
7
- ComponentContainer: new (cfg: {
8
- name: string;
9
- manifest: boolean;
10
- async: boolean;
11
- settings: Record<string, unknown>;
12
- }) => { placeAt(el: HTMLElement): void; destroy(): void },
13
- ) => void,
14
- ) => void;
15
-
16
- export function mountSapCard(
17
- cfg: CardConfig,
18
- container: ViewContainerRef,
19
- onCleanup: EffectCleanupRegisterFn,
20
- ): void {
21
- const host = container.element.nativeElement;
22
-
23
- let sapContainer: { destroy(): void } | null = null;
24
- let isDestroyed = false;
25
- const sapRequire = (
26
- window as unknown as { sap?: { ui: { require: SapUiRequire } } }
27
- ).sap?.ui?.require;
28
-
29
- if (sapRequire) {
30
- sapRequire(['sap/ui/core/ComponentContainer'], (ComponentContainer) => {
31
- if (isDestroyed) return;
32
-
33
- const container = new ComponentContainer({
34
- name: cfg.component,
35
- manifest: true,
36
- async: true,
37
- settings: cfg.componentInputs ?? {},
38
- });
39
-
40
- container.placeAt(host);
41
- sapContainer = container;
42
- });
43
- } else {
44
- console.error('[DashboardCard] SAP UI5 is not available on window.sap');
45
- }
46
-
47
- onCleanup(() => {
48
- isDestroyed = true;
49
- sapContainer?.destroy();
50
- host.innerHTML = '';
51
- });
52
- }
@@ -1,107 +0,0 @@
1
- import { EffectCleanupRegisterFn, Renderer2, ViewContainerRef } from '@angular/core';
2
- import { mountWcCard } from './mount-wc-card';
3
- import { CardConfig } from '../../models';
4
-
5
- function makeCleanup(): {
6
- onCleanup: EffectCleanupRegisterFn;
7
- runCleanup: () => void;
8
- } {
9
- let registered: (() => void) | undefined;
10
- return {
11
- onCleanup: (fn) => {
12
- registered = fn;
13
- },
14
- runCleanup: () => registered?.(),
15
- };
16
- }
17
-
18
- function makeContainer(): { container: ViewContainerRef; el: HTMLElement } {
19
- const el = document.createElement('div');
20
- document.body.appendChild(el);
21
- const container = { element: { nativeElement: el } } as unknown as ViewContainerRef;
22
- return { container, el };
23
- }
24
-
25
- function makeRenderer(): Renderer2 {
26
- return {
27
- createElement: vi.fn((tag: string) => document.createElement(tag)),
28
- setProperty: vi.fn((el: Record<string, unknown>, key: string, value: unknown) => {
29
- el[key] = value;
30
- }),
31
- appendChild: vi.fn((parent: HTMLElement, child: HTMLElement) => {
32
- parent.appendChild(child);
33
- }),
34
- } as unknown as Renderer2;
35
- }
36
-
37
- function makeCfg(overrides: Partial<CardConfig> = {}): CardConfig {
38
- return { id: 'card-1', component: 'demo-widget', ...overrides };
39
- }
40
-
41
- describe('mountWcCard', () => {
42
- it('appends the web component element to the host', () => {
43
- const { onCleanup } = makeCleanup();
44
- const { container, el } = makeContainer();
45
- mountWcCard(makeCfg(), container, onCleanup, makeRenderer());
46
-
47
- expect(el.querySelector('demo-widget')).not.toBeNull();
48
- });
49
-
50
- it('applies componentInputs as properties on the element', () => {
51
- const { onCleanup } = makeCleanup();
52
- const { container, el } = makeContainer();
53
- mountWcCard(
54
- makeCfg({ componentInputs: { title: 'Pods', count: 5 } }),
55
- container,
56
- onCleanup,
57
- makeRenderer(),
58
- );
59
-
60
- const wc = el.querySelector('demo-widget') as HTMLElement & {
61
- title?: string;
62
- count?: number;
63
- };
64
- expect(wc?.title).toBe('Pods');
65
- expect(wc?.count).toBe(5);
66
- });
67
-
68
- it('renders without errors when componentInputs is omitted', () => {
69
- const { onCleanup } = makeCleanup();
70
- expect(() => {
71
- mountWcCard(makeCfg(), makeContainer().container, onCleanup, makeRenderer());
72
- }).not.toThrow();
73
- });
74
-
75
- it('clears the host innerHTML on cleanup', () => {
76
- const { onCleanup, runCleanup } = makeCleanup();
77
- const { container, el } = makeContainer();
78
- mountWcCard(makeCfg(), container, onCleanup, makeRenderer());
79
-
80
- runCleanup();
81
-
82
- expect(el.innerHTML).toBe('');
83
- });
84
-
85
- it('calls renderer.createElement, setProperty and appendChild in order', () => {
86
- const { onCleanup } = makeCleanup();
87
- const { container, el } = makeContainer();
88
- const renderer = makeRenderer();
89
- mountWcCard(
90
- makeCfg({ componentInputs: { label: 'test' } }),
91
- container,
92
- onCleanup,
93
- renderer,
94
- );
95
-
96
- expect(renderer.createElement).toHaveBeenCalledWith('demo-widget');
97
- expect(renderer.setProperty).toHaveBeenCalledWith(
98
- expect.any(HTMLElement),
99
- 'label',
100
- 'test',
101
- );
102
- expect(renderer.appendChild).toHaveBeenCalledWith(
103
- el,
104
- expect.any(HTMLElement),
105
- );
106
- });
107
- });
@@ -1,22 +0,0 @@
1
- import { EffectCleanupRegisterFn, Renderer2, ViewContainerRef } from '@angular/core';
2
- import { CardConfig } from '../../models';
3
-
4
- export function mountWcCard(
5
- cfg: CardConfig,
6
- container: ViewContainerRef,
7
- onCleanup: EffectCleanupRegisterFn,
8
- renderer: Renderer2,
9
- ): void {
10
- const host = container.element.nativeElement;
11
- const element = renderer.createElement(cfg.component);
12
-
13
- for (const [key, value] of Object.entries(cfg.componentInputs ?? {})) {
14
- renderer.setProperty(element, key, value);
15
- }
16
-
17
- renderer.appendChild(host, element);
18
-
19
- onCleanup(() => {
20
- host.innerHTML = '';
21
- });
22
- }
@@ -1,134 +0,0 @@
1
- <div class="mfp-dashboard">
2
- <div class="mfp-dashboard__topbar">
3
- <div class="mfp-dashboard__topbar-row">
4
- <div class="mfp-dashboard__header">
5
- @if (config().title) {
6
- <ui5-title level="H2" size="H2" wrapping-type="Normal">{{
7
- config().title
8
- }}</ui5-title>
9
- }
10
- @if (config().description) {
11
- <ui5-text>{{ config().description }}</ui5-text>
12
- }
13
- </div>
14
-
15
- <div class="mfp-dashboard__toolbar">
16
- @if (editMode()) {
17
- <ui5-button
18
- id="add-card-btn"
19
- [design]="addCardButton().design"
20
- [icon]="addCardButton().icon"
21
- [tooltip]="addCardButton().tooltip"
22
- (click)="openCardPanel()"
23
- >{{ addCardButton().text }}</ui5-button
24
- >
25
- } @else if (compactToolbar()) {
26
- <ui5-button
27
- #menuBtn
28
- design="Transparent"
29
- icon="menu2"
30
- tooltip="Actions"
31
- (click)="toolbarMenuOpen.update((v) => !v)"
32
- ></ui5-button>
33
- <ui5-menu
34
- [open]="toolbarMenuOpen()"
35
- [opener]="menuBtn.element"
36
- (ui5Open)="toolbarMenuOpen.set(true)"
37
- (ui5Close)="toolbarMenuOpen.set(false)"
38
- (ui5ItemClick)="
39
- onMenuItemClick(
40
- $any($event).detail.item.dataset['action'],
41
- $event
42
- )
43
- "
44
- >
45
- @for (action of customActions(); track action.action) {
46
- <ui5-menu-item
47
- [icon]="action.icon ?? ''"
48
- [text]="action.text ?? ''"
49
- [attr.data-action]="action.action"
50
- ></ui5-menu-item>
51
- }
52
- @if (config().editable) {
53
- <ui5-menu-separator></ui5-menu-separator>
54
- <ui5-menu-item
55
- [icon]="editViewButton().icon"
56
- [text]="editViewButton().text || 'Edit View'"
57
- data-action="edit-view"
58
- ></ui5-menu-item>
59
- }
60
- </ui5-menu>
61
- } @else {
62
- @for (action of customActions(); track action.action) {
63
- <ui5-button
64
- [design]="action.design ?? 'Default'"
65
- [icon]="action.icon ?? ''"
66
- [endIcon]="action?.endIcon"
67
- [tooltip]="action.tooltip ?? ''"
68
- (click)="
69
- actionButtonClick.emit({ event: $event, action: action })
70
- "
71
- >{{ action.text }}</ui5-button
72
- >
73
- }
74
- @if (config().editable) {
75
- <ui5-button
76
- [design]="editViewButton().design"
77
- [icon]="editViewButton().icon"
78
- [tooltip]="editViewButton().tooltip"
79
- (click)="enterEditMode()"
80
- >{{ editViewButton().text }}</ui5-button
81
- >
82
- }
83
- }
84
- </div>
85
- </div>
86
-
87
- <div class="mfp-dashboard__subheader">
88
- <slot name="dashboard-subheader"></slot>
89
- </div>
90
- </div>
91
- <div class="mfp-sections-container">
92
- @for (section of sections(); track section.id) {
93
- <mfp-dashboard-section
94
- [section]="section"
95
- [cards]="sectionCards()(section.id)"
96
- [columns]="12"
97
- [editMode]="editMode()"
98
- (removeSection)="removeSection(section.id)"
99
- (removeCard)="removeCard($event)"
100
- />
101
- }
102
- </div>
103
- <gridstack #grid (changeCB)="onOrderChange($event)" [options]="gridOptions()">
104
- @for (card of looseCards(); track card.id) {
105
- <gridstack-item
106
- [style.cursor]="editMode() ? 'pointer' : 'auto'"
107
- [options]="card"
108
- >
109
- <mfp-dashboard-card
110
- [card]="card"
111
- [editMode]="editMode()"
112
- (removeCard)="removeCard($any(card.id))"
113
- />
114
- </gridstack-item>
115
- }
116
- </gridstack>
117
-
118
- @if (editMode()) {
119
- <div class="mfp-dashboard__edit-bar">
120
- <ui5-button design="Emphasized" (click)="saveEdit()">Save</ui5-button>
121
- <ui5-button design="Transparent" (click)="cancelEdit()"
122
- >Cancel</ui5-button
123
- >
124
- </div>
125
- }
126
- </div>
127
-
128
- <mfp-add-card-dialog
129
- [open]="cardDialogOpen()"
130
- [availableCards]="availableCards()"
131
- [addedCardsIds]="addedCardsIds()"
132
- (confirm)="onCardsAdded($event)"
133
- (cancel)="closeCardPanel()"
134
- />