Skip to content

Commit 3895156

Browse files
authored
Merge pull request #757 from devtron-labs/feat/profile-menu
feat: Revamp Profile Menu in Page Header, DocLink, Button - component updates
2 parents b3cae14 + 13ddf40 commit 3895156

36 files changed

+408
-255
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devtron-labs/devtron-fe-common-lib",
3-
"version": "1.14.2-pre-1",
3+
"version": "1.14.2-pre-2",
44
"description": "Supporting common component library",
55
"type": "module",
66
"main": "dist/index.js",

src/Common/Constants.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ export const Host = window?.__ORCHESTRATOR_ROOT__ ?? '/orchestrator'
1919

2020
export const DOCUMENTATION_HOME_PAGE = 'https://docs.devtron.ai'
2121
export const DEVTRON_HOME_PAGE = 'https://devtron.ai/'
22-
export const DOCUMENTATION_VERSION = '/v/v0.7'
22+
export const DOCUMENTATION_VERSION = '/devtron/v0.7'
2323
export const DISCORD_LINK = 'https://discord.devtron.ai/'
2424
export const DEFAULT_JSON_SCHEMA_URI = 'https://json-schema.org/draft/2020-12/schema'
25+
export const LICENSE_DASHBOARD_HOME_PAGE = 'https://license.devtron.ai/dashboard'
2526

2627
export const PATTERNS = {
2728
STRING: /^[a-zA-Z0-9_]+$/,
@@ -47,6 +48,7 @@ export const PATTERNS = {
4748
const GLOBAL_CONFIG_TEMPLATES_DEVTRON_APP = '/global-config/templates/devtron-apps'
4849

4950
export const URLS = {
51+
LOGIN: '/login',
5052
LOGIN_SSO: '/login/sso',
5153
PERMISSION_GROUPS: '/global-config/auth/groups',
5254
APP: '/app',

src/Shared/Components/ActionMenu/ActionMenu.component.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import { MutableRefObject } from 'react'
2-
31
import { CustomInput } from '../CustomInput'
42
import { Popover } from '../Popover'
53
import { SelectPickerMenuListFooter } from '../SelectPicker/common'
64
import { ActionMenuItem } from './ActionMenuItem'
7-
import { ActionMenuItemType, ActionMenuProps } from './types'
5+
import { ActionMenuItemProps, ActionMenuProps } from './types'
86
import { useActionMenu } from './useActionMenu.hook'
97

108
import './actionMenu.scss'
@@ -51,8 +49,8 @@ export const ActionMenu = <T extends string | number = string | number>({
5149
// HANDLERS
5250
const handleOptionMouseEnter = (index: number) => () => setFocusedIndex(index)
5351

54-
const handleOptionOnClick = (item: ActionMenuItemType<T>) => () => {
55-
onClick(item)
52+
const handleOptionOnClick: ActionMenuItemProps<T>['onClick'] = (item, e) => {
53+
onClick(item, e)
5654
closePopover()
5755
}
5856

@@ -82,7 +80,7 @@ export const ActionMenu = <T extends string | number = string | number>({
8280
</div>
8381
)}
8482
<ul
85-
ref={scrollableRef as MutableRefObject<HTMLUListElement>}
83+
ref={scrollableRef}
8684
role="menu"
8785
className="action-menu m-0 p-0 flex-grow-1 dc__overflow-auto dc__overscroll-none"
8886
>
@@ -114,7 +112,7 @@ export const ActionMenu = <T extends string | number = string | number>({
114112
itemRef={itemsRef.current[index]}
115113
isFocused={index === focusedIndex}
116114
onMouseEnter={handleOptionMouseEnter(index)}
117-
onClick={handleOptionOnClick(item)}
115+
onClick={handleOptionOnClick}
118116
disableDescriptionEllipsis={disableDescriptionEllipsis}
119117
/>
120118
)
@@ -132,7 +130,7 @@ export const ActionMenu = <T extends string | number = string | number>({
132130
)}
133131
</ul>
134132
{footerConfig && (
135-
<div className="bg__menu--secondary border__secondary-translucent--top">
133+
<div className="border__secondary-translucent--top">
136134
<SelectPickerMenuListFooter menuListFooterConfig={footerConfig} />
137135
</div>
138136
)}

src/Shared/Components/ActionMenu/ActionMenuItem.tsx

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
import { LegacyRef, Ref } from 'react'
1+
import { LegacyRef, MouseEvent, Ref } from 'react'
22
import { Link } from 'react-router-dom'
33

44
import { Tooltip } from '@Common/Tooltip'
5+
import { ComponentSizeType } from '@Shared/constants'
56

7+
import { Button, ButtonProps, ButtonVariantType } from '../Button'
68
import { Icon } from '../Icon'
9+
import { NumbersCount } from '../NumbersCount'
710
import { getTooltipProps } from '../SelectPicker/common'
8-
import { ActionMenuItemProps } from './types'
11+
import { DTSwitch, DTSwitchProps } from '../Switch'
12+
import { ActionMenuItemProps, ActionMenuItemType } from './types'
913

10-
const COMMON_ACTION_MENU_ITEM_CLASS = 'flex-grow-1 flex left top dc__gap-8 py-6 px-8'
14+
const COMMON_ACTION_MENU_ITEM_CLASS = 'w-100 flex left top dc__gap-8 py-6 px-8'
1115

1216
export const ActionMenuItem = <T extends string | number>({
1317
item,
@@ -22,7 +26,7 @@ export const ActionMenuItem = <T extends string | number>({
2226
description,
2327
label,
2428
startIcon,
25-
endIcon,
29+
trailingItem,
2630
tooltipProps,
2731
type = 'neutral',
2832
isDisabled,
@@ -40,22 +44,83 @@ export const ActionMenuItem = <T extends string | number>({
4044
const isNegativeType = type === 'negative'
4145

4246
// HANDLERS
43-
const handleClick = () => {
44-
onClick(item)
47+
const handleClick = (e: MouseEvent<HTMLAnchorElement> | MouseEvent<HTMLButtonElement>) => {
48+
onClick(item, e)
4549
}
4650

51+
const handleTrailingSwitchChange =
52+
({ type: trailingItemType, config }: ActionMenuItemType<T>['trailingItem']): DTSwitchProps['onChange'] =>
53+
(e) => {
54+
if (trailingItemType === 'switch') {
55+
e.stopPropagation()
56+
config.onChange(e)
57+
}
58+
}
59+
60+
const handleTrailingButtonClick =
61+
({ type: trailingItemType, config }: ActionMenuItemType<T>['trailingItem']): ButtonProps['onClick'] =>
62+
(e) => {
63+
e.stopPropagation()
64+
if (trailingItemType === 'button' && config.onClick) {
65+
config.onClick(e)
66+
}
67+
}
68+
4769
// RENDERERS
4870
const renderIcon = (iconProps: typeof startIcon) =>
4971
iconProps && (
50-
<div className="mt-2 flex dc__no-shrink">
72+
<span className="mt-2 flex dc__no-shrink">
5173
<Icon {...iconProps} color={iconProps.color || (isNegativeType ? 'R500' : 'N800')} />
52-
</div>
74+
</span>
5375
)
5476

77+
const renderTrailingItem = () => {
78+
if (!trailingItem) {
79+
return null
80+
}
81+
82+
const { type: trailingItemType, config } = trailingItem
83+
84+
switch (trailingItemType) {
85+
case 'icon':
86+
return renderIcon(config)
87+
case 'text': {
88+
const { value, icon } = config
89+
return (
90+
<span className="flex dc__gap-2 mt-2">
91+
<span className="fs-12 lh-1-5 fw-4 cn-7">{value}</span>
92+
{icon && <Icon name={icon.name} color={icon.color || (isNegativeType ? 'R500' : 'N700')} />}
93+
</span>
94+
)
95+
}
96+
case 'counter':
97+
return <NumbersCount count={config.value} />
98+
case 'switch':
99+
return (
100+
<DTSwitch
101+
{...config}
102+
onChange={handleTrailingSwitchChange(trailingItem)}
103+
size={ComponentSizeType.small}
104+
/>
105+
)
106+
case 'button':
107+
return (
108+
<Button
109+
{...(config as ButtonProps)}
110+
onClick={handleTrailingButtonClick(trailingItem)}
111+
variant={ButtonVariantType.borderLess}
112+
size={ComponentSizeType.xxs}
113+
/>
114+
)
115+
default:
116+
return null
117+
}
118+
}
119+
55120
const renderContent = () => (
56121
<>
57122
{renderIcon(startIcon)}
58-
<span>
123+
<span className="flex-grow-1">
59124
<Tooltip content={label} placement="right">
60125
<span className={`m-0 fs-13 fw-4 lh-20 dc__truncate ${isNegativeType ? 'cr-5' : 'cn-9'}`}>
61126
{label}
@@ -72,7 +137,7 @@ export const ActionMenuItem = <T extends string | number>({
72137
description
73138
))}
74139
</span>
75-
{renderIcon(endIcon)}
140+
{renderTrailingItem()}
76141
</>
77142
)
78143

@@ -86,6 +151,7 @@ export const ActionMenuItem = <T extends string | number>({
86151
href={item.href}
87152
target="_blank"
88153
rel="noreferrer"
154+
onClick={handleClick}
89155
>
90156
{renderContent()}
91157
</a>
@@ -96,6 +162,7 @@ export const ActionMenuItem = <T extends string | number>({
96162
ref={itemRef as Ref<HTMLAnchorElement>}
97163
className={COMMON_ACTION_MENU_ITEM_CLASS}
98164
to={item.to}
165+
onClick={handleClick}
99166
>
100167
{renderContent()}
101168
</Link>
@@ -107,6 +174,7 @@ export const ActionMenuItem = <T extends string | number>({
107174
ref={itemRef as LegacyRef<HTMLButtonElement>}
108175
type="button"
109176
className={`dc__transparent ${COMMON_ACTION_MENU_ITEM_CLASS}`}
177+
onClick={handleClick}
110178
>
111179
{renderContent()}
112180
</button>
@@ -124,7 +192,6 @@ export const ActionMenuItem = <T extends string | number>({
124192
tabIndex={-1}
125193
// Intentionally added margin to the left and right to have the gap on the edges of the options
126194
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' : ''}` : ''}`}
127-
onClick={!isDisabled ? handleClick : undefined}
128195
aria-disabled={isDisabled}
129196
>
130197
{renderComponent()}

src/Shared/Components/ActionMenu/types.ts

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
import { LegacyRef, Ref } from 'react'
1+
import { LegacyRef, MouseEvent, ReactElement, Ref } from 'react'
22
import { LinkProps } from 'react-router-dom'
33

4+
import { OmitNever } from '@Shared/types'
5+
6+
import { ButtonProps } from '../Button'
47
import { IconsProps } from '../Icon'
8+
import { NumbersCountProps } from '../NumbersCount'
59
import { PopoverProps, UsePopoverProps } from '../Popover'
610
import { SelectPickerOptionType, SelectPickerProps } from '../SelectPicker'
11+
import { DTSwitchProps } from '../Switch'
712

813
type ConditionalActionMenuComponentType =
914
| {
@@ -32,6 +37,43 @@ type ActionMenuItemIconType = Pick<IconsProps, 'name'> & {
3237
color?: IconsProps['color']
3338
}
3439

40+
type TrailingItemType =
41+
| {
42+
type: 'icon'
43+
config: ActionMenuItemIconType
44+
}
45+
| {
46+
type: 'text'
47+
config: {
48+
value: string
49+
icon?: ActionMenuItemIconType
50+
}
51+
}
52+
| {
53+
type: 'counter'
54+
config: {
55+
value: NumbersCountProps['count']
56+
}
57+
}
58+
| {
59+
type: 'switch'
60+
config: Pick<
61+
DTSwitchProps,
62+
| 'ariaLabel'
63+
| 'isChecked'
64+
| 'indeterminate'
65+
| 'isDisabled'
66+
| 'isLoading'
67+
| 'name'
68+
| 'onChange'
69+
| 'tooltipContent'
70+
>
71+
}
72+
| {
73+
type: 'button'
74+
config: OmitNever<Omit<Extract<ButtonProps, { icon: ReactElement }>, 'size' | 'variant'>>
75+
}
76+
3577
export type ActionMenuItemType<T extends string | number = string | number> = Omit<
3678
SelectPickerOptionType,
3779
'label' | 'value' | 'endIcon' | 'startIcon'
@@ -49,8 +91,8 @@ export type ActionMenuItemType<T extends string | number = string | number> = Om
4991
type?: 'neutral' | 'negative'
5092
/** Defines the icon to be displayed at the start of the menu item. */
5193
startIcon?: ActionMenuItemIconType
52-
/** Defines the icon to be displayed at the end of the menu item. */
53-
endIcon?: ActionMenuItemIconType
94+
/** Defines the item to be displayed at the end of the menu item. */
95+
trailingItem?: TrailingItemType
5496
} & ConditionalActionMenuComponentType
5597

5698
export type ActionMenuOptionType<T extends string | number> = {
@@ -85,7 +127,7 @@ export type ActionMenuProps<T extends string | number = string | number> = UseAc
85127
* Callback function triggered when an item is clicked.
86128
* @param item - The selected item.
87129
*/
88-
onClick: (item: ActionMenuItemType<T>) => void
130+
onClick: (item: ActionMenuItemType<T>, e: MouseEvent<HTMLAnchorElement> | MouseEvent<HTMLButtonElement>) => void
89131
/**
90132
* Config for the footer at the bottom of action menu list. It is sticky by default
91133
*/

src/Shared/Components/ActionMenu/useActionMenu.hook.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ChangeEvent, createRef, RefObject, useEffect, useMemo, useRef, useState } from 'react'
1+
import { ChangeEvent, createRef, RefObject, useEffect, useRef, useState } from 'react'
22

33
import { usePopover, UsePopoverProps } from '../Popover'
44
import { UseActionMenuProps } from './types'
@@ -17,13 +17,9 @@ export const useActionMenu = <T extends string | number>({
1717
const [focusedIndex, setFocusedIndex] = useState(-1)
1818
const [searchTerm, setSearchTerm] = useState('')
1919

20-
// MEMOIZED CONSTANTS
21-
const filteredOptions = useMemo(
22-
() => (isSearchable ? filterActionMenuOptions(options, searchTerm) : options),
23-
[isSearchable, JSON.stringify(options), searchTerm],
24-
)
25-
26-
const flatOptions = useMemo(() => getActionMenuFlatOptions(filteredOptions), [filteredOptions])
20+
// CONSTANTS
21+
const filteredOptions = isSearchable ? filterActionMenuOptions(options, searchTerm) : options
22+
const flatOptions = getActionMenuFlatOptions(filteredOptions)
2723

2824
// REFS
2925
const itemsRef = useRef<RefObject<HTMLAnchorElement | HTMLButtonElement>[]>(

0 commit comments

Comments
 (0)