Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@devtron-labs/devtron-fe-common-lib",
"version": "1.12.0-pre-2",
"version": "1.12.0-pre-3",
"description": "Supporting common component library",
"type": "module",
"main": "dist/index.js",
Expand Down
79 changes: 79 additions & 0 deletions src/Shared/Components/AboutDevtron/AboutDevtronBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import ReactGA from 'react-ga4'

import DevtronCopyright from '@Common/DevtronCopyright'
import { EULA_LINK, PRIVACY_POLICY_LINK, TERMS_OF_USE_LINK } from '@Shared/constants'
import { useMainContext } from '@Shared/Providers'

import { Button, ButtonComponentType, ButtonStyleType, ButtonVariantType } from '../Button'
import { InstallationType } from '../Header/types'
import { Icon } from '../Icon'

const AboutDevtronBody = ({ isFELibAvailable }: { isFELibAvailable: boolean }) => {
const { currentServerInfo } = useMainContext()

const currentVersion = currentServerInfo?.serverInfo?.currentVersion
const isEnterprise = currentServerInfo?.serverInfo?.installationType === InstallationType.ENTERPRISE

const isVersionCompatible = isFELibAvailable === isEnterprise

const handleEULAClick = () => {
ReactGA.event({
category: 'about-devtron',
action: 'ABOUT_DEVTRON_LICENSE_AGREEMENT_CLICKED',
})
}

return (
<div className="flexbox-col p-32 dc__gap-24 br-16 border__secondary bg__secondary">
<div className="flexbox-col dc__align-items-center dc__gap-16 text-center">
<div className="flex p-6 border__primary br-8">
<Icon name="ic-devtron" color="B500" size={40} />
</div>
<div>
<p className="fs-16 cn-9 fw-6 lh-1-5 m-0">Devtron</p>
{isVersionCompatible && (
<p className="fs-13 cn-7 fw-4 lh-20 m-0">{`${isEnterprise ? 'Enterprise' : 'OSS'} Version${currentVersion ? `(${currentVersion})` : ''}`}</p>
)}
</div>
<DevtronCopyright />
</div>
<div className="flexbox flex-wrap dc__content-center dc__gap-4">
<Button
dataTestId="terms-of-service"
text="Terms of service"
variant={ButtonVariantType.text}
style={ButtonStyleType.neutral}
component={ButtonComponentType.anchor}
anchorProps={{
href: TERMS_OF_USE_LINK,
}}
/>
<span>•</span>
<Button
dataTestId="privacy-policy"
text="Privacy policy"
variant={ButtonVariantType.text}
style={ButtonStyleType.neutral}
component={ButtonComponentType.anchor}
anchorProps={{
href: PRIVACY_POLICY_LINK,
}}
/>
<span>•</span>
<Button
dataTestId="license-agreement"
text="End-User License agreement"
variant={ButtonVariantType.text}
style={ButtonStyleType.neutral}
onClick={handleEULAClick}
component={ButtonComponentType.anchor}
anchorProps={{
href: EULA_LINK,
}}
/>
</div>
</div>
)
}

export default AboutDevtronBody
31 changes: 31 additions & 0 deletions src/Shared/Components/AboutDevtron/AboutDevtronDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ComponentSizeType } from '@Shared/constants'

import { Backdrop } from '../Backdrop'
import { Button } from '../Button'
import AboutDevtronBody from './AboutDevtronBody'

const AboutDevtronDialog = ({
handleCloseLicenseInfoDialog,
isFELibAvailable,
}: {
handleCloseLicenseInfoDialog: () => void
isFELibAvailable: boolean
}) => (
<Backdrop onEscape={handleCloseLicenseInfoDialog}>
<div className="flexbox-col w-400 br-12 bg__primary border__primary dc__m-auto mt-40">
<div className="p-24">
<AboutDevtronBody isFELibAvailable={isFELibAvailable} />
</div>
<div className="flex px-24 py-20 dc__content-end">
<Button
dataTestId="license-info-okay"
text="Okay"
size={ComponentSizeType.medium}
onClick={handleCloseLicenseInfoDialog}
/>
</div>
</div>
</Backdrop>
)

export default AboutDevtronDialog
2 changes: 2 additions & 0 deletions src/Shared/Components/AboutDevtron/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as AboutDevtronBody } from './AboutDevtronBody'
export { default as AboutDevtronDialog } from './AboutDevtronDialog'
31 changes: 15 additions & 16 deletions src/Shared/Components/Header/HelpNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const HelpNav = ({
onClickHelpOptions(option)
}

const handleOpenLicenseDialog = () => {
const handleOpenAboutDevtron = () => {
ReactGA.event({
category: 'help-nav__about-devtron',
action: 'ABOUT_DEVTRON_CLICKED',
Expand All @@ -100,23 +100,22 @@ const HelpNav = ({
<option.icon />
<div className="ml-12 cn-9 fs-14">{option.name}</div>
</a>
{/* licenseData is only set when showLicenseData is received true */}
{isEnterprise && index === 1 && (
{index === 1 && (
<>
{licenseData && (
<button
type="button"
className="dc__transparent help-card__option flexbox dc__align-items-center cn-9 dc__gap-12 fs-14"
onClick={handleOpenLicenseDialog}
data-testid="about-devtron"
>
<Icon name="ic-devtron" color="N600" size={20} />
About Devtron
</button>
<button
type="button"
className="dc__transparent help-card__option flexbox dc__align-items-center cn-9 dc__gap-12 fs-14"
onClick={handleOpenAboutDevtron}
data-testid="about-devtron"
>
<Icon name="ic-devtron" color="N600" size={20} />
About Devtron
</button>
{isEnterprise && (
<div className="help__enterprise pl-8 pb-4-imp pt-4-imp dc__gap-12 flexbox dc__align-items-center h-28">
Comment thread
arunjaindev marked this conversation as resolved.
Enterprise Support
</div>
)}
<div className="help__enterprise pl-8 pb-4-imp pt-4-imp dc__gap-12 flexbox dc__align-items-center h-28">
Enterprise Support
</div>
</>
)}
</Fragment>
Expand Down
70 changes: 45 additions & 25 deletions src/Shared/Components/TabGroup/TabGroup.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,23 @@
* limitations under the License.
*/

import { Link, NavLink } from 'react-router-dom'
import { useMemo } from 'react'
import { Link, NavLink, useRouteMatch } from 'react-router-dom'
import { motion } from 'framer-motion'

import { Tooltip } from '@Common/Tooltip'
import { ComponentSizeType } from '@Shared/constants'

import { getTabBadge, getTabDescription, getTabIcon, getTabIndicator } from './TabGroup.helpers'
import { TabGroupProps, TabProps } from './TabGroup.types'
import { getPathnameToMatch, getTabBadge, getTabDescription, getTabIcon, getTabIndicator } from './TabGroup.helpers'
import { AdditionalTabProps, TabGroupProps, TabProps } from './TabGroup.types'
import { getClassNameBySizeMap, tabGroupClassMap } from './TabGroup.utils'

import './TabGroup.scss'

const MotionLayoutUnderline = ({ layoutId }: { layoutId: string }) => (
<motion.div layout="position" layoutId={layoutId} className="underline bcb-5" />
)

const Tab = ({
label,
props,
Expand All @@ -33,7 +39,6 @@ const Tab = ({
icon,
size,
badge = null,
alignActiveBorderWithContainer,
hideTopPadding,
showIndicator,
showError,
Expand All @@ -42,10 +47,19 @@ const Tab = ({
description,
shouldWrapTooltip,
tooltipProps,
}: TabProps & Pick<TabGroupProps, 'size' | 'alignActiveBorderWithContainer' | 'hideTopPadding'>) => {
uniqueGroupId,
}: TabProps & Pick<TabGroupProps, 'size' | 'hideTopPadding'> & AdditionalTabProps) => {
const { path } = useRouteMatch()
const pathToMatch = tabType === 'navLink' || tabType === 'link' ? getPathnameToMatch(props.to, path) : ''

// using match to define if tab is active as useRouteMatch return an object if path is matched otherwise return null/undefined
const match = useRouteMatch(pathToMatch)

const isTabActive = tabType === 'button' ? active : !!match

const { tabClassName, iconClassName, badgeClassName } = getClassNameBySizeMap({
hideTopPadding,
alignActiveBorderWithContainer,
isTabActive,
})[size]

const onClickHandler = (
Expand Down Expand Up @@ -121,9 +135,10 @@ const Tab = ({

const renderTabContainer = () => (
<li
className={`tab-group__tab lh-20 ${active ? 'tab-group__tab--active cb-5 fw-6' : 'cn-9 fw-4'} ${alignActiveBorderWithContainer ? 'tab-group__tab--align-active-border' : ''} ${tabType === 'block' ? 'tab-group__tab--block' : ''} ${disabled ? 'dc__disabled' : 'cursor'}`}
className={`tab-group__tab lh-20 ${active ? 'cb-5 fw-6' : 'cn-9 fw-4'} ${tabType === 'block' ? 'tab-group__tab--block' : ''} ${disabled ? 'dc__disabled' : 'cursor'}`}
>
{getTabComponent()}
{isTabActive && <MotionLayoutUnderline layoutId={uniqueGroupId} />}
</li>
)

Expand All @@ -138,22 +153,27 @@ export const TabGroup = ({
tabs = [],
size = ComponentSizeType.large,
rightComponent,
alignActiveBorderWithContainer,
hideTopPadding,
}: TabGroupProps) => (
<div className="flexbox dc__align-items-center dc__content-space">
<ul role="tablist" className={`tab-group flexbox dc__align-items-center p-0 m-0 ${tabGroupClassMap[size]}`}>
{tabs.map(({ id, ...resProps }) => (
<Tab
key={id}
id={id}
size={size}
alignActiveBorderWithContainer={alignActiveBorderWithContainer}
hideTopPadding={hideTopPadding}
{...resProps}
/>
))}
</ul>
{rightComponent || null}
</div>
)
}: TabGroupProps) => {
// Unique layoutId for motion.div to handle multiple tab groups on same page
// Using tab labels so that id remains same on re mount as well
const uniqueGroupId = useMemo(() => tabs.map((tab) => tab.label).join('-'), [])

return (
<div className="flexbox dc__align-items-center dc__content-space">
<ul role="tablist" className={`tab-group flexbox dc__align-items-center p-0 m-0 ${tabGroupClassMap[size]}`}>
{tabs.map(({ id, ...resProps }) => (
<Tab
key={id}
id={id}
size={size}
hideTopPadding={hideTopPadding}
uniqueGroupId={uniqueGroupId}
{...resProps}
/>
))}
</ul>
{rightComponent || null}
</div>
)
}
13 changes: 13 additions & 0 deletions src/Shared/Components/TabGroup/TabGroup.helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* limitations under the License.
*/

import { LinkProps, NavLinkProps } from 'react-router-dom'

import { ReactComponent as ICErrorExclamation } from '@Icons/ic-error-exclamation.svg'
import { ReactComponent as ICWarning } from '@Icons/ic-warning.svg'

Expand Down Expand Up @@ -65,3 +67,14 @@ export const getTabDescription = (description: TabProps['description']) =>
: description}
</ul>
)

const replaceTrailingSlash = (pathname: string) => pathname.replace(/\/+$/, '')

export const getPathnameToMatch = (to: NavLinkProps['to'] | LinkProps['to'], currentPathname: string): string => {
if (typeof to === 'string' || (to && typeof to === 'object' && 'pathname' in to)) {
const pathname = typeof to === 'string' ? to : to.pathname || ''
// handling absolute and relative paths
return pathname.startsWith('/') ? pathname : `${replaceTrailingSlash(currentPathname)}/${pathname}`
}
return ''
}
26 changes: 3 additions & 23 deletions src/Shared/Components/TabGroup/TabGroup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,14 @@

@include svg-styles(var(--N700));

&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
.underline {
height: 2px;
background-color: transparent;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}

&--align-active-border::after {
bottom: -1px;
&--active {
@include svg-styles(var(--B500));
}

&:hover:not(.tab-group__tab--block):not(.dc__disabled) {
Expand All @@ -58,14 +52,6 @@
}
}

&--active {
@include svg-styles(var(--B500));

&::after {
background-color: var(--B500);
}
}

&__badge {
border-radius: 10px;
min-width: 20px;
Expand Down Expand Up @@ -104,11 +90,5 @@
color: var(--B500);
}
}

&:has(.active) {
&::after {
background-color: var(--B500);
}
}
}
}
9 changes: 4 additions & 5 deletions src/Shared/Components/TabGroup/TabGroup.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,13 @@ export interface TabGroupProps {
* Optional component to be rendered on the right side of the tab list.
*/
rightComponent?: React.ReactElement
/**
* Set to `true` to align the active tab's border with the bottom border of the parent container.
* @default false
*/
alignActiveBorderWithContainer?: boolean
/**
* Determines if the top padding of the tab group should be hidden.
* @default false
*/
hideTopPadding?: boolean
}

export type AdditionalTabProps = {
uniqueGroupId: string
}
Loading