@devtron-labs/devtron-fe-common-lib 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/Common/BreadCrumb/BreadCrumb.d.ts +1 -29
  2. package/dist/Common/BreadCrumb/Types.d.ts +28 -0
  3. package/dist/Common/Checkbox.d.ts +1 -14
  4. package/dist/Common/Constants.d.ts +13 -0
  5. package/dist/Common/CustomTagSelector/ResizableTagTextArea.d.ts +1 -1
  6. package/dist/Common/CustomTagSelector/TagLabelValueSelector.d.ts +1 -1
  7. package/dist/Common/CustomTagSelector/Types.d.ts +11 -3
  8. package/dist/Common/CustomTagSelector/ValidationRules.d.ts +1 -1
  9. package/dist/Common/DeleteComponentModal/DeleteComponent.d.ts +4 -0
  10. package/dist/Common/DeleteComponentModal/types.d.ts +15 -0
  11. package/dist/Common/Dialogs/ConfirmationDialog.d.ts +5 -16
  12. package/dist/Common/Dialogs/DialogForm.d.ts +1 -9
  13. package/dist/Common/Dialogs/Types.d.ts +27 -0
  14. package/dist/Common/EmptyState/EmptyState.d.ts +2 -1
  15. package/dist/Common/EmptyState/GenericEmptyState.d.ts +5 -0
  16. package/dist/Common/Error.d.ts +2 -2
  17. package/dist/Common/Helper.d.ts +31 -1
  18. package/dist/Common/InfoColorBar/InfoColourbar.d.ts +2 -15
  19. package/dist/Common/Modals/Modal.d.ts +2 -9
  20. package/dist/Common/MultiSelectCustomization.d.ts +1 -2
  21. package/dist/Common/PopupMenu.d.ts +4 -21
  22. package/dist/Common/Progressing.d.ts +4 -13
  23. package/dist/Common/RadioGroup/RadioGroup.d.ts +17 -0
  24. package/dist/Common/RadioGroup.d.ts +1 -10
  25. package/dist/Common/Reload.d.ts +2 -4
  26. package/dist/Common/Security/ScanVulnerabilitiesTable.d.ts +4 -0
  27. package/dist/Common/TippyCustomized.d.ts +2 -30
  28. package/dist/Common/Toggle/Toggle.d.ts +12 -0
  29. package/dist/Common/Types.d.ts +266 -0
  30. package/dist/Common/index.d.ts +6 -1
  31. package/dist/index.js +2388 -1943
  32. package/package.json +2 -2
  33. package/rollup.config.js +3 -3
  34. package/src/Assets/Icon/ic-info-filled.svg +7 -0
  35. package/src/Assets/Icon/ic-warning.svg +7 -0
  36. package/src/Assets/Img/app-not-deployed.png +0 -0
  37. package/src/Common/Api.ts +7 -1
  38. package/src/Common/BreadCrumb/BreadCrumb.tsx +1 -30
  39. package/src/Common/BreadCrumb/Types.ts +30 -0
  40. package/src/Common/Checkbox.tsx +3 -16
  41. package/src/Common/Constants.ts +15 -0
  42. package/src/Common/CustomTagSelector/ResizableTagTextArea.tsx +6 -4
  43. package/src/Common/CustomTagSelector/TagDetails.tsx +15 -2
  44. package/src/Common/CustomTagSelector/TagLabelSelect.tsx +7 -3
  45. package/src/Common/CustomTagSelector/TagLabelValueSelector.tsx +66 -26
  46. package/src/Common/CustomTagSelector/Types.ts +11 -3
  47. package/src/Common/CustomTagSelector/ValidationRules.ts +21 -18
  48. package/src/Common/DeleteComponentModal/DeleteComponent.tsx +95 -0
  49. package/src/Common/DeleteComponentModal/types.ts +15 -0
  50. package/src/Common/Dialogs/ConfirmationDialog.tsx +6 -4
  51. package/src/Common/Dialogs/DeleteDialog.tsx +2 -2
  52. package/src/Common/Dialogs/DialogForm.tsx +2 -11
  53. package/src/Common/Dialogs/Types.ts +28 -0
  54. package/src/Common/Drawer/Drawer.scss +12 -11
  55. package/src/Common/EmptyState/EmptyState.tsx +5 -5
  56. package/src/Common/EmptyState/GenericEmptyState.tsx +41 -0
  57. package/src/Common/EmptyState/emptyState.scss +4 -15
  58. package/src/Common/Error.tsx +13 -16
  59. package/src/Common/ErrorScreenNotFound.tsx +23 -30
  60. package/src/Common/Helper.tsx +258 -2
  61. package/src/Common/InfoColorBar/InfoColourbar.tsx +6 -18
  62. package/src/Common/Modals/Modal.tsx +7 -1
  63. package/src/Common/Modals/OpaqueModal.tsx +2 -2
  64. package/src/Common/Modals/VisibleModal.tsx +1 -1
  65. package/src/Common/Modals/VisibleModal2.tsx +1 -1
  66. package/src/Common/MultiSelectCustomization.tsx +3 -3
  67. package/src/Common/PopupMenu.tsx +23 -6
  68. package/src/Common/Progressing.tsx +8 -15
  69. package/src/Common/RadioGroup/RadioGroup.tsx +88 -0
  70. package/src/Common/RadioGroup/radioGroup.scss +46 -0
  71. package/src/Common/RadioGroup.tsx +3 -13
  72. package/src/Common/Reload.tsx +15 -10
  73. package/src/Common/Security/ScanVulnerabilitiesTable.tsx +55 -0
  74. package/src/Common/Security/scanVulnerabilities.css +67 -0
  75. package/src/Common/TippyCustomized.tsx +3 -31
  76. package/src/Common/Toggle/Toggle.scss +70 -0
  77. package/src/Common/Toggle/Toggle.tsx +38 -0
  78. package/src/Common/Types.ts +290 -0
  79. package/src/Common/index.ts +6 -1
  80. package/dist/27bfa7a87d0b7039.png +0 -0
  81. package/dist/b5d52f036bdcd3ed.png +0 -0
  82. package/src/Assets/Icon/ic-close.svg +0 -9
@@ -1,13 +1,29 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react'
1
2
  import { ServerErrors } from './ServerError'
2
3
  import { toast } from 'react-toastify'
3
4
  import * as Sentry from '@sentry/browser'
4
5
  import { toastAccessDenied } from './ToastBody'
5
- import { useEffect, useRef } from 'react'
6
+ import { ERROR_EMPTY_SCREEN, TOKEN_COOKIE_NAME } from './Constants'
7
+ import { ReactComponent as FormError } from '../Assets/Icon/ic-warning.svg'
8
+
9
+ toast.configure({
10
+ autoClose: 3000,
11
+ hideProgressBar: true,
12
+ pauseOnHover: true,
13
+ pauseOnFocusLoss: true,
14
+ closeOnClick: false,
15
+ newestOnTop: true,
16
+ toastClassName: 'devtron-toast',
17
+ bodyClassName: 'devtron-toast__body',
18
+ })
6
19
 
7
20
  export function showError(serverError, showToastOnUnknownError = true, hideAccessError = false) {
8
21
  if (serverError instanceof ServerErrors && Array.isArray(serverError.errors)) {
9
22
  serverError.errors.map(({ userMessage, internalMessage }) => {
10
- if (serverError.code === 403 && userMessage === 'unauthorized') {
23
+ if (
24
+ serverError.code === 403 &&
25
+ (userMessage === ERROR_EMPTY_SCREEN.UNAUTHORIZED || userMessage === ERROR_EMPTY_SCREEN.FORBIDDEN)
26
+ ) {
11
27
  if (!hideAccessError) {
12
28
  toastAccessDenied()
13
29
  }
@@ -75,3 +91,243 @@ export function useThrottledEffect(callback, delay, deps = []) {
75
91
  }
76
92
  }, [delay, ...deps])
77
93
  }
94
+
95
+ const colors = [
96
+ '#FFB900',
97
+ '#D83B01',
98
+ '#B50E0E',
99
+ '#E81123',
100
+ '#B4009E',
101
+ '#5C2D91',
102
+ '#0078D7',
103
+ '#00B4FF',
104
+ '#008272',
105
+ '#107C10',
106
+ ]
107
+
108
+ export function getRandomColor(email: string): string {
109
+ let sum = 0
110
+ for (let i = 0; i < email.length; i++) {
111
+ sum += email.charCodeAt(i)
112
+ }
113
+ return colors[sum % colors.length]
114
+ }
115
+
116
+ export const getAlphabetIcon = (str: string) => {
117
+ if (!str) return null
118
+ return (
119
+ <span
120
+ className="alphabet-icon__initial fs-13 icon-dim-20 flex cn-0 mr-8"
121
+ style={{ backgroundColor: getRandomColor(str) }}
122
+ >
123
+ {str[0]}
124
+ </span>
125
+ )
126
+ }
127
+
128
+ export const getEmptyArrayOfLength = (length: number) => {
129
+ return Array.from({ length })
130
+ }
131
+
132
+ export function noop(...args): any {}
133
+
134
+ export function not(e) {
135
+ return !e
136
+ }
137
+
138
+ export function useEffectAfterMount(cb, dependencies) {
139
+ const justMounted = React.useRef(true)
140
+ React.useEffect(() => {
141
+ if (!justMounted.current) {
142
+ return cb()
143
+ }
144
+ justMounted.current = false
145
+ }, dependencies)
146
+ }
147
+
148
+ export function getCookie(sKey) {
149
+ if (!sKey) {
150
+ return null
151
+ }
152
+ return (
153
+ document.cookie.replace(
154
+ new RegExp('(?:(?:^|.*;)\\s*' + sKey.replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'),
155
+ '$1',
156
+ ) || null
157
+ )
158
+ }
159
+
160
+ export function getLoginInfo() {
161
+ const argocdToken = getCookie(TOKEN_COOKIE_NAME)
162
+ if (argocdToken) {
163
+ const jwts = argocdToken.split('.')
164
+ try {
165
+ return JSON.parse(atob(jwts[1]))
166
+ } catch (err) {
167
+ console.error('error in setting user ', err)
168
+ return null
169
+ }
170
+ }
171
+ }
172
+
173
+ export function useForm(stateSchema, validationSchema = {}, callback) {
174
+ const [state, setState] = useState(stateSchema)
175
+ const [disable, setDisable] = useState(true)
176
+ const [isDirty, setIsDirty] = useState(false)
177
+
178
+ // Disable button in initial render.
179
+ useEffect(() => {
180
+ setDisable(true)
181
+ }, [])
182
+
183
+ // For every changed in our state this will be fired
184
+ // To be able to disable the button
185
+ useEffect(() => {
186
+ if (isDirty) {
187
+ setDisable(validateState(state))
188
+ }
189
+ }, [state, isDirty])
190
+
191
+ // Used to disable submit button if there's an error in state
192
+ // or the required field in state has no value.
193
+ // Wrapped in useCallback to cached the function to avoid intensive memory leaked
194
+ // in every re-render in component
195
+ const validateState = useCallback(
196
+ (state) => {
197
+ //check errors in all fields
198
+ const hasErrorInState = Object.keys(validationSchema).some((key) => {
199
+ const isInputFieldRequired = validationSchema[key].required
200
+ const stateValue = state[key].value // state value
201
+ const stateError = state[key].error // state error
202
+ return (isInputFieldRequired && !stateValue) || stateError
203
+ })
204
+ return hasErrorInState
205
+ },
206
+ [state, validationSchema],
207
+ )
208
+
209
+ function validateField(name, value): string | string[] {
210
+ if (validationSchema[name].required) {
211
+ if (!value) {
212
+ return 'This is a required field.'
213
+ }
214
+ }
215
+
216
+ function _validateSingleValidator(validator, value) {
217
+ if (value && !validator.regex.test(value)) {
218
+ return false
219
+ }
220
+ return true
221
+ }
222
+
223
+ // single validator
224
+ let _validator = validationSchema[name].validator
225
+ if (_validator && typeof _validator === 'object') {
226
+ if (!_validateSingleValidator(_validator, value)) {
227
+ return _validator.error
228
+ }
229
+ }
230
+
231
+ // multiple validators
232
+ let _validators = validationSchema[name].validators
233
+ if (_validators && typeof _validators === 'object' && Array.isArray(_validators)) {
234
+ let errors = []
235
+ _validators.forEach((_validator) => {
236
+ if (!_validateSingleValidator(_validator, value)) {
237
+ errors.push(_validator.error)
238
+ }
239
+ })
240
+ if (errors.length > 0) {
241
+ return errors
242
+ }
243
+ }
244
+
245
+ return ''
246
+ }
247
+
248
+ const handleOnChange = useCallback(
249
+ (event) => {
250
+ setIsDirty(true)
251
+
252
+ const { name, value } = event.target
253
+ let error = validateField(name, value)
254
+ setState((prevState) => ({
255
+ ...prevState,
256
+ [name]: { value, error },
257
+ }))
258
+ },
259
+ [validationSchema],
260
+ )
261
+
262
+ const handleOnSubmit = (event) => {
263
+ event.preventDefault()
264
+ const newState = Object.keys(validationSchema).reduce((agg, curr) => {
265
+ agg[curr] = { ...state[curr], error: validateField(curr, state[curr].value) }
266
+ return agg
267
+ }, state)
268
+ if (!validateState(newState)) {
269
+ callback(state)
270
+ } else {
271
+ setState({ ...newState })
272
+ }
273
+ }
274
+ return { state, disable, handleOnChange, handleOnSubmit }
275
+ }
276
+
277
+ function handleError(error: any): any[] {
278
+ if (!error) {
279
+ return []
280
+ }
281
+
282
+ if (!Array.isArray(error)) {
283
+ return [error]
284
+ }
285
+
286
+ return error
287
+ }
288
+
289
+ export function CustomInput({
290
+ name,
291
+ value,
292
+ error,
293
+ onChange,
294
+ onBlur = (e) => {},
295
+ onFocus = (e) => {},
296
+ label,
297
+ type = 'text',
298
+ disabled = false,
299
+ autoComplete = 'off',
300
+ labelClassName = '',
301
+ placeholder = '',
302
+ tabIndex = 1,
303
+ dataTestid = '',
304
+ }) {
305
+ return (
306
+ <div className="flex column left top">
307
+ <label className={`form__label ${labelClassName}`}>{label}</label>
308
+ <input
309
+ data-testid={dataTestid}
310
+ type={type}
311
+ name={name}
312
+ autoComplete="off"
313
+ className="form__input"
314
+ onChange={(e) => {
315
+ e.persist()
316
+ onChange(e)
317
+ }}
318
+ onBlur={onBlur}
319
+ onFocus={onFocus}
320
+ placeholder={placeholder}
321
+ value={value}
322
+ disabled={disabled}
323
+ tabIndex={tabIndex}
324
+ />
325
+ {handleError(error).map((err) => (
326
+ <div className="form__error">
327
+ <FormError className="form__icon form__icon--error" />
328
+ {err}
329
+ </div>
330
+ ))}
331
+ </div>
332
+ )
333
+ }
@@ -1,22 +1,8 @@
1
- import React, { CSSProperties } from 'react'
1
+ import React from 'react'
2
2
  import { Link } from 'react-router-dom'
3
+ import { InfoColourBarType } from '../Types'
3
4
  import './infoColourBar.scss'
4
5
 
5
- interface InfoColourBarType {
6
- message: React.ReactNode
7
- classname: string
8
- Icon
9
- iconClass?: string
10
- iconSize?: number // E.g. 16, 20, etc.. Currently, there are around 12 sizes supported. Check `icons.css` or `base.scss` for supported sizes or add new size (class names starts with `icon-dim-`).
11
- renderActionButton?: () => JSX.Element
12
- linkText?: React.ReactNode
13
- redirectLink?: string
14
- linkOnClick?: () => void
15
- linkClass?: string
16
- internalLink?: boolean
17
- styles?: CSSProperties
18
- }
19
-
20
6
  function InfoColourBar({
21
7
  message,
22
8
  classname,
@@ -40,6 +26,7 @@ function InfoColourBar({
40
26
  <Link
41
27
  to={redirectLink}
42
28
  onClick={linkOnClick}
29
+ data-testid="info-bar-internal-link"
43
30
  className="cursor dc__link dc__underline-onhover mr-5"
44
31
  >
45
32
  {linkText}
@@ -52,6 +39,7 @@ function InfoColourBar({
52
39
  href={redirectLink}
53
40
  target="_blank"
54
41
  onClick={linkOnClick}
42
+ data-testid="info-bar-redirectLink"
55
43
  className="cursor dc__link dc__underline-onhover mr-5"
56
44
  >
57
45
  {linkText}
@@ -61,7 +49,7 @@ function InfoColourBar({
61
49
 
62
50
  return (
63
51
  linkOnClick && (
64
- <div onClick={linkOnClick} className="cursor dc__link dc__underline-onhover">
52
+ <div onClick={linkOnClick} className="cursor dc__link dc__underline-onhover" data-testid="info-bar-linkText">
65
53
  {linkText}
66
54
  </div>
67
55
  )
@@ -71,7 +59,7 @@ function InfoColourBar({
71
59
  return (
72
60
  <div className="info-bar-container">
73
61
  <div
74
- className={`${classname} info_text flex dc__content-space pt-8 pb-8 pl-16 pr-16 br-4 top fs-13 fw-4`}
62
+ className={`${classname} info_text flex dc__content-space pt-8 pb-8 pl-12 pr-12 br-4 top fs-13 fw-4`}
75
63
  style={styles}
76
64
  >
77
65
  <div className={`flex top ${typeof renderActionButton === 'function' ? 'mr-5' : ''}`}>
@@ -1,5 +1,7 @@
1
1
  import React, { useEffect } from 'react'
2
2
  import ReactDOM from 'react-dom'
3
+ import { ModalType } from '../Types'
4
+
3
5
  export const Modal = ({
4
6
  style = {},
5
7
  children,
@@ -8,7 +10,8 @@ export const Modal = ({
8
10
  onClick = null,
9
11
  callbackRef = null,
10
12
  preventWheelDisable = false,
11
- }) => {
13
+ noBackDrop,
14
+ }: ModalType) => {
12
15
  const innerRef = React.useRef(null)
13
16
  function handleClick(e) {
14
17
  if (typeof onClick !== 'function') return
@@ -34,17 +37,20 @@ export const Modal = ({
34
37
  document.addEventListener('click', handleClick)
35
38
  let modal = document.getElementById('visible-modal')
36
39
  if (modal) modal.classList.add('show')
40
+ if (noBackDrop) modal.classList.add('no-back-drop')
37
41
  if (!preventWheelDisable) document.body.addEventListener('wheel', disableWheel, { passive: false })
38
42
  return () => {
39
43
  if (!preventWheelDisable) document.body.removeEventListener('wheel', disableWheel)
40
44
  document.removeEventListener('click', handleClick)
41
45
  if (modal) modal.classList.remove('show')
46
+ if (noBackDrop) modal.classList.remove('no-back-drop')
42
47
  }
43
48
  }, [])
44
49
  return ReactDOM.createPortal(
45
50
  <div
46
51
  tabIndex={0}
47
52
  onClick={onClick}
53
+ data-testid="common-modal"
48
54
  ref={(el) => {
49
55
  if (typeof callbackRef === 'function') {
50
56
  callbackRef(el)
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import ReactDOM from 'react-dom';
3
- import closeIcon from '../../Assets/Icon/ic-close.svg'
3
+ import closeIcon from '../../Assets/Icon/ic-cross.svg'
4
4
 
5
5
  export interface OpaqueModalProps {
6
6
  className?: string;
@@ -28,7 +28,7 @@ export class OpaqueModal extends React.Component<OpaqueModalProps> {
28
28
  return ReactDOM.createPortal(
29
29
  <div className={`full-screen-modal__body-container ${className}`}>
30
30
  {this.props.children}
31
- {typeof onHide === 'function' && <div className="close-btn icon-dim-24" onClick={e => onHide(false)}>
31
+ {typeof onHide === 'function' && <div className="close-btn icon-dim-24" onClick={e => onHide(false)} data-testid="opaque-modal-close">
32
32
  <img className="close-img" src={closeIcon} alt="close" />
33
33
  </div>}
34
34
  </div>, document.getElementById('full-screen-modal'))
@@ -46,7 +46,7 @@ export class VisibleModal extends React.Component<{
46
46
 
47
47
  render() {
48
48
  return ReactDOM.createPortal(
49
- <div className={`visible-modal__body ${this.props.className}`} onClick={this.props?.close}>
49
+ <div className={`visible-modal__body ${this.props.className}`} onClick={this.props?.close} data-testid="visible-modal-close">
50
50
  {this.props.children}
51
51
  </div>,
52
52
  document.getElementById('visible-modal'),
@@ -28,7 +28,7 @@ export class VisibleModal2 extends React.Component<{ className: string; close?:
28
28
 
29
29
  render() {
30
30
  return ReactDOM.createPortal(
31
- <div className={`visible-modal__body ${this.props.className}`} onClick={this.props?.close}>
31
+ <div className={`visible-modal__body ${this.props.className}`} onClick={this.props?.close} data-testid="visible-modal2-close">
32
32
  {this.props.children}
33
33
  </div>, document.getElementById('visible-modal-2'))
34
34
  }
@@ -87,9 +87,9 @@ export const MultiValueChipContainer = ({ validator, isAllSelected = false, ...p
87
87
 
88
88
  return (
89
89
  <components.MultiValueContainer {...{ data, innerProps, selectProps }}>
90
- <div className={`flex fs-12 pl-4 pr-4`}>
90
+ <div className="flex left fs-12 pl-4 pr-4 dc__ellipsis-right">
91
91
  {!isValidEmail && <RedWarning className="mr-4 icon-dim-16" />}
92
- <div className={`${isValidEmail ? 'cn-9' : 'cr-5'}`}>{label}</div>
92
+ <div className={`dc__ellipsis-right ${isValidEmail ? 'cn-9' : 'cr-5'}`}>{label}</div>
93
93
  </div>
94
94
  {children[1]}
95
95
  </components.MultiValueContainer>
@@ -124,7 +124,7 @@ export const multiSelectStyles = {
124
124
  }),
125
125
  }
126
126
 
127
- interface CustomSelect {
127
+ export interface CustomSelect {
128
128
  sortSelected?: boolean
129
129
  options: any[]
130
130
  onChange: (...args) => void
@@ -1,6 +1,8 @@
1
- import React, { useEffect } from 'react';
2
- import {Modal} from './Modals/Modal';
3
- const PopupContext = React.createContext(null);
1
+ import React, { useEffect } from 'react'
2
+ import { Modal } from './Modals/Modal'
3
+ import { PopupMenuBodyType, PopupMenuButtonType, PopupMenuType } from './Types'
4
+
5
+ const PopupContext = React.createContext(null)
4
6
 
5
7
  function usePopupContext() {
6
8
  const context = React.useContext(PopupContext)
@@ -10,7 +12,12 @@ function usePopupContext() {
10
12
  return context
11
13
  }
12
14
 
13
- function PopupMenu({ children = null, onToggleCallback = null, autoClose = false, autoPosition = false }) {
15
+ function PopupMenu({
16
+ children = null,
17
+ onToggleCallback = null,
18
+ autoClose = false,
19
+ autoPosition = false,
20
+ }: PopupMenuType) {
14
21
  const [popupPosition, setPopupPosition] = React.useState(null)
15
22
  const [opacity, setOpacity] = React.useState(0)
16
23
  const observer = React.useRef<IntersectionObserver | null>(null)
@@ -135,7 +142,8 @@ function Button({
135
142
  tabIndex = 0,
136
143
  onHover = false,
137
144
  isKebab = false,
138
- }) {
145
+ dataTestId = "",
146
+ }: PopupMenuButtonType) {
139
147
  const { handleOpen, popupPosition, buttonRef, initialiseButtonWidth } = usePopupContext()
140
148
  return (
141
149
  <button
@@ -148,13 +156,21 @@ function Button({
148
156
  }`}
149
157
  onMouseEnter={onHover ? (disabled ? null : handleOpen) : () => {}}
150
158
  onClick={disabled ? null : handleOpen}
159
+ data-testid={dataTestId}
151
160
  >
152
161
  {children}
153
162
  </button>
154
163
  )
155
164
  }
156
165
 
157
- function Body({ children = null, rootClassName = '', style = {}, autoWidth = false, preventWheelDisable = false }) {
166
+ function Body({
167
+ children = null,
168
+ rootClassName = '',
169
+ style = {},
170
+ autoWidth = false,
171
+ preventWheelDisable = false,
172
+ noBackDrop,
173
+ }: PopupMenuBodyType) {
158
174
  const { handleClose, popupPosition, opacity, callbackRef, buttonWidth } = usePopupContext()
159
175
  return popupPosition ? (
160
176
  <Modal
@@ -170,6 +186,7 @@ function Body({ children = null, rootClassName = '', style = {}, autoWidth = fal
170
186
  opacity: opacity,
171
187
  }}
172
188
  preventWheelDisable={preventWheelDisable}
189
+ noBackDrop={noBackDrop}
173
190
  >
174
191
  {children}
175
192
  </Modal>
@@ -1,19 +1,10 @@
1
1
  import React from 'react'
2
+ import { ProgressingProps } from './Types'
2
3
 
3
- interface ProgressingProps {
4
- pageLoader?: boolean
5
- loadingText?: string
6
- size?: number
7
- fullHeight?: boolean
8
- theme?: 'white' | 'default'
9
- styles?: React.CSSProperties
10
- children?: React.ReactNode
11
- }
12
-
13
- export function Progressing({ pageLoader, size, theme, styles }: ProgressingProps): JSX.Element {
4
+ export function Progressing({ pageLoader, size, theme, styles, fillColor }: ProgressingProps): JSX.Element {
14
5
  const loaderSize = size ? `${size}px` : pageLoader ? '48px' : '20px'
15
6
  return (
16
- <div className={`loader ${theme || 'default'}-background`} style={styles}>
7
+ <div className={`loader ${theme || 'default'}-background`} style={styles} data-testid="progressing">
17
8
  <div style={{ width: loaderSize, height: loaderSize }}>
18
9
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" className="loader__svg">
19
10
  <g fill="none" fillRule="evenodd" strokeLinecap="round">
@@ -27,7 +18,7 @@ export function Progressing({ pageLoader, size, theme, styles }: ProgressingProp
27
18
  type="rotate"
28
19
  />
29
20
  <path
30
- fill="#06C"
21
+ fill={fillColor || '#06C'}
31
22
  fillRule="nonzero"
32
23
  d="M12 2.5A9.5 9.5 0 1 1 2.5 12a1.5 1.5 0 0 1 3 0A6.5 6.5 0 1 0 12 5.5a1.5 1.5 0 0 1 0-3z"
33
24
  />
@@ -42,16 +33,18 @@ export function DetailsProgressing({
42
33
  loadingText,
43
34
  size = 24,
44
35
  fullHeight = false,
45
- children,
46
36
  styles,
37
+ fillColor,
38
+ children,
47
39
  }: ProgressingProps): JSX.Element {
48
40
  return (
49
41
  <div
50
42
  className={`details-loader bcn-0 flex column fs-14 fw-6 ${fullHeight ? 'h-100' : 'details-loader-height'}`}
51
43
  style={styles}
44
+ data-testid="details-progressing"
52
45
  >
53
46
  <span style={{ width: `${size}px`, height: `${size}px` }}>
54
- <Progressing size={size} />
47
+ <Progressing size={size} fillColor={fillColor} />
55
48
  </span>
56
49
  {loadingText && <span className="mt-10">{loadingText}</span>}
57
50
  {children}
@@ -0,0 +1,88 @@
1
+ import Tippy from '@tippyjs/react'
2
+ import React, { useState, useEffect } from 'react'
3
+ import { ConditionalWrap } from '../Helper'
4
+ import './radioGroup.scss'
5
+
6
+ const RadioContext = React.createContext(null)
7
+
8
+ function useRadioContext() {
9
+ const context = React.useContext(RadioContext)
10
+ if (!context) {
11
+ throw new Error(`Radio compound components cannot be rendered outside the Toggle component`)
12
+ }
13
+ return context
14
+ }
15
+
16
+ interface RadioGroupInterface {
17
+ name: string
18
+ onChange: any
19
+ className?: string
20
+ initialTab: string
21
+ disabled: boolean
22
+ showTippy?: boolean
23
+ tippyContent?: any
24
+ dataTestId?: string
25
+ }
26
+
27
+ interface RadioGroupComposition {
28
+ Radio?: React.FC<any>
29
+ }
30
+
31
+ const RadioGroup: React.FC<RadioGroupInterface> & RadioGroupComposition = React.memo(function RadioGroup({
32
+ name,
33
+ onChange,
34
+ children,
35
+ className = '',
36
+ initialTab,
37
+ disabled = false,
38
+ dataTestId
39
+ }) {
40
+ const [selected, select] = useState(null)
41
+
42
+ useEffect(() => {
43
+ if (initialTab === selected) return
44
+ select(initialTab)
45
+ }, [initialTab])
46
+
47
+ return (
48
+ <RadioContext.Provider value={{ name, selected, select, disabled, onChange }}>
49
+ <div className={`${className} radio-group`}>{children}</div>
50
+ </RadioContext.Provider>
51
+ )
52
+ })
53
+
54
+ function Radio({ value, children, className = '', showTippy = false, tippyContent = '', canSelect = true, isDisabled = false, tippyClass = '', dataTestId }) {
55
+ const { name, selected, select, disabled, onChange } = useRadioContext()
56
+ return (
57
+ <ConditionalWrap
58
+ condition={showTippy}
59
+ wrap={(children) => (
60
+ <Tippy className={`default-tt w-250 ${tippyClass}`} arrow={false} placement="bottom" content={tippyContent}>
61
+ {children}
62
+ </Tippy>
63
+ )}
64
+ >
65
+ <label className={`${className} radio ${(isDisabled || disabled)? 'disabled': ''}`}>
66
+ <input
67
+ type="checkbox"
68
+ value={value}
69
+ name={name}
70
+
71
+ checked={value === selected}
72
+ onChange={(e) => {
73
+ e.persist()
74
+ if (canSelect) {
75
+ select(e.target.value)
76
+ }
77
+ onChange(e)
78
+ }}
79
+ disabled={isDisabled || disabled}
80
+ />
81
+ <span className="radio__item-label" data-testid={dataTestId}>{children}</span>
82
+ </label>
83
+ </ConditionalWrap>
84
+ )
85
+ }
86
+
87
+ RadioGroup.Radio = Radio
88
+ export default RadioGroup
@@ -0,0 +1,46 @@
1
+ .radio-group {
2
+ .label,
3
+ .radio,
4
+ .checkbox {
5
+ margin: 0;
6
+ }
7
+ height: 30px;
8
+ background: white;
9
+ display: flex;
10
+ justify-content: center;
11
+ align-items: center;
12
+ padding: 4px;
13
+ border-radius: 4px;
14
+ .radio {
15
+ height: 100%;
16
+ width: auto;
17
+ color: var(--N400);
18
+ display: flex;
19
+ align-items: center;
20
+ }
21
+ .radio+.radio {
22
+ margin-top: 0;
23
+ }
24
+ input[type=checkbox] {
25
+ display: contents;
26
+ overflow: hidden;
27
+ max-width: 0px;
28
+ height: 0;
29
+ opacity: 0;
30
+ margin: unset;
31
+ }
32
+ input+.radio__item-label {
33
+ padding: 4px 6px;
34
+ height: 100%;
35
+ width: 100%;
36
+ display: flex;
37
+ align-items: center;
38
+ transition: all 0.5s;
39
+ cursor: pointer;
40
+ }
41
+ input[type=checkbox]:checked+.radio__item-label {
42
+ background: #2c3354;
43
+ color: white;
44
+ border-radius: 4px;
45
+ }
46
+ }