@bluealba/platform-cli 1.0.1 → 1.1.0
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.
- package/dist/index.js +278 -15
- package/docs/404.mdx +5 -0
- package/docs/architecture/api-explorer.mdx +478 -0
- package/docs/architecture/architecture-diagrams.mdx +12 -0
- package/docs/architecture/authentication-system.mdx +903 -0
- package/docs/architecture/authorization-system.mdx +886 -0
- package/docs/architecture/bootstrap.mdx +1442 -0
- package/docs/architecture/gateway-architecture.mdx +845 -0
- package/docs/architecture/multi-tenancy.mdx +1150 -0
- package/docs/architecture/overview.mdx +776 -0
- package/docs/architecture/scheduler.mdx +818 -0
- package/docs/architecture/shell.mdx +885 -0
- package/docs/architecture/ui-extension-points.mdx +781 -0
- package/docs/architecture/user-states.mdx +794 -0
- package/docs/development/overview.mdx +21 -0
- package/docs/development/workflow.mdx +914 -0
- package/docs/getting-started/core-concepts.mdx +892 -0
- package/docs/getting-started/installation.mdx +780 -0
- package/docs/getting-started/overview.mdx +83 -0
- package/docs/getting-started/quick-start.mdx +940 -0
- package/docs/guides/adding-documentation-sites.mdx +1367 -0
- package/docs/guides/creating-services.mdx +1736 -0
- package/docs/guides/creating-ui-modules.mdx +1860 -0
- package/docs/guides/identity-providers.mdx +1007 -0
- package/docs/guides/mermaid-diagrams.mdx +212 -0
- package/docs/guides/using-feature-flags.mdx +1059 -0
- package/docs/guides/working-with-rooms.mdx +566 -0
- package/docs/index.mdx +57 -0
- package/docs/platform-cli/commands.mdx +604 -0
- package/docs/platform-cli/overview.mdx +195 -0
- package/package.json +5 -2
- package/skills/ba-platform/platform-cli.skill.md +26 -0
- package/skills/ba-platform/platform.skill.md +35 -0
- package/templates/application-monorepo-template/gitignore +95 -0
- package/templates/bootstrap-service-template/Dockerfile.development +1 -1
- package/templates/bootstrap-service-template/gitignore +57 -0
- package/templates/bootstrap-service-template/package.json +1 -1
- package/templates/bootstrap-service-template/src/main.ts +6 -16
- package/templates/customization-ui-module-template/Dockerfile.development +1 -1
- package/templates/customization-ui-module-template/gitignore +73 -0
- package/templates/nestjs-service-module-template/Dockerfile.development +1 -1
- package/templates/nestjs-service-module-template/gitignore +56 -0
- package/templates/platform-init-template/{{platformName}}-core/gitignore +97 -0
- package/templates/platform-init-template/{{platformName}}-core/local/.env.example +1 -1
- package/templates/platform-init-template/{{platformName}}-core/local/platform-docker-compose.yml +1 -1
- package/templates/platform-init-template/{{platformName}}-core/local/{{platformName}}-core-docker-compose.yml +0 -1
- package/templates/react-ui-module-template/Dockerfile +1 -1
- package/templates/react-ui-module-template/Dockerfile.development +1 -3
- package/templates/react-ui-module-template/caddy/Caddyfile +1 -1
- package/templates/react-ui-module-template/gitignore +72 -0
- package/templates/react-ui-module-template/Dockerfile_nginx +0 -11
- package/templates/react-ui-module-template/nginx/default.conf +0 -23
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: UI Extension Points
|
|
3
|
+
description: The extension point mechanism for customizing and extending micro-frontend components in the Blue Alba Platform
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
import { Card, CardGrid, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
|
7
|
+
|
|
8
|
+
**UI Extension Points** provide a powerful mechanism for micro-frontends to declare customizable extension points that can be replaced or extended by other micro-frontends. This enables deep customization of the platform UI without modifying core code.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
Extension points solve a key challenge in micro-frontend architectures: **how to allow one micro-frontend to customize or extend components defined in another micro-frontend**.
|
|
13
|
+
|
|
14
|
+
<CardGrid stagger>
|
|
15
|
+
<Card title="Declarative Extension" icon="puzzle">
|
|
16
|
+
Modules declare extension points where others can plug in custom components
|
|
17
|
+
</Card>
|
|
18
|
+
|
|
19
|
+
<Card title="React Component Replacement" icon="seti:react">
|
|
20
|
+
Replace entire React components with your own implementations
|
|
21
|
+
</Card>
|
|
22
|
+
|
|
23
|
+
<Card title="Dynamic Extension" icon="random">
|
|
24
|
+
Extensions are registered/unregistered following React component lifecycle
|
|
25
|
+
</Card>
|
|
26
|
+
|
|
27
|
+
<Card title="Scoped by Module" icon="seti:folder">
|
|
28
|
+
Extension points are namespaced by module to avoid collisions
|
|
29
|
+
</Card>
|
|
30
|
+
|
|
31
|
+
<Card title="Fallback Support" icon="approve-check">
|
|
32
|
+
Default components render when no extension is provided
|
|
33
|
+
</Card>
|
|
34
|
+
|
|
35
|
+
<Card title="Type-Safe" icon="seti:typescript">
|
|
36
|
+
Full TypeScript support with typed props
|
|
37
|
+
</Card>
|
|
38
|
+
</CardGrid>
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Core Concepts
|
|
43
|
+
|
|
44
|
+
### Extension Point
|
|
45
|
+
|
|
46
|
+
An **extension point** is a placeholder in a micro-frontend where other modules can inject custom components.
|
|
47
|
+
|
|
48
|
+
**Example**: The Shell declares an `ApplicationSelector` extension point allowing products to customize how applications are displayed in the navbar.
|
|
49
|
+
|
|
50
|
+
### Extension
|
|
51
|
+
|
|
52
|
+
An **extension** is a React component that replaces or enhances an extension point.
|
|
53
|
+
|
|
54
|
+
**Example**: A product creates a custom `ApplicationSelector` component with a different layout and extends the Shell's extension point.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Declaring Extension Points
|
|
59
|
+
|
|
60
|
+
Modules can declare extension points in two ways:
|
|
61
|
+
|
|
62
|
+
### 1. Inline Extension Point Component
|
|
63
|
+
|
|
64
|
+
Use the `<ExtensionPoint>` component to create a placeholder within JSX:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { ExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
68
|
+
|
|
69
|
+
const MyComponent = () => {
|
|
70
|
+
return (
|
|
71
|
+
<div>
|
|
72
|
+
<h1>Welcome</h1>
|
|
73
|
+
<ExtensionPoint name="Greeting" />
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**With Default Content**:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const MyComponent = () => {
|
|
83
|
+
return (
|
|
84
|
+
<div>
|
|
85
|
+
<h1>Welcome</h1>
|
|
86
|
+
<ExtensionPoint name="Greeting">
|
|
87
|
+
<span>Hello, World!</span>
|
|
88
|
+
</ExtensionPoint>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Scoping**:
|
|
95
|
+
|
|
96
|
+
The extension point is automatically scoped to the current module:
|
|
97
|
+
- Module: `@mycompany/my-ui`
|
|
98
|
+
- Extension point name: `Greeting`
|
|
99
|
+
- **Full name**: `@mycompany/my-ui::Greeting`
|
|
100
|
+
|
|
101
|
+
### 2. Component as Extension Point (HOC)
|
|
102
|
+
|
|
103
|
+
Use the `extensionPoint()` higher-order component to make an entire component replaceable:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { extensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
107
|
+
|
|
108
|
+
const MenuApplicationSelector: React.FC = () => {
|
|
109
|
+
return (
|
|
110
|
+
<div className="app-selector">
|
|
111
|
+
{/* Application selector implementation */}
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Make this component an extension point
|
|
117
|
+
MenuApplicationSelector.displayName = 'ApplicationSelector';
|
|
118
|
+
export default extensionPoint(MenuApplicationSelector);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Behavior**:
|
|
122
|
+
- If no extension is provided, `MenuApplicationSelector` renders normally
|
|
123
|
+
- If an extension is registered, `MenuApplicationSelector` is completely replaced
|
|
124
|
+
- The `displayName` becomes the extension point name
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Extending Extension Points
|
|
129
|
+
|
|
130
|
+
Other modules can extend these points in two ways:
|
|
131
|
+
|
|
132
|
+
### 1. Using ExtendExtensionPoint Component
|
|
133
|
+
|
|
134
|
+
The simplest way to extend an extension point:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { ExtendExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
138
|
+
import MyCustomSelector from './MyCustomSelector';
|
|
139
|
+
|
|
140
|
+
function MyCustomizationUI() {
|
|
141
|
+
return (
|
|
142
|
+
<ExtendExtensionPoint
|
|
143
|
+
module="@bluealba/pae-shell-ui"
|
|
144
|
+
point="ApplicationSelector"
|
|
145
|
+
>
|
|
146
|
+
<MyCustomSelector />
|
|
147
|
+
</ExtendExtensionPoint>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Inline Content**:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
<ExtendExtensionPoint
|
|
156
|
+
module="@bluealba/pae-shell-ui"
|
|
157
|
+
point="ApplicationSelector"
|
|
158
|
+
>
|
|
159
|
+
<div className="custom-selector">
|
|
160
|
+
<h3>My Custom App Selector</h3>
|
|
161
|
+
{/* Custom implementation */}
|
|
162
|
+
</div>
|
|
163
|
+
</ExtendExtensionPoint>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 2. Using useExtendExtensionPoint Hook
|
|
167
|
+
|
|
168
|
+
For more programmatic control:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { useExtendExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
172
|
+
import MyCustomSelector from './MyCustomSelector';
|
|
173
|
+
|
|
174
|
+
const MyCustomizationUI = () => {
|
|
175
|
+
// Register the extension
|
|
176
|
+
useExtendExtensionPoint(
|
|
177
|
+
'@bluealba/pae-shell-ui',
|
|
178
|
+
'ApplicationSelector',
|
|
179
|
+
MyCustomSelector
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<div>
|
|
184
|
+
{/* Rest of your component */}
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
};
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Conditional Extension**:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
const MyCustomizationUI = () => {
|
|
194
|
+
const [enableCustomSelector, setEnableCustomSelector] = useState(true);
|
|
195
|
+
|
|
196
|
+
// Only extend when condition is true
|
|
197
|
+
useExtendExtensionPoint(
|
|
198
|
+
'@bluealba/pae-shell-ui',
|
|
199
|
+
'ApplicationSelector',
|
|
200
|
+
enableCustomSelector ? MyCustomSelector : null
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<div>
|
|
205
|
+
<button onClick={() => setEnableCustomSelector(!enableCustomSelector)}>
|
|
206
|
+
Toggle Custom Selector
|
|
207
|
+
</button>
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Extension Lifecycle
|
|
216
|
+
|
|
217
|
+
Extensions follow the React component lifecycle:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
┌─────────────────────────────────────────────────────────┐
|
|
221
|
+
│ 1. Extension Component Mounts │
|
|
222
|
+
│ <ExtendExtensionPoint> component renders │
|
|
223
|
+
└────────────────────┬────────────────────────────────────┘
|
|
224
|
+
│
|
|
225
|
+
▼
|
|
226
|
+
┌─────────────────────────────────────────────────────────┐
|
|
227
|
+
│ 2. Extension Registers │
|
|
228
|
+
│ Extension point registry updated │
|
|
229
|
+
│ Original component replaced (if using HOC) │
|
|
230
|
+
└────────────────────┬────────────────────────────────────┘
|
|
231
|
+
│
|
|
232
|
+
▼
|
|
233
|
+
┌─────────────────────────────────────────────────────────┐
|
|
234
|
+
│ 3. Extension Renders │
|
|
235
|
+
│ Custom component displays in place of original │
|
|
236
|
+
└────────────────────┬────────────────────────────────────┘
|
|
237
|
+
│
|
|
238
|
+
▼
|
|
239
|
+
┌─────────────────────────────────────────────────────────┐
|
|
240
|
+
│ 4. Extension Component Unmounts │
|
|
241
|
+
│ <ExtendExtensionPoint> unmounts (route change, etc.) │
|
|
242
|
+
└────────────────────┬────────────────────────────────────┘
|
|
243
|
+
│
|
|
244
|
+
▼
|
|
245
|
+
┌─────────────────────────────────────────────────────────┐
|
|
246
|
+
│ 5. Extension Unregisters │
|
|
247
|
+
│ Extension point registry updated │
|
|
248
|
+
│ Original component restored (if using HOC) │
|
|
249
|
+
└─────────────────────────────────────────────────────────┘
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Key Point**: Extensions are **dynamic**. When the extending component unmounts, the extension is automatically removed and the original component restored.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Common Use Cases
|
|
257
|
+
|
|
258
|
+
### Customize Platform Shell
|
|
259
|
+
|
|
260
|
+
Replace Shell components with branded alternatives:
|
|
261
|
+
|
|
262
|
+
<Tabs>
|
|
263
|
+
<TabItem label="Custom Logo">
|
|
264
|
+
```typescript
|
|
265
|
+
import { ExtendExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
266
|
+
import CompanyLogo from './CompanyLogo';
|
|
267
|
+
|
|
268
|
+
<ExtendExtensionPoint
|
|
269
|
+
module="@bluealba/pae-shell-ui"
|
|
270
|
+
point="NavbarPortalLogo"
|
|
271
|
+
>
|
|
272
|
+
<CompanyLogo />
|
|
273
|
+
</ExtendExtensionPoint>
|
|
274
|
+
```
|
|
275
|
+
</TabItem>
|
|
276
|
+
|
|
277
|
+
<TabItem label="Custom Splash Screen">
|
|
278
|
+
```typescript
|
|
279
|
+
import { ExtendExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
280
|
+
import CustomSplash from './CustomSplash';
|
|
281
|
+
|
|
282
|
+
<ExtendExtensionPoint
|
|
283
|
+
module="@bluealba/pae-shell-ui"
|
|
284
|
+
point="SplashScreen"
|
|
285
|
+
>
|
|
286
|
+
<CustomSplash />
|
|
287
|
+
</ExtendExtensionPoint>
|
|
288
|
+
```
|
|
289
|
+
</TabItem>
|
|
290
|
+
|
|
291
|
+
<TabItem label="Custom Application Selector">
|
|
292
|
+
```typescript
|
|
293
|
+
import { ExtendExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
294
|
+
import GridAppSelector from './GridAppSelector';
|
|
295
|
+
|
|
296
|
+
<ExtendExtensionPoint
|
|
297
|
+
module="@bluealba/pae-shell-ui"
|
|
298
|
+
point="ApplicationSelector"
|
|
299
|
+
>
|
|
300
|
+
<GridAppSelector />
|
|
301
|
+
</ExtendExtensionPoint>
|
|
302
|
+
```
|
|
303
|
+
</TabItem>
|
|
304
|
+
</Tabs>
|
|
305
|
+
|
|
306
|
+
### Add Navbar Tools
|
|
307
|
+
|
|
308
|
+
Inject custom tools into the Shell navbar:
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { ExtendExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
312
|
+
import ThemeToggleButton from './ThemeToggleButton';
|
|
313
|
+
|
|
314
|
+
function MyFeatureUI() {
|
|
315
|
+
return (
|
|
316
|
+
<ExtendExtensionPoint
|
|
317
|
+
module="@bluealba/pae-shell-ui"
|
|
318
|
+
point="NavbarTools"
|
|
319
|
+
>
|
|
320
|
+
<ThemeToggleButton />
|
|
321
|
+
</ExtendExtensionPoint>
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Extend Application Components
|
|
327
|
+
|
|
328
|
+
Applications can also define extension points:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// In @mycompany/crm-ui
|
|
332
|
+
import { ExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
333
|
+
|
|
334
|
+
export const CustomerDetailView = () => {
|
|
335
|
+
return (
|
|
336
|
+
<div>
|
|
337
|
+
<h2>Customer Details</h2>
|
|
338
|
+
{/* Standard fields */}
|
|
339
|
+
|
|
340
|
+
<ExtensionPoint name="CustomerDetailActions">
|
|
341
|
+
<button>Email Customer</button>
|
|
342
|
+
</ExtensionPoint>
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
};
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Then another module extends it:
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
// In @mycompany/crm-advanced-features-ui
|
|
352
|
+
import { ExtendExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
353
|
+
|
|
354
|
+
<ExtendExtensionPoint
|
|
355
|
+
module="@mycompany/crm-ui"
|
|
356
|
+
point="CustomerDetailActions"
|
|
357
|
+
>
|
|
358
|
+
<div>
|
|
359
|
+
<button>Email Customer</button>
|
|
360
|
+
<button>Schedule Call</button>
|
|
361
|
+
<button>Create Task</button>
|
|
362
|
+
</div>
|
|
363
|
+
</ExtendExtensionPoint>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Passing Props to Extensions
|
|
369
|
+
|
|
370
|
+
Extension points can pass props to extending components:
|
|
371
|
+
|
|
372
|
+
### Declaring with Props
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
import { ExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
376
|
+
|
|
377
|
+
interface GreetingProps {
|
|
378
|
+
userName: string;
|
|
379
|
+
isAdmin: boolean;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const MyComponent = ({ userName, isAdmin }: { userName: string; isAdmin: boolean }) => {
|
|
383
|
+
return (
|
|
384
|
+
<div>
|
|
385
|
+
<ExtensionPoint<GreetingProps>
|
|
386
|
+
name="Greeting"
|
|
387
|
+
userName={userName}
|
|
388
|
+
isAdmin={isAdmin}
|
|
389
|
+
>
|
|
390
|
+
<span>Hello, {userName}!</span>
|
|
391
|
+
</ExtensionPoint>
|
|
392
|
+
</div>
|
|
393
|
+
);
|
|
394
|
+
};
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Receiving Props in Extension
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
interface GreetingProps {
|
|
401
|
+
userName: string;
|
|
402
|
+
isAdmin: boolean;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const CustomGreeting: React.FC<GreetingProps> = ({ userName, isAdmin }) => {
|
|
406
|
+
return (
|
|
407
|
+
<div className="custom-greeting">
|
|
408
|
+
<h2>Welcome, {userName}!</h2>
|
|
409
|
+
{isAdmin && <span className="badge">Admin</span>}
|
|
410
|
+
</div>
|
|
411
|
+
);
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// Extend with typed component
|
|
415
|
+
<ExtendExtensionPoint
|
|
416
|
+
module="@mycompany/my-ui"
|
|
417
|
+
point="Greeting"
|
|
418
|
+
>
|
|
419
|
+
<CustomGreeting userName="" isAdmin={false} />
|
|
420
|
+
</ExtendExtensionPoint>
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Multiple Extensions
|
|
426
|
+
|
|
427
|
+
Only **one extension** can be active per extension point at a time:
|
|
428
|
+
|
|
429
|
+
**Last Registered Wins**:
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// Module A extends the point
|
|
433
|
+
<ExtendExtensionPoint module="shell" point="Logo">
|
|
434
|
+
<LogoA />
|
|
435
|
+
</ExtendExtensionPoint>
|
|
436
|
+
|
|
437
|
+
// Module B also extends the same point (loaded after A)
|
|
438
|
+
<ExtendExtensionPoint module="shell" point="Logo">
|
|
439
|
+
<LogoB /> {/* This one will be used */}
|
|
440
|
+
</ExtendExtensionPoint>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Control with Module Load Order**:
|
|
444
|
+
|
|
445
|
+
Ensure the module with the desired extension loads last by using `dependsOn`:
|
|
446
|
+
|
|
447
|
+
```json
|
|
448
|
+
{
|
|
449
|
+
"name": "@bluealba/pae-shell-ui",
|
|
450
|
+
"dependsOn": [
|
|
451
|
+
"@mycompany/platform-customization-ui"
|
|
452
|
+
]
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
This ensures `platform-customization-ui` loads first and its extensions can be overridden by Shell if needed.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Best Practices
|
|
461
|
+
|
|
462
|
+
<CardGrid>
|
|
463
|
+
<Card title="Use Descriptive Names" icon="pencil">
|
|
464
|
+
Choose clear, descriptive names for extension points that indicate their purpose
|
|
465
|
+
</Card>
|
|
466
|
+
|
|
467
|
+
<Card title="Provide Fallback Content" icon="approve-check">
|
|
468
|
+
Always include default content for extension points
|
|
469
|
+
</Card>
|
|
470
|
+
|
|
471
|
+
<Card title="Document Extension Points" icon="document">
|
|
472
|
+
Document available extension points, their props, and usage examples
|
|
473
|
+
</Card>
|
|
474
|
+
|
|
475
|
+
<Card title="Type Your Props" icon="seti:typescript">
|
|
476
|
+
Use TypeScript interfaces for extension point props
|
|
477
|
+
</Card>
|
|
478
|
+
|
|
479
|
+
<Card title="Respect Lifecycles" icon="seti:clock">
|
|
480
|
+
Understand that extensions are dynamic and can mount/unmount
|
|
481
|
+
</Card>
|
|
482
|
+
|
|
483
|
+
<Card title="Avoid Deep Nesting" icon="warning">
|
|
484
|
+
Don't create chains of extensions extending extensions
|
|
485
|
+
</Card>
|
|
486
|
+
</CardGrid>
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## Platform Shell Extension Points
|
|
491
|
+
|
|
492
|
+
The Shell provides these commonly-used extension points:
|
|
493
|
+
|
|
494
|
+
<Tabs>
|
|
495
|
+
<TabItem label="Branding">
|
|
496
|
+
```typescript
|
|
497
|
+
// Platform logo in navbar
|
|
498
|
+
NAVBAR_PORTAL_LOGO: "NavbarPortalLogo"
|
|
499
|
+
EXPANDED_NAVBAR_PORTAL_LOGO: "ExpandedNavbarPortalLogo"
|
|
500
|
+
|
|
501
|
+
// Splash screen
|
|
502
|
+
SPLASH_SCREEN: "SplashScreen"
|
|
503
|
+
SPLASH_LOGO: "SplashLogo"
|
|
504
|
+
|
|
505
|
+
// Versions dialog
|
|
506
|
+
VERSIONS_LOGO: "VersionsLogo"
|
|
507
|
+
```
|
|
508
|
+
</TabItem>
|
|
509
|
+
|
|
510
|
+
<TabItem label="Navigation">
|
|
511
|
+
```typescript
|
|
512
|
+
// Application selector
|
|
513
|
+
APPLICATIONS_SELECTOR: "ApplicationSelector"
|
|
514
|
+
|
|
515
|
+
// Application menu
|
|
516
|
+
CURRENT_APPLICATION_MENU: "CurrentApplicationMenu"
|
|
517
|
+
CURRENT_APPLICATION_MENU_CONTENT: "CurrentApplicationMenuContent"
|
|
518
|
+
|
|
519
|
+
// Menu buttons
|
|
520
|
+
OPEN_MENU_BUTTON: "OpenMenuButton"
|
|
521
|
+
CLOSE_MENU_BUTTON: "CloseMenuButton"
|
|
522
|
+
```
|
|
523
|
+
</TabItem>
|
|
524
|
+
|
|
525
|
+
<TabItem label="Navbar">
|
|
526
|
+
```typescript
|
|
527
|
+
// Navbar sections
|
|
528
|
+
NAVBAR_CONTENT: "NavbarContent"
|
|
529
|
+
NAVBAR_TOOLS: "NavbarTools"
|
|
530
|
+
|
|
531
|
+
// Navbar controls
|
|
532
|
+
NAVBAR_TOGGLE_STATE_BUTTON: "NavbarToggleStateButton"
|
|
533
|
+
|
|
534
|
+
// Admin
|
|
535
|
+
ADMIN_APPLICATION_ICON: "AdminApplication"
|
|
536
|
+
```
|
|
537
|
+
</TabItem>
|
|
538
|
+
|
|
539
|
+
<TabItem label="User">
|
|
540
|
+
```typescript
|
|
541
|
+
// User section
|
|
542
|
+
LOGGED_IN_USER_SECTION: "LoggedInUserSection"
|
|
543
|
+
LOGGED_IN_USER_AVATAR: "LoggedInUser"
|
|
544
|
+
```
|
|
545
|
+
</TabItem>
|
|
546
|
+
|
|
547
|
+
<TabItem label="Top-Level">
|
|
548
|
+
```typescript
|
|
549
|
+
// Top-level placeholder for global styles, etc.
|
|
550
|
+
SHELL_TOP_ELEMENTS: "ShellTopElements"
|
|
551
|
+
```
|
|
552
|
+
</TabItem>
|
|
553
|
+
</Tabs>
|
|
554
|
+
|
|
555
|
+
See the [Shell Architecture](/architecture/shell/) documentation for detailed information on each extension point.
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## Example: Theme Toggle Extension
|
|
560
|
+
|
|
561
|
+
Complete example of adding a theme toggle to the navbar:
|
|
562
|
+
|
|
563
|
+
### 1. Create the Component
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
// ThemeToggleButton.tsx
|
|
567
|
+
import React, { useState } from 'react';
|
|
568
|
+
|
|
569
|
+
const ThemeToggleButton: React.FC = () => {
|
|
570
|
+
const [isDark, setIsDark] = useState(false);
|
|
571
|
+
|
|
572
|
+
const toggleTheme = () => {
|
|
573
|
+
setIsDark(!isDark);
|
|
574
|
+
document.documentElement.setAttribute('data-theme', isDark ? 'light' : 'dark');
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
return (
|
|
578
|
+
<button
|
|
579
|
+
onClick={toggleTheme}
|
|
580
|
+
className="theme-toggle"
|
|
581
|
+
title={isDark ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
|
|
582
|
+
>
|
|
583
|
+
{isDark ? '☀️' : '🌙'}
|
|
584
|
+
</button>
|
|
585
|
+
);
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
export default ThemeToggleButton;
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### 2. Register the Extension
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
// In your customization UI module's root component
|
|
595
|
+
import { ExtendExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
596
|
+
import ThemeToggleButton from './ThemeToggleButton';
|
|
597
|
+
|
|
598
|
+
function PlatformCustomizationUI() {
|
|
599
|
+
return (
|
|
600
|
+
<ExtendExtensionPoint
|
|
601
|
+
module="@bluealba/pae-shell-ui"
|
|
602
|
+
point="NavbarTools"
|
|
603
|
+
>
|
|
604
|
+
<ThemeToggleButton />
|
|
605
|
+
</ExtendExtensionPoint>
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
export default PlatformCustomizationUI;
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### 3. Configure Bootstrap
|
|
613
|
+
|
|
614
|
+
Ensure your customization module loads:
|
|
615
|
+
|
|
616
|
+
```json
|
|
617
|
+
{
|
|
618
|
+
"name": "@mycompany/platform-customization-ui",
|
|
619
|
+
"type": "app",
|
|
620
|
+
"baseUrl": "/platform-customization-ui",
|
|
621
|
+
"service": {
|
|
622
|
+
"host": "platform-customization-ui",
|
|
623
|
+
"port": 80
|
|
624
|
+
},
|
|
625
|
+
"ui": {
|
|
626
|
+
"route": "/",
|
|
627
|
+
"mountAtSelector": "body",
|
|
628
|
+
"bundleFile": "platform-customization-ui.js",
|
|
629
|
+
"isPlatformCustomization": true
|
|
630
|
+
},
|
|
631
|
+
"dependsOn": []
|
|
632
|
+
}
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
**Key**: Set `isPlatformCustomization: true` to ensure early loading.
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## Troubleshooting
|
|
640
|
+
|
|
641
|
+
### Extension Not Applying
|
|
642
|
+
|
|
643
|
+
**Check**:
|
|
644
|
+
1. Module declaring extension point is loaded
|
|
645
|
+
2. Module providing extension is loaded
|
|
646
|
+
3. Extension module loads after the module it's extending
|
|
647
|
+
4. Extension point name matches exactly (case-sensitive)
|
|
648
|
+
5. Module name matches exactly
|
|
649
|
+
|
|
650
|
+
**Debug**:
|
|
651
|
+
```typescript
|
|
652
|
+
// Add logging to verify registration
|
|
653
|
+
useExtendExtensionPoint(
|
|
654
|
+
'@bluealba/pae-shell-ui',
|
|
655
|
+
'NavbarTools',
|
|
656
|
+
() => {
|
|
657
|
+
console.log('NavbarTools extension registered');
|
|
658
|
+
return <ThemeToggleButton />;
|
|
659
|
+
}
|
|
660
|
+
);
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Extension Disappears
|
|
664
|
+
|
|
665
|
+
**Cause**: The component registering the extension unmounted.
|
|
666
|
+
|
|
667
|
+
**Solution**: Ensure the component registering the extension remains mounted. For persistent extensions, register them in a root-level component that doesn't unmount.
|
|
668
|
+
|
|
669
|
+
### Multiple Extensions Conflict
|
|
670
|
+
|
|
671
|
+
**Cause**: Multiple modules extending the same point.
|
|
672
|
+
|
|
673
|
+
**Solution**:
|
|
674
|
+
- Use module load order to control priority
|
|
675
|
+
- Consolidate extensions into a single module
|
|
676
|
+
- Create separate extension points for different features
|
|
677
|
+
|
|
678
|
+
### TypeScript Type Errors
|
|
679
|
+
|
|
680
|
+
**Cause**: Props mismatch between extension point and extension component.
|
|
681
|
+
|
|
682
|
+
**Solution**:
|
|
683
|
+
- Export prop types from the module declaring the extension point
|
|
684
|
+
- Import and use these types in your extension component
|
|
685
|
+
|
|
686
|
+
```typescript
|
|
687
|
+
// In module declaring extension point
|
|
688
|
+
export interface NavbarToolsProps {
|
|
689
|
+
isCollapsed: boolean;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// In extending module
|
|
693
|
+
import { NavbarToolsProps } from '@bluealba/pae-shell-ui';
|
|
694
|
+
|
|
695
|
+
const MyTool: React.FC<NavbarToolsProps> = ({ isCollapsed }) => {
|
|
696
|
+
// Implementation
|
|
697
|
+
};
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
702
|
+
## Advanced Patterns
|
|
703
|
+
|
|
704
|
+
### Conditional Extension
|
|
705
|
+
|
|
706
|
+
Only extend under certain conditions:
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
const ConditionalExtension = () => {
|
|
710
|
+
const { user } = useAuth();
|
|
711
|
+
const { features } = useTenantConfig();
|
|
712
|
+
|
|
713
|
+
if (user.isAdmin && features.customTheme) {
|
|
714
|
+
return (
|
|
715
|
+
<ExtendExtensionPoint
|
|
716
|
+
module="@bluealba/pae-shell-ui"
|
|
717
|
+
point="NavbarTools"
|
|
718
|
+
>
|
|
719
|
+
<ThemeToggleButton />
|
|
720
|
+
</ExtendExtensionPoint>
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return null;
|
|
725
|
+
};
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
### Composing Extensions
|
|
729
|
+
|
|
730
|
+
Create a component that extends multiple points:
|
|
731
|
+
|
|
732
|
+
```typescript
|
|
733
|
+
const PlatformCustomizations = () => {
|
|
734
|
+
return (
|
|
735
|
+
<>
|
|
736
|
+
<ExtendExtensionPoint module="shell" point="NavbarPortalLogo">
|
|
737
|
+
<CompanyLogo />
|
|
738
|
+
</ExtendExtensionPoint>
|
|
739
|
+
|
|
740
|
+
<ExtendExtensionPoint module="shell" point="SplashScreen">
|
|
741
|
+
<CompanySplash />
|
|
742
|
+
</ExtendExtensionPoint>
|
|
743
|
+
|
|
744
|
+
<ExtendExtensionPoint module="shell" point="NavbarTools">
|
|
745
|
+
<ThemeToggle />
|
|
746
|
+
</ExtendExtensionPoint>
|
|
747
|
+
</>
|
|
748
|
+
);
|
|
749
|
+
};
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
### Wrapping Original Component
|
|
753
|
+
|
|
754
|
+
Extend while preserving original behavior:
|
|
755
|
+
|
|
756
|
+
```typescript
|
|
757
|
+
import { useExtensionPoint } from '@bluealba-public/pae-ui-react-core';
|
|
758
|
+
|
|
759
|
+
const EnhancedApplicationSelector = () => {
|
|
760
|
+
// Get the original component
|
|
761
|
+
const OriginalSelector = useExtensionPoint(
|
|
762
|
+
'@bluealba/pae-shell-ui',
|
|
763
|
+
'ApplicationSelector'
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
return (
|
|
767
|
+
<div className="enhanced-selector">
|
|
768
|
+
<div className="banner">Featured Apps</div>
|
|
769
|
+
<OriginalSelector />
|
|
770
|
+
</div>
|
|
771
|
+
);
|
|
772
|
+
};
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
---
|
|
776
|
+
|
|
777
|
+
## Next Steps
|
|
778
|
+
|
|
779
|
+
- **[Shell Architecture](/_/docs/architecture/shell/)** - Available Shell extension points
|
|
780
|
+
- **[Bootstrap](/_/docs/architecture/bootstrap/)** - Configuring module load order
|
|
781
|
+
- **[Getting Started with UI Development](/_/docs/development/ui-development/)** - Creating micro-frontends
|