github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/frontend-service/src/ui/components/dialog/ConfirmationDialog.tsx (about) 1 /*This file is part of kuberpult. 2 3 Kuberpult is free software: you can redistribute it and/or modify 4 it under the terms of the Expat(MIT) License as published by 5 the Free Software Foundation. 6 7 Kuberpult is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 MIT License for more details. 11 12 You should have received a copy of the MIT License 13 along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>. 14 15 Copyright 2023 freiheit.com*/ 16 17 import React from 'react'; 18 import { Button } from '../button'; 19 20 export type ConfirmationDialogProps = { 21 onConfirm: () => void; 22 onCancel: () => void; 23 open: boolean; 24 children: JSX.Element; 25 headerLabel: string; 26 confirmLabel: string; 27 classNames: string; 28 testIdRootRefParent?: string; 29 }; 30 31 /** 32 * A dialog that is used to confirm a question with either yes or no. 33 */ 34 export const ConfirmationDialog: React.FC<ConfirmationDialogProps> = (props) => ( 35 <PlainDialog 36 open={props.open} 37 onClose={props.onCancel} 38 classNames={props.classNames} 39 disableBackground={true} 40 center={true} 41 testIdRootRefParent={props.testIdRootRefParent}> 42 <> 43 <div className={'confirmation-dialog-header'}>{props.headerLabel}</div> 44 <hr /> 45 <div className={'confirmation-dialog-content'}>{props.children}</div> 46 <hr /> 47 <div className={'confirmation-dialog-footer'}> 48 <div className={'item'} key={'button-menu-cancel'} title={'ESC also closes the dialog'}> 49 <Button 50 className="mdc-button--ripple button-cancel" 51 testId="test-confirm-button-cancel" 52 label={'Cancel'} 53 onClick={props.onCancel} 54 /> 55 </div> 56 <div className={'item'} key={'button-menu-confirm'}> 57 <Button 58 className="mdc-button--unelevated button-confirm test-confirm-button-confirm" 59 testId="test-confirm-button-confirm" 60 label={props.confirmLabel} 61 onClick={props.onConfirm} 62 /> 63 </div> 64 </div> 65 </> 66 </PlainDialog> 67 ); 68 69 export type PlainDialogProps = { 70 open: boolean; 71 onClose: () => void; 72 children: JSX.Element; 73 classNames: string; 74 // NOTE: disableBackground only works for ConfirmationDialog for now, there is no plain-dialog-container-open CSS specification 75 disableBackground: boolean; 76 center: boolean; 77 testIdRootRefParent?: string; 78 }; 79 80 /** 81 * A dialog that just renders its children. Invoker must take care of all buttons. 82 */ 83 84 export const PlainDialog: React.FC<PlainDialogProps> = (props) => { 85 const { onClose, open, children, center, disableBackground, classNames, testIdRootRefParent } = props; 86 const classPrefix = center ? 'confirmation' : 'plain'; 87 const initialRef: HTMLElement | null = null; 88 const rootRef = React.useRef(initialRef); 89 90 React.useEffect(() => { 91 if (!open) { 92 return () => {}; 93 } 94 const winListener = (event: KeyboardEvent): void => { 95 if (event.key === 'Escape') { 96 onClose(); 97 } 98 }; 99 const docListener = (event: MouseEvent): void => { 100 if (!(event.target instanceof HTMLElement)) { 101 return; 102 } 103 const eventTarget: HTMLElement = event.target; 104 105 if (rootRef.current === null) { 106 return; 107 } 108 const rootRefCurrent: HTMLElement = rootRef.current; 109 110 const isInside = rootRefCurrent.contains(eventTarget); 111 if (!isInside) { 112 onClose(); 113 } 114 }; 115 window.addEventListener('keyup', winListener); 116 document.addEventListener('pointerup', docListener); 117 return () => { 118 document.removeEventListener('keyup', winListener); 119 document.removeEventListener('pointerup', docListener); 120 }; 121 }, [onClose, open, classPrefix, center]); 122 123 if (!open) { 124 return <div className={''}></div>; 125 } 126 const clas = open && disableBackground ? classPrefix + '-dialog-container-open' : ''; 127 return ( 128 <div className={classPrefix + '-dialog-container ' + clas} data-testid={testIdRootRefParent}> 129 <div ref={rootRef} className={classPrefix + '-dialog-open ' + (classNames ?? '')}> 130 {children} 131 </div> 132 </div> 133 ); 134 };