Skip to content

Commit ff7f0c0

Browse files
Merge pull request #615 from devtron-labs/feat/theme-dialog
feat: add theme dialog prompt for first time and migrate existing theme switcher to dialog with some minor css enhancement in logout card and page header.
2 parents 84bf352 + 5f5fc99 commit ff7f0c0

File tree

18 files changed

+151
-113
lines changed

18 files changed

+151
-113
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.8.8",
3+
"version": "1.8.9",
44
"description": "Supporting common component library",
55
"type": "module",
66
"main": "dist/index.js",

src/Assets/IconV2/ic-logout.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 8 additions & 0 deletions
Loading

src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { AnimatePresence, motion } from 'framer-motion'
1919
import { noop, stopPropagation, useRegisterShortcut, UseRegisterShortcutProvider } from '@Common/index'
2020
import { ComponentSizeType } from '@Shared/constants'
2121
import { getUniqueId } from '@Shared/Helpers'
22-
import { ConfirmationModalBodyProps, ConfirmationModalProps } from './types'
22+
import { ConfirmationModalBodyProps, ConfirmationModalProps, ConfirmationModalVariantType } from './types'
2323
import { getPrimaryButtonStyleFromVariant, getConfirmationLabel, getIconFromVariant } from './utils'
2424
import { Button, ButtonStyleType, ButtonVariantType } from '../Button'
2525
import { Backdrop } from '../Backdrop'
@@ -37,6 +37,7 @@ const ConfirmationModalBody = ({
3737
children,
3838
handleClose,
3939
shouldCloseOnEscape = true,
40+
isLandscapeView = false,
4041
}: ConfirmationModalBodyProps) => {
4142
const { registerShortcut, unregisterShortcut } = useRegisterShortcut()
4243

@@ -48,6 +49,7 @@ const ConfirmationModalBody = ({
4849
const { primaryButtonConfig, secondaryButtonConfig } = buttonConfig
4950

5051
const RenderIcon = Icon ?? getIconFromVariant(variant)
52+
const hideIcon = variant === ConfirmationModalVariantType.custom && !Icon
5153

5254
const disablePrimaryButton: boolean =
5355
('disabled' in primaryButtonConfig && primaryButtonConfig.disabled) ||
@@ -80,18 +82,20 @@ const ConfirmationModalBody = ({
8082
return (
8183
<Backdrop onEscape={shouldCloseOnEscape ? handleCloseWrapper : noop}>
8284
<motion.div
83-
className="confirmation-modal border__secondary flexbox-col br-8 bg__primary dc__m-auto mt-40 w-400"
85+
className={`${isLandscapeView ? 'w-500' : 'w-400'} confirmation-modal border__secondary flexbox-col br-8 bg__primary dc__m-auto mt-40 w-400`}
8486
exit={{ y: 100, opacity: 0, scale: 0.75, transition: { duration: 0.35 } }}
8587
initial={{ y: 100, opacity: 0, scale: 0.75 }}
8688
animate={{ y: 0, opacity: 1, scale: 1 }}
8789
onClick={stopPropagation}
8890
>
8991
<div className="flexbox-col dc__gap-16 p-20">
90-
{cloneElement(RenderIcon, {
91-
className: `${RenderIcon.props?.className ?? ''} icon-dim-48 dc__no-shrink`,
92-
})}
92+
{hideIcon
93+
? null
94+
: cloneElement(RenderIcon, {
95+
className: `${RenderIcon.props?.className ?? ''} icon-dim-48 dc__no-shrink`,
96+
})}
9397

94-
<div className="flexbox-col dc__gap-8">
98+
<div className={`flexbox-col ${isLandscapeView ? '' : 'dc__gap-8'}`}>
9599
<span className="cn-9 fs-16 fw-6 lh-24 dc__word-break">{title}</span>
96100

97101
{typeof subtitle === 'string' ? (

src/Shared/Components/ConfirmationModal/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ type ButtonConfigAndVariantType<isConfig extends boolean> =
6464
}
6565
| {
6666
variant: ConfirmationModalVariantType.custom
67-
Icon: ReactElement
67+
Icon?: ReactElement
6868
buttonConfig: ButtonConfig<isConfig, true>
6969
}
7070

@@ -94,6 +94,11 @@ export type ConfirmationModalProps<isConfig extends boolean = false> = PropsWith
9494
* Configuration object for confirmation behavior.
9595
*/
9696
confirmationConfig?: ConfirmationConfigType
97+
/**
98+
* @default false
99+
* @deprecated Used to extend the width to 500px and remove gap between title and subTitle.
100+
*/
101+
isLandscapeView?: boolean
97102
}> &
98103
ButtonConfigAndVariantType<isConfig> &
99104
(isConfig extends false

src/Shared/Components/Header/PageHeader.tsx

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@ import { useEffect, useState } from 'react'
1818
import Tippy from '@tippyjs/react'
1919
import './pageHeader.css'
2020
import ReactGA from 'react-ga4'
21-
import { getRandomColor } from '../../../Common'
21+
import { ReactComponent as ICMediumPaintBucket } from '@IconsV2/ic-medium-paintbucket.svg'
22+
import { ReactComponent as ICCaretDown } from '@Icons/ic-caret-down.svg'
23+
import { ReactComponent as Close } from '@Icons/ic-close.svg'
24+
import { ReactComponent as Question } from '@Icons/ic-help-outline.svg'
25+
import { getAlphabetIcon, TippyCustomized, TippyTheme } from '../../../Common'
2226
import LogoutCard from '../LogoutCard'
2327
import { setActionWithExpiry, handlePostHogEventUpdate } from './utils'
2428
import { InstallationType, ServerInfo, PageHeaderType } from './types'
2529
import { getServerInfo } from './service'
2630
import GettingStartedCard from '../GettingStartedCard/GettingStarted'
2731
import { POSTHOG_EVENT_ONBOARDING, MAX_LOGIN_COUNT } from '../../../Common/Constants'
2832
import HelpNav from './HelpNav'
29-
import { ReactComponent as Question } from '../../../Assets/Icon/ic-help-outline.svg'
30-
import { ReactComponent as Close } from '../../../Assets/Icon/ic-close.svg'
31-
import { ReactComponent as DropDownIcon } from '../../../Assets/Icon/ic-chevron-down.svg'
3233
import AnnouncementBanner from '../AnnouncementBanner/AnnouncementBanner'
33-
import { useMainContext, useUserEmail } from '../../Providers'
34+
import { useMainContext, useTheme, useUserEmail } from '../../Providers'
3435
import { InfoIconTippy } from '../InfoIconTippy'
3536
import { IframePromoButton } from './IframePromoButton'
3637

@@ -50,6 +51,8 @@ const PageHeader = ({
5051
}: PageHeaderType) => {
5152
const { loginCount, setLoginCount, showGettingStartedCard, setShowGettingStartedCard, setGettingStartedClicked } =
5253
useMainContext()
54+
const { showSwitchThemeLocationTippy, handleShowSwitchThemeLocationTippyChange } = useTheme()
55+
5356
const { isTippyCustomized, tippyRedirectLink, TippyIcon, tippyMessage, onClickTippyButton, additionalContent } =
5457
tippyProps || {}
5558
const [showHelpCard, setShowHelpCard] = useState(false)
@@ -120,6 +123,17 @@ const PageHeader = ({
120123
})
121124
}
122125

126+
const handleCloseSwitchThemeLocationTippyChange = () => {
127+
handleShowSwitchThemeLocationTippyChange(false)
128+
}
129+
130+
const renderThemePreferenceLocationTippyContent = () => (
131+
<div className="px-16 pb-16 flexbox-col dc__gap-4">
132+
<h6 className="m-0 fs-14 fw-6 lh-20">Theme Preference</h6>
133+
<p className="m-0 fs-13 fw-4 lh-20">You can change your theme preference here</p>
134+
</div>
135+
)
136+
123137
const renderLogoutHelpSection = () => (
124138
<>
125139
<div className="flex left cursor dc__gap-8" onClick={onClickHelp}>
@@ -129,20 +143,39 @@ const PageHeader = ({
129143
<span className="fs-13 cn-9" data-testid="go-to-get-started">
130144
Help
131145
</span>
132-
<DropDownIcon
146+
<ICCaretDown
133147
style={{ ['--rotateBy' as any]: `${180 * Number(showHelpCard)}deg` }}
134-
className="fcn-9 icon-dim-20 rotate pointer"
148+
className="scn-7 icon-dim-16 rotate pointer"
135149
/>
136150
</div>
137151
{!window._env_.K8S_CLIENT && (
138-
<div
139-
className="logout-card__initial cursor fs-13 icon-dim-24 flex logout-card__initial--nav"
140-
onClick={onClickLogoutButton}
141-
style={{ backgroundColor: getRandomColor(email) }}
142-
data-testid="profile-button"
152+
<TippyCustomized
153+
theme={TippyTheme.black}
154+
className="w-250"
155+
placement="bottom"
156+
visible={showSwitchThemeLocationTippy}
157+
Icon={ICMediumPaintBucket}
158+
iconClass="icon-dim-48 dc__no-shrink"
159+
iconSize={48}
160+
additionalContent={renderThemePreferenceLocationTippyContent()}
161+
showCloseButton
162+
trigger="manual"
163+
interactive
164+
arrow
165+
onClose={handleCloseSwitchThemeLocationTippyChange}
143166
>
144-
{email[0]}
145-
</div>
167+
<button
168+
type="button"
169+
data-testid="profile-button"
170+
onClick={onClickLogoutButton}
171+
className="dc__transparent flex p-4 dc__gap-4 br-18 bg__secondary border__secondary"
172+
>
173+
{getAlphabetIcon(email, 'm-0-imp')}
174+
<ICCaretDown
175+
className={`scn-7 icon-dim-16 ${showLogOutCard ? 'dc__flip-180' : ''} dc__transition--transform`}
176+
/>
177+
</button>
178+
</TippyCustomized>
146179
)}
147180
</>
148181
)
@@ -255,7 +288,7 @@ const PageHeader = ({
255288
)}
256289
{showLogOutCard && (
257290
<LogoutCard
258-
className="logout-card__more-option"
291+
className="logout-card__more-option mt-8"
259292
userFirstLetter={email}
260293
setShowLogOutCard={setShowLogOutCard}
261294
showLogOutCard={showLogOutCard}

src/Shared/Components/Icon/Icon.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import { ReactComponent as ICJobColor } from '@IconsV2/ic-job-color.svg'
4444
import { ReactComponent as ICK8sJob } from '@IconsV2/ic-k8s-job.svg'
4545
import { ReactComponent as ICLdap } from '@IconsV2/ic-ldap.svg'
4646
import { ReactComponent as ICLoginDevtronLogo } from '@IconsV2/ic-login-devtron-logo.svg'
47+
import { ReactComponent as ICLogout } from '@IconsV2/ic-logout.svg'
48+
import { ReactComponent as ICMediumPaintbucket } from '@IconsV2/ic-medium-paintbucket.svg'
4749
import { ReactComponent as ICMicrosoft } from '@IconsV2/ic-microsoft.svg'
4850
import { ReactComponent as ICMissing } from '@IconsV2/ic-missing.svg'
4951
import { ReactComponent as ICMonitoring } from '@IconsV2/ic-monitoring.svg'
@@ -113,6 +115,8 @@ export const iconMap = {
113115
'ic-k8s-job': ICK8sJob,
114116
'ic-ldap': ICLdap,
115117
'ic-login-devtron-logo': ICLoginDevtronLogo,
118+
'ic-logout': ICLogout,
119+
'ic-medium-paintbucket': ICMediumPaintbucket,
116120
'ic-microsoft': ICMicrosoft,
117121
'ic-missing': ICMissing,
118122
'ic-monitoring': ICMonitoring,

src/Shared/Components/LogoutCard.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { useHistory } from 'react-router-dom'
1919
import { useMainContext } from '@Shared/Providers'
2020
import { getRandomColor, stopPropagation } from '../../Common'
2121
import { ThemeSwitcher } from './ThemeSwitcher'
22+
import { Icon } from './Icon'
2223

2324
interface LogoutCardType {
2425
className: string
@@ -27,6 +28,9 @@ interface LogoutCardType {
2728
showLogOutCard: boolean
2829
}
2930

31+
export const LOGOUT_CARD_BASE_BUTTON_CLASS =
32+
'dc__unset-button-styles dc__content-space px-12 py-6 fs-13 fw-4 lh-20 cursor w-100 flex left br-4'
33+
3034
const LogoutCard = ({ className, userFirstLetter, setShowLogOutCard, showLogOutCard }: LogoutCardType) => {
3135
const history = useHistory()
3236
const { viewIsPipelineRBACConfiguredNode } = useMainContext()
@@ -55,18 +59,19 @@ const LogoutCard = ({ className, userFirstLetter, setShowLogOutCard, showLogOutC
5559
{userFirstLetter[0]}
5660
</p>
5761
</div>
58-
<div className="dc__border-top-n1 py-4">
62+
<div className="dc__border-top-n1 py-4 px-4 flexbox-col">
5963
<ThemeSwitcher onChange={toggleLogoutCard} />
6064

6165
{viewIsPipelineRBACConfiguredNode}
6266

6367
<button
64-
className="dc__unset-button-styles px-12 py-6 fs-13 fw-4 lh-20 cr-5 dc__hover-n50 cursor w-100 flex left"
68+
className={`${LOGOUT_CARD_BASE_BUTTON_CLASS} cr-5 dc__hover-r50`}
6569
data-testid="logout-button"
6670
onClick={onLogout}
6771
type="button"
6872
>
6973
Logout
74+
<Icon name="ic-logout" color="R500" />
7075
</button>
7176
</div>
7277
</div>

src/Shared/Components/ThemeSwitcher/ThemeSwitcher.component.tsx

Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,57 +14,36 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { useTheme } from '@Shared/Providers'
18-
import { THEME_PREFERENCE_MAP, ThemePreferenceType } from '@Shared/Providers/ThemeProvider/types'
19-
import { SegmentedControl, SegmentedControlProps, SegmentedControlVariant } from '@Common/SegmentedControl'
20-
import { ChangeEvent, useMemo } from 'react'
21-
import { stopPropagation } from '@Common/Helper'
22-
import { ComponentSizeType } from '@Shared/constants'
23-
import { THEME_PREFERENCE_TO_ICON_MAP } from './constants'
17+
import { getThemePreferenceText, useTheme } from '@Shared/Providers'
18+
import { ReactComponent as ICCaretLeftSmall } from '@Icons/ic-caret-left-small.svg'
2419
import { ThemeSwitcherProps } from './types'
20+
import { LOGOUT_CARD_BASE_BUTTON_CLASS } from '../LogoutCard'
2521

2622
const ThemeSwitcher = ({ onChange }: ThemeSwitcherProps) => {
27-
const { themePreference, handleThemePreferenceChange } = useTheme()
28-
29-
const { tabs, tooltips } = useMemo<Required<Pick<SegmentedControlProps, 'tabs' | 'tooltips'>>>(() => {
30-
const availableThemePreferences = Object.values(THEME_PREFERENCE_MAP)
31-
32-
return {
33-
tabs: availableThemePreferences.map((value) => ({
34-
label: (
35-
<span className="dc__no-shrink icon-dim-16 flex dc__fill-available-space">
36-
{THEME_PREFERENCE_TO_ICON_MAP[value].icon}
37-
</span>
38-
),
39-
value,
40-
})),
41-
tooltips: availableThemePreferences.map((value) => THEME_PREFERENCE_TO_ICON_MAP[value].tippyContent),
42-
}
43-
}, [])
23+
const { handleThemeSwitcherDialogVisibilityChange, themePreference } = useTheme()
4424

4525
if (!window._env_.FEATURE_EXPERIMENTAL_THEMING_ENABLE) {
4626
return null
4727
}
4828

49-
const handleThemeSwitch = (e: ChangeEvent<HTMLInputElement>) => {
50-
const updatedThemePreference = e.target.value as ThemePreferenceType
51-
handleThemePreferenceChange(updatedThemePreference)
29+
const handleShowThemeSwitcherDialog = () => {
30+
handleThemeSwitcherDialogVisibilityChange(true)
5231
onChange()
5332
}
5433

5534
return (
56-
<div className="flex dc__content-space dc__gap-8 px-12 py-6" onClick={stopPropagation}>
57-
<p className="m-0 fs-13 fw-4 lh-20 cn-9">Theme</p>
58-
<SegmentedControl
59-
initialTab={themePreference}
60-
name="theme-preference-selector"
61-
onChange={handleThemeSwitch}
62-
tabs={tabs}
63-
tooltips={tooltips}
64-
size={ComponentSizeType.medium}
65-
variant={SegmentedControlVariant.GRAY_ON_WHITE}
66-
/>
67-
</div>
35+
<button
36+
type="button"
37+
data-testid="open-theme-switcher-dialog"
38+
className={`${LOGOUT_CARD_BASE_BUTTON_CLASS} dc__hover-n50`}
39+
onClick={handleShowThemeSwitcherDialog}
40+
>
41+
Theme
42+
<div className="flexbox dc__gap-4 dc__align-items-center">
43+
<span className="cn-7 fs-13 fw-4 lh-20">{getThemePreferenceText(themePreference)}</span>
44+
<ICCaretLeftSmall className="dc__flip-180 icon-16 dc__no-shrink scn-7" />
45+
</div>
46+
</button>
6847
)
6948
}
7049

0 commit comments

Comments
 (0)