Skip to content

Commit c057c95

Browse files
fix window.open in onClick popup problem (#586)
1 parent 35dbb10 commit c057c95

File tree

2 files changed

+100
-83
lines changed

2 files changed

+100
-83
lines changed

src/components/directory-content-dialog.tsx

Lines changed: 94 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@
66
*/
77

88
import type { UUID } from 'crypto';
9-
import { type Dispatch, type SetStateAction, useCallback, useEffect, useState } from 'react';
9+
import {
10+
type Dispatch,
11+
ForwardedRef,
12+
forwardRef,
13+
type SetStateAction,
14+
useCallback,
15+
useImperativeHandle,
16+
useState,
17+
} from 'react';
1018
import {
1119
DescriptionModificationDialog,
1220
type ElementAttributes,
@@ -32,9 +40,11 @@ import { useParameterState } from './dialogs/use-parameters-dialog';
3240
import { PARAM_LANGUAGE } from '../utils/config-params';
3341
import type { useDirectoryContent } from '../hooks/useDirectoryContent';
3442

43+
export type DirectoryContentDialogApi = {
44+
handleClick: (event: CellClickedEvent) => void;
45+
};
46+
3547
export type DirectoryContentDialogProps = {
36-
cellClicked?: CellClickedEvent;
37-
setCellClicked: Dispatch<SetStateAction<CellClickedEvent | undefined>>;
3848
broadcastChannel: BroadcastChannel;
3949
setOpenDialog: Dispatch<SetStateAction<string>>;
4050
activeElement?: ElementAttributes;
@@ -43,16 +53,17 @@ export type DirectoryContentDialogProps = {
4353
childrenMetadata: ReturnType<typeof useDirectoryContent>[1];
4454
};
4555

46-
export default function DirectoryContentDialog({
47-
cellClicked: event,
48-
setCellClicked: setEvent,
49-
setOpenDialog,
50-
activeElement,
51-
setActiveElement,
52-
broadcastChannel,
53-
selectedDirectoryElementUuid,
54-
childrenMetadata,
55-
}: Readonly<DirectoryContentDialogProps>) {
56+
function DirectoryContentDialog(
57+
{
58+
setOpenDialog,
59+
activeElement,
60+
setActiveElement,
61+
broadcastChannel,
62+
selectedDirectoryElementUuid,
63+
childrenMetadata,
64+
}: Readonly<DirectoryContentDialogProps>,
65+
refApi: ForwardedRef<DirectoryContentDialogApi>
66+
) {
5667
const intl = useIntl();
5768
const dispatch = useDispatch();
5869
const { snackError } = useSnackMessage();
@@ -137,73 +148,77 @@ export default function DirectoryContentDialog({
137148
setElementName('');
138149
}, [setActiveElement, setOpenDialog]);
139150

140-
useEffect(() => {
141-
if (event !== undefined) {
142-
if (event.colDef.field === 'description') {
143-
setActiveElement(event.data);
144-
setOpenDescModificationDialog(true);
145-
} else if (childrenMetadata[event.data.elementUuid] !== undefined) {
146-
setElementName(childrenMetadata[event.data.elementUuid].elementName);
147-
const subtype = childrenMetadata[event.data.elementUuid].specificMetadata.type as unknown as string;
148-
/** set active directory on the store because it will be used while editing the contingency name */
149-
dispatch(setActiveDirectory(selectedDirectoryElementUuid));
150-
switch (event.data.type) {
151-
case ElementType.STUDY: {
152-
const url = getStudyUrl(event.data.elementUuid);
153-
if (url) {
154-
window.open(url, '_blank');
155-
} else {
156-
snackError({
157-
messageTxt: intl.formatMessage({ id: 'getAppLinkError' }, { type: event.data.type }),
158-
});
151+
useImperativeHandle(
152+
refApi,
153+
() => ({
154+
handleClick: (event: CellClickedEvent) => {
155+
if (event.colDef.field === 'description') {
156+
setActiveElement(event.data);
157+
setOpenDescModificationDialog(true);
158+
} else if (childrenMetadata[event.data.elementUuid] !== undefined) {
159+
setElementName(childrenMetadata[event.data.elementUuid].elementName);
160+
const subtype = childrenMetadata[event.data.elementUuid].specificMetadata.type as unknown as string;
161+
/** set active directory on the store because it will be used while editing the contingency name */
162+
dispatch(setActiveDirectory(selectedDirectoryElementUuid));
163+
switch (event.data.type) {
164+
case ElementType.STUDY: {
165+
const url = getStudyUrl(event.data.elementUuid);
166+
if (url) {
167+
window.open(url, '_blank');
168+
} else {
169+
snackError({
170+
messageTxt: intl.formatMessage(
171+
{ id: 'getAppLinkError' },
172+
{ type: event.data.type }
173+
),
174+
});
175+
}
176+
break;
159177
}
160-
break;
178+
case ElementType.CONTINGENCY_LIST:
179+
if (subtype === ContingencyListType.CRITERIA_BASED.id) {
180+
setCurrentFiltersContingencyListId(event.data.elementUuid);
181+
setOpenDialog(subtype);
182+
} else if (subtype === ContingencyListType.SCRIPT.id) {
183+
setCurrentScriptContingencyListId(event.data.elementUuid);
184+
setOpenDialog(subtype);
185+
} else if (subtype === ContingencyListType.EXPLICIT_NAMING.id) {
186+
setCurrentExplicitNamingContingencyListId(event.data.elementUuid);
187+
setOpenDialog(subtype);
188+
}
189+
break;
190+
case ElementType.FILTER:
191+
if (subtype === FilterType.EXPLICIT_NAMING.id) {
192+
setCurrentExplicitNamingFilterId(event.data.elementUuid);
193+
setOpenDialog(subtype);
194+
} else if (subtype === FilterType.EXPERT.id) {
195+
setCurrentExpertFilterId(event.data.elementUuid);
196+
setOpenDialog(subtype);
197+
}
198+
break;
199+
case ElementType.MODIFICATION:
200+
if (subtype === NetworkModificationType.COMPOSITE.id) {
201+
setCurrentNetworkModificationId(event.data.elementUuid);
202+
setOpenDialog(subtype);
203+
}
204+
break;
205+
default:
206+
break;
161207
}
162-
case ElementType.CONTINGENCY_LIST:
163-
if (subtype === ContingencyListType.CRITERIA_BASED.id) {
164-
setCurrentFiltersContingencyListId(event.data.elementUuid);
165-
setOpenDialog(subtype);
166-
} else if (subtype === ContingencyListType.SCRIPT.id) {
167-
setCurrentScriptContingencyListId(event.data.elementUuid);
168-
setOpenDialog(subtype);
169-
} else if (subtype === ContingencyListType.EXPLICIT_NAMING.id) {
170-
setCurrentExplicitNamingContingencyListId(event.data.elementUuid);
171-
setOpenDialog(subtype);
172-
}
173-
break;
174-
case ElementType.FILTER:
175-
if (subtype === FilterType.EXPLICIT_NAMING.id) {
176-
setCurrentExplicitNamingFilterId(event.data.elementUuid);
177-
setOpenDialog(subtype);
178-
} else if (subtype === FilterType.EXPERT.id) {
179-
setCurrentExpertFilterId(event.data.elementUuid);
180-
setOpenDialog(subtype);
181-
}
182-
break;
183-
case ElementType.MODIFICATION:
184-
if (subtype === NetworkModificationType.COMPOSITE.id) {
185-
setCurrentNetworkModificationId(event.data.elementUuid);
186-
setOpenDialog(subtype);
187-
}
188-
break;
189-
default:
190-
break;
191208
}
192-
}
193-
setEvent(undefined); // acknowledge parent event
194-
}
195-
}, [
196-
childrenMetadata,
197-
dispatch,
198-
event,
199-
getStudyUrl,
200-
intl,
201-
selectedDirectoryElementUuid,
202-
setActiveElement,
203-
setEvent,
204-
setOpenDialog,
205-
snackError,
206-
]);
209+
},
210+
}),
211+
[
212+
childrenMetadata,
213+
dispatch,
214+
getStudyUrl,
215+
intl,
216+
selectedDirectoryElementUuid,
217+
setActiveElement,
218+
setOpenDialog,
219+
snackError,
220+
]
221+
);
207222

208223
if (openDescModificationDialog && activeElement) {
209224
return (
@@ -305,3 +320,5 @@ export default function DirectoryContentDialog({
305320
);
306321
}
307322
}
323+
324+
export default forwardRef(DirectoryContentDialog);

src/components/directory-content.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
import { type ElementAttributes, type ItemSelectionForCopy, NO_ITEM_SELECTION_FOR_COPY } from '@gridsuite/commons-ui';
2222
import { Add as AddIcon } from '@mui/icons-material';
2323
import { AgGridReact } from 'ag-grid-react';
24-
import type { CellClickedEvent } from 'ag-grid-community';
2524
import * as constants from '../utils/UIconstants';
2625
import { setActiveDirectory, setItemSelectionForCopy } from '../redux/actions';
2726
import { AnchorStatesType, defaultAnchorStates } from './menus/common-contextual-menu';
@@ -40,7 +39,7 @@ import { CUSTOM_ROW_CLASS, DirectoryContentTable, type DirectoryContentTableProp
4039
import { useHighlightSearchedElement } from './search/use-highlight-searched-element';
4140
import EmptyDirectory, { type EmptyDirectoryProps } from './empty-directory';
4241
import { AppState } from '../redux/types';
43-
import DirectoryContentDialog from './directory-content-dialog';
42+
import DirectoryContentDialog, { type DirectoryContentDialogApi } from './directory-content-dialog';
4443

4544
const circularProgressSize = '70px';
4645

@@ -192,9 +191,11 @@ export default function DirectoryContent() {
192191
[checkedRows, childrenMetadata, dispatch, selectedDirectory?.elementUuid, onContextMenu]
193192
);
194193

195-
const [cellClicked, setCellClicked] = useState<CellClickedEvent>();
194+
const dialogsApi = useRef<DirectoryContentDialogApi>(null);
196195
const handleCellClick = useCallback<DirectoryContentTableProps['handleCellClick']>(
197-
(event) => setCellClicked(event),
196+
/* The `window.open()` call MUST be inside the on-click callback, or else navigators like Firefox will
197+
* block it with their anti-popup, and user must explicitly whitelist the url/domain */
198+
(event) => dialogsApi.current?.handleClick(event),
198199
[]
199200
);
200201

@@ -339,9 +340,8 @@ export default function DirectoryContent() {
339340
/>
340341
</Box>
341342
<DirectoryContentDialog
343+
ref={dialogsApi}
342344
broadcastChannel={broadcastChannel}
343-
cellClicked={cellClicked}
344-
setCellClicked={setCellClicked}
345345
activeElement={activeElement}
346346
setActiveElement={setActiveElement}
347347
setOpenDialog={setOpenDialog}

0 commit comments

Comments
 (0)