Skip to content

Commit 58fab3c

Browse files
Merge pull request #573 from devtron-labs/feat/info-bar
feat: add info bar component
2 parents 0515c5f + 625d0e7 commit 58fab3c

File tree

14 files changed

+246
-40
lines changed

14 files changed

+246
-40
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.7.1",
3+
"version": "1.7.2",
44
"description": "Supporting common component library",
55
"type": "module",
66
"main": "dist/index.js",

src/Common/InfoColorBar/InfoColourbar.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import { InfoColourBarType } from '../Types'
1919
import { Tooltip } from '@Common/Tooltip'
2020
import './infoColourBar.scss'
2121

22+
/**
23+
* @deprecated Use InfoBlock instead
24+
*/
2225
const InfoColourBar = ({
2326
message,
2427
classname,

src/Common/Types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
Severity,
2626
PolicyBlockInfo,
2727
TargetPlatformItemDTO,
28+
ComponentLayoutType,
2829
} from '../Shared'
2930
import {
3031
ACTION_STATE,
@@ -171,7 +172,7 @@ export interface GenericEmptyStateType {
171172
/**
172173
* @default 'column'
173174
*/
174-
layout?: 'row' | 'column'
175+
layout?: ComponentLayoutType
175176
contentClassName?: string
176177
}
177178

src/Shared/Components/FormFieldWrapper/types.ts

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { ReactElement, ReactNode } from 'react'
18+
import { BorderConfigType, ComponentLayoutType } from '@Shared/types'
1819

1920
export type LabelOrAriaLabelType =
2021
| {
@@ -38,7 +39,7 @@ export type FormFieldLabelProps = LabelOrAriaLabelType & {
3839
/**
3940
* Layout of the field
4041
*/
41-
layout?: 'row' | 'column'
42+
layout?: ComponentLayoutType
4243
}
4344

4445
export interface FormFieldInfoProps extends Pick<FormFieldLabelProps, 'inputId'> {
@@ -71,30 +72,5 @@ export interface FormFieldWrapperProps
7172
*/
7273
fullWidth?: boolean
7374
children: ReactElement
74-
borderRadiusConfig?: {
75-
/**
76-
* If false, the top border radius is not applied
77-
*
78-
* @default true
79-
*/
80-
top?: boolean
81-
/**
82-
* If false, the right border radius is not applied
83-
*
84-
* @default true
85-
*/
86-
right?: boolean
87-
/**
88-
* If false, the bottom border radius is not applied
89-
*
90-
* @default true
91-
*/
92-
bottom?: boolean
93-
/**
94-
* If false, the left border radius is not applied
95-
*
96-
* @default true
97-
*/
98-
left?: boolean
99-
}
75+
borderRadiusConfig?: BorderConfigType
10076
}

src/Shared/Components/FormFieldWrapper/utils.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { FormFieldInfoProps, FormFieldLabelProps, FormFieldWrapperProps } from './types'
17+
import { FormFieldInfoProps, FormFieldLabelProps } from './types'
1818

1919
export const getFormErrorElementId = (inputId: FormFieldLabelProps['inputId']) => `${inputId}-error-msg`
2020

@@ -53,9 +53,3 @@ export const getFormFieldAriaAttributes = ({
5353
'aria-label': ariaLabel,
5454
}),
5555
})
56-
57-
export const getFormFieldBorderClassName = (borderRadiusConfig: FormFieldWrapperProps['borderRadiusConfig'] = {}) => {
58-
const { top = true, right = true, bottom = true, left = true } = borderRadiusConfig
59-
60-
return `${!top ? 'dc__no-top-radius' : ''} ${!right ? 'dc__no-right-radius' : ''} ${!bottom ? 'dc__no-bottom-radius' : ''} ${!left ? 'dc__no-left-radius' : ''}`
61-
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { ComponentSizeType } from '@Shared/constants'
2+
import { deriveBorderClassFromConfig, deriveBorderRadiusClassFromConfig } from '@Shared/Helpers'
3+
import { InfoBlockProps } from './types'
4+
import {
5+
CONTAINER_SIZE_TO_CLASS_MAP,
6+
SIZE_TO_ICON_CLASS_MAP,
7+
VARIANT_TO_BG_MAP,
8+
VARIANT_TO_ICON_MAP,
9+
} from './constants'
10+
import { Button } from '../Button'
11+
12+
const InfoBlock = ({
13+
layout = 'row',
14+
variant = 'information',
15+
size = ComponentSizeType.large,
16+
customIcon,
17+
buttonProps,
18+
heading,
19+
description,
20+
borderRadiusConfig,
21+
borderConfig,
22+
}: InfoBlockProps) => {
23+
const baseContainerClass = `${CONTAINER_SIZE_TO_CLASS_MAP[size]} ${VARIANT_TO_BG_MAP[variant]} ${deriveBorderRadiusClassFromConfig(borderRadiusConfig)} ${deriveBorderClassFromConfig(borderConfig)} w-100 py-8 br-4 bw-1`
24+
const iconClass = `dc__no-shrink flex dc__fill-available-space ${SIZE_TO_ICON_CLASS_MAP[size]}`
25+
const icon = customIcon ?? VARIANT_TO_ICON_MAP[variant]
26+
27+
const renderIcon = () => <span className={iconClass}>{icon}</span>
28+
29+
const renderHeading = () => {
30+
if (!heading) {
31+
return null
32+
}
33+
34+
if (typeof heading === 'string') {
35+
return (
36+
<h6
37+
className={`cn-9 ${size === ComponentSizeType.large ? 'fs-13 lh-20' : 'fs-12 lh-18'} fw-6 m-0 dc__truncate--clamp-3 dc__word-break`}
38+
>
39+
{heading}
40+
</h6>
41+
)
42+
}
43+
44+
return heading
45+
}
46+
47+
const renderDescription = () => {
48+
if (!description) {
49+
return null
50+
}
51+
52+
if (typeof description === 'string') {
53+
return (
54+
<p
55+
className={`m-0 cn-9 fw-4 ${size === ComponentSizeType.large ? 'fs-13 lh-20' : 'fs-12 lh-18'} dc__truncate--clamp-6 dc__word-break`}
56+
>
57+
{description}
58+
</p>
59+
)
60+
}
61+
62+
return description
63+
}
64+
65+
const renderContent = () => {
66+
const shouldAddGap = layout === 'column'
67+
const columnLayoutGapClass = size === ComponentSizeType.medium ? 'dc__gap-2' : 'dc__gap-4'
68+
69+
return (
70+
<div className={`flexbox-col flex-grow-1 ${shouldAddGap ? columnLayoutGapClass : ''}`}>
71+
{renderHeading()}
72+
{renderDescription()}
73+
</div>
74+
)
75+
}
76+
77+
if (layout === 'row') {
78+
return (
79+
<div className={`${baseContainerClass} flexbox dc__gap-16`}>
80+
<div className="flexbox dc__gap-8 flex-grow-1">
81+
{renderIcon()}
82+
{renderContent()}
83+
</div>
84+
85+
{buttonProps && <Button {...buttonProps} />}
86+
</div>
87+
)
88+
}
89+
90+
if (layout === 'column') {
91+
return (
92+
<div className={`${baseContainerClass} flexbox-col dc__gap-8`}>
93+
<div className="flexbox dc__gap-8">
94+
{renderContent()}
95+
{renderIcon()}
96+
</div>
97+
98+
{buttonProps && <Button {...buttonProps} />}
99+
</div>
100+
)
101+
}
102+
103+
return null
104+
}
105+
106+
export default InfoBlock
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { ReactComponent as ICInfoFilled } from '@Icons/ic-info-filled.svg'
2+
import { ReactComponent as ICError } from '@Icons/ic-error.svg'
3+
import { ReactComponent as ICSuccess } from '@Icons/ic-success.svg'
4+
import { ReactComponent as ICWarningY5 } from '@Icons/ic-warning-y5.svg'
5+
import { ReactComponent as ICHelp } from '@Icons/ic-help.svg'
6+
import { ComponentSizeType } from '@Shared/constants'
7+
import { InfoBlockProps } from './types'
8+
9+
export const VARIANT_TO_BG_MAP: Record<InfoBlockProps['variant'], string> = {
10+
error: 'bcr-1 er-2',
11+
help: 'bcv-1 ev-2',
12+
information: 'bcb-1 eb-2',
13+
success: 'bcg-1 eg-2',
14+
warning: 'bcy-1 ey-2',
15+
}
16+
17+
export const VARIANT_TO_ICON_MAP: Record<InfoBlockProps['variant'], InfoBlockProps['customIcon']> = {
18+
error: <ICError />,
19+
help: <ICHelp className="fcv-5" />,
20+
information: <ICInfoFilled />,
21+
success: <ICSuccess />,
22+
warning: <ICWarningY5 />,
23+
}
24+
25+
export const CONTAINER_SIZE_TO_CLASS_MAP: Record<InfoBlockProps['size'], string> = {
26+
[ComponentSizeType.large]: 'px-12',
27+
[ComponentSizeType.medium]: 'px-8',
28+
}
29+
30+
export const SIZE_TO_ICON_CLASS_MAP: Record<InfoBlockProps['size'], string> = {
31+
[ComponentSizeType.large]: 'icon-dim-20',
32+
[ComponentSizeType.medium]: 'icon-dim-18',
33+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as InfoBlock } from './InfoBlock.component'
2+
export * from './types'
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { ComponentSizeType } from '@Shared/constants'
2+
import { BorderConfigType, ComponentLayoutType } from '@Shared/types'
3+
import { ReactElement, ReactNode } from 'react'
4+
import { ButtonProps } from '../Button'
5+
6+
export type InfoBlockProps = {
7+
/**
8+
* @default 'row'
9+
*/
10+
layout?: ComponentLayoutType
11+
/**
12+
* @default 'information'
13+
*/
14+
variant?: 'error' | 'help' | 'information' | 'success' | 'warning'
15+
/**
16+
* @default ComponentSizeType.large
17+
*/
18+
size?: Extract<ComponentSizeType, ComponentSizeType.large | ComponentSizeType.medium>
19+
/**
20+
* If given would override the default icon derived from type
21+
*/
22+
customIcon?: ReactElement
23+
buttonProps?: ButtonProps
24+
borderConfig?: BorderConfigType
25+
borderRadiusConfig?: BorderConfigType
26+
} & (
27+
| {
28+
/**
29+
* If string, would apply h tag with necessary classes
30+
*/
31+
heading: ReactNode
32+
/**
33+
* If string, would apply p tag with necessary classes
34+
*/
35+
description: ReactNode
36+
}
37+
| {
38+
heading?: never
39+
description: ReactNode
40+
}
41+
| {
42+
heading: ReactNode
43+
description?: never
44+
}
45+
)

0 commit comments

Comments
 (0)