@openshift-assisted/ui-lib 2.39.4 → 2.40.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.
Files changed (129) hide show
  1. package/build/@types/cim/components/InfraEnv/InfraTableToolbar.d.ts.map +1 -1
  2. package/build/@types/cim/components/helpers/toAssisted.d.ts.map +1 -1
  3. package/build/@types/cim/index.d.ts +1 -0
  4. package/build/@types/cim/index.d.ts.map +1 -1
  5. package/build/@types/common/api/assisted-service/ClustersAPI.d.ts +1 -0
  6. package/build/@types/common/api/assisted-service/ClustersAPI.d.ts.map +1 -1
  7. package/build/@types/common/components/clusterDetail/ClusterCredentials.d.ts.map +1 -1
  8. package/build/@types/common/components/clusterWizard/ClusterWizardStep.d.ts.map +1 -1
  9. package/build/@types/common/components/operators/operatorSpecs.d.ts.map +1 -1
  10. package/build/@types/common/components/ui/ClusterEventsToolbar.d.ts.map +1 -1
  11. package/build/@types/common/components/ui/WizardFooter.d.ts +1 -0
  12. package/build/@types/common/components/ui/WizardFooter.d.ts.map +1 -1
  13. package/build/@types/common/components/ui/WizardNavItem.d.ts +1 -1
  14. package/build/@types/common/components/ui/WizardNavItem.d.ts.map +1 -1
  15. package/build/@types/common/components/ui/formik/MultiSelectField.d.ts.map +1 -1
  16. package/build/@types/common/components/ui/formik/types.d.ts +2 -2
  17. package/build/@types/common/components/ui/formik/types.d.ts.map +1 -1
  18. package/build/@types/common/components/ui/utils.d.ts +1 -1
  19. package/build/@types/common/components/ui/utils.d.ts.map +1 -1
  20. package/build/@types/common/config/constants.d.ts.map +1 -1
  21. package/build/@types/common/config/docs_links.d.ts +1 -0
  22. package/build/@types/common/config/docs_links.d.ts.map +1 -1
  23. package/build/@types/common/index.d.ts +0 -3
  24. package/build/@types/common/index.d.ts.map +1 -1
  25. package/build/@types/ocm/components/clusterConfiguration/networkConfiguration/NetworkConfigurationForm.d.ts.map +1 -1
  26. package/build/@types/ocm/components/clusterConfiguration/operators/bundleSpecs.d.ts.map +1 -1
  27. package/build/@types/ocm/components/clusterDetail/ClusterDetailStatusMessages.d.ts.map +1 -1
  28. package/build/@types/ocm/components/clusterDetail/ClusterDetailsButtonGroup.d.ts +1 -0
  29. package/build/@types/ocm/components/clusterDetail/ClusterDetailsButtonGroup.d.ts.map +1 -1
  30. package/build/@types/ocm/components/clusterWizard/CredentialsDownload.d.ts +7 -0
  31. package/build/@types/ocm/components/clusterWizard/CredentialsDownload.d.ts.map +1 -0
  32. package/build/@types/ocm/components/clusterWizard/OperatorsBundle.d.ts.map +1 -1
  33. package/build/@types/ocm/components/clusterWizard/wizardTransition.d.ts +1 -1
  34. package/build/@types/ocm/components/clusterWizard/wizardTransition.d.ts.map +1 -1
  35. package/build/@types/ocm/components/clusters/ClustersListToolbar.d.ts.map +1 -1
  36. package/build/cjs/cim/components/InfraEnv/InfraTableToolbar.js +12 -12
  37. package/build/cjs/cim/components/InfraEnv/InfraTableToolbar.js.map +1 -1
  38. package/build/cjs/cim/components/helpers/toAssisted.js +3 -5
  39. package/build/cjs/cim/components/helpers/toAssisted.js.map +1 -1
  40. package/build/cjs/cim/components/helpers/versions.js +1 -1
  41. package/build/cjs/cim/components/helpers/versions.js.map +1 -1
  42. package/build/cjs/cim/index.js +1 -0
  43. package/build/cjs/cim/index.js.map +1 -1
  44. package/build/cjs/common/api/assisted-service/ClustersAPI.js +5 -0
  45. package/build/cjs/common/api/assisted-service/ClustersAPI.js.map +1 -1
  46. package/build/cjs/common/components/clusterDetail/ClusterCredentials.js +1 -1
  47. package/build/cjs/common/components/clusterDetail/ClusterCredentials.js.map +1 -1
  48. package/build/cjs/common/components/clusterWizard/ClusterWizardStep.js +2 -4
  49. package/build/cjs/common/components/clusterWizard/ClusterWizardStep.js.map +1 -1
  50. package/build/cjs/common/components/operators/operatorSpecs.js +2 -2
  51. package/build/cjs/common/components/operators/operatorSpecs.js.map +1 -1
  52. package/build/cjs/common/components/ui/ClusterEventsToolbar.js +18 -10
  53. package/build/cjs/common/components/ui/ClusterEventsToolbar.js.map +1 -1
  54. package/build/cjs/common/components/ui/WizardFooter.js +2 -2
  55. package/build/cjs/common/components/ui/WizardFooter.js.map +1 -1
  56. package/build/cjs/common/components/ui/WizardNavItem.js +2 -2
  57. package/build/cjs/common/components/ui/WizardNavItem.js.map +1 -1
  58. package/build/cjs/common/components/ui/formik/MultiSelectField.js +26 -28
  59. package/build/cjs/common/components/ui/formik/MultiSelectField.js.map +1 -1
  60. package/build/cjs/common/components/ui/utils.js +1 -1
  61. package/build/cjs/common/components/ui/utils.js.map +1 -1
  62. package/build/cjs/common/config/constants.js +1 -0
  63. package/build/cjs/common/config/constants.js.map +1 -1
  64. package/build/cjs/common/config/docs_links.js +2 -1
  65. package/build/cjs/common/config/docs_links.js.map +1 -1
  66. package/build/cjs/common/index.js +0 -3
  67. package/build/cjs/common/index.js.map +1 -1
  68. package/build/cjs/ocm/components/AddHosts/day2Wizard/Day2Wizard.js +2 -2
  69. package/build/cjs/ocm/components/AddHosts/day2Wizard/Day2Wizard.js.map +1 -1
  70. package/build/cjs/ocm/components/AddHosts/day2Wizard/Day2WizardNav.js +5 -5
  71. package/build/cjs/ocm/components/AddHosts/day2Wizard/Day2WizardNav.js.map +1 -1
  72. package/build/cjs/ocm/components/clusterConfiguration/networkConfiguration/NetworkConfigurationForm.js +23 -1
  73. package/build/cjs/ocm/components/clusterConfiguration/networkConfiguration/NetworkConfigurationForm.js.map +1 -1
  74. package/build/cjs/ocm/components/clusterConfiguration/operators/bundleSpecs.js +4 -18
  75. package/build/cjs/ocm/components/clusterConfiguration/operators/bundleSpecs.js.map +1 -1
  76. package/build/cjs/ocm/components/clusterDetail/ClusterDetailStatusMessages.js +16 -14
  77. package/build/cjs/ocm/components/clusterDetail/ClusterDetailStatusMessages.js.map +1 -1
  78. package/build/cjs/ocm/components/clusterDetail/ClusterDetailsButtonGroup.js +1 -0
  79. package/build/cjs/ocm/components/clusterDetail/ClusterDetailsButtonGroup.js.map +1 -1
  80. package/build/cjs/ocm/components/clusterWizard/ClusterWizard.js +3 -3
  81. package/build/cjs/ocm/components/clusterWizard/ClusterWizard.js.map +1 -1
  82. package/build/cjs/ocm/components/clusterWizard/ClusterWizardContextProvider.js +1 -1
  83. package/build/cjs/ocm/components/clusterWizard/ClusterWizardContextProvider.js.map +1 -1
  84. package/build/cjs/ocm/components/clusterWizard/ClusterWizardNavigation.js +5 -5
  85. package/build/cjs/ocm/components/clusterWizard/ClusterWizardNavigation.js.map +1 -1
  86. package/build/cjs/ocm/components/clusterWizard/CredentialsDownload.js +62 -0
  87. package/build/cjs/ocm/components/clusterWizard/CredentialsDownload.js.map +1 -0
  88. package/build/cjs/ocm/components/clusterWizard/OperatorsBundle.js +1 -1
  89. package/build/cjs/ocm/components/clusterWizard/OperatorsBundle.js.map +1 -1
  90. package/build/cjs/ocm/components/clusterWizard/constants.js +1 -1
  91. package/build/cjs/ocm/components/clusterWizard/constants.js.map +1 -1
  92. package/build/cjs/ocm/components/clusterWizard/wizardTransition.js +3 -3
  93. package/build/cjs/ocm/components/clusterWizard/wizardTransition.js.map +1 -1
  94. package/build/cjs/ocm/components/clusters/ClustersListToolbar.js +3 -3
  95. package/build/cjs/ocm/components/clusters/ClustersListToolbar.js.map +1 -1
  96. package/lib/cim/components/InfraEnv/InfraTableToolbar.tsx +40 -28
  97. package/lib/cim/components/helpers/toAssisted.ts +3 -4
  98. package/lib/cim/components/helpers/versions.ts +1 -1
  99. package/lib/cim/index.ts +1 -0
  100. package/lib/common/api/assisted-service/ClustersAPI.ts +6 -0
  101. package/lib/common/components/clusterDetail/ClusterCredentials.tsx +12 -2
  102. package/lib/common/components/clusterWizard/ClusterWizardStep.tsx +2 -4
  103. package/lib/common/components/operators/operatorSpecs.tsx +3 -2
  104. package/lib/common/components/ui/ClusterEventsToolbar.tsx +78 -45
  105. package/lib/common/components/ui/WizardFooter.tsx +3 -0
  106. package/lib/common/components/ui/WizardNavItem.tsx +1 -1
  107. package/lib/common/components/ui/formik/MultiSelectField.tsx +98 -58
  108. package/lib/common/components/ui/formik/types.ts +2 -2
  109. package/lib/common/components/ui/utils.ts +4 -2
  110. package/lib/common/config/constants.ts +1 -0
  111. package/lib/common/config/docs_links.ts +3 -0
  112. package/lib/common/index.ts +0 -4
  113. package/lib/ocm/components/AddHosts/day2Wizard/Day2Wizard.tsx +1 -1
  114. package/lib/ocm/components/AddHosts/day2Wizard/Day2WizardNav.tsx +5 -5
  115. package/lib/ocm/components/clusterConfiguration/networkConfiguration/NetworkConfigurationForm.tsx +29 -1
  116. package/lib/ocm/components/clusterConfiguration/operators/bundleSpecs.tsx +5 -25
  117. package/lib/ocm/components/clusterDetail/ClusterDetailStatusMessages.tsx +18 -19
  118. package/lib/ocm/components/clusterDetail/ClusterDetailsButtonGroup.tsx +2 -0
  119. package/lib/ocm/components/clusterWizard/ClusterWizard.tsx +3 -3
  120. package/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx +1 -1
  121. package/lib/ocm/components/clusterWizard/ClusterWizardNavigation.tsx +5 -5
  122. package/lib/ocm/components/clusterWizard/CredentialsDownload.tsx +105 -0
  123. package/lib/ocm/components/clusterWizard/OperatorsBundle.tsx +1 -0
  124. package/lib/ocm/components/clusterWizard/constants.ts +1 -1
  125. package/lib/ocm/components/clusterWizard/wizardTransition.ts +4 -4
  126. package/lib/ocm/components/clusters/ClustersListToolbar.tsx +39 -19
  127. package/package.json +3 -3
  128. /package/build/cjs/{common/styles.css → ocm/components/clusterDetail/ClusterDetailsButtonGroup.css} +0 -0
  129. /package/lib/{common/styles.css → ocm/components/clusterDetail/ClusterDetailsButtonGroup.css} +0 -0
@@ -1,19 +1,18 @@
1
1
  import {
2
2
  Badge,
3
+ Dropdown,
4
+ DropdownGroup,
5
+ DropdownItem,
3
6
  Grid,
4
7
  GridItem,
8
+ MenuToggle,
9
+ MenuToggleElement,
5
10
  SearchInput,
6
11
  Split,
7
12
  SplitItem,
8
13
  ToolbarFilter,
9
14
  ToolbarItem,
10
15
  } from '@patternfly/react-core';
11
- import {
12
- Select,
13
- SelectGroup,
14
- SelectOption,
15
- SelectVariant,
16
- } from '@patternfly/react-core/deprecated';
17
16
  import { FilterIcon } from '@patternfly/react-icons/dist/js/icons/filter-icon';
18
17
  import * as React from 'react';
19
18
  import { HostStatus, HostStatusDef } from '../../../common/components/hosts/types';
@@ -94,6 +93,19 @@ const InfraTableToolbar: React.FC<InfraTableToolbarProps> = ({
94
93
  const filterStatuses = React.useMemo(() => getStatusesForFilter(agentStatuses), [agentStatuses]);
95
94
  const categoryLabels = getCategoryLabels(t);
96
95
 
96
+ const onStatusFilterToggle = () => setStatusFilterOpen(!statusFilterOpen);
97
+ const onStatusFilterSelect = (
98
+ e?: React.MouseEvent<Element, MouseEvent>,
99
+ value?: string | number,
100
+ ) => {
101
+ if (!statusFilter?.includes(value as string)) {
102
+ const newItems = (statusFilter ? [...statusFilter, value] : [value]) as string[];
103
+ setStatusFilter(newItems);
104
+ } else {
105
+ setStatusFilter(statusFilter?.filter((f) => f !== value));
106
+ }
107
+ };
108
+
97
109
  return (
98
110
  <TableToolbar
99
111
  selectedIDs={selectedHostIDs || []}
@@ -118,34 +130,34 @@ const InfraTableToolbar: React.FC<InfraTableToolbarProps> = ({
118
130
  deleteChipGroup={() => setStatusFilter([])}
119
131
  categoryName="Status"
120
132
  >
121
- <Select
122
- variant={SelectVariant.checkbox}
123
- aria-label={t('ai:Status')}
124
- onToggle={(_event, value) => setStatusFilterOpen(value)}
125
- onSelect={(e, value) => {
126
- // eslint-disable-next-line
127
- if ((e.target as any).checked) {
128
- const newItems = (statusFilter ? [...statusFilter, value] : [value]) as string[];
129
- setStatusFilter(newItems);
130
- } else {
131
- setStatusFilter(statusFilter?.filter((f) => f !== value));
132
- }
133
- }}
134
- selections={statusFilter}
133
+ <Dropdown
135
134
  isOpen={statusFilterOpen}
136
- placeholderText={t('ai:Status')}
137
- toggleIcon={<FilterIcon />}
135
+ onSelect={onStatusFilterSelect}
136
+ onOpenChange={onStatusFilterToggle}
137
+ toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
138
+ <MenuToggle
139
+ ref={toggleRef}
140
+ isFullWidth
141
+ onClick={onStatusFilterToggle}
142
+ isExpanded={statusFilterOpen}
143
+ icon={<FilterIcon />}
144
+ >
145
+ {t('ai:Status')}
146
+ </MenuToggle>
147
+ )}
148
+ shouldFocusToggleOnSelect
138
149
  >
139
150
  {[
140
151
  <Grid key="statuses" hasGutter className="table-toolbar__dropdown">
141
152
  {Object.keys(filterStatuses).map((category) => (
142
153
  <GridItem key={category} span={4}>
143
- <SelectGroup label={categoryLabels[category as HostStatusDef['category']]}>
154
+ <DropdownGroup label={categoryLabels[category as HostStatusDef['category']]}>
144
155
  {Object.keys(filterStatuses[category]).map((label) => (
145
- <SelectOption
156
+ <DropdownItem
157
+ hasCheckbox
146
158
  key={label}
147
159
  value={label}
148
- isChecked={statusFilter?.includes(label)}
160
+ isSelected={statusFilter?.includes(label)}
149
161
  >
150
162
  <Split hasGutter>
151
163
  <SplitItem>{label}</SplitItem>
@@ -153,14 +165,14 @@ const InfraTableToolbar: React.FC<InfraTableToolbarProps> = ({
153
165
  <Badge isRead>{statusCount[label] || 0}</Badge>
154
166
  </SplitItem>
155
167
  </Split>
156
- </SelectOption>
168
+ </DropdownItem>
157
169
  ))}
158
- </SelectGroup>
170
+ </DropdownGroup>
159
171
  </GridItem>
160
172
  ))}
161
173
  </Grid>,
162
174
  ]}
163
- </Select>
175
+ </Dropdown>
164
176
  </ToolbarFilter>
165
177
  </ToolbarItem>
166
178
  </TableToolbar>
@@ -76,10 +76,9 @@ export const getAIHosts = (
76
76
  const restBmhs =
77
77
  infraEnv && bmhs
78
78
  ? bmhs
79
- ?.filter((h) =>
80
- h.metadata?.namespace === infraEnv.metadata?.namespace &&
81
- getInfraEnvNameOfAgent(h) === infraEnv.metadata?.name &&
82
- h.metadata?.name
79
+ ?.filter((h) => h.metadata?.namespace === infraEnv.metadata?.namespace)
80
+ .filter((h) =>
81
+ getInfraEnvNameOfAgent(h) === infraEnv.metadata?.name && h.metadata?.name
83
82
  ? !bmhAgents.includes(h.metadata.name)
84
83
  : true,
85
84
  )
@@ -11,7 +11,7 @@ import { OpenshiftVersion } from '@openshift-assisted/types/assisted-installer-s
11
11
  import { getMajorMinorVersion } from '../../../common/utils';
12
12
 
13
13
  export const getVersionFromReleaseImage = (releaseImage = '') => {
14
- const match = /.+:(.*)/gm.exec(releaseImage);
14
+ const match = /.+:(\d+\.\d+(?:\.\d+)?(?:-\w+)?)/gm.exec(releaseImage);
15
15
  if (match && match.length > 1 && match[1]) {
16
16
  return match[1];
17
17
  }
package/lib/cim/index.ts CHANGED
@@ -5,6 +5,7 @@ export * from './components';
5
5
  // re-export selected from common
6
6
  export * as Reducers from '../common/reducers';
7
7
  export * from '../common/api';
8
+ export * from '../common/utils';
8
9
  export * from '../common/types';
9
10
  export * from '../common/features';
10
11
 
@@ -239,6 +239,12 @@ const ClustersAPI = {
239
239
  params,
240
240
  );
241
241
  },
242
+
243
+ updateInstallConfig(clusterId: Cluster['id'], config: string) {
244
+ return client.patch(`${ClustersAPI.makeBaseURI(clusterId)}/install-config`, config, {
245
+ headers: { 'Content-Type': 'application/json' },
246
+ });
247
+ },
242
248
  };
243
249
 
244
250
  export default ClustersAPI;
@@ -1,5 +1,12 @@
1
1
  import React from 'react';
2
- import { GridItem, ClipboardCopy, clipboardCopyFunc, Button, Alert } from '@patternfly/react-core';
2
+ import {
3
+ GridItem,
4
+ ClipboardCopy,
5
+ clipboardCopyFunc,
6
+ Button,
7
+ Alert,
8
+ TextInput,
9
+ } from '@patternfly/react-core';
3
10
  import { Credentials, Cluster } from '@openshift-assisted/types/assisted-installer-service';
4
11
  import { LoadingState, ErrorState } from '../../components/ui/uiState';
5
12
  import { DetailList, DetailItem } from '../../components/ui/DetailList';
@@ -98,7 +105,10 @@ const ClusterCredentials: React.FC<ClusterCredentialsProps> = ({
98
105
  )}
99
106
  {credentials.username && (
100
107
  <>
101
- <DetailItem title="Username" value={credentials.username} />
108
+ <DetailItem
109
+ title={t('ai:Username')}
110
+ value={<TextInput value={credentials.username} readOnlyVariant="default" />}
111
+ />
102
112
  <DetailItem
103
113
  title={t('ai:Password')}
104
114
  value={
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { WizardBody, WizardBodyProps } from '@patternfly/react-core/deprecated';
2
+ import { WizardBody } from '@patternfly/react-core';
3
3
  import { css } from '@patternfly/react-styles';
4
4
 
5
5
  import './ClusterWizardStep.css';
@@ -14,13 +14,11 @@ export const ClusterWizardStep = ({
14
14
  footer,
15
15
  children,
16
16
  }: React.PropsWithChildren<ClusterWizardStepProps>) => {
17
- // activeStep is required, but we're not passing it. Using the cast to avoid TS error
18
- const activeStep = undefined as unknown as WizardBodyProps['activeStep'];
19
17
  return (
20
18
  <div className={css('pf-v5-c-wizard__outer-wrap', 'cluster-wizard-step')}>
21
19
  <div className={css('pf-v5-c-wizard__inner-wrap')}>
22
20
  {navigation}
23
- <WizardBody aria-labelledby="step-id" hasNoBodyPadding={false} activeStep={activeStep}>
21
+ <WizardBody aria-labelledby="step-id" hasNoPadding={false}>
24
22
  {children}
25
23
  </WizardBody>
26
24
  </div>
@@ -30,6 +30,7 @@ import {
30
30
  AUTHORINO_OPERATOR_LINK,
31
31
  CNV_LINK,
32
32
  FENCE_AGENTS_REMEDIATION_LINK,
33
+ getKubeDeschedulerLink,
33
34
  getLsoLink,
34
35
  getLvmsDocsLink,
35
36
  getMceDocsLink,
@@ -154,10 +155,10 @@ export const getOperatorSpecs = (useLVMS?: boolean): { [key: string]: OperatorSp
154
155
  [OPERATOR_NAME_KUBE_DESCHEDULER]: {
155
156
  title: 'Kube Descheduler',
156
157
  featureId: 'KUBE_DESCHEDULER',
157
- Description: () => (
158
+ Description: ({ openshiftVersion }) => (
158
159
  <>
159
160
  Evicts pods to reschedule them onto more suitable nodes.{' '}
160
- <ExternalLink href={NODE_HEALTHCHECK_LINK}>Learn more</ExternalLink>
161
+ <ExternalLink href={getKubeDeschedulerLink(openshiftVersion)}>Learn more</ExternalLink>
161
162
  </>
162
163
  ),
163
164
  notStandalone: true,
@@ -16,8 +16,12 @@ import {
16
16
  Badge,
17
17
  TextInputTypes,
18
18
  InputGroupItem,
19
+ Dropdown,
20
+ DropdownItem,
21
+ DropdownList,
22
+ MenuToggle,
23
+ MenuToggleElement,
19
24
  } from '@patternfly/react-core';
20
- import { Select, SelectOption, SelectProps } from '@patternfly/react-core/deprecated';
21
25
  import { SearchIcon } from '@patternfly/react-icons/dist/js/icons/search-icon';
22
26
  import { FilterIcon } from '@patternfly/react-icons/dist/js/icons/filter-icon';
23
27
  import type { ClusterEventsFiltersType } from '../../types';
@@ -137,7 +141,7 @@ const ClusterEventsToolbar = ({
137
141
  setFilters(nextFilters);
138
142
  };
139
143
 
140
- const onHostToggle: SelectProps['onToggle'] = () => setHostExpanded(!isHostExpanded);
144
+ const onHostToggle = () => setHostExpanded(!isHostExpanded);
141
145
  const onHostSelect = (value: string, isChecked: boolean) => {
142
146
  switch (value) {
143
147
  case DELETED_HOSTS:
@@ -157,7 +161,7 @@ const ClusterEventsToolbar = ({
157
161
  }
158
162
  };
159
163
 
160
- const onSeverityToggle: SelectProps['onToggle'] = () => setSeverityExpanded(!isSeverityExpanded);
164
+ const onSeverityToggle = () => setSeverityExpanded(!isSeverityExpanded);
161
165
  const onSeveritySelect = (value: string, isChecked: boolean) => {
162
166
  onSelect('severities', isChecked, value);
163
167
  };
@@ -227,41 +231,56 @@ const ClusterEventsToolbar = ({
227
231
  }
228
232
  deleteChipGroup={onDeleteChipGroup}
229
233
  >
230
- <Select
231
- variant="checkbox"
232
- aria-label="hosts"
233
- toggleId="cluster-events-hosts-dropdown-button"
234
- onToggle={onHostToggle}
235
- onSelect={(e, value) => onHostSelect(value as string, isSelectEventChecked(e))}
236
- selections={getSelections()}
237
- customBadgeText={hostChips.length}
234
+ <Dropdown
238
235
  isOpen={isHostExpanded}
239
- placeholderText={<Placeholder text="Hosts" />}
240
- maxHeight={280}
241
- zIndex={600}
236
+ onSelect={(e, value) => onHostSelect(value as string, isSelectEventChecked(e))}
237
+ onOpenChange={onHostToggle}
238
+ toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
239
+ <MenuToggle
240
+ id="cluster-events-hosts-dropdown-button"
241
+ ref={toggleRef}
242
+ isFullWidth
243
+ onClick={onHostToggle}
244
+ isExpanded={isHostExpanded}
245
+ badge={hostChips.length && <Badge isRead>{hostChips.length}</Badge>}
246
+ >
247
+ <Placeholder text="Hosts" />
248
+ </MenuToggle>
249
+ )}
250
+ shouldFocusToggleOnSelect
242
251
  >
243
- {[
244
- <SelectOption
245
- inputId={`checkbox-${CLUSTER_LEVEL}`}
252
+ <DropdownList>
253
+ <DropdownItem
254
+ id={`checkbox-${CLUSTER_LEVEL}`}
255
+ hasCheckbox
256
+ isSelected={getSelections().includes(CLUSTER_LEVEL)}
246
257
  key={CLUSTER_LEVEL}
247
258
  value={CLUSTER_LEVEL}
248
259
  >
249
260
  {t('ai:Cluster-level events')}
250
- </SelectOption>,
251
- <SelectOption
252
- inputId={`checkbox-${DELETED_HOSTS}`}
261
+ </DropdownItem>
262
+ <DropdownItem
263
+ id={`checkbox-${DELETED_HOSTS}`}
264
+ hasCheckbox
265
+ isSelected={getSelections().includes(DELETED_HOSTS)}
253
266
  key={DELETED_HOSTS}
254
267
  value={DELETED_HOSTS}
255
268
  >
256
269
  {t('ai:Deleted hosts')}
257
- </SelectOption>,
258
- ...sortedHosts.map((host) => (
259
- <SelectOption inputId={`checkbox-${host.id}`} key={host.id} value={host.id}>
270
+ </DropdownItem>
271
+ {...sortedHosts.map((host) => (
272
+ <DropdownItem
273
+ id={`checkbox-${host.id}`}
274
+ hasCheckbox
275
+ isSelected={getSelections().includes(host.id)}
276
+ key={host.id}
277
+ value={host.id}
278
+ >
260
279
  {host.hostname}
261
- </SelectOption>
262
- )),
263
- ]}
264
- </Select>
280
+ </DropdownItem>
281
+ ))}
282
+ </DropdownList>
283
+ </Dropdown>
265
284
  </CustomToolbarFilter>
266
285
  )}
267
286
 
@@ -278,28 +297,42 @@ const ClusterEventsToolbar = ({
278
297
  deleteChipGroup={onDeleteChipGroup}
279
298
  categoryName="Severity"
280
299
  >
281
- <Select
282
- variant="checkbox"
283
- aria-label="Severity"
284
- toggleId="cluster-events-severity-dropdown-button"
285
- onToggle={onSeverityToggle}
300
+ <Dropdown
301
+ isOpen={isSeverityExpanded}
286
302
  onSelect={(event, value) =>
287
303
  onSeveritySelect(value as string, isSelectEventChecked(event))
288
304
  }
289
- selections={filters.severities}
290
- isOpen={isSeverityExpanded}
291
- placeholderText={<Placeholder text="Severity" />}
292
- >
293
- {EVENT_SEVERITIES.map((severity) => (
294
- <SelectOption
295
- data-testid={`${severity}-filter-option`}
296
- key={severity}
297
- value={severity}
305
+ onOpenChange={onSeverityToggle}
306
+ toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
307
+ <MenuToggle
308
+ id="cluster-events-severity-dropdown-button"
309
+ ref={toggleRef}
310
+ isFullWidth
311
+ onClick={onSeverityToggle}
312
+ isExpanded={isSeverityExpanded}
313
+ badge={
314
+ filters.severities?.length && <Badge isRead>{filters.severities.length}</Badge>
315
+ }
298
316
  >
299
- {capitalize(severity)} <Badge isRead>{severityCounts[severity]}</Badge>
300
- </SelectOption>
301
- ))}
302
- </Select>
317
+ <Placeholder text="Severity" />
318
+ </MenuToggle>
319
+ )}
320
+ shouldFocusToggleOnSelect
321
+ >
322
+ <DropdownList>
323
+ {EVENT_SEVERITIES.map((severity) => (
324
+ <DropdownItem
325
+ data-testid={`${severity}-filter-option`}
326
+ hasCheckbox
327
+ isSelected={filters.severities?.includes(severity)}
328
+ key={severity}
329
+ value={severity}
330
+ >
331
+ {capitalize(severity)} <Badge isRead>{severityCounts[severity]}</Badge>
332
+ </DropdownItem>
333
+ ))}
334
+ </DropdownList>
335
+ </Dropdown>
303
336
  </ToolbarFilter>
304
337
 
305
338
  <ToolbarItem>
@@ -26,6 +26,7 @@ export type WizardFooterGenericProps = {
26
26
  isSubmitting?: boolean;
27
27
  submittingText?: string;
28
28
  nextButtonText?: string;
29
+ isNextButtonLoading?: boolean;
29
30
  };
30
31
 
31
32
  type WizardFooterProps = WizardFooterGenericProps & {
@@ -51,6 +52,7 @@ export const WizardFooter: React.FC<WizardFooterProps> = ({
51
52
  nextButtonText,
52
53
  cluster,
53
54
  onFetchEvents,
55
+ isNextButtonLoading,
54
56
  }) => {
55
57
  const { t } = useTranslation();
56
58
  submittingText = submittingText || t('ai:Saving changes...');
@@ -68,6 +70,7 @@ export const WizardFooter: React.FC<WizardFooterProps> = ({
68
70
  name="next"
69
71
  onClick={onNext}
70
72
  isDisabled={isNextDisabled}
73
+ isLoading={isNextButtonLoading}
71
74
  >
72
75
  {nextButtonText || t('ai:Next')}
73
76
  </Button>
@@ -1,5 +1,5 @@
1
1
  import React, { PropsWithChildren, ReactNode } from 'react';
2
- import { WizardNavItem, WizardNavItemProps } from '@patternfly/react-core/deprecated';
2
+ import { WizardNavItem, WizardNavItemProps } from '@patternfly/react-core';
3
3
  import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon';
4
4
  import UiIcon from './UiIcon';
5
5
 
@@ -1,19 +1,27 @@
1
1
  import React from 'react';
2
2
  import { useField } from 'formik';
3
- import Fuse from 'fuse.js';
4
- import { FormGroup, FormHelperText, HelperText, HelperTextItem } from '@patternfly/react-core';
5
3
  import {
6
- Select,
7
- SelectOption,
8
- SelectOptionObject,
9
- SelectOptionProps,
10
- SelectProps,
11
- SelectVariant,
12
- } from '@patternfly/react-core/deprecated';
13
- import { MultiSelectFieldProps, MultiSelectOption } from './types';
4
+ FormGroup,
5
+ FormHelperText,
6
+ HelperText,
7
+ HelperTextItem,
8
+ Dropdown,
9
+ DropdownItem,
10
+ DropdownList,
11
+ MenuToggle,
12
+ MenuToggleElement,
13
+ TextInputGroup,
14
+ TextInputGroupMain,
15
+ TextInputGroupUtilities,
16
+ Button,
17
+ LabelGroup,
18
+ Label,
19
+ } from '@patternfly/react-core';
20
+ import { MultiSelectFieldProps } from './types';
14
21
  import { getFieldId } from './utils';
15
- import { useTranslation } from '../../../hooks/use-translation-wrapper';
16
22
  import ExclamationCircleIcon from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon';
23
+ import TimesIcon from '@patternfly/react-icons/dist/js/icons/times-icon';
24
+ import { useTranslation } from '../../../hooks/use-translation-wrapper';
17
25
 
18
26
  // Field value is a string[]
19
27
  const MultiSelectField: React.FC<MultiSelectFieldProps> = ({
@@ -29,21 +37,24 @@ const MultiSelectField: React.FC<MultiSelectFieldProps> = ({
29
37
  ...props
30
38
  }) => {
31
39
  const [isOpen, setOpen] = React.useState(false);
40
+ const [textValue, setTextValue] = React.useState('');
32
41
  const [field, { touched, error }, { setValue }] = useField<string[]>(props.name);
33
42
  const fieldId = getFieldId(props.name, 'multiinput', idPostfix);
34
43
  const isValid = !(touched && error);
35
44
  const errorMessage = !isValid ? error : '';
36
45
  const hText = getHelperText ? getHelperText(field.value?.join(',') || '') : helperText;
46
+ const { t } = useTranslation();
37
47
 
38
48
  const onToggle = (isOpen: boolean) => setOpen(isOpen);
39
49
  const onClearSelection = () => {
40
50
  // onChange && onChange(event);
41
51
  setValue([]);
52
+ setTextValue('');
42
53
  onChange && onChange([]);
43
54
  setOpen(false);
44
55
  };
45
56
 
46
- const onSelect: SelectProps['onSelect'] = (event, selection) => {
57
+ const onSelect = (selection: string) => {
47
58
  // already selected
48
59
  const selected = field.value;
49
60
 
@@ -53,7 +64,7 @@ const MultiSelectField: React.FC<MultiSelectFieldProps> = ({
53
64
  selection as {
54
65
  /* TypeScript hack, debug hint: created as part of "selections" array below */ value?: string;
55
66
  }
56
- ).value || (selection as string);
67
+ ).value || selection;
57
68
 
58
69
  let newValue: string[];
59
70
  if (selected.includes(selectionValue)) {
@@ -65,61 +76,90 @@ const MultiSelectField: React.FC<MultiSelectFieldProps> = ({
65
76
  onChange && onChange(newValue);
66
77
  };
67
78
 
68
- // list of already selected options
69
- const selections: (SelectOptionObject | string)[] = field.value.map((value: string) => {
70
- const option = options.find((opt) => opt.value === value);
71
- return option
72
- ? {
73
- value: option.value,
74
- toString: () => option.displayName,
75
- compareTo: (selectOption: { value: string }) => selectOption.value === value,
76
- }
77
- : value;
79
+ const deleteChip = (chip: string) => {
80
+ const newValue = field.value.filter((val) => val !== chip);
81
+ setValue(newValue);
82
+ setTextValue('');
83
+ onChange && onChange(newValue);
84
+ };
85
+
86
+ const filteredOptions = options.filter((option) => {
87
+ const value = option.value as string;
88
+ return (
89
+ option.displayName.toLowerCase().includes(textValue.toLowerCase()) &&
90
+ !(field.value || []).includes(value.toString())
91
+ );
78
92
  });
79
93
 
80
- const children = options
81
- .filter((option) => !(field.value || []).includes(option.value.toString()))
82
- .map((option) => (
83
- <SelectOption key={option.id} id={option.id} value={option.value}>
94
+ const children = filteredOptions.map((option) => {
95
+ const value = option.value as string;
96
+ return (
97
+ <DropdownItem key={option.id} id={option.id} value={value}>
84
98
  {option.displayName}
85
- </SelectOption>
86
- ));
87
-
88
- const fuse = new Fuse(options, {
89
- ignoreLocation: true,
90
- keys: ['displayName'],
99
+ </DropdownItem>
100
+ );
91
101
  });
92
- const { t } = useTranslation();
93
102
 
94
103
  return (
95
104
  <FormGroup fieldId={fieldId} label={label} isRequired={isRequired} labelIcon={labelIcon}>
96
- <Select
97
- {...field}
98
- {...props}
105
+ <Dropdown
99
106
  id={fieldId}
100
- variant={SelectVariant.typeaheadMulti}
101
- typeAheadAriaLabel={t('ai:Select a state')}
102
- validated={isValid ? 'default' : 'error'}
103
- aria-describedby={`${fieldId}-helper`}
104
- isCreatable={false}
105
- placeholderText={placeholderText}
106
107
  isOpen={isOpen}
107
- onToggle={(_event, isOpen: boolean) => onToggle(isOpen)}
108
- onSelect={onSelect}
109
- onClear={onClearSelection}
110
- selections={selections}
111
- onFilter={(e, val) => {
112
- if (!val || val === '') {
113
- return children;
114
- }
115
- const results = fuse.search<MultiSelectOption>(val).map((result) => result.item.id);
116
- return (
117
- React.Children.toArray(children) as React.ReactElement<SelectOptionProps>[]
118
- ).filter(({ props }) => results.includes(props.id as string));
119
- }}
108
+ onSelect={(event, value) => onSelect(value as string)}
109
+ onOpenChange={onToggle}
110
+ toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
111
+ <MenuToggle
112
+ ref={toggleRef}
113
+ isFullWidth
114
+ onClick={() => onToggle(!isOpen)}
115
+ isExpanded={isOpen}
116
+ variant="typeahead"
117
+ >
118
+ <TextInputGroup>
119
+ <LabelGroup>
120
+ {field.value.map((currentChip) => (
121
+ <Label
122
+ key={currentChip}
123
+ variant="outline"
124
+ onClose={() => deleteChip(currentChip)}
125
+ >
126
+ {currentChip}
127
+ </Label>
128
+ ))}
129
+ </LabelGroup>
130
+ <TextInputGroupMain
131
+ placeholder={placeholderText}
132
+ onClick={() => onToggle(!isOpen)}
133
+ value={textValue}
134
+ onChange={(event, value) => setTextValue(value)}
135
+ />
136
+ <TextInputGroupUtilities>
137
+ <Button
138
+ variant="plain"
139
+ onClick={onClearSelection}
140
+ aria-label="Clear button and input"
141
+ icon={<TimesIcon />}
142
+ />
143
+ </TextInputGroupUtilities>
144
+ </TextInputGroup>
145
+ </MenuToggle>
146
+ )}
147
+ shouldFocusToggleOnSelect
120
148
  >
121
- {children}
122
- </Select>
149
+ <DropdownList>
150
+ {children.length ? (
151
+ children
152
+ ) : (
153
+ <DropdownItem
154
+ key="No options available"
155
+ id="No options available"
156
+ value="No options available"
157
+ >
158
+ {t('ai:No options available')}
159
+ </DropdownItem>
160
+ )}
161
+ </DropdownList>
162
+ </Dropdown>
123
163
  {(errorMessage || hText) && (
124
164
  <FormHelperText>
125
165
  <HelperText>
@@ -7,7 +7,7 @@ import {
7
7
  FormSelectProps,
8
8
  DropEvent,
9
9
  } from '@patternfly/react-core';
10
- import { SelectOptionProps } from '@patternfly/react-core/deprecated';
10
+ import { DropdownItemProps } from '@patternfly/react-core';
11
11
  import { FieldValidator, FieldHelperProps } from 'formik';
12
12
  import { DropzoneProps } from 'react-dropzone';
13
13
  import { CodeEditorProps } from '@patternfly/react-code-editor';
@@ -44,7 +44,7 @@ export interface SelectFieldProps extends FieldProps {
44
44
  // onBlur?: (event: React.FormEvent<HTMLSelectElement>) => void;
45
45
  }
46
46
 
47
- export type MultiSelectOption = SelectOptionProps & { id: string; displayName: string };
47
+ export type MultiSelectOption = DropdownItemProps & { id: string; displayName: string };
48
48
 
49
49
  export interface MultiSelectFieldProps extends FieldProps {
50
50
  options: MultiSelectOption[];