Skip to content

Commit 03fdfe7

Browse files
committed
refactor: hook
1 parent 7375b3d commit 03fdfe7

25 files changed

+285
-423
lines changed

packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { css } from '@leafygreen-ui/emotion';
66
import { palette } from '@leafygreen-ui/palette';
77
import { spacing } from '@leafygreen-ui/tokens';
88
import { useDarkMode } from '../../hooks/use-theme';
9+
import { useErrorDetailsModal } from '../../hooks/use-error-details';
910

1011
type Status =
1112
| 'Initial'
@@ -284,7 +285,6 @@ const EditActionsFooter: React.FunctionComponent<{
284285
onUpdate(force: boolean): void;
285286
onDelete(): void;
286287
onCancel?: () => void;
287-
onOpenErrorDetails?: (details: Record<string, unknown>) => void;
288288
}> = ({
289289
doc,
290290
editing,
@@ -295,7 +295,6 @@ const EditActionsFooter: React.FunctionComponent<{
295295
onUpdate,
296296
onDelete,
297297
onCancel,
298-
onOpenErrorDetails,
299298
}) => {
300299
const {
301300
status: _status,
@@ -305,6 +304,8 @@ const EditActionsFooter: React.FunctionComponent<{
305304

306305
const darkMode = useDarkMode();
307306

307+
const { showErrorDetails } = useErrorDetailsModal();
308+
308309
// Allow props to override event based status of the document (helpful for
309310
// JSON editor where changing the document text doesn't really generate any
310311
// changes of the HadronDocument)
@@ -336,7 +337,12 @@ const EditActionsFooter: React.FunctionComponent<{
336337
<Button
337338
className={button}
338339
size="xsmall"
339-
onClick={() => onOpenErrorDetails?.(error.details!)}
340+
onClick={() =>
341+
showErrorDetails({
342+
details: error.details,
343+
closeAction: 'close',
344+
})
345+
}
340346
data-testid="edit-actions-footer-error-details-button"
341347
>
342348
VIEW ERROR DETAILS

packages/compass-components/src/components/modals/error-details-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const leftDirectionFooter = css({
1212
});
1313

1414
type ModalProps = React.ComponentProps<typeof Modal>;
15-
type ErrorDetailsModalProps = Omit<ModalProps, 'children'> & {
15+
export type ErrorDetailsModalProps = Omit<ModalProps, 'children'> & {
1616
title?: string;
1717
subtitle?: string;
1818
details?: Record<string, unknown>;
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import React, { useContext, useEffect, useState } from 'react';
2+
import {
3+
ErrorDetailsModal,
4+
type ErrorDetailsModalProps,
5+
} from '../components/modals/error-details-modal';
6+
7+
type ErrorDetailsOptions = Omit<ErrorDetailsModalProps, 'onClose'>;
8+
9+
interface ErrorDetailsModalContextData {
10+
showErrorDetails: (props: ErrorDetailsOptions) => void;
11+
isMounted: boolean;
12+
}
13+
14+
let errorDetailsId = 0;
15+
16+
interface ErrorDetailsEventMap {
17+
'show-error-details': CustomEvent<ErrorDetailsOptions>;
18+
}
19+
20+
interface GlobalErrorDetails extends EventTarget {
21+
addEventListener<K extends keyof ErrorDetailsEventMap>(
22+
type: K,
23+
listener: (this: GlobalErrorDetails, ev: ErrorDetailsEventMap[K]) => void
24+
): void;
25+
addEventListener(
26+
type: string,
27+
listener: EventListenerOrEventListenerObject
28+
): void;
29+
removeEventListener<K extends keyof ErrorDetailsEventMap>(
30+
type: K,
31+
listener: (this: GlobalErrorDetails, ev: ErrorDetailsEventMap[K]) => void
32+
): void;
33+
removeEventListener(
34+
type: string,
35+
listener: EventListenerOrEventListenerObject
36+
): void;
37+
}
38+
39+
type ShowErrorDetailsEventDetail = ErrorDetailsOptions & {
40+
errorDetailsId: number;
41+
};
42+
43+
class GlobalErrorDetails extends EventTarget {
44+
showErrorDetails(props: ErrorDetailsOptions) {
45+
this.dispatchEvent(
46+
new CustomEvent<ShowErrorDetailsEventDetail>('show-error-details', {
47+
detail: {
48+
...props,
49+
errorDetailsId: ++errorDetailsId,
50+
},
51+
})
52+
);
53+
}
54+
}
55+
const globalErrorDetails = new GlobalErrorDetails();
56+
57+
export const showErrorDetails =
58+
globalErrorDetails.showErrorDetails.bind(globalErrorDetails);
59+
60+
const ErrorDetailsModalContext =
61+
React.createContext<ErrorDetailsModalContextData>({
62+
isMounted: false,
63+
showErrorDetails,
64+
});
65+
66+
type ErrorDetailsModalAreaProps = Partial<ShowErrorDetailsEventDetail> & {
67+
open: boolean;
68+
};
69+
70+
export const ErrorDetailsModalArea: React.FC = ({ children }) => {
71+
const hasParentContext = useContext(ErrorDetailsModalContext).isMounted;
72+
73+
const [errorDetailsProps, setErrorDetailsProps] =
74+
useState<ErrorDetailsModalAreaProps>({
75+
open: false,
76+
errorDetailsId: -1,
77+
});
78+
79+
const contextValue = React.useMemo(
80+
() => ({ showErrorDetails, isMounted: true }),
81+
[]
82+
);
83+
84+
// Event listener to use confirmation modal outside of react
85+
useEffect(() => {
86+
const listener = ({ detail }: CustomEvent<ErrorDetailsOptions>) => {
87+
setErrorDetailsProps({ open: true, ...detail });
88+
};
89+
globalErrorDetails.addEventListener('show-error-details', listener);
90+
return () => {
91+
globalErrorDetails.removeEventListener('show-error-details', listener);
92+
};
93+
}, []);
94+
95+
const handleClose = () => {
96+
setErrorDetailsProps((state: ErrorDetailsModalAreaProps) => ({
97+
...state,
98+
open: false,
99+
}));
100+
};
101+
102+
if (hasParentContext) {
103+
return <>{children}</>;
104+
}
105+
106+
return (
107+
<ErrorDetailsModalContext.Provider value={contextValue}>
108+
{children}
109+
<ErrorDetailsModal
110+
// To make sure that confirmation modal internal state is reset for
111+
// every confirmation request triggered with showConfirmation method we
112+
// pass `errorDetailsId` as a component key to force React to remount it
113+
// when request starts
114+
key={errorDetailsId}
115+
data-testid="import-error-details-modal"
116+
open={errorDetailsProps.open}
117+
title={errorDetailsProps.title}
118+
closeAction={errorDetailsProps.closeAction || 'close'}
119+
onClose={handleClose}
120+
details={errorDetailsProps.details}
121+
/>
122+
</ErrorDetailsModalContext.Provider>
123+
);
124+
};
125+
126+
export const useErrorDetailsModal = () => {
127+
const { isMounted, showErrorDetails } = useContext(ErrorDetailsModalContext);
128+
if (!isMounted) {
129+
throw new Error(
130+
'useErrorDetailsModal must be used within a ErrorDetailsModalArea'
131+
);
132+
}
133+
return { showErrorDetails };
134+
};

packages/compass-components/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ export {
183183
ConfirmationModalArea,
184184
showConfirmation,
185185
} from './hooks/use-confirmation';
186+
export {
187+
useErrorDetailsModal,
188+
ErrorDetailsModalArea,
189+
showErrorDetails,
190+
} from './hooks/use-error-details';
186191
export {
187192
useHotkeys,
188193
formatHotkey,

packages/compass-crud/src/actions/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ const configureActions = () => {
1919
'toggleInsertDocumentView',
2020
'toggleInsertDocument',
2121
'openInsertDocumentDialog',
22-
'openErrorDetailsDialog',
23-
'closeErrorDetailsDialog',
2422
'openBulkUpdateModal',
2523
'updateBulkUpdatePreview',
2624
'runBulkUpdate',

packages/compass-crud/src/components/document-json-view.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ export type DocumentJsonViewProps = {
3333
| 'removeDocument'
3434
| 'replaceDocument'
3535
| 'updateDocument'
36-
| 'openErrorDetailsDialog'
3736
| 'openInsertDocumentDialog'
3837
>;
3938

@@ -68,7 +67,6 @@ class DocumentJsonView extends React.Component<DocumentJsonViewProps> {
6867
replaceDocument={this.props.replaceDocument}
6968
updateDocument={this.props.updateDocument}
7069
openInsertDocumentDialog={this.props.openInsertDocumentDialog}
71-
openErrorDetailsDialog={this.props.openErrorDetailsDialog}
7270
/>
7371
</KeylineCard>
7472
</li>

packages/compass-crud/src/components/document-list-view.spec.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ describe('<DocumentListView />', function () {
2121
replaceDocument={sinon.spy()}
2222
updateDocument={sinon.spy()}
2323
openInsertDocumentDialog={sinon.spy()}
24-
openErrorDetailsDialog={sinon.spy()}
2524
/>
2625
);
2726

packages/compass-crud/src/components/document-list-view.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ class DocumentListView extends React.Component<DocumentListViewProps> {
9797
replaceDocument: PropTypes.func,
9898
updateDocument: PropTypes.func,
9999
openInsertDocumentDialog: PropTypes.func,
100-
openErrorDetailsDialog: PropTypes.func,
101100
copyToClipboard: PropTypes.func,
102101
className: PropTypes.string,
103102
};

0 commit comments

Comments
 (0)