From a04382089183eec8f8e8df6416da4b8bc0a622fb Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Mon, 18 Nov 2024 16:40:09 +0530 Subject: [PATCH 01/14] feat: add new confirmation modal --- src/Assets/Icon/ic-medium-info.svg | 5 + .../Components/CICDHistory/TriggerOutput.tsx | 85 +++++++------- .../ConfirmationModal/ConfirmationModal.tsx | 105 ++++++++++++++++++ .../Components/ConfirmationModal/index.tsx | 2 + .../Components/ConfirmationModal/types.tsx | 78 +++++++++++++ .../Components/ConfirmationModal/utils.tsx | 32 ++++++ src/Shared/Components/index.ts | 1 + 7 files changed, 268 insertions(+), 40 deletions(-) create mode 100644 src/Assets/Icon/ic-medium-info.svg create mode 100644 src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx create mode 100644 src/Shared/Components/ConfirmationModal/index.tsx create mode 100644 src/Shared/Components/ConfirmationModal/types.tsx create mode 100644 src/Shared/Components/ConfirmationModal/utils.tsx diff --git a/src/Assets/Icon/ic-medium-info.svg b/src/Assets/Icon/ic-medium-info.svg new file mode 100644 index 000000000..b578ce9c2 --- /dev/null +++ b/src/Assets/Icon/ic-medium-info.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Shared/Components/CICDHistory/TriggerOutput.tsx b/src/Shared/Components/CICDHistory/TriggerOutput.tsx index 1113074dc..4f3d27e2e 100644 --- a/src/Shared/Components/CICDHistory/TriggerOutput.tsx +++ b/src/Shared/Components/CICDHistory/TriggerOutput.tsx @@ -27,7 +27,6 @@ import { ReactComponent as ICArrowRight } from '@Icons/ic-arrow-right.svg' import { ToastManager, ToastVariantType } from '@Shared/Services' import { getDeploymentStageTitle } from '@Pages/Applications' import { - ConfirmationDialog, DATE_TIME_FORMATS, DeploymentAppTypes, GenericEmptyState, @@ -63,7 +62,6 @@ import { import { getTagDetails, getTriggerDetails, cancelCiTrigger, cancelPrePostCdTrigger } from './service' import { DEFAULT_ENV, TIMEOUT_VALUE, WORKER_POD_BASE_URL } from './constants' import { GitTriggers } from '../../types' -import warn from '../../../Assets/Icon/ic-warning.svg' import LogsRenderer from './LogsRenderer' import DeploymentDetailSteps from './DeploymentDetailSteps' import { DeploymentHistoryConfigDiff } from './DeploymentHistoryConfigDiff' @@ -71,6 +69,7 @@ import { GitChanges, Scroller } from './History.components' import Artifacts from './Artifacts' import { statusColor as colorMap, EMPTY_STATE_STATUS, PULSATING_STATUS_MAP } from '../../constants' import './cicdHistory.scss' +import { ConfirmationModal, ConfirmationModalVariantType } from '../ConfirmationModal' const Finished = React.memo( ({ status, finishedOn, artifact, type }: FinishedType): JSX.Element => ( @@ -218,53 +217,59 @@ const ProgressingStatus = React.memo(({ status, stage, type }: ProgressingStatus {abortConfirmation && ( - - - -

- {type === HistoryComponentType.CD + - - - - - + : 'Are you sure you want to abort this build?' + } + buttonConfig={{ + secondaryButtonConfig: { + dataTestId: 'cancel-abort-button', + disabled: aborting, + onClick: toggleAbortConfiguration, + text: 'Cancel', + }, + primaryButtonConfig: { + dataTestId: 'abort-build-or-stage', + isLoading: aborting, + onClick: abortRunning, + text: 'Yes, Abort', + }, + }} + /> )} - {abortError.status && ( - - - -

- Error: {abortError.message} -
+
Please try to force abort
Some resource might get orphaned which will be cleaned up with Job-lifecycle
- - - - -
+ )} ) diff --git a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx new file mode 100644 index 000000000..f7db7750a --- /dev/null +++ b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx @@ -0,0 +1,105 @@ +import { ButtonHTMLAttributes, ChangeEvent, useState } from 'react' +import { CustomInput, VisibleModal } from '@Common/index' +import { ComponentSizeType } from '@Shared/constants' +import { ConfirmationModalProps } from './types' +import { getPrimaryButtonStyleFromVariant, getConfirmationLabel, getIconFromVariant } from './utils' +import { Button, ButtonStyleType, ButtonVariantType } from '../Button' + +// Todo outside click and escape + +const ConfirmationModal = ({ + title, + subtitle, + Icon, + variant, + buttonConfig, + customInputConfig, + children, +}: ConfirmationModalProps) => { + const customInputIdentifier = customInputConfig?.identifier + const confirmationKeyword = customInputConfig?.confirmationKeyword + const { primaryButtonConfig, secondaryButtonConfig } = buttonConfig + const [confirmationText, setConfirmationText] = useState('') + const RenderIcon = Icon ?? getIconFromVariant(variant) + + const handleCustomInputChange = (e: ChangeEvent) => { + setConfirmationText(e.target.value) + } + + const disablePrimaryButton: boolean = confirmationKeyword && confirmationText.trim() !== confirmationKeyword + + return ( + +
+
+ + {typeof title === 'string' ? ( +
{title}
+ ) : ( + title + )} + {typeof subtitle === 'string' ? ( +
{subtitle}
+ ) : ( + subtitle + )} + {customInputConfig && ( + + )} + {children} +
+
+ {secondaryButtonConfig && ( +
+
+
+ ) +} + +export default ConfirmationModal diff --git a/src/Shared/Components/ConfirmationModal/index.tsx b/src/Shared/Components/ConfirmationModal/index.tsx new file mode 100644 index 000000000..42fdcf275 --- /dev/null +++ b/src/Shared/Components/ConfirmationModal/index.tsx @@ -0,0 +1,2 @@ +export { default as ConfirmationModal } from './ConfirmationModal' +export { ConfirmationModalVariantType } from './types' diff --git a/src/Shared/Components/ConfirmationModal/types.tsx b/src/Shared/Components/ConfirmationModal/types.tsx new file mode 100644 index 000000000..ef9833af1 --- /dev/null +++ b/src/Shared/Components/ConfirmationModal/types.tsx @@ -0,0 +1,78 @@ +import { FunctionComponent, ReactNode, SVGProps } from 'react' +import { ButtonProps } from '../Button' + +export enum ConfirmationModalVariantType { + info = 'info', + delete = 'delete', + warning = 'warning', + custom = 'custom', +} + +interface CommonButtonProps + extends Pick, + Partial> {} + +interface CustomInputConfig { + identifier: string + confirmationKeyword: string +} + +type ButtonConfig = + | { + primaryButtonConfig: PrimaryButtonConfig & CommonButtonProps + secondaryButtonConfig: SecondaryButtonConfig & CommonButtonProps + } + | { + primaryButtonConfig: PrimaryButtonConfig & CommonButtonProps + secondaryButtonConfig?: never + } + | { + primaryButtonConfig?: never + secondaryButtonConfig?: SecondaryButtonConfig & CommonButtonProps + } + +type CustomInputConfigOrChildrenType = + | { + customInputConfig: CustomInputConfig + children?: never + } + | { + customInputConfig?: never + children: ReactNode + } + | { + customInputConfig?: never + children?: never + } + +export type ConfirmationModalProps = { + title: ReactNode + subtitle: ReactNode +} & ( + | { + variant: Exclude + Icon?: never + buttonConfig: ButtonConfig, Pick> + } + | { + variant: ConfirmationModalVariantType.custom + Icon: FunctionComponent> + customInputConfig?: never + children?: ReactNode + buttonConfig: ButtonConfig< + Pick, + Pick + > + } +) & + CustomInputConfigOrChildrenType +// | { +// variant: ConfirmationModalVariantType.custom +// Icon: FunctionComponent> +// customInputConfig: CustomInputConfig +// children?: never +// buttonConfig: ButtonConfig< +// Pick, +// Pick +// > +// } diff --git a/src/Shared/Components/ConfirmationModal/utils.tsx b/src/Shared/Components/ConfirmationModal/utils.tsx new file mode 100644 index 000000000..fd3cc017f --- /dev/null +++ b/src/Shared/Components/ConfirmationModal/utils.tsx @@ -0,0 +1,32 @@ +import { ReactComponent as ICInfo } from '@Icons/ic-medium-info.svg' +import { ReactComponent as ICWarning } from '@Icons/ic-warning-y5.svg' +import { FunctionComponent, ReactNode, SVGProps } from 'react' +import { ReactComponent as ICDelete } from '@Images/delete-medium.svg' +import { ConfirmationModalVariantType } from './types' +import { ButtonStyleType } from '../Button' + +export const getIconFromVariant = ( + variant: ConfirmationModalVariantType, +): FunctionComponent> => { + switch (variant) { + case ConfirmationModalVariantType.delete: + return ICDelete + case ConfirmationModalVariantType.warning: + return ICWarning + default: + return ICInfo + } +} + +export const getConfirmationLabel = (confirmationKeyword: string): ReactNode => ( + + Type ‘{confirmationKeyword}’ to confirm + +) + +export const getPrimaryButtonStyleFromVariant = (variant: ConfirmationModalVariantType): ButtonStyleType => { + if (variant === ConfirmationModalVariantType.delete) { + return ButtonStyleType.negative + } + return ButtonStyleType.default +} diff --git a/src/Shared/Components/index.ts b/src/Shared/Components/index.ts index 87fccf22f..dcf35cb72 100644 --- a/src/Shared/Components/index.ts +++ b/src/Shared/Components/index.ts @@ -57,3 +57,4 @@ export * from './Collapse' export * from './Security' export * from './Button' export * from './InvalidYAMLTippy' +export * from './ConfirmationModal' From ed6af977f1a01b974bca48a820aad09346052ded Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Mon, 18 Nov 2024 18:13:41 +0530 Subject: [PATCH 02/14] feat: add close on escape --- package-lock.json | 4 ++-- package.json | 2 +- src/Shared/Components/CICDHistory/TriggerOutput.tsx | 2 ++ .../ConfirmationModal/ConfirmationModal.tsx | 8 ++++---- .../ConfirmationModal/confirmationModal.scss | 3 +++ src/Shared/Components/ConfirmationModal/types.tsx | 11 +---------- 6 files changed, 13 insertions(+), 17 deletions(-) create mode 100644 src/Shared/Components/ConfirmationModal/confirmationModal.scss diff --git a/package-lock.json b/package-lock.json index 4e29cda9f..20a8ff5ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.0.8", + "version": "1.0.8-beta-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.0.8", + "version": "1.0.8-beta-1", "license": "ISC", "dependencies": { "@types/react-dates": "^21.8.6", diff --git a/package.json b/package.json index ff8e2d42f..5aa81ab82 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.0.8", + "version": "1.0.8-beta-1", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Shared/Components/CICDHistory/TriggerOutput.tsx b/src/Shared/Components/CICDHistory/TriggerOutput.tsx index 4f3d27e2e..a06cc1a5d 100644 --- a/src/Shared/Components/CICDHistory/TriggerOutput.tsx +++ b/src/Shared/Components/CICDHistory/TriggerOutput.tsx @@ -241,6 +241,7 @@ const ProgressingStatus = React.memo(({ status, stage, type }: ProgressingStatus text: 'Yes, Abort', }, }} + handleClose={toggleAbortConfiguration} /> )} {abortError.status && ( @@ -262,6 +263,7 @@ const ProgressingStatus = React.memo(({ status, stage, type }: ProgressingStatus text: 'Force Abort', }, }} + handleClose={closeForceAbortModal} >
Please try to force abort diff --git a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx index f7db7750a..cd46a83e9 100644 --- a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx +++ b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx @@ -4,8 +4,7 @@ import { ComponentSizeType } from '@Shared/constants' import { ConfirmationModalProps } from './types' import { getPrimaryButtonStyleFromVariant, getConfirmationLabel, getIconFromVariant } from './utils' import { Button, ButtonStyleType, ButtonVariantType } from '../Button' - -// Todo outside click and escape +import './confirmationModal.scss' const ConfirmationModal = ({ title, @@ -15,6 +14,7 @@ const ConfirmationModal = ({ buttonConfig, customInputConfig, children, + handleClose, }: ConfirmationModalProps) => { const customInputIdentifier = customInputConfig?.identifier const confirmationKeyword = customInputConfig?.confirmationKeyword @@ -29,8 +29,8 @@ const ConfirmationModal = ({ const disablePrimaryButton: boolean = confirmationKeyword && confirmationText.trim() !== confirmationKeyword return ( - -
+ +
{typeof title === 'string' ? ( diff --git a/src/Shared/Components/ConfirmationModal/confirmationModal.scss b/src/Shared/Components/ConfirmationModal/confirmationModal.scss new file mode 100644 index 000000000..e6232c0f1 --- /dev/null +++ b/src/Shared/Components/ConfirmationModal/confirmationModal.scss @@ -0,0 +1,3 @@ +.confirmation-modal { + box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.04), 0px 2px 8px 0px rgba(0, 0, 0, 0.04), 0px 3px 17px 0px rgba(0, 0, 0, 0.04), 0px 4px 30px 0px rgba(0, 0, 0, 0.13); +} \ No newline at end of file diff --git a/src/Shared/Components/ConfirmationModal/types.tsx b/src/Shared/Components/ConfirmationModal/types.tsx index ef9833af1..72c0b5f59 100644 --- a/src/Shared/Components/ConfirmationModal/types.tsx +++ b/src/Shared/Components/ConfirmationModal/types.tsx @@ -48,6 +48,7 @@ type CustomInputConfigOrChildrenType = export type ConfirmationModalProps = { title: ReactNode subtitle: ReactNode + handleClose: (e?: any) => void } & ( | { variant: Exclude @@ -66,13 +67,3 @@ export type ConfirmationModalProps = { } ) & CustomInputConfigOrChildrenType -// | { -// variant: ConfirmationModalVariantType.custom -// Icon: FunctionComponent> -// customInputConfig: CustomInputConfig -// children?: never -// buttonConfig: ButtonConfig< -// Pick, -// Pick -// > -// } From 7c749246db38c04ffaf96baeca2d0abb44ae981b Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Mon, 25 Nov 2024 16:35:36 +0530 Subject: [PATCH 03/14] feat: add backdrop component --- package-lock.json | 140 ++++++++++++ package.json | 1 + src/Common/Hooks/UseRegisterShortcut/types.ts | 2 + src/Shared/Components/Backdrop/Backdrop.tsx | 46 ++++ src/Shared/Components/Backdrop/backdrop.scss | 5 + src/Shared/Components/Backdrop/index.tsx | 1 + .../Components/CICDHistory/TriggerOutput.tsx | 110 +++++---- .../ConfirmationModal/ConfirmationModal.tsx | 212 +++++++++++------- .../Components/ConfirmationModal/types.tsx | 7 +- src/Shared/Helpers.tsx | 10 + src/Shared/constants.tsx | 2 + src/Shared/types.ts | 10 + 12 files changed, 408 insertions(+), 138 deletions(-) create mode 100644 src/Shared/Components/Backdrop/Backdrop.tsx create mode 100644 src/Shared/Components/Backdrop/backdrop.scss create mode 100644 src/Shared/Components/Backdrop/index.tsx diff --git a/package-lock.json b/package-lock.json index 20a8ff5ee..5773b038a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "ansi_up": "^5.2.1", "dayjs": "^1.11.13", "fast-json-patch": "^3.1.1", + "framer-motion": "^6.5.1", "jsonpath-plus": "^10.0.0", "react-dates": "^21.8.0", "react-monaco-editor": "^0.54.0", @@ -652,6 +653,23 @@ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", "peer": true }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/is-prop-valid/node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT", + "optional": true + }, "node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", @@ -1666,6 +1684,70 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@motionone/animation": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.18.0.tgz", + "integrity": "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==", + "license": "MIT", + "dependencies": { + "@motionone/easing": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/dom": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", + "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", + "license": "MIT", + "dependencies": { + "@motionone/animation": "^10.12.0", + "@motionone/generators": "^10.12.0", + "@motionone/types": "^10.12.0", + "@motionone/utils": "^10.12.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/easing": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.18.0.tgz", + "integrity": "sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==", + "license": "MIT", + "dependencies": { + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/generators": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.18.0.tgz", + "integrity": "sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==", + "license": "MIT", + "dependencies": { + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/types": { + "version": "10.17.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.1.tgz", + "integrity": "sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==", + "license": "MIT" + }, + "node_modules/@motionone/utils": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.18.0.tgz", + "integrity": "sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==", + "license": "MIT", + "dependencies": { + "@motionone/types": "^10.17.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5620,6 +5702,36 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/framer-motion": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", + "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", + "license": "MIT", + "dependencies": { + "@motionone/dom": "10.12.0", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": ">=16.8 || ^17.0.0 || ^18.0.0", + "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -5934,6 +6046,12 @@ "he": "bin/he" } }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "license": "MIT" + }, "node_modules/history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -7939,6 +8057,18 @@ "pathe": "^1.1.2" } }, + "node_modules/popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "license": "MIT", + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -9187,6 +9317,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "license": "MIT", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", diff --git a/package.json b/package.json index 5aa81ab82..3c1d6e423 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "ansi_up": "^5.2.1", "dayjs": "^1.11.13", "fast-json-patch": "^3.1.1", + "framer-motion": "^6.5.1", "jsonpath-plus": "^10.0.0", "react-dates": "^21.8.0", "react-monaco-editor": "^0.54.0", diff --git a/src/Common/Hooks/UseRegisterShortcut/types.ts b/src/Common/Hooks/UseRegisterShortcut/types.ts index 7239058c2..ea44dd32e 100644 --- a/src/Common/Hooks/UseRegisterShortcut/types.ts +++ b/src/Common/Hooks/UseRegisterShortcut/types.ts @@ -25,6 +25,8 @@ export const KEYBOARD_KEYS_MAP = { E: 'E', R: 'R', K: 'K', + Escape: 'Escape', + Enter: 'Enter', } as const export type SupportedKeyboardKeysType = keyof typeof KEYBOARD_KEYS_MAP diff --git a/src/Shared/Components/Backdrop/Backdrop.tsx b/src/Shared/Components/Backdrop/Backdrop.tsx new file mode 100644 index 000000000..92b62524a --- /dev/null +++ b/src/Shared/Components/Backdrop/Backdrop.tsx @@ -0,0 +1,46 @@ +import { ReactNode, useEffect } from 'react' +import { motion } from 'framer-motion' +import { useRegisterShortcut } from '@Common/Hooks' +import { preventBodyScroll, toggleOutsideFocus } from '@Shared/Helpers' +import './backdrop.scss' +import { createPortal } from 'react-dom' +import { ToggleFocusType } from '@Shared/types' +import { DEVTRON_BASE_MAIN_ID } from '@Shared/constants' + +const Backdrop = ({ children, onEscape }: { children: ReactNode; onEscape: () => void }) => { + const { registerShortcut, unregisterShortcut } = useRegisterShortcut() + + // useEffect on onEscape since onEscape might change based on conditions + useEffect(() => { + registerShortcut({ keys: ['Escape'], callback: onEscape }) + + return () => { + unregisterShortcut(['Escape']) + } + }, [onEscape]) + + useEffect(() => { + preventBodyScroll(true) + // Setting main as inert to that focus is trapped inside the new portal + toggleOutsideFocus({ identifier: DEVTRON_BASE_MAIN_ID, toggleFocus: ToggleFocusType.Disable }) + + return () => { + preventBodyScroll(false) + toggleOutsideFocus({ identifier: DEVTRON_BASE_MAIN_ID, toggleFocus: ToggleFocusType.Enable }) + } + }, []) + + return createPortal( + + {children} + , + document.getElementById('backdrop'), + ) +} + +export default Backdrop diff --git a/src/Shared/Components/Backdrop/backdrop.scss b/src/Shared/Components/Backdrop/backdrop.scss new file mode 100644 index 000000000..b937d8e25 --- /dev/null +++ b/src/Shared/Components/Backdrop/backdrop.scss @@ -0,0 +1,5 @@ +.backdrop { + background: #000000bf; + z-index: var(--modal-index); + backdrop-filter: blur(1px); +} diff --git a/src/Shared/Components/Backdrop/index.tsx b/src/Shared/Components/Backdrop/index.tsx new file mode 100644 index 000000000..81f132b47 --- /dev/null +++ b/src/Shared/Components/Backdrop/index.tsx @@ -0,0 +1 @@ +export { default as Backdrop } from './Backdrop' diff --git a/src/Shared/Components/CICDHistory/TriggerOutput.tsx b/src/Shared/Components/CICDHistory/TriggerOutput.tsx index a06cc1a5d..e60b91b07 100644 --- a/src/Shared/Components/CICDHistory/TriggerOutput.tsx +++ b/src/Shared/Components/CICDHistory/TriggerOutput.tsx @@ -34,7 +34,6 @@ import { Reload, createGitCommitUrl, useAsync, - not, ZERO_TIME_STRING, useInterval, URLS, @@ -187,7 +186,7 @@ const ProgressingStatus = React.memo(({ status, stage, type }: ProgressingStatus } const toggleAbortConfiguration = (): void => { - setAbortConfirmation(not) + setAbortConfirmation(!abortConfirmation) } const closeForceAbortModal = (): void => { setAbortError({ @@ -215,64 +214,55 @@ const ProgressingStatus = React.memo(({ status, stage, type }: ProgressingStatus )}
- - {abortConfirmation && ( - - )} - {abortError.status && ( - -
- Please try to force abort -
-
- Some resource might get orphaned which will be cleaned up with Job-lifecycle -
-
- )} + + +
+ Please try to force abort +
+
+ Some resource might get orphaned which will be cleaned up with Job-lifecycle +
+
) }) diff --git a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx index cd46a83e9..a67eab561 100644 --- a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx +++ b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx @@ -1,10 +1,12 @@ -import { ButtonHTMLAttributes, ChangeEvent, useState } from 'react' -import { CustomInput, VisibleModal } from '@Common/index' +import { ButtonHTMLAttributes, ChangeEvent, useCallback, useEffect, useState } from 'react' +import { AnimatePresence, motion } from 'framer-motion' +import { CustomInput, useRegisterShortcut, UseRegisterShortcutProvider } from '@Common/index' import { ComponentSizeType } from '@Shared/constants' import { ConfirmationModalProps } from './types' import { getPrimaryButtonStyleFromVariant, getConfirmationLabel, getIconFromVariant } from './utils' import { Button, ButtonStyleType, ButtonVariantType } from '../Button' import './confirmationModal.scss' +import { Backdrop } from '../Backdrop' const ConfirmationModal = ({ title, @@ -14,92 +16,152 @@ const ConfirmationModal = ({ buttonConfig, customInputConfig, children, + showConfirmationModal, handleClose, }: ConfirmationModalProps) => { + const { registerShortcut, unregisterShortcut } = useRegisterShortcut() const customInputIdentifier = customInputConfig?.identifier const confirmationKeyword = customInputConfig?.confirmationKeyword const { primaryButtonConfig, secondaryButtonConfig } = buttonConfig const [confirmationText, setConfirmationText] = useState('') const RenderIcon = Icon ?? getIconFromVariant(variant) + const disablePrimaryButton: boolean = confirmationKeyword && confirmationText.trim() !== confirmationKeyword + + const handleEnterKeyPress = () => { + if (primaryButtonConfig && !disablePrimaryButton) { + primaryButtonConfig.onClick() + } + } + + const handleCustomInputKeyDown = (e) => { + if (e.key === 'Enter') { + handleEnterKeyPress() + } + } + + const handleCloseWrapper = useCallback(() => { + if (!primaryButtonConfig?.isLoading && !secondaryButtonConfig?.disabled) { + handleClose() + } + }, [primaryButtonConfig, secondaryButtonConfig]) + + useEffect(() => { + if (showConfirmationModal) { + // Timeout so that if modal is opened on enter press, it does not trigger onClick + setTimeout(() => { + registerShortcut({ keys: ['Enter'], callback: handleEnterKeyPress }) + }, 100) + } + + return () => { + if (showConfirmationModal) { + unregisterShortcut(['Enter']) + } + } + }, [showConfirmationModal, primaryButtonConfig, disablePrimaryButton]) + const handleCustomInputChange = (e: ChangeEvent) => { setConfirmationText(e.target.value) } - const disablePrimaryButton: boolean = confirmationKeyword && confirmationText.trim() !== confirmationKeyword - return ( - -
-
- - {typeof title === 'string' ? ( -
{title}
- ) : ( - title - )} - {typeof subtitle === 'string' ? ( -
{subtitle}
- ) : ( - subtitle - )} - {customInputConfig && ( - - )} - {children} -
-
- {secondaryButtonConfig && ( -
-
-
+ + {showConfirmationModal ? ( + + +
+ + + {typeof title === 'string' ? ( + {title} + ) : ( + title + )} + + {typeof subtitle === 'string' ? ( + {subtitle} + ) : ( + subtitle + )} + + {customInputConfig && ( + + )} + + {children} +
+
+ {secondaryButtonConfig && ( +
+
+
+ ) : null} +
) } -export default ConfirmationModal +const WrapWithShortcutProvider = (props: ConfirmationModalProps) => ( + + + +) + +export default WrapWithShortcutProvider diff --git a/src/Shared/Components/ConfirmationModal/types.tsx b/src/Shared/Components/ConfirmationModal/types.tsx index 72c0b5f59..4a236a0dd 100644 --- a/src/Shared/Components/ConfirmationModal/types.tsx +++ b/src/Shared/Components/ConfirmationModal/types.tsx @@ -8,9 +8,9 @@ export enum ConfirmationModalVariantType { custom = 'custom', } -interface CommonButtonProps - extends Pick, - Partial> {} +interface CommonButtonProps extends Pick, Partial> { + onClick: (e?: any) => void +} interface CustomInputConfig { identifier: string @@ -49,6 +49,7 @@ export type ConfirmationModalProps = { title: ReactNode subtitle: ReactNode handleClose: (e?: any) => void + showConfirmationModal: boolean } & ( | { variant: Exclude diff --git a/src/Shared/Helpers.tsx b/src/Shared/Helpers.tsx index 8f5e8af10..69654e821 100644 --- a/src/Shared/Helpers.tsx +++ b/src/Shared/Helpers.tsx @@ -40,6 +40,8 @@ import { IntersectionChangeHandler, IntersectionOptions, Nodes, + ToggleFocusType, + ToggleOutsideFocus, WebhookEventNameType, } from './types' import { ReactComponent as ICPullRequest } from '../Assets/Icon/ic-pull-request.svg' @@ -96,6 +98,14 @@ export const preventBodyScroll = (lock: boolean): void => { } } +export const toggleOutsideFocus = ({ identifier, toggleFocus }: ToggleOutsideFocus) => { + if (toggleFocus === ToggleFocusType.Disable) { + document.getElementById(identifier).setAttribute('inert', 'true') + } else { + document.getElementById(identifier).removeAttribute('inert') + } +} + const getIsMaterialInfoValid = (materialInfo: MaterialInfo): boolean => !!( materialInfo.webhookData || diff --git a/src/Shared/constants.tsx b/src/Shared/constants.tsx index 65d383eb4..870558fe6 100644 --- a/src/Shared/constants.tsx +++ b/src/Shared/constants.tsx @@ -503,3 +503,5 @@ export const ALL_RESOURCE_KIND_FILTER = 'all' export const OPEN_NEW_TICKET = 'https://enterprise.devtron.ai/portal/en/newticket' export const VIEW_ALL_TICKETS = 'https://enterprise.devtron.ai/portal/en/myarea' export const RAISE_ISSUE = 'https://github.com/devtron-labs/devtron/issues/new/choose' + +export const DEVTRON_BASE_MAIN_ID = 'devtron-base-main-identifier' diff --git a/src/Shared/types.ts b/src/Shared/types.ts index 7a15c7ef0..80206f4b0 100644 --- a/src/Shared/types.ts +++ b/src/Shared/types.ts @@ -853,3 +853,13 @@ export interface DynamicTabType extends CommonTabArgsType { */ lastActiveTabId: string | null } + +export enum ToggleFocusType { + Enable = 'Enable', + Disable = 'Disable', +} + +export interface ToggleOutsideFocus { + identifier: string + toggleFocus: ToggleFocusType +} From a0004232a8f1d055af9b9105125adb622e71ed84 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Mon, 25 Nov 2024 17:16:22 +0530 Subject: [PATCH 04/14] feat: update toggle focus function --- src/Shared/Components/Backdrop/Backdrop.tsx | 16 ++++++++-------- src/Shared/Components/Backdrop/backdrop.scss | 2 +- src/Shared/Components/Backdrop/types.tsx | 6 ++++++ src/Shared/Helpers.tsx | 10 +++++----- src/Shared/types.ts | 7 +------ 5 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 src/Shared/Components/Backdrop/types.tsx diff --git a/src/Shared/Components/Backdrop/Backdrop.tsx b/src/Shared/Components/Backdrop/Backdrop.tsx index 92b62524a..405764787 100644 --- a/src/Shared/Components/Backdrop/Backdrop.tsx +++ b/src/Shared/Components/Backdrop/Backdrop.tsx @@ -1,13 +1,13 @@ -import { ReactNode, useEffect } from 'react' +import { useEffect } from 'react' +import { createPortal } from 'react-dom' import { motion } from 'framer-motion' import { useRegisterShortcut } from '@Common/Hooks' -import { preventBodyScroll, toggleOutsideFocus } from '@Shared/Helpers' -import './backdrop.scss' -import { createPortal } from 'react-dom' -import { ToggleFocusType } from '@Shared/types' +import { preventBodyScroll, preventOutsideFocus } from '@Shared/Helpers' import { DEVTRON_BASE_MAIN_ID } from '@Shared/constants' +import './backdrop.scss' +import { BackdropProps } from './types' -const Backdrop = ({ children, onEscape }: { children: ReactNode; onEscape: () => void }) => { +const Backdrop = ({ children, onEscape }: BackdropProps) => { const { registerShortcut, unregisterShortcut } = useRegisterShortcut() // useEffect on onEscape since onEscape might change based on conditions @@ -22,11 +22,11 @@ const Backdrop = ({ children, onEscape }: { children: ReactNode; onEscape: () => useEffect(() => { preventBodyScroll(true) // Setting main as inert to that focus is trapped inside the new portal - toggleOutsideFocus({ identifier: DEVTRON_BASE_MAIN_ID, toggleFocus: ToggleFocusType.Disable }) + preventOutsideFocus({ identifier: DEVTRON_BASE_MAIN_ID, preventFocus: true }) return () => { preventBodyScroll(false) - toggleOutsideFocus({ identifier: DEVTRON_BASE_MAIN_ID, toggleFocus: ToggleFocusType.Enable }) + preventOutsideFocus({ identifier: DEVTRON_BASE_MAIN_ID, preventFocus: false }) } }, []) diff --git a/src/Shared/Components/Backdrop/backdrop.scss b/src/Shared/Components/Backdrop/backdrop.scss index b937d8e25..bf00acda6 100644 --- a/src/Shared/Components/Backdrop/backdrop.scss +++ b/src/Shared/Components/Backdrop/backdrop.scss @@ -1,5 +1,5 @@ .backdrop { - background: #000000bf; + background: rgba(0, 0, 0, 0.75); z-index: var(--modal-index); backdrop-filter: blur(1px); } diff --git a/src/Shared/Components/Backdrop/types.tsx b/src/Shared/Components/Backdrop/types.tsx new file mode 100644 index 000000000..7aec2e0d2 --- /dev/null +++ b/src/Shared/Components/Backdrop/types.tsx @@ -0,0 +1,6 @@ +import { ReactNode } from 'react' + +export interface BackdropProps { + children: ReactNode + onEscape: () => void +} diff --git a/src/Shared/Helpers.tsx b/src/Shared/Helpers.tsx index 69654e821..537004323 100644 --- a/src/Shared/Helpers.tsx +++ b/src/Shared/Helpers.tsx @@ -40,7 +40,6 @@ import { IntersectionChangeHandler, IntersectionOptions, Nodes, - ToggleFocusType, ToggleOutsideFocus, WebhookEventNameType, } from './types' @@ -98,11 +97,12 @@ export const preventBodyScroll = (lock: boolean): void => { } } -export const toggleOutsideFocus = ({ identifier, toggleFocus }: ToggleOutsideFocus) => { - if (toggleFocus === ToggleFocusType.Disable) { - document.getElementById(identifier).setAttribute('inert', 'true') +export const preventOutsideFocus = ({ identifier, preventFocus }: ToggleOutsideFocus) => { + const identifierElement = document.getElementById(identifier) + if (preventFocus) { + identifierElement.setAttribute('inert', 'true') } else { - document.getElementById(identifier).removeAttribute('inert') + identifierElement.removeAttribute('inert') } } diff --git a/src/Shared/types.ts b/src/Shared/types.ts index 80206f4b0..6fea26282 100644 --- a/src/Shared/types.ts +++ b/src/Shared/types.ts @@ -854,12 +854,7 @@ export interface DynamicTabType extends CommonTabArgsType { lastActiveTabId: string | null } -export enum ToggleFocusType { - Enable = 'Enable', - Disable = 'Disable', -} - export interface ToggleOutsideFocus { identifier: string - toggleFocus: ToggleFocusType + preventFocus: boolean } From ed3df67aecbe9cc56ddae74d120deb39149c7f56 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Mon, 25 Nov 2024 21:47:39 +0530 Subject: [PATCH 05/14] feat: rename prevent focus props type --- package-lock.json | 4 ++-- package.json | 2 +- src/Shared/Components/Backdrop/Backdrop.tsx | 4 ++-- src/Shared/Components/CICDHistory/Sidebar.tsx | 2 +- .../ConfirmationModal/ConfirmationModal.tsx | 16 ++++++++++------ src/Shared/Helpers.tsx | 4 ++-- src/Shared/types.ts | 2 +- 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index e4fff0fab..aa21e96ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4", + "version": "1.1.4-beta-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4", + "version": "1.1.4-beta-1", "license": "ISC", "dependencies": { "@types/react-dates": "^21.8.6", diff --git a/package.json b/package.json index 28b594131..2d3fdbcd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4", + "version": "1.1.4-beta-1", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Shared/Components/Backdrop/Backdrop.tsx b/src/Shared/Components/Backdrop/Backdrop.tsx index 405764787..45e2c1707 100644 --- a/src/Shared/Components/Backdrop/Backdrop.tsx +++ b/src/Shared/Components/Backdrop/Backdrop.tsx @@ -4,8 +4,8 @@ import { motion } from 'framer-motion' import { useRegisterShortcut } from '@Common/Hooks' import { preventBodyScroll, preventOutsideFocus } from '@Shared/Helpers' import { DEVTRON_BASE_MAIN_ID } from '@Shared/constants' -import './backdrop.scss' import { BackdropProps } from './types' +import './backdrop.scss' const Backdrop = ({ children, onEscape }: BackdropProps) => { const { registerShortcut, unregisterShortcut } = useRegisterShortcut() @@ -39,7 +39,7 @@ const Backdrop = ({ children, onEscape }: BackdropProps) => { > {children} , - document.getElementById('backdrop'), + document.getElementById('animated-dialog-backdrop'), ) } diff --git a/src/Shared/Components/CICDHistory/Sidebar.tsx b/src/Shared/Components/CICDHistory/Sidebar.tsx index 491f78b14..fccab5eb1 100644 --- a/src/Shared/Components/CICDHistory/Sidebar.tsx +++ b/src/Shared/Components/CICDHistory/Sidebar.tsx @@ -240,7 +240,7 @@ const HistorySummaryCard = React.memo( )} -
+
{triggeredBy === 1 ? 'auto trigger' : triggeredByEmail}
diff --git a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx index a67eab561..34603060d 100644 --- a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx +++ b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx @@ -20,23 +20,27 @@ const ConfirmationModal = ({ handleClose, }: ConfirmationModalProps) => { const { registerShortcut, unregisterShortcut } = useRegisterShortcut() + + const [confirmationText, setConfirmationText] = useState('') + const customInputIdentifier = customInputConfig?.identifier const confirmationKeyword = customInputConfig?.confirmationKeyword + const { primaryButtonConfig, secondaryButtonConfig } = buttonConfig - const [confirmationText, setConfirmationText] = useState('') + const RenderIcon = Icon ?? getIconFromVariant(variant) const disablePrimaryButton: boolean = confirmationKeyword && confirmationText.trim() !== confirmationKeyword - const handleEnterKeyPress = () => { + const handleTriggerPrimaryActionButton = () => { if (primaryButtonConfig && !disablePrimaryButton) { primaryButtonConfig.onClick() } } - const handleCustomInputKeyDown = (e) => { + const handleEnterKeyPress = (e: KeyboardEvent) => { if (e.key === 'Enter') { - handleEnterKeyPress() + handleTriggerPrimaryActionButton() } } @@ -50,7 +54,7 @@ const ConfirmationModal = ({ if (showConfirmationModal) { // Timeout so that if modal is opened on enter press, it does not trigger onClick setTimeout(() => { - registerShortcut({ keys: ['Enter'], callback: handleEnterKeyPress }) + registerShortcut({ keys: ['Enter'], callback: handleTriggerPrimaryActionButton }) }, 100) } @@ -98,7 +102,7 @@ const ConfirmationModal = ({ label={getConfirmationLabel(confirmationKeyword)} inputWrapClassName="w-100" placeholder="Type to confirm" - onKeyDown={handleCustomInputKeyDown} + onKeyDown={handleEnterKeyPress} isRequiredField autoFocus /> diff --git a/src/Shared/Helpers.tsx b/src/Shared/Helpers.tsx index 537004323..5541ec7fe 100644 --- a/src/Shared/Helpers.tsx +++ b/src/Shared/Helpers.tsx @@ -40,7 +40,7 @@ import { IntersectionChangeHandler, IntersectionOptions, Nodes, - ToggleOutsideFocus, + PreventOutsideFocusProps, WebhookEventNameType, } from './types' import { ReactComponent as ICPullRequest } from '../Assets/Icon/ic-pull-request.svg' @@ -97,7 +97,7 @@ export const preventBodyScroll = (lock: boolean): void => { } } -export const preventOutsideFocus = ({ identifier, preventFocus }: ToggleOutsideFocus) => { +export const preventOutsideFocus = ({ identifier, preventFocus }: PreventOutsideFocusProps) => { const identifierElement = document.getElementById(identifier) if (preventFocus) { identifierElement.setAttribute('inert', 'true') diff --git a/src/Shared/types.ts b/src/Shared/types.ts index 6fea26282..a120c29c4 100644 --- a/src/Shared/types.ts +++ b/src/Shared/types.ts @@ -854,7 +854,7 @@ export interface DynamicTabType extends CommonTabArgsType { lastActiveTabId: string | null } -export interface ToggleOutsideFocus { +export interface PreventOutsideFocusProps { identifier: string preventFocus: boolean } From 86d0b812fca52dcab6b69dae758fda605fd4a795 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Tue, 26 Nov 2024 15:36:32 +0530 Subject: [PATCH 06/14] feat: clear timeout on unmount --- package-lock.json | 4 ++-- package.json | 2 +- src/Shared/Components/Backdrop/Backdrop.tsx | 1 - src/Shared/Components/Backdrop/backdrop.scss | 5 ----- src/Shared/Components/Backdrop/types.tsx | 3 +++ src/Shared/Components/CICDHistory/TriggerOutput.tsx | 2 ++ .../ConfirmationModal/ConfirmationModal.tsx | 12 +++++------- src/Shared/Components/ConfirmationModal/types.tsx | 10 ++++------ src/Shared/Helpers.tsx | 3 +++ 9 files changed, 20 insertions(+), 22 deletions(-) delete mode 100644 src/Shared/Components/Backdrop/backdrop.scss diff --git a/package-lock.json b/package-lock.json index aa21e96ce..95c16c263 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-1", + "version": "1.1.4-beta-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-1", + "version": "1.1.4-beta-2", "license": "ISC", "dependencies": { "@types/react-dates": "^21.8.6", diff --git a/package.json b/package.json index 2d3fdbcd7..b7e640236 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-1", + "version": "1.1.4-beta-2", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Shared/Components/Backdrop/Backdrop.tsx b/src/Shared/Components/Backdrop/Backdrop.tsx index 45e2c1707..350f1de2e 100644 --- a/src/Shared/Components/Backdrop/Backdrop.tsx +++ b/src/Shared/Components/Backdrop/Backdrop.tsx @@ -5,7 +5,6 @@ import { useRegisterShortcut } from '@Common/Hooks' import { preventBodyScroll, preventOutsideFocus } from '@Shared/Helpers' import { DEVTRON_BASE_MAIN_ID } from '@Shared/constants' import { BackdropProps } from './types' -import './backdrop.scss' const Backdrop = ({ children, onEscape }: BackdropProps) => { const { registerShortcut, unregisterShortcut } = useRegisterShortcut() diff --git a/src/Shared/Components/Backdrop/backdrop.scss b/src/Shared/Components/Backdrop/backdrop.scss deleted file mode 100644 index bf00acda6..000000000 --- a/src/Shared/Components/Backdrop/backdrop.scss +++ /dev/null @@ -1,5 +0,0 @@ -.backdrop { - background: rgba(0, 0, 0, 0.75); - z-index: var(--modal-index); - backdrop-filter: blur(1px); -} diff --git a/src/Shared/Components/Backdrop/types.tsx b/src/Shared/Components/Backdrop/types.tsx index 7aec2e0d2..a797920a6 100644 --- a/src/Shared/Components/Backdrop/types.tsx +++ b/src/Shared/Components/Backdrop/types.tsx @@ -2,5 +2,8 @@ import { ReactNode } from 'react' export interface BackdropProps { children: ReactNode + /** + * @param onEscape: please wrap in a useCallback, with respective dependencies or [] + */ onEscape: () => void } diff --git a/src/Shared/Components/CICDHistory/TriggerOutput.tsx b/src/Shared/Components/CICDHistory/TriggerOutput.tsx index e60b91b07..273be8b21 100644 --- a/src/Shared/Components/CICDHistory/TriggerOutput.tsx +++ b/src/Shared/Components/CICDHistory/TriggerOutput.tsx @@ -188,12 +188,14 @@ const ProgressingStatus = React.memo(({ status, stage, type }: ProgressingStatus const toggleAbortConfiguration = (): void => { setAbortConfirmation(!abortConfirmation) } + const closeForceAbortModal = (): void => { setAbortError({ status: false, message: '', }) } + return ( <>
diff --git a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx index 34603060d..407b562b8 100644 --- a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx +++ b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx @@ -8,6 +8,8 @@ import { Button, ButtonStyleType, ButtonVariantType } from '../Button' import './confirmationModal.scss' import { Backdrop } from '../Backdrop' +let timeoutId: number + const ConfirmationModal = ({ title, subtitle, @@ -53,7 +55,7 @@ const ConfirmationModal = ({ useEffect(() => { if (showConfirmationModal) { // Timeout so that if modal is opened on enter press, it does not trigger onClick - setTimeout(() => { + timeoutId = setTimeout(() => { registerShortcut({ keys: ['Enter'], callback: handleTriggerPrimaryActionButton }) }, 100) } @@ -61,6 +63,7 @@ const ConfirmationModal = ({ return () => { if (showConfirmationModal) { unregisterShortcut(['Enter']) + clearTimeout(timeoutId) } } }, [showConfirmationModal, primaryButtonConfig, disablePrimaryButton]) @@ -81,12 +84,7 @@ const ConfirmationModal = ({ >
- - {typeof title === 'string' ? ( - {title} - ) : ( - title - )} + {title} {typeof subtitle === 'string' ? ( {subtitle} diff --git a/src/Shared/Components/ConfirmationModal/types.tsx b/src/Shared/Components/ConfirmationModal/types.tsx index 4a236a0dd..c1af36e51 100644 --- a/src/Shared/Components/ConfirmationModal/types.tsx +++ b/src/Shared/Components/ConfirmationModal/types.tsx @@ -1,4 +1,4 @@ -import { FunctionComponent, ReactNode, SVGProps } from 'react' +import { FunctionComponent, ReactNode, SVGProps, SyntheticEvent } from 'react' import { ButtonProps } from '../Button' export enum ConfirmationModalVariantType { @@ -9,7 +9,7 @@ export enum ConfirmationModalVariantType { } interface CommonButtonProps extends Pick, Partial> { - onClick: (e?: any) => void + onClick: (e?: SyntheticEvent) => void } interface CustomInputConfig { @@ -46,9 +46,9 @@ type CustomInputConfigOrChildrenType = } export type ConfirmationModalProps = { - title: ReactNode + title: string subtitle: ReactNode - handleClose: (e?: any) => void + handleClose: (e?: SyntheticEvent) => void showConfirmationModal: boolean } & ( | { @@ -59,8 +59,6 @@ export type ConfirmationModalProps = { | { variant: ConfirmationModalVariantType.custom Icon: FunctionComponent> - customInputConfig?: never - children?: ReactNode buttonConfig: ButtonConfig< Pick, Pick diff --git a/src/Shared/Helpers.tsx b/src/Shared/Helpers.tsx index 5541ec7fe..59ac44ca4 100644 --- a/src/Shared/Helpers.tsx +++ b/src/Shared/Helpers.tsx @@ -99,6 +99,9 @@ export const preventBodyScroll = (lock: boolean): void => { export const preventOutsideFocus = ({ identifier, preventFocus }: PreventOutsideFocusProps) => { const identifierElement = document.getElementById(identifier) + if (!identifierElement) { + return + } if (preventFocus) { identifierElement.setAttribute('inert', 'true') } else { From 791c3e912673da6401690cc0531c2050d366414c Mon Sep 17 00:00:00 2001 From: Amrit Kashyap Borah Date: Tue, 26 Nov 2024 16:23:20 +0530 Subject: [PATCH 07/14] fix: remove keydown listener on tippy customized unmount --- src/Common/TippyCustomized.tsx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Common/TippyCustomized.tsx b/src/Common/TippyCustomized.tsx index 3b31b86e6..b292fefd3 100644 --- a/src/Common/TippyCustomized.tsx +++ b/src/Common/TippyCustomized.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import Tippy from '@tippyjs/react' import { ReactComponent as CloseIcon } from '../Assets/Icon/ic-cross.svg' import { ReactComponent as Help } from '../Assets/Icon/ic-help.svg' @@ -33,7 +33,6 @@ export const TippyCustomized = (props: TippyCustomizedProps) => { const onTippyMount = (tippyInstance) => { tippyRef.current = tippyInstance - document.addEventListener('keydown', closeOnEsc) } const closeTippy = (e) => { @@ -49,11 +48,21 @@ export const TippyCustomized = (props: TippyCustomizedProps) => { setShowHeadingInfo(false) } - const closeOnEsc = (e) => { - if (e.keyCode === 27) { - closeTippy(e) + useEffect(() => { + const closeOnEsc = (e) => { + if (e.keyCode === 27) { + closeTippy(e) + } } - } + + document.addEventListener('keydown', closeOnEsc) + + return () => { + document.removeEventListener('keydown', closeOnEsc) + } + }, []) + + const toggleHeadingInfo = (e) => { setShowHeadingInfo(not) From cd47408d7e886651ecb466f0c8ca358436a8b659 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Tue, 26 Nov 2024 18:19:46 +0530 Subject: [PATCH 08/14] fix: tippy customized event listener --- package-lock.json | 4 ++-- package.json | 2 +- src/Common/TippyCustomized.tsx | 26 +++++++++----------------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95c16c263..520a11a68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-2", + "version": "1.1.4-beta-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-2", + "version": "1.1.4-beta-3", "license": "ISC", "dependencies": { "@types/react-dates": "^21.8.6", diff --git a/package.json b/package.json index b7e640236..4922db970 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-2", + "version": "1.1.4-beta-3", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Common/TippyCustomized.tsx b/src/Common/TippyCustomized.tsx index b292fefd3..c8f634449 100644 --- a/src/Common/TippyCustomized.tsx +++ b/src/Common/TippyCustomized.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { useEffect, useRef, useState } from 'react' +import { useRef, useState } from 'react' import Tippy from '@tippyjs/react' import { ReactComponent as CloseIcon } from '../Assets/Icon/ic-cross.svg' import { ReactComponent as Help } from '../Assets/Icon/ic-help.svg' @@ -31,8 +31,15 @@ export const TippyCustomized = (props: TippyCustomizedProps) => { const [showHeadingInfo, setShowHeadingInfo] = useState(false) const isWhiteTheme = props.theme === TippyTheme.white + const closeOnEsc = (e) => { + if (e.keyCode === 27) { + closeTippy(e) + } + } + const onTippyMount = (tippyInstance) => { tippyRef.current = tippyInstance + document.addEventListener('keydown', closeOnEsc) } const closeTippy = (e) => { @@ -46,24 +53,9 @@ export const TippyCustomized = (props: TippyCustomizedProps) => { } } setShowHeadingInfo(false) + document.removeEventListener('keydown', closeOnEsc) } - useEffect(() => { - const closeOnEsc = (e) => { - if (e.keyCode === 27) { - closeTippy(e) - } - } - - document.addEventListener('keydown', closeOnEsc) - - return () => { - document.removeEventListener('keydown', closeOnEsc) - } - }, []) - - - const toggleHeadingInfo = (e) => { setShowHeadingInfo(not) } From 4917f5f11130deffd94f8f9cdf0d6c63cbcdd8f7 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Tue, 26 Nov 2024 18:25:34 +0530 Subject: [PATCH 09/14] chore: version bump --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 520a11a68..945f98303 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-3", + "version": "1.1.4-beta-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-3", + "version": "1.1.4-beta-5", "license": "ISC", "dependencies": { "@types/react-dates": "^21.8.6", diff --git a/package.json b/package.json index 4922db970..cf3670ba6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-3", + "version": "1.1.4-beta-5", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From da99429f3c22783bb26af53553632c95e2ab7953 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Wed, 27 Nov 2024 11:49:42 +0530 Subject: [PATCH 10/14] chore: update timeout for confirmation modal --- .../ConfirmationModal/ConfirmationModal.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx index 407b562b8..455e71155 100644 --- a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx +++ b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx @@ -1,4 +1,4 @@ -import { ButtonHTMLAttributes, ChangeEvent, useCallback, useEffect, useState } from 'react' +import { ButtonHTMLAttributes, ChangeEvent, useCallback, useEffect, useRef, useState } from 'react' import { AnimatePresence, motion } from 'framer-motion' import { CustomInput, useRegisterShortcut, UseRegisterShortcutProvider } from '@Common/index' import { ComponentSizeType } from '@Shared/constants' @@ -8,8 +8,6 @@ import { Button, ButtonStyleType, ButtonVariantType } from '../Button' import './confirmationModal.scss' import { Backdrop } from '../Backdrop' -let timeoutId: number - const ConfirmationModal = ({ title, subtitle, @@ -21,6 +19,8 @@ const ConfirmationModal = ({ showConfirmationModal, handleClose, }: ConfirmationModalProps) => { + const timeoutRef = useRef(null) + const { registerShortcut, unregisterShortcut } = useRegisterShortcut() const [confirmationText, setConfirmationText] = useState('') @@ -55,7 +55,7 @@ const ConfirmationModal = ({ useEffect(() => { if (showConfirmationModal) { // Timeout so that if modal is opened on enter press, it does not trigger onClick - timeoutId = setTimeout(() => { + timeoutRef.current = setTimeout(() => { registerShortcut({ keys: ['Enter'], callback: handleTriggerPrimaryActionButton }) }, 100) } @@ -63,7 +63,9 @@ const ConfirmationModal = ({ return () => { if (showConfirmationModal) { unregisterShortcut(['Enter']) - clearTimeout(timeoutId) + } + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) } } }, [showConfirmationModal, primaryButtonConfig, disablePrimaryButton]) From cb0f7447a866544f4b052771e5c5b1158af85396 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Wed, 27 Nov 2024 12:23:03 +0530 Subject: [PATCH 11/14] chore: remove timeout from modal --- .../ConfirmationModal/ConfirmationModal.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx index 455e71155..6309a84f2 100644 --- a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx +++ b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx @@ -1,4 +1,4 @@ -import { ButtonHTMLAttributes, ChangeEvent, useCallback, useEffect, useRef, useState } from 'react' +import { ButtonHTMLAttributes, ChangeEvent, useCallback, useEffect, useState } from 'react' import { AnimatePresence, motion } from 'framer-motion' import { CustomInput, useRegisterShortcut, UseRegisterShortcutProvider } from '@Common/index' import { ComponentSizeType } from '@Shared/constants' @@ -19,8 +19,6 @@ const ConfirmationModal = ({ showConfirmationModal, handleClose, }: ConfirmationModalProps) => { - const timeoutRef = useRef(null) - const { registerShortcut, unregisterShortcut } = useRegisterShortcut() const [confirmationText, setConfirmationText] = useState('') @@ -54,19 +52,13 @@ const ConfirmationModal = ({ useEffect(() => { if (showConfirmationModal) { - // Timeout so that if modal is opened on enter press, it does not trigger onClick - timeoutRef.current = setTimeout(() => { - registerShortcut({ keys: ['Enter'], callback: handleTriggerPrimaryActionButton }) - }, 100) + registerShortcut({ keys: ['Enter'], callback: handleTriggerPrimaryActionButton }) } return () => { if (showConfirmationModal) { unregisterShortcut(['Enter']) } - if (timeoutRef.current) { - clearTimeout(timeoutRef.current) - } } }, [showConfirmationModal, primaryButtonConfig, disablePrimaryButton]) From be13a78a8bc36ffa9d5c4707fb134f447425e60e Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Thu, 28 Nov 2024 11:16:17 +0530 Subject: [PATCH 12/14] chore: version bump --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 945f98303..e29035033 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-5", + "version": "1.1.4-beta-11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-5", + "version": "1.1.4-beta-11", "license": "ISC", "dependencies": { "@types/react-dates": "^21.8.6", diff --git a/package.json b/package.json index cf3670ba6..3829ca508 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-5", + "version": "1.1.4-beta-11", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From b2e1cf0358d06dd66be1c4452f380dc5e1ce5612 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Thu, 28 Nov 2024 17:20:29 +0530 Subject: [PATCH 13/14] fix: remove on key down from custom input --- package-lock.json | 4 ++-- package.json | 2 +- .../Components/ConfirmationModal/ConfirmationModal.tsx | 7 ------- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index e29035033..511635172 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-11", + "version": "1.1.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-11", + "version": "1.1.5", "license": "ISC", "dependencies": { "@types/react-dates": "^21.8.6", diff --git a/package.json b/package.json index 3829ca508..186d67842 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.4-beta-11", + "version": "1.1.5", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx index 6309a84f2..caa9182d6 100644 --- a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx +++ b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx @@ -38,12 +38,6 @@ const ConfirmationModal = ({ } } - const handleEnterKeyPress = (e: KeyboardEvent) => { - if (e.key === 'Enter') { - handleTriggerPrimaryActionButton() - } - } - const handleCloseWrapper = useCallback(() => { if (!primaryButtonConfig?.isLoading && !secondaryButtonConfig?.disabled) { handleClose() @@ -94,7 +88,6 @@ const ConfirmationModal = ({ label={getConfirmationLabel(confirmationKeyword)} inputWrapClassName="w-100" placeholder="Type to confirm" - onKeyDown={handleEnterKeyPress} isRequiredField autoFocus /> From 8b16b07ab0691dd93d8f446bfc835d642765ede0 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Thu, 28 Nov 2024 17:23:16 +0530 Subject: [PATCH 14/14] chore: version bump --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65a1e1459..38d800c0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.5", + "version": "1.1.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.5", + "version": "1.1.6", "license": "ISC", "dependencies": { "@types/react-dates": "^21.8.6", diff --git a/package.json b/package.json index 3b39377cf..da943b130 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.1.5", + "version": "1.1.6", "description": "Supporting common component library", "type": "module", "main": "dist/index.js",