Skip to content

Commit 8bffec1

Browse files
committed
feat: add support for state based collapsible list
1 parent f111df9 commit 8bffec1

File tree

4 files changed

+99
-47
lines changed

4 files changed

+99
-47
lines changed

src/Shared/Components/CollapsibleList/CollapsibleList.component.tsx

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ConditionalWrap } from '@Common/Helper'
66
import { ReactComponent as ICExpand } from '@Icons/ic-expand.svg'
77

88
import { Collapse } from '../Collapse'
9-
import { CollapsibleListProps } from './CollapsibleList.types'
9+
import { CollapsibleListItem, CollapsibleListProps } from './CollapsibleList.types'
1010
import './CollapsibleList.scss'
1111

1212
const renderWithTippy = (tippyProps: TippyProps) => (children: React.ReactElement) => (
@@ -18,6 +18,68 @@ const renderWithTippy = (tippyProps: TippyProps) => (children: React.ReactElemen
1818
export const CollapsibleList = ({ config, onCollapseBtnClick }: CollapsibleListProps) => {
1919
const { pathname } = useLocation()
2020

21+
const getTabContent = (item: CollapsibleListItem) => {
22+
const { title, subtitle, iconConfig } = item
23+
return (
24+
<>
25+
<div className="flexbox-col flex-grow-1 mw-none dc__align-start">
26+
<span className="collapsible__item__title dc__truncate fs-13 lh-20">{title}</span>
27+
{subtitle && <span className="dc__truncate fw-4 lh-1-5 cn-7">{subtitle}</span>}
28+
</div>
29+
{iconConfig && (
30+
<ConditionalWrap
31+
condition={!!iconConfig.tooltipProps}
32+
wrap={renderWithTippy(iconConfig.tooltipProps)}
33+
>
34+
<iconConfig.Icon
35+
{...iconConfig.props}
36+
className={`icon-dim-20 dc__no-shrink cursor ${iconConfig.props?.className || ''}`}
37+
/>
38+
</ConditionalWrap>
39+
)}
40+
</>
41+
)
42+
}
43+
44+
const getTabItem = (item: CollapsibleListItem) => {
45+
const { title, href, isActive, onClick, tabType } = item
46+
if (tabType === 'navLink') {
47+
return (
48+
<NavLink
49+
key={title}
50+
to={href}
51+
className="collapsible__item flexbox dc__align-items-center dc__gap-8 dc__no-decor br-4 py-6 px-8 cursor"
52+
onClick={(e) => {
53+
// Prevent navigation to the same page
54+
if (href === pathname) {
55+
e.preventDefault()
56+
}
57+
onClick?.(e)
58+
}}
59+
>
60+
{getTabContent(item)}
61+
</NavLink>
62+
)
63+
}
64+
// Since is active is boolean we need to explicitly handle for null
65+
return (
66+
<button
67+
key={title}
68+
className={`collapsible__item flexbox dc__align-items-center dc__gap-8 dc__no-decor br-4 py-6 px-8 cursor ${isActive ? 'active' : ''} dc__unset-button-styles w-100`}
69+
onClick={(e) => {
70+
// Prevent navigation to the same page
71+
if (isActive) {
72+
e.preventDefault()
73+
}
74+
onClick?.(e)
75+
}}
76+
type="button"
77+
>
78+
{getTabContent(item)}
79+
</button>
80+
)
81+
}
82+
2183
return (
2284
<div className="mw-none bcn-0">
2385
{config.map(({ id, header, headerIconConfig, items, noItemsText, isExpanded }) => (
@@ -61,40 +123,7 @@ export const CollapsibleList = ({ config, onCollapseBtnClick }: CollapsibleListP
61123
</span>
62124
</div>
63125
) : (
64-
items.map(({ title, href, iconConfig, subtitle, onClick }) => (
65-
<NavLink
66-
key={title}
67-
to={href}
68-
className="collapsible__item flexbox dc__align-items-center dc__gap-8 dc__no-decor br-4 py-6 px-8 cursor"
69-
onClick={(e) => {
70-
// Prevent navigation to the same page
71-
if (href === pathname) {
72-
e.preventDefault()
73-
}
74-
onClick?.(e)
75-
}}
76-
>
77-
<div className="flexbox-col flex-grow-1 mw-none">
78-
<span className="collapsible__item__title dc__truncate fs-13 lh-20">
79-
{title}
80-
</span>
81-
{subtitle && (
82-
<span className="dc__truncate fw-4 lh-1-5 cn-7">{subtitle}</span>
83-
)}
84-
</div>
85-
{iconConfig && (
86-
<ConditionalWrap
87-
condition={!!iconConfig.tooltipProps}
88-
wrap={renderWithTippy(iconConfig.tooltipProps)}
89-
>
90-
<iconConfig.Icon
91-
{...iconConfig.props}
92-
className={`icon-dim-20 dc__no-shrink cursor ${iconConfig.props?.className || ''}`}
93-
/>
94-
</ConditionalWrap>
95-
)}
96-
</NavLink>
97-
))
126+
items.map((item) => getTabItem(item))
98127
)}
99128
</div>
100129
</Collapse>

src/Shared/Components/CollapsibleList/CollapsibleList.types.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,35 @@
11
import React from 'react'
22
import { TippyProps } from '@tippyjs/react'
33

4-
export interface CollapsibleListItem {
4+
interface ButtonTab {
5+
tabType: 'button'
6+
/**
7+
* Is tab active ( for button tab )
8+
*/
9+
isActive: boolean
10+
/**
11+
* The callback function to handle click events on the button.
12+
*/
13+
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
14+
href?: never
15+
}
16+
17+
interface NavLinkTab {
18+
tabType: 'navLink'
19+
/**
20+
* The URL of the nav link.
21+
*/
22+
href: string
23+
/**
24+
* The callback function to handle click events on the nav link.
25+
*/
26+
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void
27+
isActive?: never
28+
}
29+
30+
type ConditionalTabType = ButtonTab | NavLinkTab
31+
32+
export type CollapsibleListItem = ConditionalTabType & {
533
/**
634
* The title of the list item.
735
*/
@@ -27,14 +55,6 @@ export interface CollapsibleListItem {
2755
*/
2856
tooltipProps?: TippyProps
2957
}
30-
/**
31-
* The URL of the nav link.
32-
*/
33-
href?: string
34-
/**
35-
* The callback function to handle click events on the nav link.
36-
*/
37-
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void
3858
}
3959

4060
export interface CollapsibleListConfig {

src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiff.types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ export type DeploymentConfigDiffSelectPickerProps =
4141
selectPickerProps: SelectPickerProps
4242
}
4343

44-
export interface DeploymentConfigDiffNavigationItem extends Pick<CollapsibleListItem, 'href' | 'title' | 'onClick'> {
44+
export interface DeploymentConfigDiffNavigationItem extends Pick<CollapsibleListItem, 'title'> {
4545
hasDiff?: boolean
46+
href: string
47+
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => void
4648
}
4749

4850
export interface DeploymentConfigDiffNavigationCollapsibleItem

src/Shared/Components/DeploymentConfigDiff/DeploymentConfigDiffNavigation.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { ReactComponent as ICInfoOutlined } from '@Icons/ic-info-outlined.svg'
77
import { ReactComponent as ICDiffFileUpdated } from '@Icons/ic-diff-file-updated.svg'
88
import { StyledRadioGroup } from '@Common/index'
99

10-
import { CollapsibleList } from '../CollapsibleList'
10+
import { CollapsibleList, CollapsibleListConfig } from '../CollapsibleList'
1111
import { DeploymentConfigDiffNavigationProps } from './DeploymentConfigDiff.types'
1212

1313
// LOADING SHIMMER
@@ -34,10 +34,11 @@ export const DeploymentConfigDiffNavigation = ({
3434
}, [collapsibleNavList])
3535

3636
/** Collapsible List Config. */
37-
const collapsibleListConfig = collapsibleNavList.map(({ items, ...resListItem }) => ({
37+
const collapsibleListConfig: CollapsibleListConfig[] = collapsibleNavList.map(({ items, ...resListItem }) => ({
3838
...resListItem,
3939
isExpanded: expandedIds[resListItem.id],
4040
items: items.map(({ hasDiff, ...resItem }) => ({
41+
tabType: 'navLink',
4142
...resItem,
4243
...(hasDiff
4344
? {

0 commit comments

Comments
 (0)