@openshift-assisted/ui-lib 2.42.1 → 2.42.2

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 (34) hide show
  1. package/build/@types/common/components/operators/operatorSpecs.d.ts +10 -3
  2. package/build/@types/common/components/operators/operatorSpecs.d.ts.map +1 -1
  3. package/build/@types/common/components/operators/test.d.ts +50 -0
  4. package/build/@types/common/components/operators/test.d.ts.map +1 -0
  5. package/build/@types/ocm/components/clusterConfiguration/ControlPlaneNodesDropdown.d.ts.map +1 -1
  6. package/build/@types/ocm/components/clusterConfiguration/operators/OperatorCheckbox.d.ts +2 -1
  7. package/build/@types/ocm/components/clusterConfiguration/operators/OperatorCheckbox.d.ts.map +1 -1
  8. package/build/@types/ocm/components/clusterWizard/OperatorsBundle.d.ts +2 -1
  9. package/build/@types/ocm/components/clusterWizard/OperatorsBundle.d.ts.map +1 -1
  10. package/build/@types/ocm/components/clusterWizard/OperatorsSelect.d.ts +2 -1
  11. package/build/@types/ocm/components/clusterWizard/OperatorsSelect.d.ts.map +1 -1
  12. package/build/@types/ocm/components/clusterWizard/OperatorsStep.d.ts.map +1 -1
  13. package/build/cjs/common/components/operators/operatorSpecs.js +111 -139
  14. package/build/cjs/common/components/operators/operatorSpecs.js.map +1 -1
  15. package/build/cjs/common/components/operators/test.js +323 -0
  16. package/build/cjs/common/components/operators/test.js.map +1 -0
  17. package/build/cjs/ocm/components/clusterConfiguration/ControlPlaneNodesDropdown.js +13 -4
  18. package/build/cjs/ocm/components/clusterConfiguration/ControlPlaneNodesDropdown.js.map +1 -1
  19. package/build/cjs/ocm/components/clusterConfiguration/operators/OperatorCheckbox.js +3 -3
  20. package/build/cjs/ocm/components/clusterConfiguration/operators/OperatorCheckbox.js.map +1 -1
  21. package/build/cjs/ocm/components/clusterWizard/OperatorsBundle.js +8 -8
  22. package/build/cjs/ocm/components/clusterWizard/OperatorsBundle.js.map +1 -1
  23. package/build/cjs/ocm/components/clusterWizard/OperatorsSelect.js +33 -14
  24. package/build/cjs/ocm/components/clusterWizard/OperatorsSelect.js.map +1 -1
  25. package/build/cjs/ocm/components/clusterWizard/OperatorsStep.js +13 -3
  26. package/build/cjs/ocm/components/clusterWizard/OperatorsStep.js.map +1 -1
  27. package/lib/common/components/operators/operatorSpecs.tsx +123 -160
  28. package/lib/common/components/operators/test.tsx +469 -0
  29. package/lib/ocm/components/clusterConfiguration/ControlPlaneNodesDropdown.tsx +21 -9
  30. package/lib/ocm/components/clusterConfiguration/operators/OperatorCheckbox.tsx +5 -2
  31. package/lib/ocm/components/clusterWizard/OperatorsBundle.tsx +15 -7
  32. package/lib/ocm/components/clusterWizard/OperatorsSelect.tsx +58 -37
  33. package/lib/ocm/components/clusterWizard/OperatorsStep.tsx +30 -3
  34. package/package.json +3 -3
@@ -0,0 +1,469 @@
1
+ import React from 'react';
2
+ import { Cluster, SupportLevel } from '@openshift-assisted/types/assisted-installer-service';
3
+ import { FeatureId } from '../../types';
4
+ import {
5
+ OPERATOR_NAME_AMD_GPU,
6
+ OPERATOR_NAME_AUTHORINO,
7
+ OPERATOR_NAME_CNV,
8
+ OPERATOR_NAME_KMM,
9
+ OPERATOR_NAME_LSO,
10
+ OPERATOR_NAME_LVM,
11
+ OPERATOR_NAME_MCE,
12
+ OPERATOR_NAME_MTV,
13
+ OPERATOR_NAME_NMSTATE,
14
+ OPERATOR_NAME_NODE_FEATURE_DISCOVERY,
15
+ OPERATOR_NAME_NVIDIA_GPU,
16
+ OPERATOR_NAME_ODF,
17
+ OPERATOR_NAME_OPENSHIFT_AI,
18
+ OPERATOR_NAME_OSC,
19
+ OPERATOR_NAME_PIPELINES,
20
+ OPERATOR_NAME_SERVERLESS,
21
+ OPERATOR_NAME_SERVICEMESH,
22
+ OPERATOR_NAME_NODE_MAINTENANCE,
23
+ OPERATOR_NAME_KUBE_DESCHEDULER,
24
+ } from '../../config/constants';
25
+ import { ExternalLink } from '../ui';
26
+ import {
27
+ AUTHORINO_OPERATOR_LINK,
28
+ CNV_LINK,
29
+ getKmmDocsLink,
30
+ getKubeDeschedulerLink,
31
+ getLsoLink,
32
+ getLvmsDocsLink,
33
+ getMceDocsLink,
34
+ getNmstateLink,
35
+ getNodeFeatureDiscoveryLink,
36
+ getNvidiaGpuLink,
37
+ getServiceMeshLink,
38
+ MTV_LINK,
39
+ NODE_MAINTENANCE_LINK,
40
+ ODF_LINK,
41
+ ODF_REQUIREMENTS_LINK,
42
+ OPENSHIFT_AI_LINK,
43
+ OPENSHIFT_AI_REQUIREMENTS_LINK,
44
+ OSC_LINK,
45
+ OSC_REQUIREMENTS_LINK,
46
+ PIPELINES_OPERATOR_LINK,
47
+ SERVERLESS_OPERATOR_LINK,
48
+ } from '../../config';
49
+ import { getMajorMinorVersion } from '../../utils';
50
+ import { GetFeatureSupportLevel, useNewFeatureSupportLevel } from '../newFeatureSupportLevels';
51
+ import {
52
+ DESCRIPTION_AMD_GPU,
53
+ DESCRIPTION_AUTHORINO,
54
+ DESCRIPTION_LSO,
55
+ DESCRIPTION_MTV,
56
+ DESCRIPTION_CNV,
57
+ DESCRIPTION_KMM,
58
+ DESCRIPTION_KUBE_DESCHEDULER,
59
+ DESCRIPTION_MCE,
60
+ DESCRIPTION_NMSTATE,
61
+ DESCRIPTION_NODE_FEATURE_DISCOVERY,
62
+ DESCRIPTION_NODE_MAINTENANCE,
63
+ DESCRIPTION_NVIDIA_GPU,
64
+ DESCRIPTION_ODF,
65
+ DESCRIPTION_OPENSHIFT_AI,
66
+ DESCRIPTION_OSC,
67
+ DESCRIPTION_PIPELINES,
68
+ DESCRIPTION_SERVERLESS,
69
+ DESCRIPTION_SERVICEMESH,
70
+ DESCRIPTION_LVM,
71
+ } from './operatorDescriptions';
72
+
73
+ // TODO check if it's unused and it can be deleted in favor of "isMajorMinorVersionEqualOrGreater"
74
+ export const isOCPVersionEqualsOrMore = (
75
+ openshiftVersion: string,
76
+ ocpVersionToCompare: string,
77
+ ): boolean => {
78
+ const [majorA, minorA] = getMajorMinorVersion(openshiftVersion).split('.');
79
+ const [majorB, minorB] = ocpVersionToCompare.split('.');
80
+ return (
81
+ Number(majorA) > Number(majorB) ||
82
+ (Number(majorA) === Number(majorB) && Number(minorA) >= Number(minorB))
83
+ );
84
+ };
85
+
86
+ export const highlightMatch = (text: string, searchTerm?: string): React.ReactNode => {
87
+ if (!searchTerm) return text;
88
+ const escapeSearchTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
89
+ const regex = new RegExp(`(${escapeSearchTerm})`, 'gi');
90
+
91
+ const parts = text.split(regex);
92
+
93
+ return parts.map((part, i) =>
94
+ part.toLowerCase() === searchTerm.toLowerCase() ? <mark key={i}>{part}</mark> : part,
95
+ );
96
+ };
97
+
98
+ export const HighlightedText = ({ text, searchTerm }: { text: string; searchTerm?: string }) => (
99
+ <>{highlightMatch(text, searchTerm)}</>
100
+ );
101
+
102
+ export type OperatorSpec = {
103
+ operatorKey: string;
104
+ title: string;
105
+ featureId: FeatureId;
106
+ descriptionText?: string;
107
+ Description?: React.ComponentType<{ openshiftVersion?: string; searchTerm?: string }>;
108
+ notStandalone?: boolean;
109
+ Requirements?: React.ComponentType<{ cluster: Cluster }>;
110
+ supportLevel?: SupportLevel | undefined;
111
+ };
112
+
113
+ export const getOperatorSpecs = (
114
+ getFeatureSupportLevel: GetFeatureSupportLevel,
115
+ useLVMS?: boolean,
116
+ ): { [category: string]: OperatorSpec[] } => {
117
+ return {
118
+ [categories[Category.STORAGE]]: [
119
+ {
120
+ operatorKey: OPERATOR_NAME_LSO,
121
+ title: 'Local Storage Operator',
122
+ featureId: 'LSO',
123
+ descriptionText: DESCRIPTION_LSO,
124
+ Description: ({ openshiftVersion, searchTerm }) => (
125
+ <>
126
+ <HighlightedText text={DESCRIPTION_LSO} searchTerm={searchTerm} />{' '}
127
+ <ExternalLink href={getLsoLink(openshiftVersion)}>Learn more</ExternalLink>
128
+ </>
129
+ ),
130
+ notStandalone: true,
131
+ supportLevel: getFeatureSupportLevel('LSO'),
132
+ },
133
+ {
134
+ operatorKey: OPERATOR_NAME_LVM,
135
+ title: useLVMS ? 'Logical Volume Manager Storage' : 'Logical Volume Manager',
136
+ featureId: 'LVM',
137
+ descriptionText: DESCRIPTION_LVM,
138
+ Description: ({ openshiftVersion, searchTerm }) =>
139
+ useLVMS ? (
140
+ <>
141
+ <HighlightedText text={DESCRIPTION_LVM} searchTerm={searchTerm} />{' '}
142
+ <ExternalLink href={getLvmsDocsLink(openshiftVersion)}>Learn more</ExternalLink>
143
+ </>
144
+ ) : (
145
+ <>{DESCRIPTION_LVM}</>
146
+ ),
147
+ supportLevel: getFeatureSupportLevel('LVM'),
148
+ },
149
+ {
150
+ operatorKey: OPERATOR_NAME_ODF,
151
+ title: 'OpenShift Data Foundation',
152
+ featureId: 'ODF',
153
+ descriptionText: DESCRIPTION_ODF,
154
+ Requirements: () => (
155
+ <ExternalLink href={ODF_REQUIREMENTS_LINK}>
156
+ Learn more about the requirements for OpenShift Data Foundation
157
+ </ExternalLink>
158
+ ),
159
+ Description: ({ searchTerm }) => (
160
+ <>
161
+ <HighlightedText text={DESCRIPTION_ODF} searchTerm={searchTerm} />{' '}
162
+ <ExternalLink href={ODF_LINK}>Learn more</ExternalLink>
163
+ </>
164
+ ),
165
+ supportLevel: getFeatureSupportLevel('ODF'),
166
+ },
167
+ ],
168
+ [categories[Category.VIRT]]: [
169
+ {
170
+ operatorKey: OPERATOR_NAME_CNV,
171
+ title: 'OpenShift Virtualization',
172
+ featureId: 'CNV',
173
+ descriptionText: DESCRIPTION_CNV,
174
+ Requirements: () => (
175
+ <>Enabled CPU virtualization support in BIOS (Intel-VT / AMD-V) on all nodes</>
176
+ ),
177
+ Description: ({ searchTerm }) => (
178
+ <>
179
+ <HighlightedText text={DESCRIPTION_CNV} searchTerm={searchTerm} />{' '}
180
+ <ExternalLink href={CNV_LINK}>Learn more</ExternalLink>
181
+ </>
182
+ ),
183
+ supportLevel: getFeatureSupportLevel('CNV'),
184
+ },
185
+ {
186
+ operatorKey: OPERATOR_NAME_MTV,
187
+ title: 'Migration Toolkit for Virtualization',
188
+ featureId: 'MTV',
189
+ descriptionText: DESCRIPTION_MTV,
190
+ Description: ({ searchTerm }) => (
191
+ <>
192
+ <HighlightedText text={DESCRIPTION_MTV} searchTerm={searchTerm} />{' '}
193
+ <ExternalLink href={MTV_LINK}>Learn more</ExternalLink>
194
+ </>
195
+ ),
196
+ supportLevel: getFeatureSupportLevel('MTV'),
197
+ },
198
+ {
199
+ operatorKey: OPERATOR_NAME_OSC,
200
+ title: 'OpenShift sandboxed containers',
201
+ featureId: 'OSC',
202
+ descriptionText: DESCRIPTION_OSC,
203
+ Requirements: () => (
204
+ <ExternalLink href={OSC_REQUIREMENTS_LINK}>
205
+ Learn more about the requirements for OpenShift sandboxed containers
206
+ </ExternalLink>
207
+ ),
208
+ Description: () => (
209
+ <>
210
+ {DESCRIPTION_OSC} <ExternalLink href={OSC_LINK}>Learn more</ExternalLink>
211
+ </>
212
+ ),
213
+ supportLevel: getFeatureSupportLevel('OSC'),
214
+ },
215
+ ],
216
+ [categories[Category.AI]]: [
217
+ {
218
+ operatorKey: OPERATOR_NAME_OPENSHIFT_AI,
219
+ title: 'OpenShift AI',
220
+ featureId: 'OPENSHIFT_AI',
221
+ descriptionText: DESCRIPTION_OPENSHIFT_AI,
222
+ Requirements: () => (
223
+ <ExternalLink href={OPENSHIFT_AI_REQUIREMENTS_LINK}>Learn more</ExternalLink>
224
+ ),
225
+ Description: ({ searchTerm }) => (
226
+ <>
227
+ <HighlightedText text={DESCRIPTION_OPENSHIFT_AI} searchTerm={searchTerm} />{' '}
228
+ <ExternalLink href={OPENSHIFT_AI_LINK}>Learn more</ExternalLink>
229
+ </>
230
+ ),
231
+ supportLevel: getFeatureSupportLevel('OPENSHIFT_AI'),
232
+ },
233
+ {
234
+ operatorKey: OPERATOR_NAME_AMD_GPU,
235
+ title: 'AMD GPU',
236
+ featureId: 'AMD_GPU',
237
+ descriptionText: DESCRIPTION_AMD_GPU,
238
+ Requirements: () => <>Requires at least one supported AMD GPU</>,
239
+ Description: ({ searchTerm }) => (
240
+ <>
241
+ {' '}
242
+ <HighlightedText text={DESCRIPTION_AMD_GPU} searchTerm={searchTerm} />{' '}
243
+ </>
244
+ ),
245
+ supportLevel: getFeatureSupportLevel('AMD_GPU'),
246
+ },
247
+ {
248
+ operatorKey: OPERATOR_NAME_NVIDIA_GPU,
249
+ title: 'NVIDIA GPU',
250
+ featureId: 'NVIDIA_GPU',
251
+ descriptionText: DESCRIPTION_NVIDIA_GPU,
252
+ Requirements: () => <>Requires at least one supported NVIDIA GPU</>,
253
+ Description: ({ openshiftVersion, searchTerm }) => (
254
+ <>
255
+ <HighlightedText text={DESCRIPTION_NVIDIA_GPU} searchTerm={searchTerm} />{' '}
256
+ <ExternalLink href={getNvidiaGpuLink(openshiftVersion)}>Learn more</ExternalLink>
257
+ </>
258
+ ),
259
+ supportLevel: getFeatureSupportLevel('NVIDIA_GPU'),
260
+ },
261
+ ],
262
+ [categories[Category.NETWORK]]: [
263
+ {
264
+ operatorKey: OPERATOR_NAME_NMSTATE,
265
+ title: 'NMState',
266
+ featureId: 'NMSTATE',
267
+ descriptionText: DESCRIPTION_NMSTATE,
268
+ Description: ({ openshiftVersion, searchTerm }) => (
269
+ <>
270
+ <HighlightedText text={DESCRIPTION_NMSTATE} searchTerm={searchTerm} />{' '}
271
+ <ExternalLink href={getNmstateLink(openshiftVersion)}>Learn more</ExternalLink>
272
+ </>
273
+ ),
274
+ supportLevel: getFeatureSupportLevel('NMSTATE'),
275
+ },
276
+ {
277
+ operatorKey: OPERATOR_NAME_SERVICEMESH,
278
+ title: 'Service Mesh',
279
+ featureId: 'SERVICEMESH',
280
+ descriptionText: DESCRIPTION_SERVICEMESH,
281
+ Description: ({ openshiftVersion, searchTerm }) => (
282
+ <>
283
+ <HighlightedText text={DESCRIPTION_SERVICEMESH} searchTerm={searchTerm} />{' '}
284
+ <ExternalLink href={getServiceMeshLink(openshiftVersion)}>Learn more</ExternalLink>
285
+ </>
286
+ ),
287
+ notStandalone: true,
288
+ supportLevel: getFeatureSupportLevel('SERVICEMESH'),
289
+ },
290
+ ],
291
+ [categories[Category.SECURITY]]: [
292
+ {
293
+ operatorKey: OPERATOR_NAME_AUTHORINO,
294
+ title: 'Authorino',
295
+ featureId: 'AUTHORINO',
296
+ descriptionText: DESCRIPTION_AUTHORINO,
297
+ Description: ({ searchTerm }) => (
298
+ <>
299
+ <HighlightedText text={DESCRIPTION_AUTHORINO} searchTerm={searchTerm} />{' '}
300
+ <ExternalLink href={AUTHORINO_OPERATOR_LINK}>Learn more</ExternalLink>
301
+ </>
302
+ ),
303
+ notStandalone: true,
304
+ supportLevel: getFeatureSupportLevel('AUTHORINO'),
305
+ },
306
+ {
307
+ operatorKey: OPERATOR_NAME_KMM,
308
+ title: 'Kernel Module Management',
309
+ featureId: 'KMM',
310
+ descriptionText: DESCRIPTION_KMM,
311
+ Description: ({ openshiftVersion, searchTerm }) => (
312
+ <>
313
+ <HighlightedText text={DESCRIPTION_KMM} searchTerm={searchTerm} />{' '}
314
+ <ExternalLink href={getKmmDocsLink(openshiftVersion)}>Learn more</ExternalLink>
315
+ </>
316
+ ),
317
+ supportLevel: getFeatureSupportLevel('KMM'),
318
+ },
319
+ ],
320
+ [categories[Category.CI_CD]]: [
321
+ {
322
+ operatorKey: OPERATOR_NAME_PIPELINES,
323
+ title: 'Pipelines',
324
+ featureId: 'PIPELINES',
325
+ descriptionText: DESCRIPTION_PIPELINES,
326
+ Description: ({ searchTerm }) => (
327
+ <>
328
+ <HighlightedText text={DESCRIPTION_PIPELINES} searchTerm={searchTerm} />{' '}
329
+ <ExternalLink href={PIPELINES_OPERATOR_LINK}>Learn more</ExternalLink>
330
+ </>
331
+ ),
332
+ notStandalone: true,
333
+ supportLevel: getFeatureSupportLevel('PIPELINES'),
334
+ },
335
+ {
336
+ operatorKey: OPERATOR_NAME_SERVERLESS,
337
+ title: 'Serverless',
338
+ featureId: 'SERVERLESS',
339
+ descriptionText: DESCRIPTION_SERVERLESS,
340
+ Description: ({ searchTerm }) => (
341
+ <>
342
+ <HighlightedText text={DESCRIPTION_SERVERLESS} searchTerm={searchTerm} />{' '}
343
+ <ExternalLink href={SERVERLESS_OPERATOR_LINK}>Learn more</ExternalLink>
344
+ </>
345
+ ),
346
+ notStandalone: true,
347
+ supportLevel: getFeatureSupportLevel('SERVERLESS'),
348
+ },
349
+ ],
350
+ [categories[Category.PLATFORM]]: [
351
+ {
352
+ operatorKey: OPERATOR_NAME_MCE,
353
+ title: 'Multicluster engine',
354
+ featureId: 'MCE',
355
+ descriptionText: DESCRIPTION_MCE,
356
+ Description: ({ openshiftVersion, searchTerm }) => (
357
+ <>
358
+ <HighlightedText text={DESCRIPTION_MCE} searchTerm={searchTerm} />{' '}
359
+ <ExternalLink href={getMceDocsLink(openshiftVersion)}>Learn more</ExternalLink>
360
+ </>
361
+ ),
362
+ supportLevel: getFeatureSupportLevel('MCE'),
363
+ },
364
+ {
365
+ operatorKey: OPERATOR_NAME_NODE_MAINTENANCE,
366
+ title: 'Node Maintenance',
367
+ featureId: 'NODE_MAINTENANCE',
368
+ descriptionText: DESCRIPTION_NODE_MAINTENANCE,
369
+ Description: () => (
370
+ <>
371
+ {DESCRIPTION_NODE_MAINTENANCE}{' '}
372
+ <ExternalLink href={NODE_MAINTENANCE_LINK}>Learn more</ExternalLink>
373
+ </>
374
+ ),
375
+ notStandalone: true,
376
+ supportLevel: getFeatureSupportLevel('NODE_MAINTENANCE'),
377
+ },
378
+ ],
379
+ [categories[Category.SCHEDULING]]: [
380
+ {
381
+ operatorKey: OPERATOR_NAME_NODE_FEATURE_DISCOVERY,
382
+ title: 'Node Feature Discovery',
383
+ featureId: 'NODE_FEATURE_DISCOVERY',
384
+ descriptionText: DESCRIPTION_NODE_FEATURE_DISCOVERY,
385
+ Description: ({ openshiftVersion }) => (
386
+ <>
387
+ {DESCRIPTION_NODE_FEATURE_DISCOVERY}{' '}
388
+ <ExternalLink href={getNodeFeatureDiscoveryLink(openshiftVersion)}>
389
+ Learn more
390
+ </ExternalLink>
391
+ </>
392
+ ),
393
+ supportLevel: getFeatureSupportLevel('NODE_FEATURE_DISCOVERY'),
394
+ },
395
+ {
396
+ operatorKey: OPERATOR_NAME_KUBE_DESCHEDULER,
397
+ title: 'Kube Descheduler',
398
+ featureId: 'KUBE_DESCHEDULER',
399
+ descriptionText: DESCRIPTION_KUBE_DESCHEDULER,
400
+ Description: ({ openshiftVersion, searchTerm }) => (
401
+ <>
402
+ <HighlightedText text={DESCRIPTION_KUBE_DESCHEDULER} searchTerm={searchTerm} />{' '}
403
+ <ExternalLink href={getKubeDeschedulerLink(openshiftVersion)}>Learn more</ExternalLink>
404
+ </>
405
+ ),
406
+ notStandalone: true,
407
+ supportLevel: getFeatureSupportLevel('KUBE_DESCHEDULER'),
408
+ },
409
+ ],
410
+ };
411
+ };
412
+
413
+ export const useOperatorSpecs = () => {
414
+ const { getFeatureSupportLevel } = useNewFeatureSupportLevel();
415
+ const useLVMS = getFeatureSupportLevel('LVM') === 'supported';
416
+
417
+ // Grouped by category
418
+ const byCategory = React.useMemo(
419
+ () => getOperatorSpecs(getFeatureSupportLevel, useLVMS),
420
+ [getFeatureSupportLevel, useLVMS],
421
+ );
422
+
423
+ // Flat map operatorKey -> spec
424
+ const byKey: Record<string, OperatorSpec> = React.useMemo(() => {
425
+ return Object.values(byCategory).reduce((acc, specs) => {
426
+ specs.forEach((spec) => {
427
+ acc[spec.operatorKey] = spec;
428
+ });
429
+ return acc;
430
+ }, {} as Record<string, OperatorSpec>);
431
+ }, [byCategory]);
432
+
433
+ return { byCategory, byKey };
434
+ };
435
+
436
+ export const getOperatorTitleByFeatureId = (featureId: FeatureId): string | undefined => {
437
+ const allSpecs = getOperatorSpecs(() => undefined);
438
+
439
+ for (const categorySpecs of Object.values(allSpecs)) {
440
+ const spec = categorySpecs.find((s) => s.featureId === featureId);
441
+ if (spec) {
442
+ return spec.title;
443
+ }
444
+ }
445
+
446
+ return undefined;
447
+ };
448
+
449
+ enum Category {
450
+ STORAGE,
451
+ VIRT,
452
+ AI,
453
+ NETWORK,
454
+ CI_CD,
455
+ PLATFORM,
456
+ SECURITY,
457
+ SCHEDULING,
458
+ }
459
+
460
+ export const categories: { [key in Category]: string } = {
461
+ [Category.STORAGE]: 'Storage',
462
+ [Category.VIRT]: 'Virtualization',
463
+ [Category.AI]: 'AI',
464
+ [Category.NETWORK]: 'Network',
465
+ [Category.SECURITY]: 'Security & Access Control',
466
+ [Category.CI_CD]: 'CI/CD & Dev Productivity',
467
+ [Category.PLATFORM]: 'Platform Operations & Lifecycle',
468
+ [Category.SCHEDULING]: 'Scheduling',
469
+ };
@@ -9,11 +9,12 @@ import {
9
9
  DropdownList,
10
10
  } from '@patternfly/react-core';
11
11
  import { useField } from 'formik';
12
- import { getFieldId, PopoverIcon } from '../../../common';
12
+ import { getFieldId, PopoverIcon, PreviewBadgePosition, TechnologyPreview } from '../../../common';
13
13
  import {
14
14
  NewFeatureSupportLevelMap,
15
15
  useNewFeatureSupportLevel,
16
16
  } from '../../../common/components/newFeatureSupportLevels';
17
+ import { isFeatureSupportedAndAvailable } from '../featureSupportLevels/featureStateUtils';
17
18
  import OcmTNADisclaimer from './OcmTNADisclaimer';
18
19
  import toNumber from 'lodash-es/toNumber';
19
20
  import OcmSNODisclaimer from './OcmSNODisclaimer';
@@ -42,6 +43,7 @@ export const ControlPlaneNodesLabel = () => {
42
43
  interface ControlPlaneNodesOption {
43
44
  value: number;
44
45
  label: string;
46
+ badge?: React.ReactNode;
45
47
  }
46
48
 
47
49
  interface ControlPlaneNodesDropdownProps {
@@ -90,19 +92,27 @@ const ControlPlaneNodesDropdown: React.FC<ControlPlaneNodesDropdownProps> = ({
90
92
  featureSupportLevelData ?? undefined,
91
93
  );
92
94
 
95
+ const tnaSupport = newFeatureSupportLevelContext.getFeatureSupportLevel(
96
+ 'TNA',
97
+ featureSupportLevelData ?? undefined,
98
+ );
99
+ const isTnaEnabled = isFeatureSupportedAndAvailable(tnaSupport || 'unsupported');
100
+
93
101
  const options: ControlPlaneNodesOption[] = [
94
102
  { value: 1, label: '1 (Single Node OpenShift)' },
95
- { value: 2, label: '2 (Two-Nodes Arbiter)' },
103
+ {
104
+ value: 2,
105
+ label: '2 (Two-Nodes Arbiter)',
106
+ badge:
107
+ tnaSupport === 'tech-preview' ? (
108
+ <TechnologyPreview position={PreviewBadgePosition.default} />
109
+ ) : undefined,
110
+ },
96
111
  { value: 3, label: '3 (highly available cluster)' },
97
112
  { value: 4, label: '4 (highly available cluster+)' },
98
113
  { value: 5, label: '5 (highly available cluster++)' },
99
114
  ];
100
115
 
101
- const isTnaEnabled = newFeatureSupportLevelContext.isFeatureSupported(
102
- 'TNA',
103
- featureSupportLevelData ?? undefined,
104
- );
105
-
106
116
  React.useEffect(() => {
107
117
  if (!field.value) {
108
118
  setValue(DEFAULT_VALUE_CPN);
@@ -115,7 +125,7 @@ const ControlPlaneNodesDropdown: React.FC<ControlPlaneNodesDropdownProps> = ({
115
125
  setOpen(false);
116
126
  };
117
127
 
118
- const dropdownItems = options.map(({ value, label }) => {
128
+ const dropdownItems = options.map(({ value, label, badge }) => {
119
129
  const isItemEnabled = isCPNDropdownItemEnabled(
120
130
  value,
121
131
  isNonStandardControlPlaneEnabled,
@@ -124,7 +134,9 @@ const ControlPlaneNodesDropdown: React.FC<ControlPlaneNodesDropdownProps> = ({
124
134
  return (
125
135
  <DropdownItem key={value} id={value.toString()} isAriaDisabled={!isItemEnabled}>
126
136
  <Tooltip hidden={isItemEnabled} content={disabledReason} position="top">
127
- <div>{label}</div>
137
+ <div>
138
+ {label} {badge}
139
+ </div>
128
140
  </Tooltip>
129
141
  </DropdownItem>
130
142
  );
@@ -26,6 +26,7 @@ import { getFieldId, OperatorsValues, PopoverIcon } from '../../../../common';
26
26
  import { useNewFeatureSupportLevel } from '../../../../common/components/newFeatureSupportLevels';
27
27
  import { getNewOperators } from './utils';
28
28
  import {
29
+ highlightMatch,
29
30
  OperatorSpec,
30
31
  useOperatorSpecs,
31
32
  } from '../../../../common/components/operators/operatorSpecs';
@@ -118,12 +119,14 @@ const OperatorCheckbox = ({
118
119
  Requirements,
119
120
  openshiftVersion,
120
121
  preflightRequirements,
122
+ searchTerm,
121
123
  }: {
122
124
  bundles: Bundle[];
123
125
  cluster: Cluster;
124
126
  operatorId: string;
125
127
  openshiftVersion?: string;
126
128
  preflightRequirements: PreflightHardwareRequirements | undefined;
129
+ searchTerm?: string;
127
130
  } & OperatorSpec) => {
128
131
  const { getFeatureSupportLevel, getFeatureDisabledReason } = useNewFeatureSupportLevel();
129
132
  const { byKey: opSpecs } = useOperatorSpecs();
@@ -166,7 +169,7 @@ const OperatorCheckbox = ({
166
169
  label={
167
170
  <>
168
171
  <Tooltip hidden={!disabledReason} content={disabledReason}>
169
- <span>{title} </span>
172
+ <span>{highlightMatch(title, searchTerm)} </span>
170
173
  </Tooltip>
171
174
  <OperatorRequirements
172
175
  operatorId={operatorId}
@@ -196,7 +199,7 @@ const OperatorCheckbox = ({
196
199
  !!Description && (
197
200
  <HelperText>
198
201
  <HelperTextItem variant="indeterminate">
199
- <Description openshiftVersion={openshiftVersion} />
202
+ <Description openshiftVersion={openshiftVersion} searchTerm={searchTerm} />
200
203
  </HelperTextItem>
201
204
  </HelperText>
202
205
  )
@@ -30,17 +30,20 @@ import { useSelector } from 'react-redux';
30
30
  import { selectIsCurrentClusterSNO } from '../../store/slices/current-cluster/selectors';
31
31
  import { getNewBundleOperators } from '../clusterConfiguration/operators/utils';
32
32
  import { bundleSpecs } from '../clusterConfiguration/operators/bundleSpecs';
33
- import { OperatorSpec, useOperatorSpecs } from '../../../common/components/operators/operatorSpecs';
33
+ import {
34
+ highlightMatch,
35
+ OperatorSpec,
36
+ useOperatorSpecs,
37
+ } from '../../../common/components/operators/operatorSpecs';
34
38
  import { useClusterWizardContext } from './ClusterWizardContext';
35
39
  import './OperatorsBundle.css';
36
40
 
37
- const BundleLabel = ({ bundle }: { bundle: Bundle }) => {
41
+ const BundleLabel = ({ bundle, searchTerm }: { bundle: Bundle; searchTerm?: string }) => {
38
42
  const { byKey: opSpecs } = useOperatorSpecs();
39
43
  const bundleSpec = bundleSpecs[bundle.id || ''];
40
-
41
44
  return (
42
45
  <>
43
- <span>{bundle.title} </span>
46
+ <span>{highlightMatch(bundle.title || '', searchTerm)} </span>
44
47
  <PopoverIcon
45
48
  component={'a'}
46
49
  headerContent={<div>Requirements and dependencies</div>}
@@ -100,10 +103,12 @@ const BundleCard = ({
100
103
  bundle,
101
104
  bundles,
102
105
  preflightRequirements,
106
+ searchTerm,
103
107
  }: {
104
108
  bundle: Bundle;
105
109
  bundles: Bundle[];
106
110
  preflightRequirements: PreflightHardwareRequirements | undefined;
111
+ searchTerm?: string;
107
112
  }) => {
108
113
  const { values, setFieldValue } = useFormikContext<OperatorsValues>();
109
114
  const isSNO = useSelector(selectIsCurrentClusterSNO);
@@ -177,13 +182,13 @@ const BundleCard = ({
177
182
  }}
178
183
  >
179
184
  <CardTitle>
180
- <BundleLabel bundle={bundle} />
185
+ <BundleLabel bundle={bundle} searchTerm={searchTerm} />
181
186
  </CardTitle>
182
187
  </CardHeader>
183
188
  <CardBody isFilled>
184
189
  <Stack hasGutter>
185
190
  <StackItem isFilled>
186
- <div>{bundle.description}</div>
191
+ <div>{highlightMatch(bundle.description || '', searchTerm)}</div>
187
192
  </StackItem>
188
193
  <StackItem>
189
194
  <Split>
@@ -206,9 +211,11 @@ const BundleCard = ({
206
211
  const OperatorsBundle = ({
207
212
  bundles,
208
213
  preflightRequirements,
214
+ searchTerm,
209
215
  }: {
210
216
  bundles: Bundle[];
211
217
  preflightRequirements: PreflightHardwareRequirements | undefined;
218
+ searchTerm?: string;
212
219
  }) => {
213
220
  const isSingleClusterFeatureEnabled = useFeature('ASSISTED_INSTALLER_SINGLE_CLUSTER_FEATURE');
214
221
 
@@ -216,7 +223,7 @@ const OperatorsBundle = ({
216
223
  <Stack hasGutter>
217
224
  <StackItem>
218
225
  <Title headingLevel="h2" size="lg">
219
- Bundles
226
+ {bundles.length > 0 ? 'Bundles' : ''}
220
227
  </Title>
221
228
  </StackItem>
222
229
  <StackItem>
@@ -230,6 +237,7 @@ const OperatorsBundle = ({
230
237
  bundle={bundle}
231
238
  bundles={bundles}
232
239
  preflightRequirements={preflightRequirements}
240
+ searchTerm={searchTerm}
233
241
  />
234
242
  </GalleryItem>
235
243
  ))}