Skip to content

Commit 4ba1ef0

Browse files
committed
CONSOLE-4447: Migrate core modals to PatternFly v6
1 parent a5cc161 commit 4ba1ef0

11 files changed

Lines changed: 786 additions & 605 deletions
Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
11
import { useCallback } from 'react';
2-
import type { ModalComponent } from '@console/dynamic-plugin-sdk/src/app/modal-support/ModalProvider';
3-
import { useModal } from '@console/dynamic-plugin-sdk/src/app/modal-support/useModal';
2+
import { useOverlay } from '@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay';
43
import type { UseLabelsModal } from '@console/dynamic-plugin-sdk/src/extensions/console-types';
54
import { useK8sModel } from '@console/dynamic-plugin-sdk/src/utils/k8s/hooks/useK8sModel';
65
import { getGroupVersionKindForResource } from '@console/dynamic-plugin-sdk/src/utils/k8s/k8s-ref';
7-
import { ModalWrapper } from '@console/internal/components/factory/modal';
86
import type { LabelsModalProps } from '@console/internal/components/modals/labels-modal';
9-
import { LabelsModal } from '@console/internal/components/modals/labels-modal';
10-
11-
const LabelsModalComponent: ModalComponent<LabelsModalProps> = ({ closeModal, kind, resource }) => {
12-
return (
13-
<ModalWrapper blocking onClose={closeModal}>
14-
<LabelsModal cancel={closeModal} close={closeModal} kind={kind} resource={resource} />
15-
</ModalWrapper>
16-
);
17-
};
7+
import { LabelsModalOverlay } from '@console/internal/components/modals/labels-modal';
188

199
export const useLabelsModal: UseLabelsModal = (resource) => {
20-
const launcher = useModal();
10+
const launchModal = useOverlay();
2111
const groupVersionKind = getGroupVersionKindForResource(resource);
2212
const [kind] = useK8sModel(groupVersionKind);
2313
return useCallback(
24-
() => resource && kind && launcher<LabelsModalProps>(LabelsModalComponent, { kind, resource }),
25-
[launcher, kind, resource],
14+
() => resource && kind && launchModal<LabelsModalProps>(LabelsModalOverlay, { kind, resource }),
15+
[launchModal, kind, resource],
2616
);
2717
};

frontend/public/components/modals/add-secret-to-workload.tsx

Lines changed: 138 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,22 @@ import { useState, useEffect, useCallback } from 'react';
33
import * as _ from 'lodash';
44
import * as fuzzy from 'fuzzysearch';
55
import { useNavigate } from 'react-router-dom-v5-compat';
6-
import { FormGroup, Radio } from '@patternfly/react-core';
6+
import {
7+
Button,
8+
Content,
9+
ContentVariants,
10+
Form,
11+
FormGroup,
12+
Modal,
13+
ModalBody,
14+
ModalHeader,
15+
ModalVariant,
16+
Radio,
17+
TextInput,
18+
} from '@patternfly/react-core';
719

820
import { K8sKind, k8sList, k8sPatch, K8sResourceKind } from '../../module/k8s';
921
import { DeploymentModel, DeploymentConfigModel, StatefulSetModel } from '../../models';
10-
import { ModalTitle, ModalBody, ModalSubmitFooter, ModalWrapper } from '../factory/modal';
1122
import { ConsoleSelect } from '@console/internal/components/utils/console-select';
1223
import { ResourceIcon, ResourceName } from '../utils/resource-icon';
1324
import { resourcePathFromModel } from '../utils/resource-link';
@@ -16,6 +27,8 @@ import { Trans, useTranslation } from 'react-i18next';
1627
import { useOverlay } from '@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay';
1728
import { OverlayComponent } from '@console/dynamic-plugin-sdk/src/app/modal-support/OverlayProvider';
1829
import { ModalCallback } from './types';
30+
import type { ModalComponentProps } from '../factory';
31+
import { ModalFooterWithAlerts } from '@console/shared/src/components/modals/ModalFooterWithAlerts';
1932

2033
const workloadResourceModels = [DeploymentModel, DeploymentConfigModel, StatefulSetModel];
2134
const getContainers = (workload: K8sResourceKind) =>
@@ -167,132 +180,145 @@ export const AddSecretToWorkloadModal: FC<AddSecretToWorkloadModalProps> = (prop
167180
const selectWorkloadPlaceholder = t('public~Select a workload');
168181

169182
return (
170-
<form
171-
onSubmit={submit}
172-
name="co-add-secret-to-workload"
173-
className="co-add-secret-to-workload modal-content"
174-
>
175-
<ModalTitle>{t('public~Add secret to workload')}</ModalTitle>
183+
<>
184+
<ModalHeader title={t('public~Add secret to workload')} data-test-id="modal-title" />
176185
<ModalBody>
177-
<p className="modal-paragraph">
186+
<Content component={ContentVariants.p}>
178187
<Trans t={t} ns="public">
179188
Add all values from <ResourceIcon kind="Secret" />
180189
{{ secretName }} to a workload as environment variables or a volume.
181190
</Trans>
182-
</p>
183-
<div className="form-group">
184-
<label className="co-required" htmlFor="co-add-secret-to-workload__workload">
185-
{t('public~Add this secret to workload')}
186-
</label>
187-
<ConsoleSelect
188-
items={workloadOptions}
189-
selectedKey={selectedWorkloadUID}
190-
title={selectWorkloadPlaceholder}
191-
onChange={onWorkloadChange}
192-
autocompleteFilter={autocompleteFilter}
193-
autocompletePlaceholder={selectWorkloadPlaceholder}
194-
id="co-add-secret-to-workload__workload"
195-
data-test="add-secret-to-workload-button"
196-
/>
197-
</div>
198-
<fieldset>
199-
<legend className="co-legend co-required">{t('public~Add secret as')}</legend>
200-
<div className="pf-v6-c-form">
201-
<FormGroup
202-
role="radiogroup"
203-
fieldId="co-add-secret-to-workload"
204-
isStack
205-
className="form-group"
206-
>
207-
<Radio
208-
id="co-add-secret-to-workload__envvars"
209-
name="co-add-secret-to-workload__add-as"
210-
label={t('public~Environment variables')}
211-
value="environment"
212-
onChange={onAddAsChange}
213-
isChecked={addAsEnvironment}
214-
data-test="Environment variables-radio-input"
215-
data-checked-state={addAsEnvironment}
216-
/>
217-
{addAsEnvironment && (
218-
<div className="co-m-radio-desc">
219-
<div className="form-group">
220-
<label htmlFor="co-add-secret-to-workload__prefix">{t('public~Prefix')}</label>
221-
<span className="pf-v6-c-form-control">
222-
<input
223-
name="prefix"
224-
id="co-add-secret-to-workload__prefix"
225-
data-test="add-secret-to-workload-prefix"
226-
placeholder="(optional)"
227-
type="text"
228-
onChange={(e) => setPrefix(e.currentTarget.value)}
229-
/>
230-
</span>
231-
</div>
232-
</div>
233-
)}
234-
<Radio
235-
id="co-add-secret-to-workload__volume"
236-
name="co-add-secret-to-workload__add-as"
237-
label={t('public~Volume')}
238-
value="volume"
239-
onChange={onAddAsChange}
240-
isChecked={addAsVolume}
241-
data-test="Volume-radio-input"
242-
data-checked-state={addAsVolume}
243-
/>
244-
{addAsVolume && (
245-
<div className="co-m-radio-desc">
246-
<div className="form-group">
247-
<label htmlFor="co-add-secret-to-workload__mountpath" className="co-required">
248-
{t('public~Mount path')}
249-
</label>
250-
<span className="pf-v6-c-form-control">
251-
<input
252-
name="mountPath"
253-
id="co-add-secret-to-workload__mountpath"
254-
data-test="add-secret-to-workload-mountpath"
255-
type="text"
256-
onChange={(e) => setMountPath(e.currentTarget.value)}
257-
required
258-
/>
259-
</span>
260-
</div>
261-
</div>
262-
)}
263-
</FormGroup>
264-
</div>
265-
</fieldset>
191+
</Content>
192+
<Form id="co-add-secret-to-workload" onSubmit={submit}>
193+
<FormGroup
194+
label={t('public~Add this secret to workload')}
195+
isRequired
196+
fieldId="co-add-secret-to-workload__workload"
197+
>
198+
<ConsoleSelect
199+
items={workloadOptions}
200+
selectedKey={selectedWorkloadUID}
201+
title={selectWorkloadPlaceholder}
202+
onChange={onWorkloadChange}
203+
autocompleteFilter={autocompleteFilter}
204+
autocompletePlaceholder={selectWorkloadPlaceholder}
205+
id="co-add-secret-to-workload__workload"
206+
data-test="add-secret-to-workload-button"
207+
/>
208+
</FormGroup>
209+
<FormGroup
210+
label={t('public~Add secret as')}
211+
isRequired
212+
role="radiogroup"
213+
fieldId="co-add-secret-to-workload"
214+
isStack
215+
>
216+
<Radio
217+
id="co-add-secret-to-workload__envvars"
218+
name="co-add-secret-to-workload__add-as"
219+
label={t('public~Environment variables')}
220+
value="environment"
221+
onChange={onAddAsChange}
222+
isChecked={addAsEnvironment}
223+
data-test="Environment variables-radio-input"
224+
data-checked-state={addAsEnvironment}
225+
body={
226+
addAsEnvironment && (
227+
<FormGroup label={t('public~Prefix')} fieldId="co-add-secret-to-workload__prefix">
228+
<TextInput
229+
name="prefix"
230+
id="co-add-secret-to-workload__prefix"
231+
data-test="add-secret-to-workload-prefix"
232+
placeholder="(optional)"
233+
type="text"
234+
value={prefix}
235+
onChange={(_event, value) => setPrefix(value)}
236+
/>
237+
</FormGroup>
238+
)
239+
}
240+
/>
241+
<Radio
242+
id="co-add-secret-to-workload__volume"
243+
name="co-add-secret-to-workload__add-as"
244+
label={t('public~Volume')}
245+
value="volume"
246+
onChange={onAddAsChange}
247+
isChecked={addAsVolume}
248+
data-test="Volume-radio-input"
249+
data-checked-state={addAsVolume}
250+
body={
251+
addAsVolume && (
252+
<FormGroup
253+
label={t('public~Mount path')}
254+
isRequired
255+
fieldId="co-add-secret-to-workload__mountpath"
256+
>
257+
<TextInput
258+
name="mountPath"
259+
id="co-add-secret-to-workload__mountpath"
260+
data-test="add-secret-to-workload-mountpath"
261+
type="text"
262+
value={mountPath}
263+
onChange={(_event, value) => setMountPath(value)}
264+
isRequired
265+
/>
266+
</FormGroup>
267+
)
268+
}
269+
/>
270+
</FormGroup>
271+
</Form>
266272
</ModalBody>
267-
<ModalSubmitFooter
268-
errorMessage={errorMessage}
269-
inProgress={inProgress}
270-
submitText={t('public~Save')}
271-
cancel={props.cancel}
272-
/>
273-
</form>
273+
<ModalFooterWithAlerts errorMessage={errorMessage}>
274+
<Button
275+
type="submit"
276+
variant="primary"
277+
onClick={submit}
278+
isLoading={inProgress}
279+
isDisabled={inProgress}
280+
data-test="confirm-action"
281+
id="confirm-action"
282+
form="co-add-secret-to-workload"
283+
>
284+
{t('public~Save')}
285+
</Button>
286+
<Button variant="link" onClick={props.cancel} data-test-id="modal-cancel-action">
287+
{t('public~Cancel')}
288+
</Button>
289+
</ModalFooterWithAlerts>
290+
</>
274291
);
275292
};
276293

277-
export const AddSecretToWorkloadModalProvider: OverlayComponent<AddSecretToWorkloadModalProps> = (
294+
export const AddSecretToWorkloadModalOverlay: OverlayComponent<AddSecretToWorkloadModalProps> = (
278295
props,
279296
) => {
280-
return (
281-
<ModalWrapper blocking onClose={props.closeOverlay}>
282-
<AddSecretToWorkloadModal close={props.closeOverlay} cancel={props.closeOverlay} {...props} />
283-
</ModalWrapper>
284-
);
297+
const [isOpen, setIsOpen] = useState(true);
298+
const handleClose = () => {
299+
setIsOpen(false);
300+
props.closeOverlay();
301+
};
302+
303+
return isOpen ? (
304+
<Modal variant={ModalVariant.small} isOpen onClose={handleClose}>
305+
<AddSecretToWorkloadModal close={handleClose} cancel={handleClose} {...props} />
306+
</Modal>
307+
) : null;
285308
};
286309

287310
export const useAddSecretToWorkloadModalLauncher = (
288311
props: AddSecretToWorkloadModalProps,
289312
): ModalCallback => {
290-
const launcher = useOverlay();
313+
const launchModal = useOverlay();
291314

292-
return useCallback(
293-
() => launcher<AddSecretToWorkloadModalProps>(AddSecretToWorkloadModalProvider, props),
294-
[launcher, props],
295-
);
315+
return useCallback(() => {
316+
// Move focus away from the triggering element to prevent aria-hidden warning
317+
if (document.activeElement instanceof HTMLElement) {
318+
document.activeElement.blur();
319+
}
320+
return launchModal<AddSecretToWorkloadModalProps>(AddSecretToWorkloadModalOverlay, props);
321+
}, [launchModal, props]);
296322
};
297323

298324
type WorkloadItem = {
@@ -303,10 +329,7 @@ type WorkloadItem = {
303329
export type AddSecretToWorkloadModalProps = {
304330
secretName: string;
305331
namespace: string;
306-
cancel?: () => void;
307-
close?: () => void;
308-
blocking?: boolean;
309-
};
332+
} & ModalComponentProps;
310333

311334
export type AddSecretToWorkloadModalState = {
312335
inProgress: boolean;

0 commit comments

Comments
 (0)