Skip to content

Commit faaa515

Browse files
authored
ECOPROJECT-4164 | fix: prevent modal unmounting when creating environment from empty state in Environments table (#468)
The issue was that the DiscoverySourceSetupModal modal was rendered inside the EmptyState component, which is unmounted when the first source is created. This caused the modal to unmount before step 2 (downloading the OVA) could be displayed. https://github.com/user-attachments/assets/e873395c-0abc-4842-afd4-64c865a53b6c <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added visual feedback during OVA downloads * Source list automatically refreshes after environment setup completes * Actions disabled during OVA downloads to prevent user conflicts <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Montse Ortega <mortegag@redhat.com>
1 parent 734e958 commit faaa515

2 files changed

Lines changed: 50 additions & 38 deletions

File tree

src/ui/environment/views/EmptyState.tsx

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,27 @@ import {
88
StackItem,
99
} from "@patternfly/react-core";
1010
import { ExclamationCircleIcon, SearchIcon } from "@patternfly/react-icons";
11-
import React, { useCallback, useState } from "react";
11+
import React, { useCallback } from "react";
1212

1313
import { useEnvironmentPage } from "../view-models/EnvironmentPageContext";
14-
import { DiscoverySourceSetupModal } from "./DiscoverySourceSetupModal";
1514

16-
export const EmptyState: React.FC = () => {
17-
const vm = useEnvironmentPage();
18-
19-
const [
20-
shouldShowDiscoverySourceSetupModal,
21-
setShouldShowDiscoverySetupModal,
22-
] = useState(false);
15+
export interface EmptyStateProps {
16+
onAddEnvironment: () => void;
17+
isOvaDownloading: boolean;
18+
}
2319

24-
const toggleDiscoverySourceSetupModal = useCallback((): void => {
25-
setShouldShowDiscoverySetupModal((lastState) => {
26-
if (lastState === true) {
27-
void vm.listSources();
28-
}
29-
return !lastState;
30-
});
31-
}, [vm]);
20+
export const EmptyState: React.FC<EmptyStateProps> = ({
21+
onAddEnvironment,
22+
isOvaDownloading,
23+
}) => {
24+
const vm = useEnvironmentPage();
3225

3326
const handleTryAgain = useCallback(() => {
3427
if (!vm.isLoadingSources) {
3528
void vm.listSources();
3629
}
3730
}, [vm]);
3831

39-
const [isOvaDownloading, setIsOvaDownloading] = useState(false);
40-
4132
let emptyStateNode: React.ReactNode = (
4233
<PFEmptyState
4334
headingLevel="h4"
@@ -52,7 +43,7 @@ export const EmptyState: React.FC = () => {
5243

5344
<EmptyStateFooter>
5445
<EmptyStateActions>
55-
<Button variant="secondary" onClick={toggleDiscoverySourceSetupModal}>
46+
<Button variant="secondary" onClick={onAddEnvironment}>
5647
Add environment
5748
</Button>
5849
</EmptyStateActions>
@@ -90,22 +81,7 @@ export const EmptyState: React.FC = () => {
9081
);
9182
}
9283

93-
return (
94-
<>
95-
{emptyStateNode}
96-
{shouldShowDiscoverySourceSetupModal && (
97-
<DiscoverySourceSetupModal
98-
isOpen={shouldShowDiscoverySourceSetupModal}
99-
onClose={toggleDiscoverySourceSetupModal}
100-
isDisabled={vm.isDownloadingSource}
101-
onStartDownload={() => setIsOvaDownloading(true)}
102-
onAfterDownload={async () => {
103-
await vm.listSources();
104-
}}
105-
/>
106-
)}
107-
</>
108-
);
84+
return emptyStateNode;
10985
};
11086

11187
EmptyState.displayName = "SourcesTableEmptyState";

src/ui/environment/views/SourcesTable.tsx

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ import {
1111
} from "@patternfly/react-core";
1212
import { ArrowLeftIcon, EllipsisVIcon } from "@patternfly/react-icons";
1313
import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table";
14-
import React, { useEffect, useMemo, useRef, useState } from "react";
14+
import React, {
15+
useCallback,
16+
useEffect,
17+
useMemo,
18+
useRef,
19+
useState,
20+
} from "react";
1521
import { useNavigate } from "react-router-dom";
1622

1723
import type { SourceModel } from "../../../models/SourceModel";
@@ -20,6 +26,7 @@ import { ConfirmationModal } from "../../core/components/ConfirmationModal";
2026
import { useEnvironmentPage } from "../view-models/EnvironmentPageContext";
2127
import { AgentStatusView } from "./AgentStatusView";
2228
import { Columns } from "./Columns";
29+
import { DiscoverySourceSetupModal } from "./DiscoverySourceSetupModal";
2330
import { EmptyState } from "./EmptyState";
2431

2532
const VALUE_NOT_AVAILABLE = "-";
@@ -95,6 +102,20 @@ export const SourcesTable: React.FC<SourceTableProps> = ({
95102
{},
96103
);
97104
const [deleteTarget, setDeleteTarget] = useState<SourceModel | null>(null);
105+
const [
106+
shouldShowDiscoverySourceSetupModal,
107+
setShouldShowDiscoverySetupModal,
108+
] = useState(false);
109+
const [isOvaDownloading, setIsOvaDownloading] = useState(false);
110+
111+
const toggleDiscoverySourceSetupModal = useCallback((): void => {
112+
setShouldShowDiscoverySetupModal((lastState) => {
113+
if (lastState === true) {
114+
void vm.listSources();
115+
}
116+
return !lastState;
117+
});
118+
}, [vm]);
98119

99120
// Memorize ordered sources without mutating context sources
100121
const memoizedSources = useMemo(() => {
@@ -512,7 +533,10 @@ export const SourcesTable: React.FC<SourceTableProps> = ({
512533
) : (
513534
<Tr>
514535
<Td colSpan={12}>
515-
<EmptyState />
536+
<EmptyState
537+
onAddEnvironment={toggleDiscoverySourceSetupModal}
538+
isOvaDownloading={isOvaDownloading}
539+
/>
516540
</Td>
517541
</Tr>
518542
)}
@@ -540,6 +564,18 @@ export const SourcesTable: React.FC<SourceTableProps> = ({
540564
To use it again, create a new discovery image and redeploy it.
541565
</ConfirmationModal>
542566
)}
567+
568+
{shouldShowDiscoverySourceSetupModal && (
569+
<DiscoverySourceSetupModal
570+
isOpen={shouldShowDiscoverySourceSetupModal}
571+
onClose={toggleDiscoverySourceSetupModal}
572+
isDisabled={vm.isDownloadingSource}
573+
onStartDownload={() => setIsOvaDownloading(true)}
574+
onAfterDownload={async () => {
575+
await vm.listSources();
576+
}}
577+
/>
578+
)}
543579
</div>
544580
);
545581
}

0 commit comments

Comments
 (0)