From 9f46a7e7fd09565fb8f4231db9ad8e1acc0ad3ec Mon Sep 17 00:00:00 2001 From: shivani170 Date: Fri, 9 May 2025 18:38:56 +0530 Subject: [PATCH 001/109] chore: generic empty list updated --- src/Assets/Img/empty-create.png | Bin 0 -> 50560 bytes .../GenericInfoCardListing.tsx | 70 ++++++++++++++++++ .../Components/GenericInfoCard/types.ts | 15 ++++ 3 files changed, 85 insertions(+) create mode 100755 src/Assets/Img/empty-create.png create mode 100644 src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx diff --git a/src/Assets/Img/empty-create.png b/src/Assets/Img/empty-create.png new file mode 100755 index 0000000000000000000000000000000000000000..705f13dd709dc4c43bc830b35a6e184ea95529aa GIT binary patch literal 50560 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 H;Q#*sWj9s9 literal 0 HcmV?d00001 diff --git a/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx new file mode 100644 index 000000000..d5b233af4 --- /dev/null +++ b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024. Devtron Inc. + */ + +import { useMemo } from 'react' + +import emptyList from '@Images/empty-create.png' +import ErrorScreenManager from '@Common/ErrorScreenManager' +import { GenericEmptyState, GenericFilterEmptyState } from '@Common/index' + +import GenericInfoCard from './GenericInfoCard.component' +import { GenericInfoCardListingProps } from './types' + +export const GenericInfoCardListing = ({ + isLoading, + error, + list, + searchKey, + reloadList, + borderVariant, + handleClearFilters, + emptyStateConfig, +}: GenericInfoCardListingProps) => { + const filteredList = useMemo(() => { + if (!searchKey || error) { + return list + } + + const loweredSearchKey = searchKey.toLowerCase() + return list.filter(({ title }) => title.toLowerCase().includes(loweredSearchKey)) + }, [searchKey, list, error]) + + if (isLoading) { + return ( + <> + + + + + ) + } + + if (error) { + return + } + + if (filteredList.length === 0) { + if (searchKey) { + return + } + + return + } + + return ( + <> + {filteredList.map(({ id, title, description, author, Icon, onClick, linkProps }) => ( + + ))} + + ) +} diff --git a/src/Shared/Components/GenericInfoCard/types.ts b/src/Shared/Components/GenericInfoCard/types.ts index 3050292f6..0bb5f2a8c 100644 --- a/src/Shared/Components/GenericInfoCard/types.ts +++ b/src/Shared/Components/GenericInfoCard/types.ts @@ -17,6 +17,9 @@ import { MouseEventHandler, ReactElement } from 'react' import { LinkProps } from 'react-router-dom' +import { GenericFilterEmptyStateProps } from '@Common/EmptyState/types' +import { GenericEmptyStateType } from '@Common/Types' + type BaseGenericInfoCardProps = { title: string description: string @@ -46,3 +49,15 @@ export type GenericInfoCardProps = { borderVariant: GenericInfoCardBorderVariant isLoading?: boolean } & BaseGenericInfoCardProps) ) + +export interface GenericInfoCardListingProps + extends Pick, + Pick { + list: (Pick & + Record<'id', string>)[] + emptyStateConfig: Pick + searchKey?: string + reloadList?: () => void + error?: Record + isLoading?: boolean +} From 7387fac04e8c2431228d4bf1f0e29b56fcc0e2c2 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 13 May 2025 12:30:34 +0530 Subject: [PATCH 002/109] chore: import enabled --- src/Shared/Components/GenericInfoCard/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Shared/Components/GenericInfoCard/index.ts b/src/Shared/Components/GenericInfoCard/index.ts index 763c46d7d..1b8977908 100644 --- a/src/Shared/Components/GenericInfoCard/index.ts +++ b/src/Shared/Components/GenericInfoCard/index.ts @@ -15,4 +15,5 @@ */ export { default as GenericInfoCard } from './GenericInfoCard.component' +export * from './GenericInfoCardListing' export { GenericInfoCardBorderVariant, type GenericInfoCardProps } from './types' From 2d1096689a81ae9e676c9339ced4f05172ba08e4 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 13 May 2025 12:42:52 +0530 Subject: [PATCH 003/109] chore: export GenericInfoCardListingProps --- src/Shared/Components/GenericInfoCard/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/Components/GenericInfoCard/index.ts b/src/Shared/Components/GenericInfoCard/index.ts index 1b8977908..f902fb17e 100644 --- a/src/Shared/Components/GenericInfoCard/index.ts +++ b/src/Shared/Components/GenericInfoCard/index.ts @@ -16,4 +16,4 @@ export { default as GenericInfoCard } from './GenericInfoCard.component' export * from './GenericInfoCardListing' -export { GenericInfoCardBorderVariant, type GenericInfoCardProps } from './types' +export { GenericInfoCardBorderVariant, type GenericInfoCardListingProps, type GenericInfoCardProps } from './types' From 3537fa1365bdcbec7cf703d535e96520f10028cc Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 13 May 2025 12:49:40 +0530 Subject: [PATCH 004/109] 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 ed6c61b06..4444453d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.13.0-pre-1", + "version": "1.13.0-pre-1-beta-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.13.0-pre-1", + "version": "1.13.0-pre-1-beta-1", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index e81850c89..0034c9c31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.13.0-pre-1", + "version": "1.13.0-pre-1-beta-1", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From d1e0f4edafbadd182e07f2ff1e65c9b5e8c492bd Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 13 May 2025 18:55:28 +0530 Subject: [PATCH 005/109] chore: separate out loading state component --- .../GenericInfoCard/GenericInfoCardListing.tsx | 9 ++------- .../GenericInfoCard/GenericInfoListSkeleton.tsx | 9 +++++++++ src/Shared/Components/GenericInfoCard/index.ts | 1 + src/Shared/Components/GenericInfoCard/types.ts | 4 +++- 4 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx diff --git a/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx index d5b233af4..c17d77541 100644 --- a/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx +++ b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx @@ -9,6 +9,7 @@ import ErrorScreenManager from '@Common/ErrorScreenManager' import { GenericEmptyState, GenericFilterEmptyState } from '@Common/index' import GenericInfoCard from './GenericInfoCard.component' +import { GenericInfoListSkeleton } from './GenericInfoListSkeleton' import { GenericInfoCardListingProps } from './types' export const GenericInfoCardListing = ({ @@ -31,13 +32,7 @@ export const GenericInfoCardListing = ({ }, [searchKey, list, error]) if (isLoading) { - return ( - <> - - - - - ) + return } if (error) { diff --git a/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx b/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx new file mode 100644 index 000000000..6fc91fefa --- /dev/null +++ b/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx @@ -0,0 +1,9 @@ +import GenericInfoCard from './GenericInfoCard.component' + +export const GenericInfoListSkeleton = (borderVariant) => ( + <> + + + + +) diff --git a/src/Shared/Components/GenericInfoCard/index.ts b/src/Shared/Components/GenericInfoCard/index.ts index f902fb17e..bca53f3fd 100644 --- a/src/Shared/Components/GenericInfoCard/index.ts +++ b/src/Shared/Components/GenericInfoCard/index.ts @@ -16,4 +16,5 @@ export { default as GenericInfoCard } from './GenericInfoCard.component' export * from './GenericInfoCardListing' +export { GenericInfoListSkeleton } from './GenericInfoListSkeleton' export { GenericInfoCardBorderVariant, type GenericInfoCardListingProps, type GenericInfoCardProps } from './types' diff --git a/src/Shared/Components/GenericInfoCard/types.ts b/src/Shared/Components/GenericInfoCard/types.ts index 0bb5f2a8c..5fb0d9e4d 100644 --- a/src/Shared/Components/GenericInfoCard/types.ts +++ b/src/Shared/Components/GenericInfoCard/types.ts @@ -20,6 +20,8 @@ import { LinkProps } from 'react-router-dom' import { GenericFilterEmptyStateProps } from '@Common/EmptyState/types' import { GenericEmptyStateType } from '@Common/Types' +import { APIResponseHandlerProps } from '../APIResponseHandler' + type BaseGenericInfoCardProps = { title: string description: string @@ -58,6 +60,6 @@ export interface GenericInfoCardListingProps emptyStateConfig: Pick searchKey?: string reloadList?: () => void - error?: Record + error?: APIResponseHandlerProps['error'] isLoading?: boolean } From 135edce07160c76e8097b0071e73a4d83f8aea2f Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 14 May 2025 16:45:47 +0530 Subject: [PATCH 006/109] chore: doc link component added --- src/Common/Constants.ts | 14 +++++++------- .../CustomTagSelector/PropagateTagInfo.tsx | 3 ++- src/Common/DocLink/DocLink.tsx | 19 +++++++++++++++++++ src/Common/DocLink/index.ts | 1 + src/Common/DocLink/types.ts | 6 ++++++ src/Common/index.ts | 1 + .../DTApplicationMetricsFormField.tsx | 3 ++- .../BuildInfra/Descriptor.tsx | 3 ++- .../Components/CICDHistory/Artifacts.tsx | 12 +++++++++--- .../Components/CICDHistory/LogsRenderer.tsx | 15 +++++++-------- .../Components/License/License.components.tsx | 3 ++- 11 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 src/Common/DocLink/DocLink.tsx create mode 100644 src/Common/DocLink/index.ts create mode 100644 src/Common/DocLink/types.ts diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index 49ce89f06..84f5180b5 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -23,13 +23,13 @@ export const DOCUMENTATION_VERSION = '/v/v0.7' export const DISCORD_LINK = 'https://discord.devtron.ai/' export const DEFAULT_JSON_SCHEMA_URI = 'https://json-schema.org/draft/2020-12/schema' export const DOCUMENTATION = { - APP_METRICS: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/applications/app-details/app-metrics`, - APP_TAGS: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/applications/create-application#tags`, - APP_OVERVIEW_TAGS: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/applications/overview#manage-tags`, - BLOB_STORAGE: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/getting-started/install/installation-configuration#configuration-of-blob-storage`, - GLOBAL_CONFIG_BUILD_INFRA: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/global-configurations/build-infra`, - ENTERPRISE_LICENSE: `${DOCUMENTATION_HOME_PAGE}/enterprise-license`, - KUBE_CONFIG: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/resource-browser#running-kubectl-commands-locally`, + APP_METRICS: `usage/applications/app-details/app-metrics`, + APP_TAGS: `usage/applications/create-application#tags`, + APP_OVERVIEW_TAGS: `usage/applications/overview#manage-tags`, + BLOB_STORAGE: `getting-started/install/installation-configuration#configuration-of-blob-storage`, + GLOBAL_CONFIG_BUILD_INFRA: `global-configurations/build-infra`, + ENTERPRISE_LICENSE: `enterprise-license`, + KUBE_CONFIG: `usage/resource-browser#running-kubectl-commands-locally`, TENANT_INSTALLATION: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/software-distribution-hub/tenants`, } diff --git a/src/Common/CustomTagSelector/PropagateTagInfo.tsx b/src/Common/CustomTagSelector/PropagateTagInfo.tsx index e15f2d119..d01485e5e 100644 --- a/src/Common/CustomTagSelector/PropagateTagInfo.tsx +++ b/src/Common/CustomTagSelector/PropagateTagInfo.tsx @@ -20,6 +20,7 @@ import { ReactComponent as ICHelpOutline } from '../../Assets/Icon/ic-help-outli import { TippyCustomized } from '../TippyCustomized' import { TippyTheme } from '../Types' import { DOCUMENTATION } from '../Constants' +import { getDocumentationUrl } from '@Common/DocLink' export default function PropagateTagInfo({ isCreateApp }: { isCreateApp: boolean }) { const additionalInfo = () => ( @@ -47,7 +48,7 @@ export default function PropagateTagInfo({ isCreateApp }: { isCreateApp: boolean showCloseButton trigger="click" interactive - documentationLink={isCreateApp ? DOCUMENTATION.APP_TAGS : DOCUMENTATION.APP_OVERVIEW_TAGS} + documentationLink={isCreateApp ? getDocumentationUrl(DOCUMENTATION.APP_TAGS) : getDocumentationUrl(DOCUMENTATION.APP_OVERVIEW_TAGS)} documentationLinkText="View Documentation" >
diff --git a/src/Common/DocLink/DocLink.tsx b/src/Common/DocLink/DocLink.tsx new file mode 100644 index 000000000..9c7367ac3 --- /dev/null +++ b/src/Common/DocLink/DocLink.tsx @@ -0,0 +1,19 @@ +import { DOCUMENTATION_HOME_PAGE, DOCUMENTATION_VERSION } from '..' +import { DocLinkProps } from './types' + +export const appendUtmToUrl = (docLink: string) => `${docLink}?utm_source=product` + +export const getDocumentationUrl = (docLink: string) => + `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/${appendUtmToUrl(docLink)}` + +export const DocLink = ({ docLink, docLinkText = 'Learn more', dataTestId, className = 'dc__link' }: DocLinkProps) => ( + + {docLinkText} + +) diff --git a/src/Common/DocLink/index.ts b/src/Common/DocLink/index.ts new file mode 100644 index 000000000..996744a4c --- /dev/null +++ b/src/Common/DocLink/index.ts @@ -0,0 +1 @@ +export { DocLink, getDocumentationUrl } from './DocLink' diff --git a/src/Common/DocLink/types.ts b/src/Common/DocLink/types.ts new file mode 100644 index 000000000..dfd29ec27 --- /dev/null +++ b/src/Common/DocLink/types.ts @@ -0,0 +1,6 @@ +export interface DocLinkProps { + docLink: string + dataTestId: string + docLinkText?: string + className?: string +} diff --git a/src/Common/index.ts b/src/Common/index.ts index dc619f806..17f049562 100644 --- a/src/Common/index.ts +++ b/src/Common/index.ts @@ -30,6 +30,7 @@ export * from './DeleteCINodeButton' export { default as DevtronCopyright } from './DevtronCopyright' export * from './DevtronProgressing' export * from './Dialogs' +export * from './DocLink' export * from './DraggableWrapper' export * from './Drawer' export * from './EmptyState' diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/DeploymentTemplate/DTApplicationMetricsFormField.tsx b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/DeploymentTemplate/DTApplicationMetricsFormField.tsx index 4ca82f852..23f28d353 100644 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/DeploymentTemplate/DTApplicationMetricsFormField.tsx +++ b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/DeploymentTemplate/DTApplicationMetricsFormField.tsx @@ -17,6 +17,7 @@ import { ReactComponent as ICInfoFilledOverride } from '@Icons/ic-info-filled-override.svg' import { Checkbox } from '@Common/Checkbox' import { DOCUMENTATION } from '@Common/Constants' +import { getDocumentationUrl } from '@Common/DocLink' import { Progressing } from '@Common/Progressing' import { Tooltip } from '@Common/Tooltip' import { CHECKBOX_VALUE } from '@Common/Types' @@ -96,7 +97,7 @@ const DTApplicationMetricsFormField = ({ diff --git a/src/Shared/Components/CICDHistory/Artifacts.tsx b/src/Shared/Components/CICDHistory/Artifacts.tsx index 9e4f2beda..40a2339b2 100644 --- a/src/Shared/Components/CICDHistory/Artifacts.tsx +++ b/src/Shared/Components/CICDHistory/Artifacts.tsx @@ -24,6 +24,7 @@ import folder from '@Icons/ic-folder.svg' import { ReactComponent as ICHelpOutline } from '@Icons/ic-help.svg' import { ReactComponent as MechanicalOperation } from '@Icons/ic-mechanical-operation.svg' import noartifact from '@Images/no-artifact.webp' +import { DocLink } from '@Common/DocLink' import { getIsApprovalPolicyConfigured } from '@Shared/Helpers' import { useDownload } from '@Shared/Hooks' @@ -274,14 +275,19 @@ const Artifacts = ({ {EMPTY_STATE_STATUS.ARTIFACTS_EMPTY_STATE_TEXTS.StoreFiles} - {EMPTY_STATE_STATUS.ARTIFACTS_EMPTY_STATE_TEXTS.ConfigureBlobStorage} - + */} +
diff --git a/src/Shared/Components/CICDHistory/LogsRenderer.tsx b/src/Shared/Components/CICDHistory/LogsRenderer.tsx index 3ef941bb6..fd09d0ff2 100644 --- a/src/Shared/Components/CICDHistory/LogsRenderer.tsx +++ b/src/Shared/Components/CICDHistory/LogsRenderer.tsx @@ -22,6 +22,7 @@ import DOMPurify from 'dompurify' import { ReactComponent as ICArrow } from '@Icons/ic-caret-down.svg' import { ReactComponent as ICCollapseAll } from '@Icons/ic-collapse-all.svg' import { ReactComponent as ICExpandAll } from '@Icons/ic-expand-all.svg' +import { DocLink } from '@Common/DocLink' import { ANSI_UP_REGEX, ComponentSizeType } from '@Shared/constants' import { escapeRegExp, sanitizeTargetPlatforms } from '@Shared/Helpers' import { AppThemeType, getComponentSpecificThemeClass } from '@Shared/Providers' @@ -82,14 +83,12 @@ const renderBlobNotConfigured = (): JSX.Element => (
Want to store logs to view later? - - Configure blob storage - + +
diff --git a/src/Shared/Components/License/License.components.tsx b/src/Shared/Components/License/License.components.tsx index df879988a..1306acce0 100644 --- a/src/Shared/Components/License/License.components.tsx +++ b/src/Shared/Components/License/License.components.tsx @@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from 'react' import { ReactComponent as ICCheck } from '@Icons/ic-check.svg' import { ReactComponent as ICClipboard } from '@Icons/ic-copy.svg' import { DOCUMENTATION } from '@Common/Constants' +import { getDocumentationUrl } from '@Common/DocLink' import { ClipboardButton, copyToClipboard, showError } from '@Common/index' import { Backdrop, Button, ButtonStyleType, ButtonVariantType, Icon, InfoIconTippy, QRCode } from '..' @@ -90,7 +91,7 @@ const InstallationFingerprintInfo = ({ fingerprint, showHelpTooltip = false }: I documentationLinkText="Documentation" iconClassName="icon-dim-20 fcn-6" placement="right" - documentationLink={DOCUMENTATION.ENTERPRISE_LICENSE} + documentationLink={getDocumentationUrl(DOCUMENTATION.ENTERPRISE_LICENSE)} /> )} From 82a037e45ce632b301186a4662305b994b214d51 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 15 May 2025 03:03:09 +0530 Subject: [PATCH 007/109] chore: added doc link in tippy customoze --- src/Common/DocLink/DocLink.tsx | 28 ++++++++++++++++++---------- src/Common/DocLink/types.ts | 6 ++++-- src/Common/TippyCustomized.tsx | 19 ++++++++----------- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/Common/DocLink/DocLink.tsx b/src/Common/DocLink/DocLink.tsx index 9c7367ac3..ad134cfc8 100644 --- a/src/Common/DocLink/DocLink.tsx +++ b/src/Common/DocLink/DocLink.tsx @@ -1,3 +1,6 @@ +import { Button, ButtonComponentType, ButtonVariantType, Icon } from '@Shared/Components' +import { ComponentSizeType } from '@Shared/constants' + import { DOCUMENTATION_HOME_PAGE, DOCUMENTATION_VERSION } from '..' import { DocLinkProps } from './types' @@ -6,14 +9,19 @@ export const appendUtmToUrl = (docLink: string) => `${docLink}?utm_source=produc export const getDocumentationUrl = (docLink: string) => `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/${appendUtmToUrl(docLink)}` -export const DocLink = ({ docLink, docLinkText = 'Learn more', dataTestId, className = 'dc__link' }: DocLinkProps) => ( - - {docLinkText} - +export const DocLink = ({ docLink, docLinkText = 'Learn more', dataTestId, showEndIcon, onClick }: DocLinkProps) => ( + + + + ) +} + +export default Switch diff --git a/src/Shared/Components/Switch/constants.ts b/src/Shared/Components/Switch/constants.ts new file mode 100644 index 000000000..e8fe803d2 --- /dev/null +++ b/src/Shared/Components/Switch/constants.ts @@ -0,0 +1,11 @@ +import { SwitchProps } from './types' + +export const SWITCH_VARIANTS: Record = { + theme: null, + positive: null, +} + +export const SWITCH_SHAPES: Record = { + rounded: null, + square: null, +} diff --git a/src/Shared/Components/Switch/index.ts b/src/Shared/Components/Switch/index.ts new file mode 100644 index 000000000..22aab781a --- /dev/null +++ b/src/Shared/Components/Switch/index.ts @@ -0,0 +1 @@ +export { default as Switch } from './Switch.component' diff --git a/src/Shared/Components/Switch/types.ts b/src/Shared/Components/Switch/types.ts new file mode 100644 index 000000000..e150fa34f --- /dev/null +++ b/src/Shared/Components/Switch/types.ts @@ -0,0 +1,106 @@ +import { IconBaseColorType } from '@Shared/types' + +import { IconName } from '../Icon' + +/** + * Represents the properties for configuring the shape and behavior of a switch component. + * + * - When `shape` is `rounded`: + * - The switch will have a rounded appearance. + * - `iconName`, `iconColor`, and `indeterminate` are not applicable. + * + * - When `shape` is `square`: + * - The switch will have a square appearance. + * - `iconName` specifies the name of the icon to display. + * - `iconColor` allows customization of the icon's color in the active state. + * - `indeterminate` indicates whether the switch is in an indeterminate state, typically used for checkboxes to represent a mixed state. + * If `indeterminate` is true, the switch will not be fully checked or unchecked, and its role will change to `checkbox`. + */ +type SwitchShapeProps = + | { + /** + * The shape of the switch. Defaults to `rounded` if not specified. + */ + shape?: 'rounded' + + /** + * Icon name is not applicable for the `rounded` shape. + */ + iconName?: never + + /** + * Icon color is not applicable for the `rounded` shape. + */ + iconColor?: never + indeterminate?: never + } + | { + /** + * The shape of the switch. Must be `square` to enable icon-related properties. + */ + shape: 'square' + + /** + * The name of the icon to display when the shape is `square`. + */ + iconName: IconName + + /** + * The color of the icon. If provided, this will override the default color in the active state. + */ + iconColor?: IconBaseColorType + /** + * Indicates whether the switch is in an indeterminate state. + * This state is typically used for checkboxes to indicate a mixed state. + * If true, the switch will not be fully checked or unchecked. We will change the role to checkbox in this case since the indeterminate state is not applicable for the switch. + * This property is not applicable for the `rounded` shape. + * @default false + */ + indeterminate?: boolean + } + +/** + * Represents the properties for the `Switch` component. + */ +export type SwitchProps = { + /** + * The ARIA label for the switch, used for accessibility purposes. + */ + ariaLabel: string + + /** + * A unique identifier for testing purposes. + */ + dataTestId: string + + /** + * The visual variant of the switch. + * + * @default `positive` + */ + variant?: 'theme' | 'positive' + + handleChange: () => void + + /** + * Indicates whether the switch is disabled. + */ + isDisabled?: boolean + + /** + * Indicates whether the switch is in a loading state. + */ + isLoading?: boolean + + /** + * Indicates whether the switch is currently checked (on). + */ + isChecked: boolean + + /** + * Optional tooltip content to display when hovering over the switch. + * + * @default undefined + */ + tooltipContent?: string +} & SwitchShapeProps diff --git a/src/Shared/Components/Switch/utils.ts b/src/Shared/Components/Switch/utils.ts new file mode 100644 index 000000000..3f2759eb3 --- /dev/null +++ b/src/Shared/Components/Switch/utils.ts @@ -0,0 +1,60 @@ +import { IconBaseColorType } from '@Shared/types' + +import { SwitchProps } from './types' + +// On intro of size there will changes in almost all methods +export const getSwitchContainerClass = ({ shape }: Required>): string => { + if (shape === 'rounded') { + return 'py-3 h-24 w-20' + } + + return 'w-28 h-18' +} + +export const getSwitchTrackColor = ({ + shape, + variant, + isChecked, +}: Required>): `var(--${IconBaseColorType})` => { + if (!isChecked) { + return 'var(--N200)' + } + + if (shape === 'rounded') { + if (variant === 'theme') { + return 'var(--B500)' + } + + return 'var(--G500)' + } + + if (variant === 'theme') { + return 'var(--B300)' + } + + return 'var(--G300)' +} + +export const getSwitchThumbClass = ({ + indeterminate, + shape, + isChecked, +}: Pick) => { + if (isChecked && indeterminate) { + return 'w-100 h-100 flex' + } + + return `flex p-2 ${shape === 'rounded' ? 'dc__border-radius-50-per icon-dim-10' : 'br-3'} bg__white` +} + +export const getSwitchIconColor = ({ + iconColor, + isChecked, + variant, +}: Pick): IconBaseColorType => { + if (!isChecked) { + return 'N200' + } + + return iconColor || (variant === 'theme' ? 'B500' : 'G500') +} diff --git a/src/Shared/Components/index.ts b/src/Shared/Components/index.ts index 3747ac491..43b590a17 100644 --- a/src/Shared/Components/index.ts +++ b/src/Shared/Components/index.ts @@ -87,6 +87,7 @@ export * from './SelectPicker' export * from './ShowMoreText' export * from './SSOProviderIcon' export * from './StatusComponent' +export * from './Switch' export * from './TabGroup' export * from './Table' export * from './TagsKeyValueTable' From 017029128c7807089638cce2ebec563f247b53aa Mon Sep 17 00:00:00 2001 From: shivani170 Date: Fri, 16 May 2025 15:29:57 +0530 Subject: [PATCH 017/109] chore: code feedback fixes --- .../Components/GenericInfoCard/GenericInfoCardListing.tsx | 3 ++- .../Components/GenericInfoCard/GenericInfoListSkeleton.tsx | 3 ++- src/Shared/Components/GenericInfoCard/types.ts | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx index c17d77541..3a9378b3d 100644 --- a/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx +++ b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx @@ -23,8 +23,9 @@ export const GenericInfoCardListing = ({ emptyStateConfig, }: GenericInfoCardListingProps) => { const filteredList = useMemo(() => { + const sanitizedList = list || [] if (!searchKey || error) { - return list + return sanitizedList } const loweredSearchKey = searchKey.toLowerCase() diff --git a/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx b/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx index 6fc91fefa..f78f83668 100644 --- a/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx +++ b/src/Shared/Components/GenericInfoCard/GenericInfoListSkeleton.tsx @@ -1,6 +1,7 @@ import GenericInfoCard from './GenericInfoCard.component' +import { GenericInfoListSkeletonProps } from './types' -export const GenericInfoListSkeleton = (borderVariant) => ( +export const GenericInfoListSkeleton = ({ borderVariant }: GenericInfoListSkeletonProps) => ( <> diff --git a/src/Shared/Components/GenericInfoCard/types.ts b/src/Shared/Components/GenericInfoCard/types.ts index 5fb0d9e4d..7ae90d7d9 100644 --- a/src/Shared/Components/GenericInfoCard/types.ts +++ b/src/Shared/Components/GenericInfoCard/types.ts @@ -63,3 +63,5 @@ export interface GenericInfoCardListingProps error?: APIResponseHandlerProps['error'] isLoading?: boolean } + +export interface GenericInfoListSkeletonProps extends Pick {} From 765eb00c73a992849cae35246de44a5bb18ddcd7 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Fri, 16 May 2025 15:54:00 +0530 Subject: [PATCH 018/109] fix: icon background fill change from 500 to 400 --- src/Assets/IconV2/ic-devtron-app.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Assets/IconV2/ic-devtron-app.svg b/src/Assets/IconV2/ic-devtron-app.svg index 228fde30e..adc694a17 100644 --- a/src/Assets/IconV2/ic-devtron-app.svg +++ b/src/Assets/IconV2/ic-devtron-app.svg @@ -18,6 +18,6 @@ - + From ee0a5c53354c13cef9c405884876071fc334b44a Mon Sep 17 00:00:00 2001 From: shivani170 Date: Fri, 16 May 2025 17:37:02 +0530 Subject: [PATCH 019/109] chore: generic info card fixes --- .../GenericInfoCard/GenericInfoCardListing.tsx | 9 ++++++++- src/Shared/Components/GenericInfoCard/types.ts | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx index 3a9378b3d..6b277e562 100644 --- a/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx +++ b/src/Shared/Components/GenericInfoCard/GenericInfoCardListing.tsx @@ -45,7 +45,14 @@ export const GenericInfoCardListing = ({ return } - return + return ( + + ) } return ( diff --git a/src/Shared/Components/GenericInfoCard/types.ts b/src/Shared/Components/GenericInfoCard/types.ts index 7ae90d7d9..ae8ef54d3 100644 --- a/src/Shared/Components/GenericInfoCard/types.ts +++ b/src/Shared/Components/GenericInfoCard/types.ts @@ -57,11 +57,11 @@ export interface GenericInfoCardListingProps Pick { list: (Pick & Record<'id', string>)[] - emptyStateConfig: Pick + emptyStateConfig: Pick searchKey?: string reloadList?: () => void error?: APIResponseHandlerProps['error'] isLoading?: boolean } -export interface GenericInfoListSkeletonProps extends Pick {} +export interface GenericInfoListSkeletonProps extends Partial> {} From 0228618037443c0e00395044f32864950b02c981 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Mon, 19 May 2025 00:53:21 +0530 Subject: [PATCH 020/109] feat: enhance Switch component with size and loading state support --- .../Components/Switch/Switch.component.tsx | 28 +++++----- src/Shared/Components/Switch/constants.ts | 52 ++++++++++++++++++- src/Shared/Components/Switch/types.ts | 29 +++++++---- src/Shared/Components/Switch/utils.ts | 42 ++++++--------- 4 files changed, 99 insertions(+), 52 deletions(-) diff --git a/src/Shared/Components/Switch/Switch.component.tsx b/src/Shared/Components/Switch/Switch.component.tsx index e84f5e598..c593ca819 100644 --- a/src/Shared/Components/Switch/Switch.component.tsx +++ b/src/Shared/Components/Switch/Switch.component.tsx @@ -2,8 +2,10 @@ import { AriaAttributes, HTMLAttributes } from 'react' import { AnimatePresence, motion } from 'framer-motion' import { Tooltip } from '@Common/Tooltip' +import { ComponentSizeType } from '@Shared/constants' import { Icon } from '../Icon' +import { INDETERMINATE_ICON_WIDTH_MAP, LOADING_COLOR_MAP, SQUARE_ICON_DIMENSION_MAP } from './constants' import { SwitchProps } from './types' import { getSwitchContainerClass, getSwitchIconColor, getSwitchThumbClass, getSwitchTrackColor } from './utils' @@ -19,9 +21,10 @@ const Switch = ({ iconColor, iconName, indeterminate = false, - handleChange, + size = ComponentSizeType.medium, + onChange, }: SwitchProps) => { - const getAriaChecked = (): AriaAttributes['aria-checked'] => { + const getAriaCheckedValue = (): AriaAttributes['aria-checked'] => { if (!isChecked) { return false } @@ -29,19 +32,19 @@ const Switch = ({ return indeterminate ? 'mixed' : true } - const ariaChecked = getAriaChecked() + const ariaCheckedValue = getAriaCheckedValue() - const showIndeterminateIcon = ariaChecked === 'mixed' + const showIndeterminateIcon = ariaCheckedValue === 'mixed' const role: HTMLAttributes['role'] = showIndeterminateIcon ? 'checkbox' : 'switch' const renderContent = () => { if (isLoading) { - return + return } return ( @@ -57,7 +60,7 @@ const Switch = ({ {showIndeterminateIcon ? ( ) @@ -92,16 +94,16 @@ const Switch = ({ // TODO: Can add hidden input for accessibility in case name [for forms] is given return ( -
+
diff --git a/src/Shared/Components/Switch/constants.ts b/src/Shared/Components/Switch/constants.ts index e8fe803d2..3bb9a5f8e 100644 --- a/src/Shared/Components/Switch/constants.ts +++ b/src/Shared/Components/Switch/constants.ts @@ -1,11 +1,59 @@ +import { ComponentSizeType } from '@Shared/constants' +import { IconBaseColorType } from '@Shared/types' + import { SwitchProps } from './types' -export const SWITCH_VARIANTS: Record = { +export const SWITCH_VARIANTS: Readonly> = { theme: null, positive: null, } -export const SWITCH_SHAPES: Record = { +export const SWITCH_SHAPES: Readonly> = { rounded: null, square: null, } + +export const ROUNDED_SWITCH_SIZE_MAP: Readonly> = { + [ComponentSizeType.medium]: 'w-32', + [ComponentSizeType.small]: 'w-24', +} + +export const SQUARE_SWITCH_SIZE_MAP: typeof ROUNDED_SWITCH_SIZE_MAP = { + [ComponentSizeType.medium]: 'w-28', + [ComponentSizeType.small]: 'w-24', +} + +export const SWITCH_HEIGHT_MAP: Readonly> = { + [ComponentSizeType.medium]: 'h-24', + [ComponentSizeType.small]: 'h-20', +} + +export const LOADING_COLOR_MAP: Record = { + theme: 'B500', + positive: 'G500', +} + +export const ROUNDED_SWITCH_TRACK_COLOR_MAP: Record = { + theme: 'var(--B500)', + positive: 'var(--G500)', +} + +export const SQUARE_SWITCH_TRACK_COLOR_MAP: typeof ROUNDED_SWITCH_TRACK_COLOR_MAP = { + theme: 'var(--B300)', + positive: 'var(--G300)', +} + +export const ROUNDED_SWITCH_THUMB_SIZE_MAP: Record = { + [ComponentSizeType.medium]: 'icon-dim-16', + [ComponentSizeType.small]: 'icon-dim-12', +} + +export const INDETERMINATE_ICON_WIDTH_MAP: Record = { + [ComponentSizeType.medium]: 'w-12', + [ComponentSizeType.small]: 'w-10', +} + +export const SQUARE_ICON_DIMENSION_MAP: Record = { + [ComponentSizeType.medium]: 'icon-dim-12', + [ComponentSizeType.small]: 'icon-dim-8', +} diff --git a/src/Shared/Components/Switch/types.ts b/src/Shared/Components/Switch/types.ts index e150fa34f..d61b92c20 100644 --- a/src/Shared/Components/Switch/types.ts +++ b/src/Shared/Components/Switch/types.ts @@ -1,3 +1,4 @@ +import { ComponentSizeType } from '@Shared/constants' import { IconBaseColorType } from '@Shared/types' import { IconName } from '../Icon' @@ -14,7 +15,7 @@ import { IconName } from '../Icon' * - `iconName` specifies the name of the icon to display. * - `iconColor` allows customization of the icon's color in the active state. * - `indeterminate` indicates whether the switch is in an indeterminate state, typically used for checkboxes to represent a mixed state. - * If `indeterminate` is true, the switch will not be fully checked or unchecked, and its role will change to `checkbox`. + * If `indeterminate` is true, the switch will not be fully checked or unchecked. */ type SwitchShapeProps = | { @@ -32,7 +33,14 @@ type SwitchShapeProps = * Icon color is not applicable for the `rounded` shape. */ iconColor?: never - indeterminate?: never + /** + * Indicates whether the switch is in an indeterminate state. + * This state is typically used for checkboxes to indicate a mixed state. + * If true, the switch will not be fully checked or unchecked. Due this state alone we are keeping role as `checkbox` instead of `switch`. + * This property is not applicable for the `square` shape. + * @default false + */ + indeterminate?: boolean } | { /** @@ -49,14 +57,7 @@ type SwitchShapeProps = * The color of the icon. If provided, this will override the default color in the active state. */ iconColor?: IconBaseColorType - /** - * Indicates whether the switch is in an indeterminate state. - * This state is typically used for checkboxes to indicate a mixed state. - * If true, the switch will not be fully checked or unchecked. We will change the role to checkbox in this case since the indeterminate state is not applicable for the switch. - * This property is not applicable for the `rounded` shape. - * @default false - */ - indeterminate?: boolean + indeterminate?: never } /** @@ -80,7 +81,13 @@ export type SwitchProps = { */ variant?: 'theme' | 'positive' - handleChange: () => void + /** + * The size of the switch. + * @default `ComponentSizeType.medium` + */ + size?: Extract + + onChange: () => void /** * Indicates whether the switch is disabled. diff --git a/src/Shared/Components/Switch/utils.ts b/src/Shared/Components/Switch/utils.ts index 3f2759eb3..2826859f1 100644 --- a/src/Shared/Components/Switch/utils.ts +++ b/src/Shared/Components/Switch/utils.ts @@ -1,15 +1,17 @@ import { IconBaseColorType } from '@Shared/types' +import { + ROUNDED_SWITCH_SIZE_MAP, + ROUNDED_SWITCH_THUMB_SIZE_MAP, + ROUNDED_SWITCH_TRACK_COLOR_MAP, + SQUARE_SWITCH_SIZE_MAP, + SQUARE_SWITCH_TRACK_COLOR_MAP, + SWITCH_HEIGHT_MAP, +} from './constants' import { SwitchProps } from './types' -// On intro of size there will changes in almost all methods -export const getSwitchContainerClass = ({ shape }: Required>): string => { - if (shape === 'rounded') { - return 'py-3 h-24 w-20' - } - - return 'w-28 h-18' -} +export const getSwitchContainerClass = ({ shape, size }: Required>): string => + `${SWITCH_HEIGHT_MAP[size]} ${shape === 'rounded' ? ROUNDED_SWITCH_SIZE_MAP[size] : SQUARE_SWITCH_SIZE_MAP[size]}` export const getSwitchTrackColor = ({ shape, @@ -20,31 +22,19 @@ export const getSwitchTrackColor = ({ return 'var(--N200)' } - if (shape === 'rounded') { - if (variant === 'theme') { - return 'var(--B500)' - } - - return 'var(--G500)' - } - - if (variant === 'theme') { - return 'var(--B300)' - } - - return 'var(--G300)' + return shape === 'rounded' ? ROUNDED_SWITCH_TRACK_COLOR_MAP[variant] : SQUARE_SWITCH_TRACK_COLOR_MAP[variant] } export const getSwitchThumbClass = ({ - indeterminate, shape, - isChecked, -}: Pick) => { - if (isChecked && indeterminate) { + size, + showIndeterminateIcon, +}: Pick & { showIndeterminateIcon: boolean }) => { + if (showIndeterminateIcon) { return 'w-100 h-100 flex' } - return `flex p-2 ${shape === 'rounded' ? 'dc__border-radius-50-per icon-dim-10' : 'br-3'} bg__white` + return `flex p-3 ${shape === 'rounded' ? `dc__border-radius-50-per ${ROUNDED_SWITCH_THUMB_SIZE_MAP[size]}` : 'br-3'} bg__white` } export const getSwitchIconColor = ({ From cb4315d18bb21fe7920aff8ba02bbf9d84126f77 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Mon, 19 May 2025 01:11:26 +0530 Subject: [PATCH 021/109] feat: enhance Switch component with accessibility improvements and new padding constants --- .../Components/Switch/Switch.component.tsx | 33 ++++++++++++++----- src/Shared/Components/Switch/constants.ts | 6 ++-- src/Shared/Components/Switch/types.ts | 8 ++++- src/Shared/Components/Switch/utils.ts | 3 +- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/Shared/Components/Switch/Switch.component.tsx b/src/Shared/Components/Switch/Switch.component.tsx index c593ca819..bbcc7f13e 100644 --- a/src/Shared/Components/Switch/Switch.component.tsx +++ b/src/Shared/Components/Switch/Switch.component.tsx @@ -1,11 +1,12 @@ -import { AriaAttributes, HTMLAttributes } from 'react' +import { AriaAttributes, useRef } from 'react' import { AnimatePresence, motion } from 'framer-motion' import { Tooltip } from '@Common/Tooltip' import { ComponentSizeType } from '@Shared/constants' +import { getUniqueId } from '@Shared/Helpers' import { Icon } from '../Icon' -import { INDETERMINATE_ICON_WIDTH_MAP, LOADING_COLOR_MAP, SQUARE_ICON_DIMENSION_MAP } from './constants' +import { INDETERMINATE_ICON_WIDTH_MAP, LOADING_COLOR_MAP } from './constants' import { SwitchProps } from './types' import { getSwitchContainerClass, getSwitchIconColor, getSwitchThumbClass, getSwitchTrackColor } from './utils' @@ -22,8 +23,11 @@ const Switch = ({ iconName, indeterminate = false, size = ComponentSizeType.medium, + name, onChange, }: SwitchProps) => { + const inputId = useRef(getUniqueId()) + const getAriaCheckedValue = (): AriaAttributes['aria-checked'] => { if (!isChecked) { return false @@ -35,7 +39,6 @@ const Switch = ({ const ariaCheckedValue = getAriaCheckedValue() const showIndeterminateIcon = ariaCheckedValue === 'mixed' - const role: HTMLAttributes['role'] = showIndeterminateIcon ? 'checkbox' : 'switch' const renderContent = () => { if (isLoading) { @@ -69,7 +72,7 @@ const Switch = ({ iconName && (
)} diff --git a/src/Common/Types.ts b/src/Common/Types.ts index 3329e3f5f..d626f13eb 100644 --- a/src/Common/Types.ts +++ b/src/Common/Types.ts @@ -33,6 +33,7 @@ import { ACTION_STATE, DEPLOYMENT_WINDOW_TYPE, DockerConfigOverrideType, + DOCUMENTATION, RefVariableType, SortingOrder, TaskErrorObj, @@ -144,11 +145,11 @@ export interface TippyCustomizedProps extends Pick { animation?: string duration?: number additionalContent?: ReactNode - documentationLink?: string + documentationLink?: keyof typeof DOCUMENTATION documentationLinkText?: string children: React.ReactElement disableClose?: boolean - hideVersion?: boolean + isEnterprise?: boolean } export interface InfoIconTippyProps @@ -162,8 +163,8 @@ export interface InfoIconTippyProps | 'placement' | 'Icon' | 'headingInfo' - | 'hideVersion' | 'documentationLink' + | 'isEnterprise' > { dataTestid?: string children?: TippyCustomizedProps['children'] diff --git a/src/Shared/Components/Error/ErrorBar.tsx b/src/Shared/Components/Error/ErrorBar.tsx index 1cf0b7bc1..ee5c2781f 100644 --- a/src/Shared/Components/Error/ErrorBar.tsx +++ b/src/Shared/Components/Error/ErrorBar.tsx @@ -17,7 +17,7 @@ import { NavLink } from 'react-router-dom' import { ReactComponent as ErrorInfo } from '../../../Assets/Icon/ic-errorInfo.svg' -import { URLS } from '../../../Common' +import { DISCORD_LINK, URLS } from '../../../Common' import { AppType } from '../../types' import { ErrorBarType } from './types' import { getIsImagePullBackOff, renderErrorHeaderMessage } from './utils' @@ -90,7 +90,7 @@ const ErrorBar = ({ appDetails, useParentMargin = true }: ErrorBarType) => {
Facing issues? >) => { const renderImage = () => { if (!SVGImage) { @@ -69,8 +69,8 @@ const FeatureDescriptionModalContent = ({ text={BUTTON_TEXT.VIEW_DOCUMENTATION} dataTestId="feature-desc__view-doc" showExternalIcon - hideVersion={hideVersion} variant={ButtonVariantType.secondary} + isEnterprise={isEnterprise} /> )}
diff --git a/src/Shared/Components/FeatureDescription/FeatureTitleWithInfo.tsx b/src/Shared/Components/FeatureDescription/FeatureTitleWithInfo.tsx index 88fa2f878..f36c2d2b4 100644 --- a/src/Shared/Components/FeatureDescription/FeatureTitleWithInfo.tsx +++ b/src/Shared/Components/FeatureDescription/FeatureTitleWithInfo.tsx @@ -38,6 +38,7 @@ const FeatureTitleWithInfo = ({ additionalContent, showInfoIcon = false, tabsConfig, + isEnterprise, }: DescriptorProps) => { const [showFeatureDescriptionModal, setShowFeatureDescriptionModal] = useState(false) const onClickInfoIcon = () => { @@ -63,6 +64,7 @@ const FeatureTitleWithInfo = ({ documentationLink={docLink} documentationLinkText={docLinkText} dataTestid="info-tippy-button" + isEnterprise={isEnterprise} />
) @@ -95,6 +97,7 @@ const FeatureTitleWithInfo = ({ void - hideVersion?: DocLinkProps['hideVersion'] + isEnterprise?: boolean } & ( | (BaseFeatureDescriptionModalProps & { tabsConfig?: never @@ -84,4 +84,5 @@ export type DescriptorProps = ( * @default false */ showInfoIcon?: boolean + isEnterprise?: boolean } diff --git a/src/Shared/Components/Header/types.ts b/src/Shared/Components/Header/types.ts index 0dc15f387..976331efd 100644 --- a/src/Shared/Components/Header/types.ts +++ b/src/Shared/Components/Header/types.ts @@ -16,7 +16,7 @@ import { ModuleStatus } from '@Shared/types' -import { ResponseType, TippyCustomizedProps } from '../../../Common' +import { DOCUMENTATION, ResponseType, TippyCustomizedProps } from '../../../Common' import { ActionMenuProps } from '../ActionMenu' export enum InstallationType { @@ -38,7 +38,7 @@ export interface PageHeaderType { markAsBeta?: boolean tippyProps?: Pick & { isTippyCustomized?: boolean - tippyRedirectLink?: string + tippyRedirectLink?: keyof typeof DOCUMENTATION TippyIcon?: React.FunctionComponent tippyMessage?: string onClickTippyButton?: () => void diff --git a/src/Shared/Components/InfoIconTippy/InfoIconTippy.tsx b/src/Shared/Components/InfoIconTippy/InfoIconTippy.tsx index 4f30b2de1..148b7a0fd 100644 --- a/src/Shared/Components/InfoIconTippy/InfoIconTippy.tsx +++ b/src/Shared/Components/InfoIconTippy/InfoIconTippy.tsx @@ -32,7 +32,7 @@ const InfoIconTippy = ({ children, headingInfo, buttonPadding = 'p-0', - hideVersion, + isEnterprise = false, }: InfoIconTippyProps) => ( {children || ( diff --git a/src/Shared/Components/Switch/constants.ts b/src/Shared/Components/Switch/constants.ts index 2e808ed81..28c8ef79f 100644 --- a/src/Shared/Components/Switch/constants.ts +++ b/src/Shared/Components/Switch/constants.ts @@ -38,11 +38,21 @@ export const ROUNDED_SWITCH_TRACK_COLOR_MAP: Record = { + theme: 'var(--B600)', + positive: 'var(--G600)', +} + export const SQUARE_SWITCH_TRACK_COLOR_MAP: typeof ROUNDED_SWITCH_TRACK_COLOR_MAP = { theme: 'bcb-3', positive: 'bcg-3', } +export const SQUARE_SWITCH_TRACK_HOVER_COLOR_MAP: typeof ROUNDED_SWITCH_TRACK_HOVER_COLOR_MAP = { + theme: 'var(--B400)', + positive: 'var(--G400)', +} + export const ROUNDED_SWITCH_THUMB_SIZE_MAP: Record = { [ComponentSizeType.medium]: 'icon-dim-16', [ComponentSizeType.small]: 'icon-dim-12', diff --git a/src/Shared/Components/Switch/switch.scss b/src/Shared/Components/Switch/switch.scss new file mode 100644 index 000000000..522b011c0 --- /dev/null +++ b/src/Shared/Components/Switch/switch.scss @@ -0,0 +1,9 @@ +.dt-switch { + &__track { + --switch-track-hover-color: 'transparent'; + + &:hover { + background-color: var(--switch-track-hover-color); + } + } +} \ No newline at end of file diff --git a/src/Shared/Components/Switch/utils.ts b/src/Shared/Components/Switch/utils.ts index 16113290b..2dd6cf73a 100644 --- a/src/Shared/Components/Switch/utils.ts +++ b/src/Shared/Components/Switch/utils.ts @@ -4,8 +4,10 @@ import { ROUNDED_SWITCH_SIZE_MAP, ROUNDED_SWITCH_THUMB_SIZE_MAP, ROUNDED_SWITCH_TRACK_COLOR_MAP, + ROUNDED_SWITCH_TRACK_HOVER_COLOR_MAP, SQUARE_SWITCH_SIZE_MAP, SQUARE_SWITCH_TRACK_COLOR_MAP, + SQUARE_SWITCH_TRACK_HOVER_COLOR_MAP, SWITCH_HEIGHT_MAP, SWITCH_THUMB_PADDING_MAP, THUMB_OUTER_PADDING_MAP, @@ -32,6 +34,22 @@ export const getSwitchTrackColor = ({ return shape === 'rounded' ? ROUNDED_SWITCH_TRACK_COLOR_MAP[variant] : SQUARE_SWITCH_TRACK_COLOR_MAP[variant] } +export const getSwitchTrackHoverColor = ({ + shape, + variant, + isChecked, +}: Required< + Pick +>): (typeof ROUNDED_SWITCH_TRACK_HOVER_COLOR_MAP)[DTSwitchProps['variant']] => { + if (!isChecked) { + return 'var(--N300)' + } + + return shape === 'rounded' + ? ROUNDED_SWITCH_TRACK_HOVER_COLOR_MAP[variant] + : SQUARE_SWITCH_TRACK_HOVER_COLOR_MAP[variant] +} + export const getSwitchThumbClass = ({ shape, size, From b8f14c0fd228c9653fbd6d806d931d7fe895012b Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Mon, 26 May 2025 10:55:40 +0530 Subject: [PATCH 051/109] fix: remove unnecessary keys from loading and icon spans in Switch component --- src/Shared/Components/Switch/Switch.component.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Shared/Components/Switch/Switch.component.tsx b/src/Shared/Components/Switch/Switch.component.tsx index 161f530cd..b006d743c 100644 --- a/src/Shared/Components/Switch/Switch.component.tsx +++ b/src/Shared/Components/Switch/Switch.component.tsx @@ -62,7 +62,7 @@ const Switch = ({ layoutId={`${name}-loader`} className="flex-grow-1 h-100 dc__fill-available-space dc__no-shrink" > - +
) : ( {showIndeterminateIcon ? ( Date: Mon, 26 May 2025 11:33:51 +0530 Subject: [PATCH 052/109] refactor: remove unused SWITCH_VARIANTS and SWITCH_SHAPES constants from Switch component --- src/Shared/Components/Switch/constants.ts | 10 ---------- src/Shared/Components/Switch/index.ts | 1 - src/Shared/Components/Switch/types.ts | 4 ++++ 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Shared/Components/Switch/constants.ts b/src/Shared/Components/Switch/constants.ts index 28c8ef79f..6d1c97cbc 100644 --- a/src/Shared/Components/Switch/constants.ts +++ b/src/Shared/Components/Switch/constants.ts @@ -3,16 +3,6 @@ import { IconBaseColorType } from '@Shared/types' import { DTSwitchProps } from './types' -export const SWITCH_VARIANTS: Readonly> = { - theme: null, - positive: null, -} - -export const SWITCH_SHAPES: Readonly> = { - rounded: null, - square: null, -} - export const ROUNDED_SWITCH_SIZE_MAP: Readonly> = { [ComponentSizeType.medium]: 'w-32', [ComponentSizeType.small]: 'w-24', diff --git a/src/Shared/Components/Switch/index.ts b/src/Shared/Components/Switch/index.ts index e4732a3e0..a5ff57e39 100644 --- a/src/Shared/Components/Switch/index.ts +++ b/src/Shared/Components/Switch/index.ts @@ -1,3 +1,2 @@ -export { SWITCH_SHAPES, SWITCH_VARIANTS } from './constants' export { default as DTSwitch } from './Switch.component' export type { DTSwitchProps } from './types' diff --git a/src/Shared/Components/Switch/types.ts b/src/Shared/Components/Switch/types.ts index 32f3d58e5..2af63d037 100644 --- a/src/Shared/Components/Switch/types.ts +++ b/src/Shared/Components/Switch/types.ts @@ -92,6 +92,10 @@ export type DTSwitchProps = { */ size?: Extract + /** + * Callback function that is called when the switch state changes. + * This function should handle the logic for toggling the switch. + */ onChange: () => void /** From 59b3760a8b72f638bd595b9e64622047d275b8c8 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 22 May 2025 17:44:59 +0530 Subject: [PATCH 053/109] feat: add types for sidePanelConfig --- src/Shared/Providers/index.ts | 2 +- src/Shared/Providers/types.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Shared/Providers/index.ts b/src/Shared/Providers/index.ts index 1ba004223..a1dcc842c 100644 --- a/src/Shared/Providers/index.ts +++ b/src/Shared/Providers/index.ts @@ -17,5 +17,5 @@ export * from './ImageSelectionUtility' export * from './MainContextProvider' export * from './ThemeProvider' -export type { MainContext, ReloadVersionConfigTypes } from './types' +export type { MainContext, ReloadVersionConfigTypes, SidePanelConfig } from './types' export * from './UserEmailProvider' diff --git a/src/Shared/Providers/types.ts b/src/Shared/Providers/types.ts index 82f1630cb..08f380005 100644 --- a/src/Shared/Providers/types.ts +++ b/src/Shared/Providers/types.ts @@ -29,6 +29,12 @@ export interface ReloadVersionConfigTypes { updateToastRef: MutableRefObject> | null isRefreshing: boolean } + +export interface SidePanelConfig { + open: boolean + docLink?: string | null +} + export interface MainContext { serverMode: SERVER_MODE setServerMode: (serverMode: SERVER_MODE) => void @@ -78,6 +84,9 @@ export interface MainContext { reloadVersionConfig: ReloadVersionConfigTypes intelligenceConfig: IntelligenceConfig setIntelligenceConfig: Dispatch> + + sidePanelConfig: SidePanelConfig + setSidePanelConfig: Dispatch> } export interface MainContextProviderProps { From 8e77a44ea98599b46a0bf613a5232ca9b661ee97 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 22 May 2025 17:45:21 +0530 Subject: [PATCH 054/109] feat: DocLink: enhance component to open non external link in SidePanel --- src/Shared/DocLink/DocLink.tsx | 62 +++++++++++++++++++++++----------- src/Shared/DocLink/types.ts | 5 ++- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/Shared/DocLink/DocLink.tsx b/src/Shared/DocLink/DocLink.tsx index 5722d05cf..f2f1800e6 100644 --- a/src/Shared/DocLink/DocLink.tsx +++ b/src/Shared/DocLink/DocLink.tsx @@ -1,5 +1,8 @@ +import { MouseEvent } from 'react' + import { Button, ButtonComponentType, ButtonVariantType, Icon } from '@Shared/Components' import { ComponentSizeType } from '@Shared/constants' +import { useMainContext } from '@Shared/Providers' import { DocLinkProps } from './types' import { getDocumentationUrl } from './utils' @@ -14,22 +17,43 @@ export const DocLink = ({ variant = ButtonVariantType.text, isEnterprise = false, isExternalLink, -}: DocLinkProps) => ( - +
+ +
)} @@ -183,8 +166,6 @@ const PageHeader = ({ Beta ) - const showingLicenseBar = getIsShowingLicenseData(licenseData) - const renderIframeButton = () => return ( @@ -271,14 +252,6 @@ const PageHeader = ({ loginCount={loginCount} /> )} - {showLogOutCard && ( - - )} {!showTabs && (
{typeof renderActionButtons === 'function' && renderActionButtons()} diff --git a/src/Shared/Components/Header/ProfileMenu.tsx b/src/Shared/Components/Header/ProfileMenu.tsx new file mode 100644 index 000000000..ccb744ff4 --- /dev/null +++ b/src/Shared/Components/Header/ProfileMenu.tsx @@ -0,0 +1,76 @@ +import { useMemo } from 'react' +import { Link } from 'react-router-dom' + +import { getAlphabetIcon } from '@Common/Helper' +import { clearCookieOnLogout } from '@Shared/Helpers' +import { useMainContext } from '@Shared/Providers' + +import { Icon } from '../Icon' +import { Popover, usePopover } from '../Popover' +import { ThemeSwitcher } from '../ThemeSwitcher' +import { ProfileMenuProps } from './types' + +export const ProfileMenu = ({ user, onClick }: ProfileMenuProps) => { + // HOOKS + const { viewIsPipelineRBACConfiguredNode } = useMainContext() + + const { open, overlayProps, popoverProps, triggerProps } = usePopover({ + id: 'profile-menu', + alignment: 'end', + width: 250, + }) + + // ELEMENTS + const triggerElement = useMemo( + () => ( + + ), + [open], + ) + + // HANDLERS + const onLogout = () => { + clearCookieOnLogout() + } + + return ( + + <> +
+
+
+

{user}

+

{user}

+
+ {getAlphabetIcon(user, 'dc__no-shrink m-0-imp fs-16 icon-dim-36')} +
+
+
+ + {viewIsPipelineRBACConfiguredNode} +
+
+ + Logout + + +
+ +
+ ) +} diff --git a/src/Shared/Components/Header/types.ts b/src/Shared/Components/Header/types.ts index 37313b42b..925965001 100644 --- a/src/Shared/Components/Header/types.ts +++ b/src/Shared/Components/Header/types.ts @@ -71,3 +71,8 @@ export enum HelpMenuItems { } export type HelpButtonActionMenuProps = ActionMenuProps + +export interface ProfileMenuProps { + user: string + onClick?: () => void +} diff --git a/src/Shared/Components/Header/utils.ts b/src/Shared/Components/Header/utils.ts index 769041b14..1896ea901 100644 --- a/src/Shared/Components/Header/utils.ts +++ b/src/Shared/Components/Header/utils.ts @@ -16,7 +16,6 @@ import { LOGIN_COUNT } from '@Common/Constants' -import { DevtronLicenseInfo, LicenseStatus } from '../License' import { COMMON_HELP_ACTION_MENU_ITEMS, ENTERPRISE_HELP_ACTION_MENU_ITEMS, @@ -43,9 +42,6 @@ export const setActionWithExpiry = (key: string, days: number): void => { localStorage.setItem(key, `${getDateInMilliseconds(days)}`) } -export const getIsShowingLicenseData = (licenseData: DevtronLicenseInfo) => - licenseData && (licenseData.licenseStatus !== LicenseStatus.ACTIVE || licenseData.isTrial) - export const getHelpActionMenuOptions = ({ isEnterprise, isTrial, diff --git a/src/Shared/Components/LogoutCard.tsx b/src/Shared/Components/LogoutCard.tsx deleted file mode 100644 index 6f2e4cece..000000000 --- a/src/Shared/Components/LogoutCard.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react' -import { useHistory } from 'react-router-dom' - -import { clearCookieOnLogout } from '@Shared/Helpers' -import { useMainContext } from '@Shared/Providers' - -import { getRandomColor, stopPropagation } from '../../Common' -import { Icon } from './Icon' -import { ThemeSwitcher } from './ThemeSwitcher' - -interface LogoutCardType { - className: string - userFirstLetter: string - setShowLogOutCard: React.Dispatch> - showLogOutCard: boolean -} - -export const LOGOUT_CARD_BASE_BUTTON_CLASS = - 'dc__unset-button-styles dc__content-space px-12 py-8 fs-13 fw-4 lh-20 cursor w-100 flex left br-4' - -const LogoutCard = ({ className, userFirstLetter, setShowLogOutCard, showLogOutCard }: LogoutCardType) => { - const history = useHistory() - const { viewIsPipelineRBACConfiguredNode } = useMainContext() - - const onLogout = () => { - clearCookieOnLogout() - history.push('/login') - } - - const toggleLogoutCard = () => { - setShowLogOutCard(!showLogOutCard) - } - - return ( -
-
-
-
-

{userFirstLetter}

-

{userFirstLetter}

-
-

- {userFirstLetter[0]} -

-
-
- - - {viewIsPipelineRBACConfiguredNode} - - -
-
-
- ) -} - -export default LogoutCard diff --git a/src/Shared/Components/ThemeSwitcher/ThemeSwitcher.component.tsx b/src/Shared/Components/ThemeSwitcher/ThemeSwitcher.component.tsx index ec4b635e2..4ed0e2166 100644 --- a/src/Shared/Components/ThemeSwitcher/ThemeSwitcher.component.tsx +++ b/src/Shared/Components/ThemeSwitcher/ThemeSwitcher.component.tsx @@ -14,32 +14,29 @@ * limitations under the License. */ -import { ReactComponent as ICCaretLeftSmall } from '@Icons/ic-caret-left-small.svg' import { getThemePreferenceText, useTheme } from '@Shared/Providers' -import { LOGOUT_CARD_BASE_BUTTON_CLASS } from '../LogoutCard' -import { ThemeSwitcherProps } from './types' +import { Icon } from '../Icon' -const ThemeSwitcher = ({ onChange }: ThemeSwitcherProps) => { +const ThemeSwitcher = () => { const { handleThemeSwitcherDialogVisibilityChange, themePreference } = useTheme() const handleShowThemeSwitcherDialog = () => { handleThemeSwitcherDialogVisibilityChange(true) - onChange() } return ( ) } diff --git a/src/Shared/Components/ThemeSwitcher/index.ts b/src/Shared/Components/ThemeSwitcher/index.ts index 5913dfa3f..9dba93c23 100644 --- a/src/Shared/Components/ThemeSwitcher/index.ts +++ b/src/Shared/Components/ThemeSwitcher/index.ts @@ -15,4 +15,3 @@ */ export { default as ThemeSwitcher } from './ThemeSwitcher.component' -export type { ThemeSwitcherProps } from './types' diff --git a/src/Shared/Components/ThemeSwitcher/types.ts b/src/Shared/Components/ThemeSwitcher/types.ts deleted file mode 100644 index a75045c9b..000000000 --- a/src/Shared/Components/ThemeSwitcher/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export interface ThemeSwitcherProps { - onChange: () => void -} From d7332523c884be8802375024f5c8575ac0e95f24 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 28 May 2025 12:28:30 +0530 Subject: [PATCH 069/109] feat: PageHeader - adjust gap size --- src/Shared/Components/Header/PageHeader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Shared/Components/Header/PageHeader.tsx b/src/Shared/Components/Header/PageHeader.tsx index b9ee2c934..28ef087ce 100644 --- a/src/Shared/Components/Header/PageHeader.tsx +++ b/src/Shared/Components/Header/PageHeader.tsx @@ -232,7 +232,7 @@ const PageHeader = ({ {markAsBeta && renderBetaTag()}
{showTabs && ( -
+
{renderIframeButton()} {typeof renderActionButtons === 'function' && renderActionButtons()} {renderLogoutHelpSection()} @@ -253,7 +253,7 @@ const PageHeader = ({ /> )} {!showTabs && ( -
+
{typeof renderActionButtons === 'function' && renderActionButtons()} {renderIframeButton()} {renderLogoutHelpSection()} From c0b9705af788585b7cb2fe63179eaf5cb358698c Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Tue, 27 May 2025 16:52:36 +0530 Subject: [PATCH 070/109] feat: Remove DiffViewer component, use CodeEditor to render collapse unchanged readOnly diffView --- .../DeploymentHistoryDiffView.tsx | 14 +- .../Components/CodeEditor/CodeEditor.tsx | 12 +- .../CodeEditor/CodeEditorRenderer.tsx | 58 +++++---- .../Components/CodeEditor/codeEditor.scss | 14 ++ src/Shared/Components/CodeEditor/types.ts | 8 ++ .../DeploymentConfigDiff.utils.tsx | 22 +--- .../DeploymentConfigDiffMain.tsx | 39 +++--- .../DiffViewer/DiffViewer.component.tsx | 76 ----------- src/Shared/Components/DiffViewer/constants.ts | 120 ------------------ src/Shared/Components/DiffViewer/index.ts | 17 --- src/Shared/Components/DiffViewer/types.ts | 28 ---- src/Shared/Components/index.ts | 1 - 12 files changed, 98 insertions(+), 311 deletions(-) delete mode 100644 src/Shared/Components/DiffViewer/DiffViewer.component.tsx delete mode 100644 src/Shared/Components/DiffViewer/constants.ts delete mode 100644 src/Shared/Components/DiffViewer/index.ts delete mode 100644 src/Shared/Components/DiffViewer/types.ts diff --git a/src/Shared/Components/CICDHistory/DeploymentHistoryConfigDiff/DeploymentHistoryDiffView.tsx b/src/Shared/Components/CICDHistory/DeploymentHistoryConfigDiff/DeploymentHistoryDiffView.tsx index cc9448003..1116e3324 100644 --- a/src/Shared/Components/CICDHistory/DeploymentHistoryConfigDiff/DeploymentHistoryDiffView.tsx +++ b/src/Shared/Components/CICDHistory/DeploymentHistoryConfigDiff/DeploymentHistoryDiffView.tsx @@ -19,8 +19,6 @@ import { useParams } from 'react-router-dom' import Tippy from '@tippyjs/react' import { CodeEditor } from '@Shared/Components/CodeEditor' -import { renderDiffViewNoDifferenceState } from '@Shared/Components/DeploymentConfigDiff' -import { DiffViewer } from '@Shared/Components/DiffViewer' import { ReactComponent as Info } from '../../../../Assets/Icon/ic-info-filled.svg' import { ReactComponent as ViewVariablesIcon } from '../../../../Assets/Icon/ic-view-variable-toggle.svg' @@ -71,10 +69,14 @@ const DeploymentHistoryDiffView = ({ const renderDeploymentDiffViaCodeEditor = () => previousConfigAvailable ? ( - ) : ( ({ onChange, onOriginalValueChange, onModifiedValueChange, - readOnly, placeholder, diffView, loading, @@ -88,8 +87,13 @@ const CodeEditor = ({ onBlur, onFocus, autoFocus, - disableSearch = false, + collapseUnchangedDiffView = false, + ...resProps }: CodeEditorProps) => { + // DERIVED PROPS + const disableSearch = (collapseUnchangedDiffView || resProps.disableSearch) ?? false + const readOnly = (collapseUnchangedDiffView || resProps.readOnly) ?? false + // HOOKS const { appTheme } = useTheme() @@ -209,7 +213,7 @@ const CodeEditor = ({ { key: 'Escape', run: blurOnEscape, stopPropagation: true }, ]), indentationMarkers(), - getLanguageExtension(mode), + getLanguageExtension(mode, collapseUnchangedDiffView), foldingCompartment.of(foldConfig), lintGutter(), search({ @@ -277,6 +281,8 @@ const CodeEditor = ({ modifiedViewExtensions={modifiedViewExtensions} extensions={extensions} diffMinimapExtensions={diffMinimapExtensions} + collapseUnchanged={collapseUnchangedDiffView} + disableMinimap={collapseUnchangedDiffView} /> ) diff --git a/src/Shared/Components/CodeEditor/CodeEditorRenderer.tsx b/src/Shared/Components/CodeEditor/CodeEditorRenderer.tsx index 7ec4f6408..aab206b7f 100644 --- a/src/Shared/Components/CodeEditor/CodeEditorRenderer.tsx +++ b/src/Shared/Components/CodeEditor/CodeEditorRenderer.tsx @@ -46,6 +46,8 @@ export const CodeEditorRenderer = ({ extensions, autoFocus, diffMinimapExtensions, + collapseUnchanged = false, + disableMinimap = false, }: CodeEditorRendererProps) => { // CONTEXTS const { value, lhsValue, diffMode } = useCodeEditorContext() @@ -160,9 +162,11 @@ export const CodeEditorRenderer = ({ handleLhsOnChange(val, vu) } - // Using `diffMinimapRef` instead of `diffMinimapInstance` since this extension captures the initial reference in a closure. - // Changes to `diffMinimapInstance` won't be reflected after initialization, so we rely on `diffMinimapRef.current` for updates. - updateDiffMinimapValues(diffMinimapRef.current, vu.transactions, 'a') + if (!disableMinimap) { + // Using `diffMinimapRef` instead of `diffMinimapInstance` since this extension captures the initial reference in a closure. + // Changes to `diffMinimapInstance` won't be reflected after initialization, so we rely on `diffMinimapRef.current` for updates. + updateDiffMinimapValues(diffMinimapRef.current, vu.transactions, 'a') + } }) const modifiedUpdateListener = EditorView.updateListener.of((vu: ViewUpdate) => { @@ -172,9 +176,11 @@ export const CodeEditorRenderer = ({ handleOnChange(val, vu) } - // Using `diffMinimapRef` instead of `diffMinimapInstance` since this extension captures the initial reference in a closure. - // Changes to `diffMinimapInstance` won't be reflected after initialization, so we rely on `diffMinimapRef.current` for updates. - updateDiffMinimapValues(diffMinimapRef.current, vu.transactions, 'b') + if (!disableMinimap) { + // Using `diffMinimapRef` instead of `diffMinimapInstance` since this extension captures the initial reference in a closure. + // Changes to `diffMinimapInstance` won't be reflected after initialization, so we rely on `diffMinimapRef.current` for updates. + updateDiffMinimapValues(diffMinimapRef.current, vu.transactions, 'b') + } }) useEffect(() => { @@ -196,11 +202,12 @@ export const CodeEditorRenderer = ({ ...(!readOnly ? { revertControls: 'a-to-b', renderRevertControl: getRevertControlButton } : {}), diffConfig: { scanLimit, timeout: 5000 }, parent: codeMirrorMergeParentRef.current, + collapseUnchanged: collapseUnchanged ? {} : undefined, }) setCodeMirrorMergeInstance(codeMirrorMergeView) // MINIMAP INITIALIZATION - if (codeMirrorMergeView && diffMinimapParentRef.current) { + if (!disableMinimap && codeMirrorMergeView && diffMinimapParentRef.current) { diffMinimapInstance?.destroy() diffMinimapRef.current?.destroy() @@ -228,7 +235,7 @@ export const CodeEditorRenderer = ({ setDiffMinimapInstance(null) diffMinimapRef.current = null } - }, [loading, codemirrorMergeKey, diffMode]) + }, [loading, codemirrorMergeKey, diffMode, collapseUnchanged, disableMinimap]) // Sync external changes of `lhsValue` and `value` state to the diff-editor state. useEffect(() => { @@ -251,14 +258,19 @@ export const CodeEditorRenderer = ({ // SCALING FACTOR UPDATER useEffect(() => { - setTimeout(() => { - setScalingFactor( - codeMirrorMergeInstance - ? Math.min(codeMirrorMergeInstance.dom.clientHeight / codeMirrorMergeInstance.dom.scrollHeight, 1) - : 1, - ) - }, 100) - }, [lhsValue, value, codeMirrorMergeInstance]) + if (!disableMinimap) { + setTimeout(() => { + setScalingFactor( + codeMirrorMergeInstance + ? Math.min( + codeMirrorMergeInstance.dom.clientHeight / codeMirrorMergeInstance.dom.scrollHeight, + 1, + ) + : 1, + ) + }, 100) + } + }, [lhsValue, value, codeMirrorMergeInstance, disableMinimap]) const { codeEditorClassName, codeEditorHeight, codeEditorParentClassName } = getCodeEditorHeight(height) @@ -286,12 +298,14 @@ export const CodeEditorRenderer = ({ ref={codeMirrorMergeParentRef} className={`cm-merge-theme flex-grow-1 h-100 dc__overflow-hidden ${readOnly ? 'code-editor__read-only' : ''}`} /> - + {!disableMinimap && ( + + )}
) : (
= DiffView extends true @@ -146,6 +152,8 @@ export type CodeEditorRendererProps = Required< modifiedViewExtensions: ReactCodeMirrorProps['extensions'] extensions: ReactCodeMirrorProps['extensions'] diffMinimapExtensions: ReactCodeMirrorProps['extensions'] + collapseUnchanged?: boolean + disableMinimap?: boolean } export interface DiffMinimapProps extends Pick { diff --git a/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiff.utils.tsx b/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiff.utils.tsx index b57c4cbf4..46c27b858 100644 --- a/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiff.utils.tsx +++ b/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiff.utils.tsx @@ -20,7 +20,7 @@ import { ReactComponent as ICCheck } from '@Icons/ic-check.svg' import { ReactComponent as ICCheckCircleDots } from '@Icons/ic-check-circle-dots.svg' import { ReactComponent as ICEditFile } from '@Icons/ic-edit-file.svg' import { ReactComponent as ICFileCode } from '@Icons/ic-file-code.svg' -import { deepEqual, noop, YAMLStringify } from '@Common/Helper' +import { deepEqual, YAMLStringify } from '@Common/Helper' import { AppEnvDeploymentConfigListParams, DeploymentConfigDiffProps, @@ -28,7 +28,6 @@ import { DeploymentHistoryDetail, DeploymentHistorySingleValue, DiffHeadingDataType, - GenericSectionErrorState, prepareHistoryData, } from '@Shared/Components' import { DEPLOYMENT_HISTORY_CONFIGURATION_LIST_MAP } from '@Shared/constants' @@ -47,7 +46,6 @@ import { TemplateListDTO, TemplateListType, } from '../../Services/app.types' -import { DiffViewerProps } from '../DiffViewer/types' export const getDeploymentTemplateData = (data: DeploymentTemplateDTO) => { const parsedDraftData = JSON.parse(data?.deploymentDraftData?.configData[0].draftMetadata.data || null) @@ -911,21 +909,3 @@ export const getDefaultVersionAndPreviousDeploymentOptions = (data: TemplateList previousDeployments: [], }, ) - -export const renderDiffViewNoDifferenceState = ( - lhsValue: string, - rhsValue: string, -): DiffViewerProps['codeFoldMessageRenderer'] => - lhsValue === rhsValue - ? () => ( - - ) - : null diff --git a/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiffMain.tsx b/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiffMain.tsx index 595f437a5..9cf072fb2 100644 --- a/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiffMain.tsx +++ b/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiffMain.tsx @@ -18,14 +18,14 @@ import { Fragment, useEffect, useState } from 'react' import { ReactComponent as ICSort } from '@Icons/ic-arrow-up-down.svg' import { ReactComponent as ICSortArrowDown } from '@Icons/ic-sort-arrow-down.svg' -import { SortingOrder } from '@Common/Constants' +import { MODES, SortingOrder } from '@Common/Constants' import ErrorScreenManager from '@Common/ErrorScreenManager' import { Progressing } from '@Common/Progressing' -import { DiffViewer } from '@Shared/Components/DiffViewer' import { ComponentSizeType } from '@Shared/constants' import { Button, ButtonStyleType, ButtonVariantType } from '../Button' import { DeploymentHistoryDiffView } from '../CICDHistory' +import { CodeEditor } from '../CodeEditor' import { SelectPicker } from '../SelectPicker' import { ToggleResolveScopedVariables } from '../ToggleResolveScopedVariables' import { @@ -34,7 +34,6 @@ import { DeploymentConfigDiffSelectPickerProps, DeploymentConfigDiffState, } from './DeploymentConfigDiff.types' -import { renderDiffViewNoDifferenceState } from './DeploymentConfigDiff.utils' import { DeploymentConfigDiffAccordion } from './DeploymentConfigDiffAccordion' export const DeploymentConfigDiffMain = ({ @@ -191,19 +190,25 @@ export const DeploymentConfigDiffMain = ({ hideDiffState={hideDiffState} > {singleView ? ( - + <> +
+
+

{primaryHeading}

+
+
+

{secondaryHeading}

+
+
+ + ) : (
{primaryHeading && secondaryHeading && ( @@ -245,7 +250,7 @@ export const DeploymentConfigDiffMain = ({ } return ( -
+
{!!headerText &&

{headerText}

} diff --git a/src/Shared/Components/DiffViewer/DiffViewer.component.tsx b/src/Shared/Components/DiffViewer/DiffViewer.component.tsx deleted file mode 100644 index 863d20cca..000000000 --- a/src/Shared/Components/DiffViewer/DiffViewer.component.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued' - -import { diffViewerStyles } from './constants' -import { DiffViewerProps, DiffViewTitleWrapperProps } from './types' - -const DiffViewTitleWrapper = ({ title }: DiffViewTitleWrapperProps) =>
{title}
- -/** - * Component for showing diff between two string or object. - * - * Note: Pass down the object as stringified for optimized performance. - * - * @example Usage - * - * ```tsx - * - * ``` - * - * @example With left/right title for lhs/rhs - * - * ```tsx - * Title for RHS - * } - * /> - * ``` - * - * @example With custom message for folded code - * Note: the entire section would be clickable - * - * ```tsx - * Custom text} - * /> - * ``` - */ -const DiffViewer = ({ oldValue, newValue, leftTitle, rightTitle, ...props }: DiffViewerProps) => ( - : null} - rightTitle={rightTitle ? : null} - compareMethod={DiffMethod.WORDS} - styles={diffViewerStyles} - /> -) - -export default DiffViewer diff --git a/src/Shared/Components/DiffViewer/constants.ts b/src/Shared/Components/DiffViewer/constants.ts deleted file mode 100644 index 1c7078dbf..000000000 --- a/src/Shared/Components/DiffViewer/constants.ts +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Default variables and style keys - -import { ReactDiffViewerProps } from 'react-diff-viewer-continued' - -export const diffViewerStyles: ReactDiffViewerProps['styles'] = { - variables: { - light: { - diffViewerBackground: 'var(--bg-primary)', - diffViewerColor: 'var(--N900)', - addedBackground: 'var(--G50)', - addedColor: 'var(--N900)', - removedBackground: 'var(--R50)', - removedColor: 'var(--N900)', - wordAddedBackground: 'var(--G200)', - wordRemovedBackground: 'var(--R200)', - addedGutterBackground: 'var(--G100)', - removedGutterBackground: 'var(--R100)', - gutterBackground: 'var(--bg-secondary)', - gutterBackgroundDark: 'var(--bg-secondary)', - highlightBackground: 'var(--N100)', - highlightGutterBackground: 'var(--N100)', - codeFoldGutterBackground: 'var(--B100)', - codeFoldBackground: 'var(--B50)', - emptyLineBackground: 'var(--bg-primary)', - gutterColor: 'var(--N500)', - addedGutterColor: 'var(--N700)', - removedGutterColor: 'var(--N700)', - codeFoldContentColor: 'var(--B600)', - diffViewerTitleBackground: 'var(--N100)', - diffViewerTitleColor: 'var(--N700)', - diffViewerTitleBorderColor: 'var(--N200)', - }, - }, - diffContainer: { - fontSize: '14px', - fontWeight: 400, - lineHeight: '20px', - - pre: { - fontSize: '14px', - lineHeight: '20px', - fontFamily: 'Inconsolata, monospace', - wordBreak: 'break-word', - // Reset for styling from patternfly - padding: 0, - margin: 0, - backgroundColor: 'transparent', - border: 'none', - borderRadius: 0, - }, - }, - marker: { - pre: { - display: 'none', - }, - }, - gutter: { - padding: `0 6px`, - minWidth: '36px', - // Cursor would be default for all cases in gutter till we don't support highlighting - cursor: 'default', - - pre: { - opacity: 1, - }, - }, - wordDiff: { - padding: 0, - }, - wordAdded: { - paddingInline: '2px', - lineHeight: '16px', - }, - wordRemoved: { - paddingInline: '2px', - lineHeight: '16px', - }, - codeFold: { - fontSize: '14px', - fontWeight: 400, - lineHeight: '20px', - height: '32px', - - a: { - textDecoration: 'none !important', - }, - }, - codeFoldGutter: { - '+ td': { - width: '12px', - }, - }, - titleBlock: { - padding: '8px 12px', - fontSize: '12px', - lineHeight: '20px', - fontWeight: 600, - borderBottom: 'none', - - pre: { - fontFamily: 'Open Sans', - }, - }, -} diff --git a/src/Shared/Components/DiffViewer/index.ts b/src/Shared/Components/DiffViewer/index.ts deleted file mode 100644 index 0abf9bc28..000000000 --- a/src/Shared/Components/DiffViewer/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { default as DiffViewer } from './DiffViewer.component' diff --git a/src/Shared/Components/DiffViewer/types.ts b/src/Shared/Components/DiffViewer/types.ts deleted file mode 100644 index 8b4958c6e..000000000 --- a/src/Shared/Components/DiffViewer/types.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { ReactNode } from 'react' -import { ReactDiffViewerProps } from 'react-diff-viewer-continued' - -export interface DiffViewerProps - extends Pick { - leftTitle?: ReactDiffViewerProps['leftTitle'] | ReactNode - rightTitle?: ReactDiffViewerProps['rightTitle'] | ReactNode -} - -export interface DiffViewTitleWrapperProps { - title: DiffViewerProps['leftTitle'] -} diff --git a/src/Shared/Components/index.ts b/src/Shared/Components/index.ts index c3affe06a..47ac2f913 100644 --- a/src/Shared/Components/index.ts +++ b/src/Shared/Components/index.ts @@ -42,7 +42,6 @@ export * from './DatePicker' export * from './DeploymentConfigDiff' export * from './DeploymentStatusBreakdown' export * from './DetectBottom' -export * from './DiffViewer' export * from './DynamicDataTable' export * from './EditableTextArea' export * from './EditImageFormField' From 8c3c9ecf0c4bdb2f91a99603e0aef5846e71a116 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Tue, 27 May 2025 17:18:48 +0530 Subject: [PATCH 071/109] feat: add delay to reInitHeight call in ResizableTagTextArea for improved rendering --- src/Common/CustomTagSelector/ResizableTagTextArea.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Common/CustomTagSelector/ResizableTagTextArea.tsx b/src/Common/CustomTagSelector/ResizableTagTextArea.tsx index 2a15f9b33..432d2dc17 100644 --- a/src/Common/CustomTagSelector/ResizableTagTextArea.tsx +++ b/src/Common/CustomTagSelector/ResizableTagTextArea.tsx @@ -89,7 +89,9 @@ export const ResizableTagTextArea = ({ } useEffect(() => { - reInitHeight() + setTimeout(() => { + reInitHeight() + }, 100) }, []) useThrottledEffect(reInitHeight, 500, [value]) From b48843d7a487634a469e9e9b8da8608e3e5e2aa8 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Tue, 27 May 2025 17:19:12 +0530 Subject: [PATCH 072/109] feat: CodeEditor - increase font size of collapsed lines --- src/Shared/Components/CodeEditor/codeEditor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/Components/CodeEditor/codeEditor.scss b/src/Shared/Components/CodeEditor/codeEditor.scss index c7861a351..900144ee4 100644 --- a/src/Shared/Components/CodeEditor/codeEditor.scss +++ b/src/Shared/Components/CodeEditor/codeEditor.scss @@ -248,7 +248,7 @@ .cm-collapsedLines { padding: 6px 12px; background: var(--B50); - font-size: 13px; + font-size: 14px; line-height: 20px; color: var(--B500); From 97ec2765fa0f828a99d8a4820dc284bd29631077 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Tue, 27 May 2025 17:30:18 +0530 Subject: [PATCH 073/109] feat: remove react-diff-viewer-continued dependency --- package-lock.json | 84 ++++++++++++++--------------------------------- package.json | 1 - 2 files changed, 25 insertions(+), 60 deletions(-) diff --git a/package-lock.json b/package-lock.json index 792a15a00..2a857b99d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,6 @@ "qrcode.react": "^4.2.0", "react-canvas-confetti": "^2.0.7", "react-dates": "^21.8.0", - "react-diff-viewer-continued": "^3.4.0", "react-draggable": "^4.4.5", "react-international-phone": "^4.5.0", "react-virtualized-sticky-tree": "^3.0.0-beta18", @@ -777,6 +776,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==", + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", @@ -794,12 +794,14 @@ "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "peer": true }, "node_modules/@emotion/cache": { "version": "11.13.1", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", + "peer": true, "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", @@ -808,23 +810,11 @@ "stylis": "4.2.0" } }, - "node_modules/@emotion/css": { - "version": "11.13.4", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.4.tgz", - "integrity": "sha512-CthbOD5EBw+iN0rfM96Tuv5kaZN4nxPyYDvGUs0bc7wZBBiU/0mse+l+0O9RshW2d+v5HH1cme+BAbLJ/3Folw==", - "license": "MIT", - "dependencies": { - "@emotion/babel-plugin": "^11.12.0", - "@emotion/cache": "^11.13.0", - "@emotion/serialize": "^1.3.0", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.0" - } - }, "node_modules/@emotion/hash": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "peer": true }, "node_modules/@emotion/is-prop-valid": { "version": "0.8.8", @@ -846,7 +836,8 @@ "node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "peer": true }, "node_modules/@emotion/react": { "version": "11.13.3", @@ -876,6 +867,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz", "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==", + "peer": true, "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", @@ -887,12 +879,14 @@ "node_modules/@emotion/sheet": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "peer": true }, "node_modules/@emotion/unitless": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "peer": true }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.1.0", @@ -906,12 +900,14 @@ "node_modules/@emotion/utils": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz", - "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==" + "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==", + "peer": true }, "node_modules/@emotion/weak-memoize": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "peer": true }, "node_modules/@esbuild-plugins/node-globals-polyfill": { "version": "0.2.3", @@ -4907,6 +4903,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "peer": true, "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -5160,12 +5157,6 @@ "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", "license": "MIT" }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -5826,15 +5817,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -7090,7 +7072,8 @@ "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "peer": true }, "node_modules/find-up": { "version": "5.0.0", @@ -9112,7 +9095,8 @@ "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "peer": true }, "node_modules/merge-stream": { "version": "2.0.0", @@ -10249,26 +10233,6 @@ "react-with-direction": "^1.3.1" } }, - "node_modules/react-diff-viewer-continued": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-3.4.0.tgz", - "integrity": "sha512-kMZmUyb3Pv5L9vUtCfIGYsdOHs8mUojblGy1U1Sm0D7FhAOEsH9QhnngEIRo5hXWIPNGupNRJls1TJ6Eqx84eg==", - "license": "MIT", - "dependencies": { - "@emotion/css": "^11.11.2", - "classnames": "^2.3.2", - "diff": "^5.1.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">= 8" - }, - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -11151,6 +11115,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -11488,7 +11453,8 @@ "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "peer": true }, "node_modules/supports-color": { "version": "7.2.0", diff --git a/package.json b/package.json index 83e709071..72f6d3083 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,6 @@ "qrcode.react": "^4.2.0", "react-canvas-confetti": "^2.0.7", "react-dates": "^21.8.0", - "react-diff-viewer-continued": "^3.4.0", "react-draggable": "^4.4.5", "react-international-phone": "^4.5.0", "react-virtualized-sticky-tree": "^3.0.0-beta18", From 33d7682ad12bc9d49e9a54f5219f787b5604585f Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 28 May 2025 12:35:46 +0530 Subject: [PATCH 074/109] chore(version): bump to 1.14.1-pre-4 --- 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 2a857b99d..9d1effc04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-3", + "version": "1.14.1-pre-4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-3", + "version": "1.14.1-pre-4", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 72f6d3083..cb0e32973 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-3", + "version": "1.14.1-pre-4", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From 896f9347972f39b8297a25c013478e1f3e094787 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 28 May 2025 13:02:16 +0530 Subject: [PATCH 075/109] feat: add enterprise and fe-lib availability flags to MainContext --- src/Shared/Providers/types.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Shared/Providers/types.ts b/src/Shared/Providers/types.ts index 17c0b8467..fd2b10217 100644 --- a/src/Shared/Providers/types.ts +++ b/src/Shared/Providers/types.ts @@ -88,7 +88,16 @@ export interface MainContext { sidePanelConfig: SidePanelConfig setSidePanelConfig: Dispatch> + /** + * Indicates whether the current Devtron instance is running as an Enterprise edition. \ + * This flag is determined based on server-side configuration. + */ isEnterprise: boolean + /** + * Indicates whether the fe-lib modules are available in the current instance. \ + * Used to conditionally render or enable features that depend on fe-lib + */ + isFELibAvailable: boolean } export interface MainContextProviderProps { From 2ca529c04ebebb764950635a9987a5a96c1ee8ff Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 28 May 2025 13:03:47 +0530 Subject: [PATCH 076/109] chore(version): bump to 1.14.1-pre-5 --- 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 9d1effc04..898f45fa6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-4", + "version": "1.14.1-pre-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-4", + "version": "1.14.1-pre-5", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index cb0e32973..9324c1228 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-4", + "version": "1.14.1-pre-5", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From e9cb53208e712ab66216acb8dea193d1070647f9 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 28 May 2025 15:30:26 +0530 Subject: [PATCH 077/109] feat: ActionMenu - remove footer bg, HelpButton - add footer bg --- src/Shared/Components/ActionMenu/ActionMenu.component.tsx | 2 +- src/Shared/Components/Header/HelpButton.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Shared/Components/ActionMenu/ActionMenu.component.tsx b/src/Shared/Components/ActionMenu/ActionMenu.component.tsx index 938301e16..403aff9dd 100644 --- a/src/Shared/Components/ActionMenu/ActionMenu.component.tsx +++ b/src/Shared/Components/ActionMenu/ActionMenu.component.tsx @@ -132,7 +132,7 @@ export const ActionMenu = ({ )} {footerConfig && ( -
+
)} diff --git a/src/Shared/Components/Header/HelpButton.tsx b/src/Shared/Components/Header/HelpButton.tsx index f5c952907..75e77c80d 100644 --- a/src/Shared/Components/Header/HelpButton.tsx +++ b/src/Shared/Components/Header/HelpButton.tsx @@ -17,7 +17,7 @@ const CheckForUpdates = ({ serverInfo, fetchingServerInfo, }: Pick) => ( -
+
{fetchingServerInfo ? (

Checking version

) : ( @@ -110,7 +110,7 @@ export const HelpButton = ({ serverInfo, fetchingServerInfo, onClick }: HelpButt onOpen={setIsActionMenuOpen} {...(serverInfo?.installationType === InstallationType.OSS_HELM ? { - menuListFooterConfig: { + footerConfig: { type: 'customNode', value: ( From 7fa7ac14f28ebb95e2c360952eb17be33fca69ee Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Wed, 28 May 2025 16:09:45 +0530 Subject: [PATCH 078/109] feat(CodeEditor): add onSearchBarAction prop with description for search functionality feat(Helpers): update smoothScrollToTop easing function to use CUBIC_BEZIER_CURVE constant feat(constants): define CUBIC_BEZIER_CURVE constant for consistent animation easing --- src/Shared/Components/CodeEditor/types.ts | 3 +++ src/Shared/Helpers.tsx | 4 ++-- src/Shared/constants.tsx | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Shared/Components/CodeEditor/types.ts b/src/Shared/Components/CodeEditor/types.ts index 97a667256..e0898722a 100644 --- a/src/Shared/Components/CodeEditor/types.ts +++ b/src/Shared/Components/CodeEditor/types.ts @@ -79,6 +79,9 @@ export type CodeEditorProps = { diffView?: DiffView theme?: AppThemeType onOpenSearchPanel?: () => void + /** + * This method is triggered when user types something in the search/replace bar or applies a search or replace action. + */ onSearchBarAction?: () => void } & CodeEditorPropsBasedOnDiffView diff --git a/src/Shared/Helpers.tsx b/src/Shared/Helpers.tsx index b6d5878bf..b11bccb57 100644 --- a/src/Shared/Helpers.tsx +++ b/src/Shared/Helpers.tsx @@ -53,7 +53,7 @@ import { } from '../Common' import { getAggregator } from '../Pages' import { AggregatedNodes, PodMetadatum } from './Components' -import { UNSAVED_CHANGES_PROMPT_MESSAGE } from './constants' +import { CUBIC_BEZIER_CURVE, UNSAVED_CHANGES_PROMPT_MESSAGE } from './constants' import { AggregationKeys, BorderConfigType, @@ -706,7 +706,7 @@ export const smoothScrollToTop = (scrollContainer: HTMLElement, targetPosition: const start = scrollContainer.scrollTop const controls = animate(start, targetPosition, { - ease: [0.33, 1, 0.68, 1], + ease: CUBIC_BEZIER_CURVE, onUpdate: (value) => { scrollContainer.scrollTop = value }, diff --git a/src/Shared/constants.tsx b/src/Shared/constants.tsx index 501c21c70..948a007d9 100644 --- a/src/Shared/constants.tsx +++ b/src/Shared/constants.tsx @@ -580,3 +580,4 @@ export const DEPLOYMENT_STAGE_TO_NODE_MAP: Readonly Date: Wed, 28 May 2025 16:54:00 +0530 Subject: [PATCH 079/109] refactor: rename search panel callback and update related usages --- src/Shared/Components/CodeEditor/CodeEditor.tsx | 6 +++--- .../Components/CodeEditor/Commands/findAndReplace.ts | 12 ++++++------ .../CodeEditor/Extensions/findAndReplace.tsx | 8 ++++++-- src/Shared/Components/CodeEditor/types.ts | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Shared/Components/CodeEditor/CodeEditor.tsx b/src/Shared/Components/CodeEditor/CodeEditor.tsx index 6aff2634f..42e7e65bf 100644 --- a/src/Shared/Components/CodeEditor/CodeEditor.tsx +++ b/src/Shared/Components/CodeEditor/CodeEditor.tsx @@ -87,7 +87,7 @@ const CodeEditor = ({ onBlur, onFocus, autoFocus, - onOpenSearchPanel = noop, + onSearchPanelOpen = noop, onSearchBarAction = noop, collapseUnchangedDiffView = false, ...resProps @@ -210,10 +210,10 @@ const CodeEditor = ({ keymap.of([ ...vscodeKeymap.filter(({ key }) => key !== 'Mod-Alt-Enter' && key !== 'Mod-Enter' && key !== 'Mod-f'), ...(!disableSearch - ? [{ key: 'Mod-f', run: getOpenSearchPanel(onOpenSearchPanel), scope: 'editor search-panel' }] + ? [{ key: 'Mod-f', run: getOpenSearchPanel(onSearchPanelOpen), scope: 'editor search-panel' }] : []), { key: 'Mod-Enter', run: replaceAll, scope: 'editor search-panel' }, - { key: 'Mod-Alt-f', run: getOpenSearchPanelWithReplace(onOpenSearchPanel), scope: 'editor search-panel' }, + { key: 'Mod-Alt-f', run: getOpenSearchPanelWithReplace(onSearchPanelOpen), scope: 'editor search-panel' }, { key: 'Escape', run: blurOnEscape, stopPropagation: true }, ]), indentationMarkers(), diff --git a/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts b/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts index 94d0c9397..18f2017f2 100644 --- a/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts +++ b/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts @@ -37,19 +37,19 @@ export const getShowReplaceField = (state: EditorState) => { return curState || false } -export const getOpenSearchPanel: (onOpenSearchPanel: () => void) => Command = - (onOpenSearchPanel: () => void) => (view: EditorView) => { +export const getOpenSearchPanel: (onSearchPanelOpen: () => void) => Command = + (onSearchPanelOpen: () => void) => (view: EditorView) => { view.dispatch({ effects: [setShowReplaceField.of(searchPanelOpen(view.state) ? getShowReplaceField(view.state) : false)], }) cmOpenSearchPanel(view) - onOpenSearchPanel() + onSearchPanelOpen() return true } -export const getOpenSearchPanelWithReplace: (onOpenSearchPanel: () => void) => Command = - (onOpenSearchPanel: () => void) => (view: EditorView) => { - getOpenSearchPanel(onOpenSearchPanel)(view) +export const getOpenSearchPanelWithReplace: (onSearchPanelOpen: () => void) => Command = + (onSearchPanelOpen: () => void) => (view: EditorView) => { + getOpenSearchPanel(onSearchPanelOpen)(view) view.dispatch({ effects: [setShowReplaceField.of(!view.state.readOnly && true)] }) return true } diff --git a/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx b/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx index 1bc20cfba..9d7658cd7 100644 --- a/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx +++ b/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx @@ -121,8 +121,8 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction useEffect(() => { if (!defaultQuery.eq(query)) { - setMatchesCount(getUpdatedSearchMatchesCount(defaultQuery, view)) onSearchBarAction?.() + setMatchesCount(getUpdatedSearchMatchesCount(defaultQuery, view)) setQuery(defaultQuery) } }, [defaultQuery]) @@ -146,6 +146,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction const onNext = (e?: MouseEvent) => { e?.preventDefault() e?.stopPropagation() + onSearchBarAction?.() findNext(view) setMatchesCount(getUpdatedSearchMatchesCount(query, view)) } @@ -153,6 +154,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction const onPrevious = (e?: MouseEvent) => { e?.preventDefault() e?.stopPropagation() + onSearchBarAction?.() findPrevious(view) setMatchesCount(getUpdatedSearchMatchesCount(query, view)) } @@ -164,7 +166,6 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction const onFindKeyDown = (e: ReactKeyboardEvent) => { e.stopPropagation() if (e.key === 'Enter') { - onSearchBarAction?.() e.preventDefault() if (e.shiftKey) { onPrevious() @@ -182,6 +183,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction e.stopPropagation() if (e.key === 'Enter') { e.preventDefault() + onSearchBarAction?.() replaceNext(view) } } @@ -191,10 +193,12 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction } const onReplaceTextClick = () => { + onSearchBarAction?.() replaceNext(view) } const onReplaceTextAllClick = () => { + onSearchBarAction?.() replaceAll(view) } diff --git a/src/Shared/Components/CodeEditor/types.ts b/src/Shared/Components/CodeEditor/types.ts index 7d5cb89ff..edb7a5b92 100644 --- a/src/Shared/Components/CodeEditor/types.ts +++ b/src/Shared/Components/CodeEditor/types.ts @@ -84,7 +84,7 @@ export type CodeEditorProps = { disableSearch?: boolean diffView?: DiffView theme?: AppThemeType - onOpenSearchPanel?: () => void + onSearchPanelOpen?: () => void /** * This method is triggered when user types something in the search/replace bar or applies a search or replace action. */ From b80e095380807c283ad136b98f281d0d07e42cd5 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Wed, 28 May 2025 17:15:57 +0530 Subject: [PATCH 080/109] fix(FindReplace): ensure onSearchBarAction is always called without optional chaining --- .../CodeEditor/Extensions/findAndReplace.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx b/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx index 9d7658cd7..675ed1402 100644 --- a/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx +++ b/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx @@ -111,7 +111,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction wholeWord, }) - onSearchBarAction?.() + onSearchBarAction() if (!newQuery.eq(query)) { setQuery(newQuery) view.dispatch({ effects: setSearchQuery.of(newQuery) }) @@ -121,7 +121,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction useEffect(() => { if (!defaultQuery.eq(query)) { - onSearchBarAction?.() + onSearchBarAction() setMatchesCount(getUpdatedSearchMatchesCount(defaultQuery, view)) setQuery(defaultQuery) } @@ -146,7 +146,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction const onNext = (e?: MouseEvent) => { e?.preventDefault() e?.stopPropagation() - onSearchBarAction?.() + onSearchBarAction() findNext(view) setMatchesCount(getUpdatedSearchMatchesCount(query, view)) } @@ -154,7 +154,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction const onPrevious = (e?: MouseEvent) => { e?.preventDefault() e?.stopPropagation() - onSearchBarAction?.() + onSearchBarAction() findPrevious(view) setMatchesCount(getUpdatedSearchMatchesCount(query, view)) } @@ -183,7 +183,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction e.stopPropagation() if (e.key === 'Enter') { e.preventDefault() - onSearchBarAction?.() + onSearchBarAction() replaceNext(view) } } @@ -193,12 +193,12 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction } const onReplaceTextClick = () => { - onSearchBarAction?.() + onSearchBarAction() replaceNext(view) } const onReplaceTextAllClick = () => { - onSearchBarAction?.() + onSearchBarAction() replaceAll(view) } From a8de0e0603727571079b8271188aa4c977ff4dd7 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 28 May 2025 17:30:09 +0530 Subject: [PATCH 081/109] feat: ActionMenu - add support for trailing item --- .../ActionMenu/ActionMenu.component.tsx | 8 +-- .../Components/ActionMenu/ActionMenuItem.tsx | 65 +++++++++++++++---- src/Shared/Components/ActionMenu/types.ts | 43 ++++++++++-- src/Shared/Components/Switch/types.ts | 4 +- 4 files changed, 100 insertions(+), 20 deletions(-) diff --git a/src/Shared/Components/ActionMenu/ActionMenu.component.tsx b/src/Shared/Components/ActionMenu/ActionMenu.component.tsx index 403aff9dd..07186aadd 100644 --- a/src/Shared/Components/ActionMenu/ActionMenu.component.tsx +++ b/src/Shared/Components/ActionMenu/ActionMenu.component.tsx @@ -4,7 +4,7 @@ import { CustomInput } from '../CustomInput' import { Popover } from '../Popover' import { SelectPickerMenuListFooter } from '../SelectPicker/common' import { ActionMenuItem } from './ActionMenuItem' -import { ActionMenuItemType, ActionMenuProps } from './types' +import { ActionMenuItemProps, ActionMenuProps } from './types' import { useActionMenu } from './useActionMenu.hook' import './actionMenu.scss' @@ -51,8 +51,8 @@ export const ActionMenu = ({ // HANDLERS const handleOptionMouseEnter = (index: number) => () => setFocusedIndex(index) - const handleOptionOnClick = (item: ActionMenuItemType) => () => { - onClick(item) + const handleOptionOnClick: ActionMenuItemProps['onClick'] = (item, e) => { + onClick(item, e) closePopover() } @@ -114,7 +114,7 @@ export const ActionMenu = ({ itemRef={itemsRef.current[index]} isFocused={index === focusedIndex} onMouseEnter={handleOptionMouseEnter(index)} - onClick={handleOptionOnClick(item)} + onClick={handleOptionOnClick} disableDescriptionEllipsis={disableDescriptionEllipsis} /> ) diff --git a/src/Shared/Components/ActionMenu/ActionMenuItem.tsx b/src/Shared/Components/ActionMenu/ActionMenuItem.tsx index 96300da47..ef161897f 100644 --- a/src/Shared/Components/ActionMenu/ActionMenuItem.tsx +++ b/src/Shared/Components/ActionMenu/ActionMenuItem.tsx @@ -1,13 +1,15 @@ -import { LegacyRef, Ref } from 'react' +import { LegacyRef, MouseEvent, Ref } from 'react' import { Link } from 'react-router-dom' import { Tooltip } from '@Common/Tooltip' +import { ComponentSizeType } from '@Shared/constants' import { Icon } from '../Icon' import { getTooltipProps } from '../SelectPicker/common' -import { ActionMenuItemProps } from './types' +import { DTSwitch, DTSwitchProps } from '../Switch' +import { ActionMenuItemProps, ActionMenuItemType } from './types' -const COMMON_ACTION_MENU_ITEM_CLASS = 'flex-grow-1 flex left top dc__gap-8 py-6 px-8' +const COMMON_ACTION_MENU_ITEM_CLASS = 'w-100 flex left top dc__gap-8 py-6 px-8' export const ActionMenuItem = ({ item, @@ -22,7 +24,7 @@ export const ActionMenuItem = ({ description, label, startIcon, - endIcon, + trailingItem, tooltipProps, type = 'neutral', isDisabled, @@ -40,22 +42,61 @@ export const ActionMenuItem = ({ const isNegativeType = type === 'negative' // HANDLERS - const handleClick = () => { - onClick(item) + const handleClick = (e: MouseEvent | MouseEvent) => { + onClick(item, e) } + const handleSwitchChange = + ({ type: trailingItemType, config }: ActionMenuItemType['trailingItem']): DTSwitchProps['onChange'] => + (e) => { + if (trailingItemType === 'switch') { + e.stopPropagation() + config.onChange(e) + } + } + // RENDERERS const renderIcon = (iconProps: typeof startIcon) => iconProps && ( -
+ -
+ ) + const renderTrailingItem = () => { + if (!trailingItem) { + return null + } + + const { type: trailingItemType, config } = trailingItem + + switch (trailingItemType) { + case 'icon': + return renderIcon(config) + case 'text': { + const { value, icon } = config + return ( + + {value} + {icon && } + + ) + } + case 'counter': + return {config.value} + case 'switch': + return ( + + ) + default: + return null + } + } + const renderContent = () => ( <> {renderIcon(startIcon)} - + {label} @@ -72,7 +113,7 @@ export const ActionMenuItem = ({ description ))} - {renderIcon(endIcon)} + {renderTrailingItem()} ) @@ -86,6 +127,7 @@ export const ActionMenuItem = ({ href={item.href} target="_blank" rel="noreferrer" + onClick={handleClick} > {renderContent()} @@ -96,6 +138,7 @@ export const ActionMenuItem = ({ ref={itemRef as Ref} className={COMMON_ACTION_MENU_ITEM_CLASS} to={item.to} + onClick={handleClick} > {renderContent()} @@ -107,6 +150,7 @@ export const ActionMenuItem = ({ ref={itemRef as LegacyRef} type="button" className={`dc__transparent ${COMMON_ACTION_MENU_ITEM_CLASS}`} + onClick={handleClick} > {renderContent()} @@ -124,7 +168,6 @@ export const ActionMenuItem = ({ tabIndex={-1} // Intentionally added margin to the left and right to have the gap on the edges of the options className={`action-menu__option br-4 mr-4 ml-4 ${isDisabled ? 'dc__disabled' : 'cursor'} ${isNegativeType ? 'dc__hover-r50' : 'dc__hover-n50'} ${isFocused ? `action-menu__option--focused${isNegativeType ? '-negative' : ''}` : ''}`} - onClick={!isDisabled ? handleClick : undefined} aria-disabled={isDisabled} > {renderComponent()} diff --git a/src/Shared/Components/ActionMenu/types.ts b/src/Shared/Components/ActionMenu/types.ts index 599212c4f..6fb1730d6 100644 --- a/src/Shared/Components/ActionMenu/types.ts +++ b/src/Shared/Components/ActionMenu/types.ts @@ -1,9 +1,10 @@ -import { LegacyRef, Ref } from 'react' +import { LegacyRef, MouseEvent, Ref } from 'react' import { LinkProps } from 'react-router-dom' import { IconsProps } from '../Icon' import { PopoverProps, UsePopoverProps } from '../Popover' import { SelectPickerOptionType, SelectPickerProps } from '../SelectPicker' +import { DTSwitchProps } from '../Switch' type ConditionalActionMenuComponentType = | { @@ -32,6 +33,40 @@ type ActionMenuItemIconType = Pick & { color?: IconsProps['color'] } +type TrailingItemType = + | { + type: 'icon' + config: ActionMenuItemIconType + } + | { + type: 'text' + config: { + value: string + icon?: ActionMenuItemIconType + } + } + | { + type: 'counter' + config: { + value: string | number + } + } + | { + type: 'switch' + config: Pick< + DTSwitchProps, + | 'ariaLabel' + | 'dataTestId' + | 'isChecked' + | 'indeterminate' + | 'isDisabled' + | 'isLoading' + | 'name' + | 'onChange' + | 'tooltipContent' + > + } + export type ActionMenuItemType = Omit< SelectPickerOptionType, 'label' | 'value' | 'endIcon' | 'startIcon' @@ -49,8 +84,8 @@ export type ActionMenuItemType = Om type?: 'neutral' | 'negative' /** Defines the icon to be displayed at the start of the menu item. */ startIcon?: ActionMenuItemIconType - /** Defines the icon to be displayed at the end of the menu item. */ - endIcon?: ActionMenuItemIconType + /** Defines the item to be displayed at the end of the menu item. */ + trailingItem?: TrailingItemType } & ConditionalActionMenuComponentType export type ActionMenuOptionType = { @@ -85,7 +120,7 @@ export type ActionMenuProps = UseAc * Callback function triggered when an item is clicked. * @param item - The selected item. */ - onClick: (item: ActionMenuItemType) => void + onClick: (item: ActionMenuItemType, e: MouseEvent | MouseEvent) => void /** * Config for the footer at the bottom of action menu list. It is sticky by default */ diff --git a/src/Shared/Components/Switch/types.ts b/src/Shared/Components/Switch/types.ts index 2af63d037..681870b5c 100644 --- a/src/Shared/Components/Switch/types.ts +++ b/src/Shared/Components/Switch/types.ts @@ -1,3 +1,5 @@ +import { ButtonHTMLAttributes } from 'react' + import { ComponentSizeType } from '@Shared/constants' import { IconBaseColorType } from '@Shared/types' @@ -96,7 +98,7 @@ export type DTSwitchProps = { * Callback function that is called when the switch state changes. * This function should handle the logic for toggling the switch. */ - onChange: () => void + onChange: ButtonHTMLAttributes['onClick'] /** * Indicates whether the switch is disabled. From 52588c970c80a8b0147e3ee03459dc932b7e2f92 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 28 May 2025 17:36:40 +0530 Subject: [PATCH 082/109] feat: HelpButton - open documentation item in side panel --- src/Shared/Components/Header/HelpButton.tsx | 28 +++++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Shared/Components/Header/HelpButton.tsx b/src/Shared/Components/Header/HelpButton.tsx index 75e77c80d..8d98575f4 100644 --- a/src/Shared/Components/Header/HelpButton.tsx +++ b/src/Shared/Components/Header/HelpButton.tsx @@ -2,12 +2,12 @@ import { useRef, useState } from 'react' import ReactGA from 'react-ga4' import { SliderButton } from '@typeform/embed-react' -import { URLS } from '@Common/Constants' +import { DOCUMENTATION_HOME_PAGE, URLS } from '@Common/Constants' import { ComponentSizeType } from '@Shared/constants' import { useMainContext } from '@Shared/Providers' import { InstallationType } from '@Shared/types' -import { ActionMenu, ActionMenuItemType } from '../ActionMenu' +import { ActionMenu } from '../ActionMenu' import { Button, ButtonComponentType, ButtonVariantType } from '../Button' import { Icon } from '../Icon' import { HelpButtonActionMenuProps, HelpButtonProps, HelpMenuItems } from './types' @@ -41,7 +41,8 @@ export const HelpButton = ({ serverInfo, fetchingServerInfo, onClick }: HelpButt const [isActionMenuOpen, setIsActionMenuOpen] = useState(false) // HOOKS - const { handleOpenLicenseInfoDialog, licenseData, setGettingStartedClicked, isEnterprise } = useMainContext() + const { handleOpenLicenseInfoDialog, licenseData, setGettingStartedClicked, isEnterprise, setSidePanelConfig } = + useMainContext() // REFS const typeFormSliderButtonRef = useRef(null) @@ -50,10 +51,10 @@ export const HelpButton = ({ serverInfo, fetchingServerInfo, onClick }: HelpButt const FEEDBACK_FORM_ID = `UheGN3KJ#source=${window.location.hostname}` // HANDLERS - const handleAnalytics = (option: ActionMenuItemType) => { + const handleAnalytics: HelpButtonActionMenuProps['onClick'] = (item) => { ReactGA.event({ category: 'Help Nav', - action: `${option.label} Clicked`, + action: `${item.label} Clicked`, }) } @@ -73,7 +74,16 @@ export const HelpButton = ({ serverInfo, fetchingServerInfo, onClick }: HelpButt setGettingStartedClicked(true) } - const handleActionMenuClick: HelpButtonActionMenuProps['onClick'] = (item) => { + const handleViewDocumentationClick: HelpButtonActionMenuProps['onClick'] = (item, e) => { + handleAnalytics(item, e) + // Opens documentation in side panel when clicked normally, or in a new tab when clicked with the meta/command key + if (!e.metaKey) { + e.preventDefault() + setSidePanelConfig((prev) => ({ ...prev, open: true, docLink: DOCUMENTATION_HOME_PAGE })) + } + } + + const handleActionMenuClick: HelpButtonActionMenuProps['onClick'] = (item, e) => { switch (item.id) { case HelpMenuItems.GETTING_STARTED: handleGettingStartedClick() @@ -85,12 +95,14 @@ export const HelpButton = ({ serverInfo, fetchingServerInfo, onClick }: HelpButt handleFeedbackClick() break case HelpMenuItems.JOIN_DISCORD_COMMUNITY: - case HelpMenuItems.VIEW_DOCUMENTATION: case HelpMenuItems.OPEN_NEW_TICKET: case HelpMenuItems.VIEW_ALL_TICKETS: case HelpMenuItems.CHAT_WITH_SUPPORT: case HelpMenuItems.RAISE_ISSUE_REQUEST: - handleAnalytics(item) + handleAnalytics(item, e) + break + case HelpMenuItems.VIEW_DOCUMENTATION: + handleViewDocumentationClick(item, e) break default: } From e0e2b2a3c27a3c50cf5269ca1336da2946d6c6f0 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 28 May 2025 18:57:56 +0530 Subject: [PATCH 083/109] fix: ProfileMenu - closePopover on ThemeSwitch click, on logout --- src/Shared/Components/Header/ProfileMenu.tsx | 5 +++-- .../ThemeSwitcher/ThemeSwitcher.component.tsx | 4 +++- src/Shared/Components/ThemeSwitcher/types.ts | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 src/Shared/Components/ThemeSwitcher/types.ts diff --git a/src/Shared/Components/Header/ProfileMenu.tsx b/src/Shared/Components/Header/ProfileMenu.tsx index ccb744ff4..03f69976f 100644 --- a/src/Shared/Components/Header/ProfileMenu.tsx +++ b/src/Shared/Components/Header/ProfileMenu.tsx @@ -14,7 +14,7 @@ export const ProfileMenu = ({ user, onClick }: ProfileMenuProps) => { // HOOKS const { viewIsPipelineRBACConfiguredNode } = useMainContext() - const { open, overlayProps, popoverProps, triggerProps } = usePopover({ + const { open, overlayProps, popoverProps, triggerProps, closePopover } = usePopover({ id: 'profile-menu', alignment: 'end', width: 250, @@ -38,6 +38,7 @@ export const ProfileMenu = ({ user, onClick }: ProfileMenuProps) => { // HANDLERS const onLogout = () => { + closePopover() clearCookieOnLogout() } @@ -61,7 +62,7 @@ export const ProfileMenu = ({ user, onClick }: ProfileMenuProps) => {
- + {viewIsPipelineRBACConfiguredNode}
diff --git a/src/Shared/Components/ThemeSwitcher/ThemeSwitcher.component.tsx b/src/Shared/Components/ThemeSwitcher/ThemeSwitcher.component.tsx index 4ed0e2166..587de3b63 100644 --- a/src/Shared/Components/ThemeSwitcher/ThemeSwitcher.component.tsx +++ b/src/Shared/Components/ThemeSwitcher/ThemeSwitcher.component.tsx @@ -17,12 +17,14 @@ import { getThemePreferenceText, useTheme } from '@Shared/Providers' import { Icon } from '../Icon' +import { ThemeSwitcherProps } from './types' -const ThemeSwitcher = () => { +const ThemeSwitcher = ({ onClick }: ThemeSwitcherProps) => { const { handleThemeSwitcherDialogVisibilityChange, themePreference } = useTheme() const handleShowThemeSwitcherDialog = () => { handleThemeSwitcherDialogVisibilityChange(true) + onClick?.() } return ( diff --git a/src/Shared/Components/ThemeSwitcher/types.ts b/src/Shared/Components/ThemeSwitcher/types.ts new file mode 100644 index 000000000..f86536088 --- /dev/null +++ b/src/Shared/Components/ThemeSwitcher/types.ts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface ThemeSwitcherProps { + onClick?: () => void +} From e7044c4745ca57114dcbb5ae4c21b4268ee89581 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Wed, 28 May 2025 19:03:13 +0530 Subject: [PATCH 084/109] feat(Icon): add ic-key-enter icon and update icon mapping --- src/Assets/IconV2/ic-key-enter.svg | 3 +++ .../Components/ConfirmationModal/ConfirmationModal.tsx | 9 +++++---- src/Shared/Components/Icon/Icon.tsx | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 src/Assets/IconV2/ic-key-enter.svg diff --git a/src/Assets/IconV2/ic-key-enter.svg b/src/Assets/IconV2/ic-key-enter.svg new file mode 100644 index 000000000..d6b668125 --- /dev/null +++ b/src/Assets/IconV2/ic-key-enter.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx index 9a9d6f550..583de9898 100644 --- a/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx +++ b/src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx @@ -25,6 +25,7 @@ import { Backdrop } from '../Backdrop' import { Button, ButtonStyleType, ButtonVariantType } from '../Button' import { Confetti } from '../Confetti' import { CustomInput } from '../CustomInput' +import { Icon } from '../Icon' import { useConfirmationModalContext } from './ConfirmationModalContext' import { ConfirmationModalBodyProps, ConfirmationModalProps, ConfirmationModalVariantType } from './types' import { getConfirmationLabel, getIconFromVariant, getPrimaryButtonStyleFromVariant } from './utils' @@ -34,7 +35,7 @@ import './confirmationModal.scss' const ConfirmationModalBody = ({ title, subtitle, - Icon, + Icon: ButtonIcon, variant, buttonConfig, confirmationConfig, @@ -53,8 +54,8 @@ const ConfirmationModalBody = ({ const { primaryButtonConfig, secondaryButtonConfig } = buttonConfig - const RenderIcon = Icon ?? getIconFromVariant(variant) - const hideIcon = variant === ConfirmationModalVariantType.custom && !Icon + const RenderIcon = ButtonIcon ?? getIconFromVariant(variant) + const hideIcon = variant === ConfirmationModalVariantType.custom && !ButtonIcon const disablePrimaryButton: boolean = ('disabled' in primaryButtonConfig && primaryButtonConfig.disabled) || @@ -159,7 +160,7 @@ const ConfirmationModalBody = ({ text={primaryButtonConfig.text} onClick={primaryButtonConfig.onClick as ButtonHTMLAttributes['onClick']} startIcon={primaryButtonConfig.startIcon} - endIcon={primaryButtonConfig.endIcon} + endIcon={primaryButtonConfig.endIcon || } /> )}
diff --git a/src/Shared/Components/Icon/Icon.tsx b/src/Shared/Components/Icon/Icon.tsx index 64ab92468..9fb4e2049 100644 --- a/src/Shared/Components/Icon/Icon.tsx +++ b/src/Shared/Components/Icon/Icon.tsx @@ -98,6 +98,7 @@ import { ReactComponent as ICJobColor } from '@IconsV2/ic-job-color.svg' import { ReactComponent as ICK3s } from '@IconsV2/ic-k3s.svg' import { ReactComponent as ICK8sJob } from '@IconsV2/ic-k8s-job.svg' import { ReactComponent as ICKey } from '@IconsV2/ic-key.svg' +import { ReactComponent as ICKeyEnter } from '@IconsV2/ic-key-enter.svg' import { ReactComponent as ICKind } from '@IconsV2/ic-kind.svg' import { ReactComponent as ICLaptop } from '@IconsV2/ic-laptop.svg' import { ReactComponent as ICLdap } from '@IconsV2/ic-ldap.svg' @@ -260,6 +261,7 @@ export const iconMap = { 'ic-job-color': ICJobColor, 'ic-k3s': ICK3s, 'ic-k8s-job': ICK8sJob, + 'ic-key-enter': ICKeyEnter, 'ic-key': ICKey, 'ic-kind': ICKind, 'ic-laptop': ICLaptop, From f06acb77a05dec725d0d17cb8242c8f73c57c141 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 28 May 2025 23:53:24 +0530 Subject: [PATCH 085/109] fix: CodeEditor - adjust search container width and margin --- src/Shared/Components/CodeEditor/codeEditor.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Shared/Components/CodeEditor/codeEditor.scss b/src/Shared/Components/CodeEditor/codeEditor.scss index 900144ee4..bd1894d7e 100644 --- a/src/Shared/Components/CodeEditor/codeEditor.scss +++ b/src/Shared/Components/CodeEditor/codeEditor.scss @@ -133,7 +133,8 @@ border-bottom: none; &:has(.code-editor__search) { - z-index: 0; + width: fit-content; + margin-left: auto; } } From 348e3775762519445a7b85793ae54ffec9353279 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Thu, 29 May 2025 13:09:36 +0530 Subject: [PATCH 086/109] fix: review comments --- .../Components/CodeEditor/CodeEditor.tsx | 20 +++++++++----- .../CodeEditor/Commands/findAndReplace.ts | 27 +++++++++---------- .../CodeEditor/Extensions/findAndReplace.tsx | 4 ++- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Shared/Components/CodeEditor/CodeEditor.tsx b/src/Shared/Components/CodeEditor/CodeEditor.tsx index 42e7e65bf..3560bc631 100644 --- a/src/Shared/Components/CodeEditor/CodeEditor.tsx +++ b/src/Shared/Components/CodeEditor/CodeEditor.tsx @@ -43,8 +43,8 @@ import { getCodeEditorTheme } from './CodeEditor.theme' import { CodeEditorRenderer } from './CodeEditorRenderer' import { blurOnEscape, - getOpenSearchPanel, - getOpenSearchPanelWithReplace, + openSearchPanel, + openSearchPanelWithReplace, replaceAll, showReplaceFieldState, } from './Commands' @@ -203,17 +203,25 @@ const CodeEditor = ({ setLhsCode(newLhsValue) } + const openSearchPanelWrapper: typeof openSearchPanel = (view) => { + onSearchPanelOpen() + return openSearchPanel(view) + } + + const openSearchPanelWithReplaceWrapper: typeof openSearchPanelWithReplace = (view) => { + onSearchPanelOpen() + return openSearchPanelWithReplace(view) + } + // EXTENSIONS const getBaseExtensions = (): Extension[] => [ basicSetup(basicSetupOptions), themeExtension, keymap.of([ ...vscodeKeymap.filter(({ key }) => key !== 'Mod-Alt-Enter' && key !== 'Mod-Enter' && key !== 'Mod-f'), - ...(!disableSearch - ? [{ key: 'Mod-f', run: getOpenSearchPanel(onSearchPanelOpen), scope: 'editor search-panel' }] - : []), + ...(!disableSearch ? [{ key: 'Mod-f', run: openSearchPanelWrapper, scope: 'editor search-panel' }] : []), { key: 'Mod-Enter', run: replaceAll, scope: 'editor search-panel' }, - { key: 'Mod-Alt-f', run: getOpenSearchPanelWithReplace(onSearchPanelOpen), scope: 'editor search-panel' }, + { key: 'Mod-Alt-f', run: openSearchPanelWithReplaceWrapper, scope: 'editor search-panel' }, { key: 'Escape', run: blurOnEscape, stopPropagation: true }, ]), indentationMarkers(), diff --git a/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts b/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts index 18f2017f2..d1257618b 100644 --- a/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts +++ b/src/Shared/Components/CodeEditor/Commands/findAndReplace.ts @@ -37,22 +37,19 @@ export const getShowReplaceField = (state: EditorState) => { return curState || false } -export const getOpenSearchPanel: (onSearchPanelOpen: () => void) => Command = - (onSearchPanelOpen: () => void) => (view: EditorView) => { - view.dispatch({ - effects: [setShowReplaceField.of(searchPanelOpen(view.state) ? getShowReplaceField(view.state) : false)], - }) - cmOpenSearchPanel(view) - onSearchPanelOpen() - return true - } +export const openSearchPanel: Command = (view: EditorView) => { + view.dispatch({ + effects: [setShowReplaceField.of(searchPanelOpen(view.state) ? getShowReplaceField(view.state) : false)], + }) + cmOpenSearchPanel(view) + return true +} -export const getOpenSearchPanelWithReplace: (onSearchPanelOpen: () => void) => Command = - (onSearchPanelOpen: () => void) => (view: EditorView) => { - getOpenSearchPanel(onSearchPanelOpen)(view) - view.dispatch({ effects: [setShowReplaceField.of(!view.state.readOnly && true)] }) - return true - } +export const openSearchPanelWithReplace: Command = (view: EditorView) => { + openSearchPanel(view) + view.dispatch({ effects: [setShowReplaceField.of(!view.state.readOnly && true)] }) + return true +} export const replaceAll: Command = (view: EditorView) => { const isReplaceEnabled = getShowReplaceField(view.state) diff --git a/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx b/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx index 675ed1402..db8b93faa 100644 --- a/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx +++ b/src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx @@ -103,6 +103,9 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction search = query.search, wholeWord = query.wholeWord, }: FindReplaceQuery) => { + // Calling this irrespective of whether the query has changed or not + onSearchBarAction() + const newQuery = new SearchQuery({ caseSensitive, regexp, @@ -111,7 +114,6 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction wholeWord, }) - onSearchBarAction() if (!newQuery.eq(query)) { setQuery(newQuery) view.dispatch({ effects: setSearchQuery.of(newQuery) }) From 37bcb8865fa831420b632cd0891a826b1b748278 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 29 May 2025 13:43:22 +0530 Subject: [PATCH 087/109] fix: Constants - correct DOCUMENTATION_VERSION path --- src/Common/Constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index 3243f76c2..b710065d0 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -19,7 +19,7 @@ export const Host = window?.__ORCHESTRATOR_ROOT__ ?? '/orchestrator' export const DOCUMENTATION_HOME_PAGE = 'https://docs.devtron.ai' export const DEVTRON_HOME_PAGE = 'https://devtron.ai/' -export const DOCUMENTATION_VERSION = '/v/v0.7' +export const DOCUMENTATION_VERSION = '/devtron/v0.7' export const DISCORD_LINK = 'https://discord.devtron.ai/' export const DEFAULT_JSON_SCHEMA_URI = 'https://json-schema.org/draft/2020-12/schema' From 76298421e39fcac6323401e6261cdc3dcdf785ee Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 29 May 2025 13:44:17 +0530 Subject: [PATCH 088/109] fix: Button - add inline-block class to button wrapper for better layout --- src/Shared/Components/Button/Button.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/Components/Button/Button.component.tsx b/src/Shared/Components/Button/Button.component.tsx index 525f96440..c081fb079 100644 --- a/src/Shared/Components/Button/Button.component.tsx +++ b/src/Shared/Components/Button/Button.component.tsx @@ -243,7 +243,7 @@ const Button = ({ return ( -
+
Date: Thu, 29 May 2025 14:04:19 +0530 Subject: [PATCH 089/109] chore: update version to 1.14.1-pre-6 in package.json and package-lock.json --- 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 898f45fa6..e559e8400 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-5", + "version": "1.14.1-pre-6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-5", + "version": "1.14.1-pre-6", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 9324c1228..07b7862cb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-5", + "version": "1.14.1-pre-6", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From 567179002f31282d7b3ede8b0dc3c6eb7ae2f85a Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 29 May 2025 15:04:08 +0530 Subject: [PATCH 090/109] feat: Button, DocLink - add fontWeight prop and update related styles --- .../Components/Button/Button.component.tsx | 5 +++-- src/Shared/Components/Button/constants.ts | 19 ++++++++++++------- src/Shared/Components/Button/types.ts | 7 +++++++ src/Shared/Components/Button/utils.ts | 6 ++++-- src/Shared/Components/DocLink/DocLink.tsx | 2 ++ src/Shared/Components/DocLink/types.ts | 2 +- 6 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/Shared/Components/Button/Button.component.tsx b/src/Shared/Components/Button/Button.component.tsx index c081fb079..21f6f7a38 100644 --- a/src/Shared/Components/Button/Button.component.tsx +++ b/src/Shared/Components/Button/Button.component.tsx @@ -170,6 +170,7 @@ const Button = ({ variant = ButtonVariantType.primary, size = ComponentSizeType.large, style = ButtonStyleType.default, + fontWeight = 'bold', startIcon = null, endIcon = null, disabled = false, @@ -243,11 +244,11 @@ const Button = ({ return ( -
+
= { - [ComponentSizeType.xxs_small_icon]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.xxs]} px-9 fw-6 dc__gap-6 mw-48`, - [ComponentSizeType.xxs]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.xxs]} px-9 fw-6 dc__gap-6 mw-48`, - [ComponentSizeType.xs]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.xs]} px-9 fw-6 dc__gap-6 mw-48`, - [ComponentSizeType.small]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.small]} px-9 fw-6 dc__gap-8 mw-48`, - [ComponentSizeType.medium]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.medium]} px-11 fw-6 dc__gap-8 mw-48`, - [ComponentSizeType.large]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.large]} px-13 fw-6 dc__gap-8 mw-64`, - [ComponentSizeType.xl]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.xl]} px-15 fw-6 dc__gap-12 mw-64`, + [ComponentSizeType.xxs_small_icon]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.xxs]} px-9 dc__gap-6 mw-48`, + [ComponentSizeType.xxs]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.xxs]} px-9 dc__gap-6 mw-48`, + [ComponentSizeType.xs]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.xs]} px-9 dc__gap-6 mw-48`, + [ComponentSizeType.small]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.small]} px-9 dc__gap-8 mw-48`, + [ComponentSizeType.medium]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.medium]} px-11 dc__gap-8 mw-48`, + [ComponentSizeType.large]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.large]} px-13 dc__gap-8 mw-64`, + [ComponentSizeType.xl]: `${COMPONENT_SIZE_TYPE_TO_FONT_AND_BLOCK_PADDING_MAP[ComponentSizeType.xl]} px-15 dc__gap-12 mw-64`, +} as const + +export const BUTTON_FONT_WEIGHT_TO_CLASS_NAME_MAP: Record = { + bold: 'fw-6', + normal: 'fw-4', } as const export const ICON_BUTTON_SIZE_TO_CLASS_NAME_MAP: Record = { diff --git a/src/Shared/Components/Button/types.ts b/src/Shared/Components/Button/types.ts index 5ed004be5..9f6b6b4dd 100644 --- a/src/Shared/Components/Button/types.ts +++ b/src/Shared/Components/Button/types.ts @@ -178,6 +178,12 @@ export type ButtonProps & { + Pick & { isAutoTriggerActive: boolean } >) => - `button button__${variant}--${style} ${icon ? ICON_BUTTON_SIZE_TO_CLASS_NAME_MAP[size] : BUTTON_SIZE_TO_CLASS_NAME_MAP[size]} ${isAutoTriggerActive ? 'button--auto-click' : ''} ${isLoading ? 'button--loading' : ''}` + `button button__${variant}--${style} ${icon ? ICON_BUTTON_SIZE_TO_CLASS_NAME_MAP[size] : BUTTON_SIZE_TO_CLASS_NAME_MAP[size]} ${BUTTON_FONT_WEIGHT_TO_CLASS_NAME_MAP[fontWeight]} ${isAutoTriggerActive ? 'button--auto-click' : ''} ${isLoading ? 'button--loading' : ''}` diff --git a/src/Shared/Components/DocLink/DocLink.tsx b/src/Shared/Components/DocLink/DocLink.tsx index a37450ee6..d64869606 100644 --- a/src/Shared/Components/DocLink/DocLink.tsx +++ b/src/Shared/Components/DocLink/DocLink.tsx @@ -14,6 +14,7 @@ export const DocLink = ({ dataTestId, showExternalIcon, onClick, + fontWeight, size = ComponentSizeType.medium, variant = ButtonVariantType.text, isExternalLink, @@ -52,6 +53,7 @@ export const DocLink = ({ size={size} endIcon={showExternalIcon && } fullWidth={fullWidth} + fontWeight={fontWeight} /> ) } diff --git a/src/Shared/Components/DocLink/types.ts b/src/Shared/Components/DocLink/types.ts index c5526b098..8d31536ba 100644 --- a/src/Shared/Components/DocLink/types.ts +++ b/src/Shared/Components/DocLink/types.ts @@ -12,7 +12,7 @@ export type BaseDocLink = { export type DocLinkProps = Pick< ButtonProps, - 'dataTestId' | 'size' | 'variant' | 'fullWidth' + 'dataTestId' | 'size' | 'variant' | 'fullWidth' | 'fontWeight' > & Omit, 'isEnterprise'> & { text?: string From 5e871a0be432fbd6c6ab162f2d92cf397138d958 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 29 May 2025 15:04:41 +0530 Subject: [PATCH 091/109] fix: CiPipelineSourceConfig - update class names for better text truncation --- src/Shared/Components/CICDHistory/CiPipelineSourceConfig.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Shared/Components/CICDHistory/CiPipelineSourceConfig.tsx b/src/Shared/Components/CICDHistory/CiPipelineSourceConfig.tsx index ccc7ee723..36e89ded4 100644 --- a/src/Shared/Components/CICDHistory/CiPipelineSourceConfig.tsx +++ b/src/Shared/Components/CICDHistory/CiPipelineSourceConfig.tsx @@ -162,7 +162,7 @@ export const CiPipelineSourceConfig = ({ {!baseText && ( <>
@@ -181,7 +181,7 @@ export const CiPipelineSourceConfig = ({
) : ( - {sourceValueAdv} + {sourceValueAdv} )}
)} From 8ca7525f329539f13d921b80828faf68d7a22667 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Thu, 29 May 2025 15:16:22 +0530 Subject: [PATCH 092/109] chore: update version to 1.14.1-pre-7 in package.json and package-lock.json --- 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 e559e8400..7715e01ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-6", + "version": "1.14.1-pre-7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-6", + "version": "1.14.1-pre-7", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 07b7862cb..e86923f23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.1-pre-6", + "version": "1.14.1-pre-7", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From 7a91d9102af54ecd0f832bd5854d7debc77bac2e Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 29 May 2025 16:29:54 +0530 Subject: [PATCH 093/109] feat: ActionMenu - add support for trailing item - button --- .../Components/ActionMenu/ActionMenuItem.tsx | 27 +++++++++++++++++-- src/Shared/Components/ActionMenu/types.ts | 9 ++++++- .../ActionMenu/useActionMenu.hook.ts | 2 +- src/Shared/types.ts | 21 +++++++++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/Shared/Components/ActionMenu/ActionMenuItem.tsx b/src/Shared/Components/ActionMenu/ActionMenuItem.tsx index ef161897f..15f4897f2 100644 --- a/src/Shared/Components/ActionMenu/ActionMenuItem.tsx +++ b/src/Shared/Components/ActionMenu/ActionMenuItem.tsx @@ -4,6 +4,7 @@ import { Link } from 'react-router-dom' import { Tooltip } from '@Common/Tooltip' import { ComponentSizeType } from '@Shared/constants' +import { Button, ButtonProps, ButtonVariantType } from '../Button' import { Icon } from '../Icon' import { getTooltipProps } from '../SelectPicker/common' import { DTSwitch, DTSwitchProps } from '../Switch' @@ -46,7 +47,7 @@ export const ActionMenuItem = ({ onClick(item, e) } - const handleSwitchChange = + const handleTrailingSwitchChange = ({ type: trailingItemType, config }: ActionMenuItemType['trailingItem']): DTSwitchProps['onChange'] => (e) => { if (trailingItemType === 'switch') { @@ -55,6 +56,15 @@ export const ActionMenuItem = ({ } } + const handleTrailingButtonClick = + ({ type: trailingItemType, config }: ActionMenuItemType['trailingItem']): ButtonProps['onClick'] => + (e) => { + e.stopPropagation() + if (trailingItemType === 'button' && config.onClick) { + config.onClick(e) + } + } + // RENDERERS const renderIcon = (iconProps: typeof startIcon) => iconProps && ( @@ -86,7 +96,20 @@ export const ActionMenuItem = ({ return {config.value} case 'switch': return ( - + + ) + case 'button': + return ( +
)} From d6b01a8d044963e2c3ab35c8da1440b4962a90dc Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 29 May 2025 18:06:57 +0530 Subject: [PATCH 096/109] feat: Popover, ActionMenu, ProfileMenu - modify popover implementation to render on top of all elements regardless of z-index hierarchy --- .../ActionMenu/ActionMenu.component.tsx | 4 +- src/Shared/Components/Header/ProfileMenu.tsx | 6 +-- .../Components/Popover/Popover.component.tsx | 21 ++++---- src/Shared/Components/Popover/popover.scss | 4 -- src/Shared/Components/Popover/types.ts | 8 +-- .../Components/Popover/usePopover.hook.ts | 50 +++++++++++++------ 6 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/Shared/Components/ActionMenu/ActionMenu.component.tsx b/src/Shared/Components/ActionMenu/ActionMenu.component.tsx index 07186aadd..8dd8d6f92 100644 --- a/src/Shared/Components/ActionMenu/ActionMenu.component.tsx +++ b/src/Shared/Components/ActionMenu/ActionMenu.component.tsx @@ -1,5 +1,3 @@ -import { MutableRefObject } from 'react' - import { CustomInput } from '../CustomInput' import { Popover } from '../Popover' import { SelectPickerMenuListFooter } from '../SelectPicker/common' @@ -82,7 +80,7 @@ export const ActionMenu = ({
)}
    } + ref={scrollableRef} role="menu" className="action-menu m-0 p-0 flex-grow-1 dc__overflow-auto dc__overscroll-none" > diff --git a/src/Shared/Components/Header/ProfileMenu.tsx b/src/Shared/Components/Header/ProfileMenu.tsx index 03f69976f..606c6aded 100644 --- a/src/Shared/Components/Header/ProfileMenu.tsx +++ b/src/Shared/Components/Header/ProfileMenu.tsx @@ -14,7 +14,7 @@ export const ProfileMenu = ({ user, onClick }: ProfileMenuProps) => { // HOOKS const { viewIsPipelineRBACConfiguredNode } = useMainContext() - const { open, overlayProps, popoverProps, triggerProps, closePopover } = usePopover({ + const { open, overlayProps, popoverProps, triggerProps, scrollableRef, closePopover } = usePopover({ id: 'profile-menu', alignment: 'end', width: 250, @@ -51,7 +51,7 @@ export const ProfileMenu = ({ user, onClick }: ProfileMenuProps) => { triggerElement={triggerElement} buttonProps={null} > - <> +
    @@ -71,7 +71,7 @@ export const ProfileMenu = ({ user, onClick }: ProfileMenuProps) => {
    - +
    ) } diff --git a/src/Shared/Components/Popover/Popover.component.tsx b/src/Shared/Components/Popover/Popover.component.tsx index 68640c840..d332af886 100644 --- a/src/Shared/Components/Popover/Popover.component.tsx +++ b/src/Shared/Components/Popover/Popover.component.tsx @@ -15,24 +15,25 @@ export const Popover = ({ open, popoverProps, overlayProps, - triggerProps, + triggerProps: { bounds, ...triggerProps }, buttonProps, triggerElement, children, }: PopoverProps) => ( -
    + <>
    {triggerElement ||
    {open && ( - <> - {/* Overlay to block interactions with the background */} -
    - - {children} - - +
    +
    +
    + + {children} + +
    +
    )} -
    + ) diff --git a/src/Shared/Components/Popover/popover.scss b/src/Shared/Components/Popover/popover.scss index f241ad180..26caeb6e0 100644 --- a/src/Shared/Components/Popover/popover.scss +++ b/src/Shared/Components/Popover/popover.scss @@ -6,7 +6,3 @@ left:0; z-index: var(--modal-index); } - -.popover-content { - z-index: var(--modal-index); -} diff --git a/src/Shared/Components/Popover/types.ts b/src/Shared/Components/Popover/types.ts index 5b1fdaac8..40ea7c343 100644 --- a/src/Shared/Components/Popover/types.ts +++ b/src/Shared/Components/Popover/types.ts @@ -1,4 +1,4 @@ -import { DetailedHTMLProps, KeyboardEvent, MutableRefObject, ReactElement } from 'react' +import { DetailedHTMLProps, KeyboardEvent, LegacyRef, MutableRefObject, ReactElement } from 'react' import { HTMLMotionProps } from 'framer-motion' import { ButtonProps } from '../Button' @@ -67,7 +67,9 @@ export interface UsePopoverReturnType { * Props to be spread onto the trigger element that opens the popover. * These props include standard HTML attributes for a `div` element. */ - triggerProps: DetailedHTMLProps, HTMLDivElement> + triggerProps: DetailedHTMLProps, HTMLDivElement> & { + bounds: Pick + } /** * Props to be spread onto the overlay element of the popover. * These props include standard HTML attributes for a `div` element. @@ -82,7 +84,7 @@ export interface UsePopoverReturnType { * A mutable reference to the scrollable element inside the popover. \ * This reference should be assigned to the element that is scrollable. */ - scrollableRef: MutableRefObject + scrollableRef: MutableRefObject | LegacyRef /** * A function to close the popover. */ diff --git a/src/Shared/Components/Popover/usePopover.hook.ts b/src/Shared/Components/Popover/usePopover.hook.ts index d95ff93b2..dd23cbd78 100644 --- a/src/Shared/Components/Popover/usePopover.hook.ts +++ b/src/Shared/Components/Popover/usePopover.hook.ts @@ -1,4 +1,4 @@ -import { useLayoutEffect, useRef, useState } from 'react' +import { MouseEvent, useLayoutEffect, useRef, useState } from 'react' import { UsePopoverProps, UsePopoverReturnType } from './types' import { @@ -21,6 +21,7 @@ export const usePopover = ({ const [open, setOpen] = useState(false) const [actualPosition, setActualPosition] = useState(position) const [actualAlignment, setActualAlignment] = useState(alignment) + const [triggerBounds, setTriggerBounds] = useState(null) // CONSTANTS const isAutoWidth = width === 'auto' @@ -51,23 +52,40 @@ export const usePopover = ({ const handlePopoverKeyDown = (e: React.KeyboardEvent) => onPopoverKeyDown(e, open, closePopover) + const handleOverlayClick = (e: MouseEvent) => { + if (!popover.current?.contains(e.target as Node)) { + closePopover() + } + } + useLayoutEffect(() => { if (!open || !triggerRef.current || !popover.current || !scrollableRef.current) { return } - const triggerRect = triggerRef.current.getBoundingClientRect() - const popoverRect = popover.current.getBoundingClientRect() - - const { fallbackPosition, fallbackAlignment } = getPopoverActualPositionAlignment({ - position, - alignment, - triggerRect, - popoverRect, - }) + const updatePopoverPosition = () => { + const triggerRect = triggerRef.current.getBoundingClientRect() + const popoverRect = popover.current.getBoundingClientRect() + + const { fallbackPosition, fallbackAlignment } = getPopoverActualPositionAlignment({ + position, + alignment, + triggerRect, + popoverRect, + }) + + setActualPosition(fallbackPosition) + setActualAlignment(fallbackAlignment) + setTriggerBounds({ + left: triggerRect.left, + top: triggerRect.top, + height: triggerRect.height, + width: triggerRect.width, + }) + } - setActualPosition(fallbackPosition) - setActualAlignment(fallbackAlignment) + // update position on open + updatePopoverPosition() // prevent scroll propagation unless scrollable const handleWheel = (e: WheelEvent) => { @@ -84,9 +102,12 @@ export const usePopover = ({ } scrollableRef.current.addEventListener('wheel', handleWheel, { passive: false }) + window.addEventListener('resize', updatePopoverPosition) + // eslint-disable-next-line consistent-return return () => { scrollableRef.current.removeEventListener('wheel', handleWheel) + window.removeEventListener('resize', updatePopoverPosition) } }, [open, position, alignment]) @@ -100,17 +121,18 @@ export const usePopover = ({ 'aria-haspopup': 'listbox', 'aria-expanded': open, tabIndex: 0, + bounds: triggerBounds ?? { left: 0, top: 0, height: 0, width: 0 }, }, overlayProps: { role: 'dialog', - onClick: closePopover, + onClick: handleOverlayClick, className: 'popover-overlay', }, popoverProps: { id, ref: popover, role: 'listbox', - className: `popover-content dc__position-abs bg__menu--primary shadow__menu border__primary br-6 dc__overflow-hidden ${isAutoWidth ? 'dc_width-max-content dc__mxw-250' : ''}`, + className: `dc__position-abs bg__menu--primary shadow__menu border__primary br-6 dc__overflow-hidden ${isAutoWidth ? 'dc_width-max-content dc__mxw-250' : ''}`, onKeyDown: handlePopoverKeyDown, style: { width: !isAutoWidth ? `${width}px` : undefined, From dc308fd36d3b4364568f4c6c399566eaa651e000 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Thu, 29 May 2025 18:08:50 +0530 Subject: [PATCH 097/109] feat: Constants - add LICENSE_DASHBOARD_HOME_PAGE constant for license dashboard link --- src/Common/Constants.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index b710065d0..1745ccf55 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -22,6 +22,7 @@ export const DEVTRON_HOME_PAGE = 'https://devtron.ai/' export const DOCUMENTATION_VERSION = '/devtron/v0.7' export const DISCORD_LINK = 'https://discord.devtron.ai/' export const DEFAULT_JSON_SCHEMA_URI = 'https://json-schema.org/draft/2020-12/schema' +export const LICENSE_DASHBOARD_HOME_PAGE = 'https://license.devtron.ai/dashboard' export const PATTERNS = { STRING: /^[a-zA-Z0-9_]+$/, From 642ddb978b53a9b91fe6e4c0000e9a57af4bac44 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Thu, 29 May 2025 20:19:46 +0530 Subject: [PATCH 098/109] feat: add ic-arrows-clockwise icon and update icon map --- src/Assets/IconV2/ic-arrows-clockwise.svg | 3 +++ src/Shared/Components/Icon/Icon.tsx | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 src/Assets/IconV2/ic-arrows-clockwise.svg diff --git a/src/Assets/IconV2/ic-arrows-clockwise.svg b/src/Assets/IconV2/ic-arrows-clockwise.svg new file mode 100644 index 000000000..413309aa6 --- /dev/null +++ b/src/Assets/IconV2/ic-arrows-clockwise.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Shared/Components/Icon/Icon.tsx b/src/Shared/Components/Icon/Icon.tsx index d406cedd7..a92e2e921 100644 --- a/src/Shared/Components/Icon/Icon.tsx +++ b/src/Shared/Components/Icon/Icon.tsx @@ -10,6 +10,7 @@ import { ReactComponent as ICAppTemplate } from '@IconsV2/ic-app-template.svg' import { ReactComponent as ICArrowClockwise } from '@IconsV2/ic-arrow-clockwise.svg' import { ReactComponent as ICArrowRight } from '@IconsV2/ic-arrow-right.svg' import { ReactComponent as ICArrowSquareOut } from '@IconsV2/ic-arrow-square-out.svg' +import { ReactComponent as ICArrowsClockwise } from '@IconsV2/ic-arrows-clockwise.svg' import { ReactComponent as ICArrowsLeftRight } from '@IconsV2/ic-arrows-left-right.svg' import { ReactComponent as ICAther } from '@IconsV2/ic-ather.svg' import { ReactComponent as ICAzure } from '@IconsV2/ic-azure.svg' @@ -175,6 +176,7 @@ export const iconMap = { 'ic-arrow-clockwise': ICArrowClockwise, 'ic-arrow-right': ICArrowRight, 'ic-arrow-square-out': ICArrowSquareOut, + 'ic-arrows-clockwise': ICArrowsClockwise, 'ic-arrows-left-right': ICArrowsLeftRight, 'ic-ather': ICAther, 'ic-azure-aks': ICAzureAks, From 39778e284eeb8f9dcb117172e4e03636366ed4c6 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Fri, 30 May 2025 11:40:37 +0530 Subject: [PATCH 099/109] fix: update default icon color for unchecked switch state --- src/Shared/Components/Switch/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/Components/Switch/utils.ts b/src/Shared/Components/Switch/utils.ts index 2dd6cf73a..514697ff6 100644 --- a/src/Shared/Components/Switch/utils.ts +++ b/src/Shared/Components/Switch/utils.ts @@ -68,7 +68,7 @@ export const getSwitchIconColor = ({ variant, }: Pick): IconBaseColorType => { if (!isChecked) { - return 'N200' + return 'N500' } return iconColor || (variant === 'theme' ? 'B500' : 'G500') From 410739dbd2e4138f38ef29d095d216dc861794c4 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Fri, 30 May 2025 12:44:17 +0530 Subject: [PATCH 100/109] refactor: remove dataTestId prop from Switch component and update data-testid usage --- src/Shared/Components/Switch/Switch.component.tsx | 3 +-- src/Shared/Components/Switch/types.ts | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Shared/Components/Switch/Switch.component.tsx b/src/Shared/Components/Switch/Switch.component.tsx index b006d743c..9e32a0feb 100644 --- a/src/Shared/Components/Switch/Switch.component.tsx +++ b/src/Shared/Components/Switch/Switch.component.tsx @@ -22,7 +22,6 @@ import './switch.scss' const Switch = ({ ariaLabel, - dataTestId, isDisabled, isLoading, isChecked, @@ -127,7 +126,7 @@ const Switch = ({ aria-checked={ariaCheckedValue} aria-labelledby={inputId.current} aria-label={isLoading ? 'Loading...' : ariaLabel} - data-testid={dataTestId} + data-testid={name} disabled={isDisabled || isLoading} aria-disabled={isDisabled} className={`p-0-imp h-100 flex flex-grow-1 dc__no-border dt-switch__track ${shape === 'rounded' ? 'br-12' : 'br-4'} ${getSwitchTrackColor({ shape, variant, isChecked, isLoading })} ${isDisabled ? 'dc__disabled' : ''} dc__fill-available-space`} diff --git a/src/Shared/Components/Switch/types.ts b/src/Shared/Components/Switch/types.ts index 2af63d037..9b8ff64a5 100644 --- a/src/Shared/Components/Switch/types.ts +++ b/src/Shared/Components/Switch/types.ts @@ -74,11 +74,6 @@ export type DTSwitchProps = { */ name: string - /** - * A unique identifier for testing purposes. - */ - dataTestId: string - /** * The visual variant of the switch. * From aae33757fa2aab041ed0482ed59c6442f71d89d0 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Fri, 30 May 2025 13:11:52 +0530 Subject: [PATCH 101/109] feat: DocLink - add startIcon prop and update fullWidth default value; adjust type definitions for better flexibility --- src/Shared/Components/DocLink/DocLink.tsx | 4 +++- src/Shared/Components/DocLink/constants.ts | 4 ++-- src/Shared/Components/DocLink/types.ts | 4 ++-- src/Shared/Components/FeatureDescription/types.ts | 2 +- src/Shared/Components/ModalSidebarPanel/types.ts | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Shared/Components/DocLink/DocLink.tsx b/src/Shared/Components/DocLink/DocLink.tsx index d64869606..cb9a0b8d2 100644 --- a/src/Shared/Components/DocLink/DocLink.tsx +++ b/src/Shared/Components/DocLink/DocLink.tsx @@ -12,6 +12,7 @@ export const DocLink = ({ docLinkKey, text = 'Learn more', dataTestId, + startIcon, showExternalIcon, onClick, fontWeight, @@ -19,7 +20,7 @@ export const DocLink = ({ variant = ButtonVariantType.text, isExternalLink, openInNewTab = false, - fullWidth = true, + fullWidth = false, }: DocLinkProps) => { // HOOKS const { isEnterprise, setSidePanelConfig } = useMainContext() @@ -51,6 +52,7 @@ export const DocLink = ({ text={text} variant={variant} size={size} + startIcon={startIcon} endIcon={showExternalIcon && } fullWidth={fullWidth} fontWeight={fontWeight} diff --git a/src/Shared/Components/DocLink/constants.ts b/src/Shared/Components/DocLink/constants.ts index 776298bde..f1a32de92 100644 --- a/src/Shared/Components/DocLink/constants.ts +++ b/src/Shared/Components/DocLink/constants.ts @@ -16,7 +16,7 @@ export const DOCUMENTATION = { APP_METRICS: 'usage/applications/app-details/app-metrics', APP_OVERVIEW_TAGS: 'usage/applications/overview#manage-tags', APP_ROLLOUT_DEPLOYMENT_TEMPLATE: 'usage/applications/creating-application/deployment-template/rollout-deployment', - BUILD_STAGE: 'usage/applications/creating-application/ci-pipeline#build-stage', + BUILD_STAGE: 'usage/applications/creating-application/workflow/ci-pipeline#build-stage', APP_TAGS: 'usage/applications/create-application#tags', BLOB_STORAGE: 'configurations-overview/installation-configuration#configuration-of-blob-storage', BULK_UPDATE: 'usage/bulk-update', @@ -45,7 +45,7 @@ export const DOCUMENTATION = { GLOBAL_CONFIG_CHART: 'getting-started/global-configurations/chart-repo', GLOBAL_CONFIG_CLUSTER: 'getting-started/global-configurations/cluster-and-environments', GLOBAL_CONFIG_CUSTOM_CHART: 'getting-started/global-configurations/custom-charts', - GLOBAL_CONFIG_CUSTOM_CHART_PRE_REQUISITES: 'getting-started/global-configurations/custom-charts#prerequisites', + GLOBAL_CONFIG_CUSTOM_CHART_PRE_REQUISITES: 'global-configurations/deployment-charts#preparing-a-deployment-chart', GLOBAL_CONFIG_DOCKER: 'getting-started/global-configurations/container-registries', GLOBAL_CONFIG_GIT: 'getting-started/global-configurations/git-accounts', GLOBAL_CONFIG_GITOPS: 'global-configurations/gitops', diff --git a/src/Shared/Components/DocLink/types.ts b/src/Shared/Components/DocLink/types.ts index 8d31536ba..4dfec7b99 100644 --- a/src/Shared/Components/DocLink/types.ts +++ b/src/Shared/Components/DocLink/types.ts @@ -10,9 +10,9 @@ export type BaseDocLink = { docLinkKey: T extends true ? string : keyof typeof DOCUMENTATION } -export type DocLinkProps = Pick< +export type DocLinkProps = Pick< ButtonProps, - 'dataTestId' | 'size' | 'variant' | 'fullWidth' | 'fontWeight' + 'dataTestId' | 'size' | 'variant' | 'fullWidth' | 'fontWeight' | 'startIcon' > & Omit, 'isEnterprise'> & { text?: string diff --git a/src/Shared/Components/FeatureDescription/types.ts b/src/Shared/Components/FeatureDescription/types.ts index 2861952dc..3c1ce9bf2 100644 --- a/src/Shared/Components/FeatureDescription/types.ts +++ b/src/Shared/Components/FeatureDescription/types.ts @@ -22,7 +22,7 @@ import { DocLinkProps } from '../DocLink' interface BaseFeatureDescriptionModalProps { renderDescriptionContent?: () => ReactNode - docLink?: DocLinkProps['docLinkKey'] + docLink?: DocLinkProps['docLinkKey'] imageVariant?: ImageType SVGImage?: React.FunctionComponent> imageStyles?: React.CSSProperties diff --git a/src/Shared/Components/ModalSidebarPanel/types.ts b/src/Shared/Components/ModalSidebarPanel/types.ts index e6a6b4e4e..dbee95e50 100644 --- a/src/Shared/Components/ModalSidebarPanel/types.ts +++ b/src/Shared/Components/ModalSidebarPanel/types.ts @@ -23,5 +23,5 @@ export interface ModalSidebarPanelProps { heading: string | null icon?: JSX.Element children?: ReactNode - documentationLink: DocLinkProps['docLinkKey'] + documentationLink: DocLinkProps['docLinkKey'] } From b45262fe75252c353303ab962a4e9aa12abe9003 Mon Sep 17 00:00:00 2001 From: AbhishekA1509 Date: Fri, 30 May 2025 13:37:14 +0530 Subject: [PATCH 102/109] chore: update version to 1.14.2-pre-1 in package.json and package-lock.json --- 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 ba15b2348..d9ad3c053 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2", + "version": "1.14.2-pre-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2", + "version": "1.14.2-pre-1", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 98c529a3f..d4d2bb3e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2", + "version": "1.14.2-pre-1", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From 50a2ca6f8b0584e0237ac7ef56f58a03b9536893 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Fri, 30 May 2025 14:40:46 +0530 Subject: [PATCH 103/109] feat: SidePanelConfig - add reinitialize flag and update docLink type; modify setSidePanelConfig calls to support new behavior --- src/Shared/Components/DocLink/DocLink.tsx | 2 +- src/Shared/Components/Header/HelpButton.tsx | 7 ++++++- src/Shared/Providers/types.ts | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Shared/Components/DocLink/DocLink.tsx b/src/Shared/Components/DocLink/DocLink.tsx index cb9a0b8d2..bbb698abd 100644 --- a/src/Shared/Components/DocLink/DocLink.tsx +++ b/src/Shared/Components/DocLink/DocLink.tsx @@ -36,7 +36,7 @@ export const DocLink = ({ const handleClick = (e: MouseEvent) => { if (!isExternalLink && !openInNewTab && !e.metaKey && documentationLink.startsWith(DOCUMENTATION_HOME_PAGE)) { e.preventDefault() - setSidePanelConfig((prev) => ({ ...prev, open: true, docLink: documentationLink })) + setSidePanelConfig((prev) => ({ ...prev, open: true, docLink: documentationLink, reinitialize: true })) } onClick?.(e) } diff --git a/src/Shared/Components/Header/HelpButton.tsx b/src/Shared/Components/Header/HelpButton.tsx index 8d98575f4..2815642b6 100644 --- a/src/Shared/Components/Header/HelpButton.tsx +++ b/src/Shared/Components/Header/HelpButton.tsx @@ -79,7 +79,12 @@ export const HelpButton = ({ serverInfo, fetchingServerInfo, onClick }: HelpButt // Opens documentation in side panel when clicked normally, or in a new tab when clicked with the meta/command key if (!e.metaKey) { e.preventDefault() - setSidePanelConfig((prev) => ({ ...prev, open: true, docLink: DOCUMENTATION_HOME_PAGE })) + setSidePanelConfig((prev) => ({ + ...prev, + open: true, + docLink: DOCUMENTATION_HOME_PAGE, + reinitialize: true, + })) } } diff --git a/src/Shared/Providers/types.ts b/src/Shared/Providers/types.ts index fd2b10217..4911a42b9 100644 --- a/src/Shared/Providers/types.ts +++ b/src/Shared/Providers/types.ts @@ -31,8 +31,12 @@ export interface ReloadVersionConfigTypes { } export interface SidePanelConfig { + /** Determines whether the side panel is visible */ open: boolean - docLink?: string | null + /** Optional flag to reset/reinitialize the side panel state */ + reinitialize?: boolean + /** URL to documentation that should be displayed in the panel */ + docLink: string | null } export interface MainContext { From 34db6ac3a4e87210792061ead46137b01d0fc449 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Fri, 30 May 2025 18:14:29 +0530 Subject: [PATCH 104/109] feat: Constants, ActionMenu, ProfileMenu - add LOGIN URL constant; update ActionMenuItem to use NumbersCount component; refactor useActionMenu hook for improved performance --- src/Common/Constants.ts | 1 + src/Shared/Components/ActionMenu/ActionMenuItem.tsx | 3 ++- src/Shared/Components/ActionMenu/types.ts | 3 ++- .../Components/ActionMenu/useActionMenu.hook.ts | 12 ++++-------- src/Shared/Components/Header/ProfileMenu.tsx | 3 ++- src/Shared/Components/NumbersCount/index.ts | 1 + src/Shared/Components/Popover/usePopover.hook.ts | 2 +- 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index 1745ccf55..c2218b60e 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -47,6 +47,7 @@ export const PATTERNS = { const GLOBAL_CONFIG_TEMPLATES_DEVTRON_APP = '/global-config/templates/devtron-apps' export const URLS = { + LOGIN: '/login', LOGIN_SSO: '/login/sso', PERMISSION_GROUPS: '/global-config/auth/groups', APP: '/app', diff --git a/src/Shared/Components/ActionMenu/ActionMenuItem.tsx b/src/Shared/Components/ActionMenu/ActionMenuItem.tsx index 15f4897f2..b140129d1 100644 --- a/src/Shared/Components/ActionMenu/ActionMenuItem.tsx +++ b/src/Shared/Components/ActionMenu/ActionMenuItem.tsx @@ -6,6 +6,7 @@ import { ComponentSizeType } from '@Shared/constants' import { Button, ButtonProps, ButtonVariantType } from '../Button' import { Icon } from '../Icon' +import { NumbersCount } from '../NumbersCount' import { getTooltipProps } from '../SelectPicker/common' import { DTSwitch, DTSwitchProps } from '../Switch' import { ActionMenuItemProps, ActionMenuItemType } from './types' @@ -93,7 +94,7 @@ export const ActionMenuItem = ({ ) } case 'counter': - return {config.value} + return case 'switch': return ( ({ const [focusedIndex, setFocusedIndex] = useState(-1) const [searchTerm, setSearchTerm] = useState('') - // MEMOIZED CONSTANTS - const filteredOptions = useMemo( - () => (isSearchable ? filterActionMenuOptions(options, searchTerm) : options), - [isSearchable, options, searchTerm], - ) - - const flatOptions = useMemo(() => getActionMenuFlatOptions(filteredOptions), [filteredOptions]) + // CONSTANTS + const filteredOptions = isSearchable ? filterActionMenuOptions(options, searchTerm) : options + const flatOptions = getActionMenuFlatOptions(filteredOptions) // REFS const itemsRef = useRef[]>( diff --git a/src/Shared/Components/Header/ProfileMenu.tsx b/src/Shared/Components/Header/ProfileMenu.tsx index 606c6aded..1b84c759e 100644 --- a/src/Shared/Components/Header/ProfileMenu.tsx +++ b/src/Shared/Components/Header/ProfileMenu.tsx @@ -1,6 +1,7 @@ import { useMemo } from 'react' import { Link } from 'react-router-dom' +import { URLS } from '@Common/Constants' import { getAlphabetIcon } from '@Common/Helper' import { clearCookieOnLogout } from '@Shared/Helpers' import { useMainContext } from '@Shared/Providers' @@ -66,7 +67,7 @@ export const ProfileMenu = ({ user, onClick }: ProfileMenuProps) => { {viewIsPipelineRBACConfiguredNode}
    - + Logout diff --git a/src/Shared/Components/NumbersCount/index.ts b/src/Shared/Components/NumbersCount/index.ts index b3b6ec1db..cdf8ca7ba 100644 --- a/src/Shared/Components/NumbersCount/index.ts +++ b/src/Shared/Components/NumbersCount/index.ts @@ -15,3 +15,4 @@ */ export { default as NumbersCount } from './NumbersCount.component' +export * from './types' diff --git a/src/Shared/Components/Popover/usePopover.hook.ts b/src/Shared/Components/Popover/usePopover.hook.ts index dd23cbd78..26a0b7a0e 100644 --- a/src/Shared/Components/Popover/usePopover.hook.ts +++ b/src/Shared/Components/Popover/usePopover.hook.ts @@ -50,7 +50,7 @@ export const usePopover = ({ onTriggerKeyDown?.(e, open, closePopover) } - const handlePopoverKeyDown = (e: React.KeyboardEvent) => onPopoverKeyDown(e, open, closePopover) + const handlePopoverKeyDown = (e: React.KeyboardEvent) => onPopoverKeyDown?.(e, open, closePopover) const handleOverlayClick = (e: MouseEvent) => { if (!popover.current?.contains(e.target as Node)) { From 13ddf40d4eed8f75858b5fa5daa5b1f974d3f418 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Fri, 30 May 2025 18:36:56 +0530 Subject: [PATCH 105/109] chore(version): bump to 1.14.2-pre-2 --- package-lock.json | 4 ++-- package.json | 2 +- src/Shared/Components/ActionMenu/types.ts | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index d9ad3c053..6db04829c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2-pre-1", + "version": "1.14.2-pre-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2-pre-1", + "version": "1.14.2-pre-2", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index d4d2bb3e7..406232770 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2-pre-1", + "version": "1.14.2-pre-2", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Shared/Components/ActionMenu/types.ts b/src/Shared/Components/ActionMenu/types.ts index bbb4db777..838a93f03 100644 --- a/src/Shared/Components/ActionMenu/types.ts +++ b/src/Shared/Components/ActionMenu/types.ts @@ -60,7 +60,6 @@ type TrailingItemType = config: Pick< DTSwitchProps, | 'ariaLabel' - | 'dataTestId' | 'isChecked' | 'indeterminate' | 'isDisabled' From 6597c24f0758ecd7817fea95f5d6336cbb143ba1 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Mon, 2 Jun 2025 12:53:20 +0530 Subject: [PATCH 106/109] fix: CodeEditor - reinitialize diff view on external value change to ensure consistent state --- .../DeploymentHistoryDiffView.tsx | 11 +- src/Shared/Components/CICDHistory/types.tsx | 2 +- .../CodeEditor/CodeEditorRenderer.tsx | 127 +++++++++++------- .../DeploymentConfigDiffMain.tsx | 1 - 4 files changed, 78 insertions(+), 63 deletions(-) diff --git a/src/Shared/Components/CICDHistory/DeploymentHistoryConfigDiff/DeploymentHistoryDiffView.tsx b/src/Shared/Components/CICDHistory/DeploymentHistoryConfigDiff/DeploymentHistoryDiffView.tsx index 1116e3324..edfc4bbe8 100644 --- a/src/Shared/Components/CICDHistory/DeploymentHistoryConfigDiff/DeploymentHistoryDiffView.tsx +++ b/src/Shared/Components/CICDHistory/DeploymentHistoryConfigDiff/DeploymentHistoryDiffView.tsx @@ -32,7 +32,6 @@ const DeploymentHistoryDiffView = ({ baseTemplateConfiguration, previousConfigAvailable, rootClassName, - codeEditorKey, }: DeploymentTemplateHistoryType) => { const { historyComponent, historyComponentName } = useParams() @@ -79,15 +78,7 @@ const DeploymentHistoryDiffView = ({ collapseUnchangedDiffView /> ) : ( - + ) const handleShowVariablesClick = () => { diff --git a/src/Shared/Components/CICDHistory/types.tsx b/src/Shared/Components/CICDHistory/types.tsx index 280b5124e..8fe87ce26 100644 --- a/src/Shared/Components/CICDHistory/types.tsx +++ b/src/Shared/Components/CICDHistory/types.tsx @@ -550,8 +550,8 @@ export interface DeploymentTemplateHistoryType { baseTemplateConfiguration: DeploymentHistoryDetail previousConfigAvailable: boolean rootClassName?: string - codeEditorKey?: string } + export interface DeploymentHistoryDetailRes extends ResponseType { result?: DeploymentHistoryDetail } diff --git a/src/Shared/Components/CodeEditor/CodeEditorRenderer.tsx b/src/Shared/Components/CodeEditor/CodeEditorRenderer.tsx index aab206b7f..29010db87 100644 --- a/src/Shared/Components/CodeEditor/CodeEditorRenderer.tsx +++ b/src/Shared/Components/CodeEditor/CodeEditorRenderer.tsx @@ -61,6 +61,7 @@ export const CodeEditorRenderer = ({ // REFS const codeMirrorRef = useRef() const codeMirrorMergeParentRef = useRef() + const codeMirrorMergeRef = useRef() const diffMinimapRef = useRef() const diffMinimapParentRef = useRef() @@ -183,78 +184,102 @@ export const CodeEditorRenderer = ({ } }) - useEffect(() => { - // DIFF VIEW INITIALIZATION - if (!loading && codeMirrorMergeParentRef.current) { - const scanLimit = getScanLimit(lhsValue, value) + /** + * Initializes or reinitializes the CodeMirror merge view for diff comparison. + * + * This function: + * 1. Destroys any existing merge view instances to prevent memory leaks + * 2. Creates a new MergeView instance with the current values and configurations + * 3. Initializes the diff minimap if enabled + * 4. Updates the component state with the new instances + * + */ + const initializeCodeMirrorMergeView = () => { + // Destroy existing merge view instance if it exists + codeMirrorMergeInstance?.destroy() + codeMirrorMergeRef.current?.destroy() + + const codeMirrorMergeView = new MergeView({ + a: { + doc: lhsValue, + extensions: [...originalViewExtensions, originalUpdateListener], + }, + b: { + doc: value, + extensions: [...modifiedViewExtensions, modifiedUpdateListener], + }, + ...(!readOnly ? { revertControls: 'a-to-b', renderRevertControl: getRevertControlButton } : {}), + diffConfig: { scanLimit: getScanLimit(lhsValue, value), timeout: 5000 }, + parent: codeMirrorMergeParentRef.current, + collapseUnchanged: collapseUnchanged ? {} : undefined, + }) - codeMirrorMergeInstance?.destroy() + codeMirrorMergeRef.current = codeMirrorMergeView + setCodeMirrorMergeInstance(codeMirrorMergeView) - const codeMirrorMergeView = new MergeView({ + // MINIMAP INITIALIZATION + if (!disableMinimap && codeMirrorMergeView && diffMinimapParentRef.current) { + diffMinimapInstance?.destroy() + diffMinimapRef.current?.destroy() + + const diffMinimapMergeView = new MergeView({ a: { doc: lhsValue, - extensions: [...originalViewExtensions, originalUpdateListener], + extensions: diffMinimapExtensions, }, b: { doc: value, - extensions: [...modifiedViewExtensions, modifiedUpdateListener], + extensions: diffMinimapExtensions, }, - ...(!readOnly ? { revertControls: 'a-to-b', renderRevertControl: getRevertControlButton } : {}), - diffConfig: { scanLimit, timeout: 5000 }, - parent: codeMirrorMergeParentRef.current, - collapseUnchanged: collapseUnchanged ? {} : undefined, + gutter: false, + diffConfig: { scanLimit: getScanLimit(lhsValue, value), timeout: 5000 }, + parent: diffMinimapParentRef.current, }) - setCodeMirrorMergeInstance(codeMirrorMergeView) - - // MINIMAP INITIALIZATION - if (!disableMinimap && codeMirrorMergeView && diffMinimapParentRef.current) { - diffMinimapInstance?.destroy() - diffMinimapRef.current?.destroy() - - const diffMinimapMergeView = new MergeView({ - a: { - doc: lhsValue, - extensions: diffMinimapExtensions, - }, - b: { - doc: value, - extensions: diffMinimapExtensions, - }, - gutter: false, - diffConfig: { scanLimit, timeout: 5000 }, - parent: diffMinimapParentRef.current, - }) - - diffMinimapRef.current = diffMinimapMergeView - setDiffMinimapInstance(diffMinimapMergeView) - } + + diffMinimapRef.current = diffMinimapMergeView + setDiffMinimapInstance(diffMinimapMergeView) + } + } + + useEffect(() => { + // DIFF VIEW INITIALIZATION + if (!loading && codeMirrorMergeParentRef.current) { + initializeCodeMirrorMergeView() } return () => { setCodeMirrorMergeInstance(null) setDiffMinimapInstance(null) + codeMirrorMergeRef.current = null diffMinimapRef.current = null } }, [loading, codemirrorMergeKey, diffMode, collapseUnchanged, disableMinimap]) - // Sync external changes of `lhsValue` and `value` state to the diff-editor state. + /** + * Synchronizes external changes of `lhsValue` and `value` with the diff-editor state. + * + * When the external state (lhsValue for left-hand side or value for right-hand side) changes, + * we need to update the CodeMirror merge view to reflect these changes. This effect detects + * discrepancies between the current editor content and the external state. + * + * Instead of trying to update the existing editors directly (which can be complex and error-prone), + * we reinitialize the entire merge view when the external state differs from the editor content. + * This ensures a clean, consistent state that properly reflects the external data. + * + */ useEffect(() => { - if (codeMirrorMergeInstance) { - const originalDoc = codeMirrorMergeInstance.a.state.doc.toString() - if (originalDoc !== lhsValue) { - codeMirrorMergeInstance.a.dispatch({ - changes: { from: 0, to: originalDoc.length, insert: lhsValue || '' }, - }) - } - - const modifiedDoc = codeMirrorMergeInstance.b.state.doc.toString() - if (modifiedDoc !== value) { - codeMirrorMergeInstance.b.dispatch({ - changes: { from: 0, to: modifiedDoc.length, insert: value || '' }, - }) + if (codeMirrorMergeRef.current) { + // Get the current content from both editors + const originalDoc = codeMirrorMergeRef.current.a.state.doc.toString() + const modifiedDoc = codeMirrorMergeRef.current.b.state.doc.toString() + + // If either editor's content doesn't match the external state, + // reinitialize the entire merge view with the current values + if (originalDoc !== lhsValue || modifiedDoc !== value) { + initializeCodeMirrorMergeView() } } - }, [lhsValue, value, codeMirrorMergeInstance]) + }, [lhsValue, value]) // SCALING FACTOR UPDATER useEffect(() => { diff --git a/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiffMain.tsx b/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiffMain.tsx index 9cf072fb2..db751dd79 100644 --- a/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiffMain.tsx +++ b/src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiffMain.tsx @@ -218,7 +218,6 @@ export const DeploymentConfigDiffMain = ({
    )} Date: Mon, 2 Jun 2025 12:55:12 +0530 Subject: [PATCH 107/109] chore(version): bump to 1.14.2-pre-3 --- 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 6db04829c..28f328095 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2-pre-2", + "version": "1.14.2-pre-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2-pre-2", + "version": "1.14.2-pre-3", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 406232770..c43aa93d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2-pre-2", + "version": "1.14.2-pre-3", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From bb9a33b3431272b6c0e0c31724407e89d322dd0b Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 4 Jun 2025 17:08:32 +0530 Subject: [PATCH 108/109] chore(version): bump to 1.15.0 --- 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 28f328095..3e097b652 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2-pre-3", + "version": "1.15.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2-pre-3", + "version": "1.15.0", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index c43aa93d8..50b1a46a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.14.2-pre-3", + "version": "1.15.0", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", From f96cfc578057fd4650446fb9a51fe67cee26e8b5 Mon Sep 17 00:00:00 2001 From: Rohit Raj Date: Wed, 4 Jun 2025 17:19:40 +0530 Subject: [PATCH 109/109] fix(CodeEditor): enable drawSelection for improved user experience --- src/Shared/Components/CodeEditor/CodeEditor.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Shared/Components/CodeEditor/CodeEditor.tsx b/src/Shared/Components/CodeEditor/CodeEditor.tsx index 3560bc631..b1cdcbcaa 100644 --- a/src/Shared/Components/CodeEditor/CodeEditor.tsx +++ b/src/Shared/Components/CodeEditor/CodeEditor.tsx @@ -190,7 +190,7 @@ const CodeEditor = ({ defaultKeymap: false, searchKeymap: false, foldGutter: false, - drawSelection: false, + drawSelection: true, highlightActiveLineGutter: true, tabSize, } @@ -260,6 +260,7 @@ const CodeEditor = ({ codeEditorTheme, basicSetup({ ...basicSetupOptions, + drawSelection: false, lineNumbers: false, highlightActiveLine: false, highlightActiveLineGutter: false,