@devtron-labs/devtron-fe-common-lib 0.0.11 → 0.0.13

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.
@@ -0,0 +1,585 @@
1
+ import React, { useEffect, useMemo, useRef, useState } from 'react'
2
+ import { ReactComponent as Add } from '../Assets/Icon/ic-add.svg'
3
+ import { ReactComponent as Close } from '../Assets/Icon/ic-cross.svg'
4
+ import { ReactComponent as QuestionFilled } from '../Assets/Icon/ic-help.svg'
5
+ import { ReactComponent as Question } from '../Assets/Icon/ic-help-outline.svg'
6
+
7
+ import { ReactComponent as EditIcon } from '../Assets/Icon/ic-pencil.svg'
8
+ import { ReactComponent as Redo } from '../Assets/Icon/ic-arrow-counter-clockwise.svg'
9
+ import { ReactComponent as Minus } from '../Assets/Icon/ic-minus.svg'
10
+ import { toast } from 'react-toastify'
11
+ import { ReactComponent as Info } from '../Assets/Icon/ic-info-filled.svg'
12
+ import { ReactComponent as Error } from '../Assets/Icon/ic-warning.svg'
13
+ import { ReactComponent as Warning } from '../Assets/Icon/ic-error-exclamation.svg'
14
+ import { ReactComponent as Enter } from '../Assets/Icon/ic-enter.svg'
15
+ import { TippyTheme } from './Types'
16
+ import { ImageButtonType, ImageTaggingContainerType, ReleaseTag } from './ImageTags.Types'
17
+ import { showError, stopPropagation } from './Helper'
18
+ import { TippyCustomized } from './TippyCustomized'
19
+ import { setImageTags, getUserRole } from './Common.service'
20
+ import Tippy from '@tippyjs/react'
21
+
22
+ export const ImageTagsContainer = ({
23
+ ciPipelineId,
24
+ artifactId,
25
+ imageComment,
26
+ imageReleaseTags,
27
+ updateCurrentAppMaterial,
28
+ appReleaseTagNames,
29
+ setAppReleaseTagNames,
30
+ tagsEditable,
31
+ setTagsEditable,
32
+ toggleCardMode,
33
+ hideHardDelete,
34
+ forceReInit,
35
+ }: ImageTaggingContainerType) => {
36
+ const [initialTags, setInitialTags] = useState<ReleaseTag[]>(imageReleaseTags ? imageReleaseTags : [])
37
+ const [initialDescription, setInitialDescription] = useState(imageComment ? imageComment.comment : '')
38
+ const [existingTags, setExistingTags] = useState(appReleaseTagNames ? appReleaseTagNames : [])
39
+ const [newDescription, setNewDescription] = useState(imageComment ? imageComment.comment : '')
40
+ const [isEditing, setIsEditing] = useState(false)
41
+ const [showTagsWarning, setShowTagsWarning] = useState(false)
42
+ const [displayedTags, setDisplayedTags] = useState<ReleaseTag[]>(imageReleaseTags ? imageReleaseTags : [])
43
+ const [tagErrorMessage, setTagErrorMessage] = useState('')
44
+ const [createTags, setCreateTags] = useState<ReleaseTag[]>([])
45
+ const [softDeleteTags, setSoftDeleteTags] = useState<ReleaseTag[]>([])
46
+ const [hardDeleteTags, setHardDeleteTags] = useState<ReleaseTag[]>([])
47
+ const [isSuperAdmin, setSuperAdmin] = useState<boolean>(false)
48
+ const [descriptionValidationMessage, setDescriptionValidationMessage] = useState<string>('')
49
+ const [textInput, setTextInput] = useState<string>('')
50
+
51
+ useEffect(() => {
52
+ initialise()
53
+ }, [])
54
+
55
+ useEffect(() => {
56
+ reInitState()
57
+ }, [imageReleaseTags, imageComment, tagsEditable])
58
+
59
+ useEffect(() => {
60
+ setExistingTags(appReleaseTagNames ? appReleaseTagNames : [])
61
+ }, [appReleaseTagNames])
62
+
63
+ async function initialise() {
64
+ try {
65
+ const userRole = await getUserRole()
66
+
67
+ const superAdmin = userRole?.result?.roles?.includes('role:super-admin___')
68
+ setSuperAdmin(superAdmin)
69
+ } catch (err) {
70
+ showError(err)
71
+ }
72
+ }
73
+
74
+ const reInitState = () => {
75
+ if (forceReInit || !isEditing) {
76
+ setInitialTags(imageReleaseTags ? imageReleaseTags : [])
77
+ setInitialDescription(imageComment ? imageComment.comment : '')
78
+ setNewDescription(imageComment ? imageComment.comment : '')
79
+ setDisplayedTags(imageReleaseTags ? imageReleaseTags : [])
80
+ setIsEditing(false)
81
+ }
82
+ }
83
+
84
+ const handleEditClick = () => {
85
+ setIsEditing(!isEditing)
86
+ if (typeof toggleCardMode === 'function') {
87
+ toggleCardMode(artifactId)
88
+ }
89
+ }
90
+
91
+ const handleDescriptionChange = (e) => {
92
+ const description = e.target.value
93
+ description?.length > 500
94
+ ? setDescriptionValidationMessage('comment length cannot exceed 500 characters')
95
+ : setDescriptionValidationMessage('')
96
+ setNewDescription(description)
97
+ }
98
+
99
+ const handleCancel = () => {
100
+ setDisplayedTags(initialTags)
101
+ setNewDescription(initialDescription)
102
+ setCreateTags([])
103
+ setSoftDeleteTags([])
104
+ setHardDeleteTags([])
105
+ handleEditClick()
106
+ setShowTagsWarning(false)
107
+ setTagErrorMessage('')
108
+ }
109
+
110
+ const validateTag = (lowercaseValue) : boolean => {
111
+ if (
112
+ lowercaseValue.length == 0 ||
113
+ lowercaseValue.length >= 128 ||
114
+ lowercaseValue[0] == '.' ||
115
+ lowercaseValue[0] == '-'
116
+ ) {
117
+ setTagErrorMessage('tag name cannot be empty or exceed 128 characters or cannot start with . or -')
118
+ return false
119
+ }
120
+ setTagErrorMessage('')
121
+ const isTagExistsInExistingTags = existingTags.includes(lowercaseValue)
122
+ let isTagExistsInDisplayedTags = false
123
+ for (let i = 0; i < displayedTags?.length; i++) {
124
+ if (displayedTags[i].tagName.toLowerCase() === lowercaseValue) isTagExistsInDisplayedTags = true
125
+ }
126
+ if (isTagExistsInExistingTags || isTagExistsInDisplayedTags || lowercaseValue === 'latest') {
127
+ setTagErrorMessage('This tag is already being used in this application')
128
+ return false
129
+ }
130
+ return true
131
+ }
132
+
133
+ const handleTagCreate = (newValue) => {
134
+ const lowercaseValue = newValue.toLowerCase().trim()
135
+ if(validateTag(lowercaseValue)) {
136
+ const newTag: ReleaseTag = {
137
+ id: 0,
138
+ tagName: lowercaseValue,
139
+ appId: 0,
140
+ deleted: false,
141
+ artifactId: 0,
142
+ duplicateTag: false,
143
+ }
144
+ setCreateTags([...createTags, newTag])
145
+ setDisplayedTags([...displayedTags, newTag])
146
+ setShowTagsWarning(true)
147
+ setTextInput('')
148
+ }
149
+ }
150
+
151
+ const handleTagSoftDelete = (index) => {
152
+ const updatedTags = [...displayedTags]
153
+ updatedTags[index] = {
154
+ ...updatedTags[index],
155
+ deleted: !updatedTags[index].deleted,
156
+ }
157
+ const updatedTag = updatedTags[index]
158
+ let updatedSoftDeleteTags = [...softDeleteTags]
159
+ const tagIndex = updatedSoftDeleteTags.findIndex((tag) => tag.tagName === updatedTag.tagName)
160
+ if (tagIndex !== -1) {
161
+ // Tag already exists in softDeleteTags array, remove it
162
+ updatedSoftDeleteTags.splice(tagIndex, 1)
163
+ } else {
164
+ // Tag doesn't exist in softDeleteTags array, add it
165
+ updatedSoftDeleteTags.push(updatedTag)
166
+ }
167
+ setDisplayedTags(updatedTags)
168
+ setSoftDeleteTags(updatedSoftDeleteTags)
169
+ }
170
+
171
+ const handleTagHardDelete = (index) => {
172
+ const deletedTag = displayedTags[index]
173
+ const updatedCreateTags = createTags.filter((tag) => tag.tagName !== deletedTag.tagName)
174
+ setCreateTags(updatedCreateTags)
175
+ const updatedDisplayedTags = [...displayedTags]
176
+ updatedDisplayedTags.splice(index, 1)
177
+ setDisplayedTags(updatedDisplayedTags)
178
+ const updatedExistingTags = existingTags.filter((tag) => tag.toLowerCase() !== deletedTag.tagName.toLowerCase())
179
+ setExistingTags(updatedExistingTags)
180
+ if (deletedTag.id !== 0) {
181
+ const updatedHardDeleteTags = [...hardDeleteTags, deletedTag]
182
+ setHardDeleteTags(updatedHardDeleteTags)
183
+ }
184
+ }
185
+
186
+ const errorStateHandling = (err) => {
187
+ err.forEach(({ userMessage }) => {
188
+ const tagList = createTags.map((tags) => {
189
+ if (userMessage?.appReleaseTags?.includes(tags.tagName)) {
190
+ return {
191
+ ...tags,
192
+ duplicateTag: true,
193
+ }
194
+ }
195
+ return tags
196
+ })
197
+ setCreateTags(tagList)
198
+
199
+ const displayList = displayedTags.map((obj) => {
200
+ const matchingTag = tagList.find((tag) => tag.tagName === obj.tagName)
201
+ if (matchingTag) {
202
+ return { ...obj, duplicateTag: matchingTag.duplicateTag }
203
+ }
204
+ return obj
205
+ })
206
+
207
+ setDisplayedTags(displayList)
208
+ })
209
+ }
210
+
211
+ const handleSave = async () => {
212
+ if (tagErrorMessage) return
213
+
214
+ const payload = {
215
+ createTags: createTags,
216
+ softDeleteTags: softDeleteTags,
217
+ imageComment: {
218
+ id: 0,
219
+ comment: newDescription.trim(),
220
+ artifactId: 0,
221
+ },
222
+ hardDeleteTags: hardDeleteTags,
223
+ }
224
+
225
+ // set loading state true
226
+ setImageTags(payload, ciPipelineId, artifactId)
227
+ .then((res) => {
228
+ const tags = res.result?.imageReleaseTags?.map((tag) => ({
229
+ id: tag.id,
230
+ tagName: tag.tagName,
231
+ deleted: tag.deleted,
232
+ appId: 0,
233
+ artifactId: 0,
234
+ }))
235
+ if (setAppReleaseTagNames) {
236
+ setAppReleaseTagNames(res.result?.appReleaseTags)
237
+ }
238
+ if (setTagsEditable) {
239
+ setTagsEditable(res.result?.tagsEditable)
240
+ }
241
+ setInitialTags(tags)
242
+ setInitialDescription(res.result?.imageComment?.comment)
243
+ setDisplayedTags(tags)
244
+ setNewDescription(res.result?.imageComment?.comment)
245
+ setCreateTags([])
246
+ setSoftDeleteTags([])
247
+ setHardDeleteTags([])
248
+ handleEditClick()
249
+ setShowTagsWarning(false)
250
+ setTagErrorMessage('')
251
+ if(updateCurrentAppMaterial)updateCurrentAppMaterial(artifactId,tags,res.result?.imageComment)
252
+ })
253
+ .catch((err) => {
254
+ // Fix toast message
255
+ if (err.errors?.[0]?.userMessage?.appReleaseTags?.length) {
256
+ toast.error(err.errors?.[0]?.internalMessage)
257
+ errorStateHandling(err.errors)
258
+ } else {
259
+ showError(err)
260
+ }
261
+ })
262
+ }
263
+
264
+ const renderInfoCard = (): JSX.Element => {
265
+ return (
266
+ <TippyCustomized
267
+ theme={TippyTheme.white}
268
+ className="w-300 fcv-5"
269
+ placement="right"
270
+ Icon={QuestionFilled}
271
+ heading="Release tags"
272
+ showCloseButton={true}
273
+ trigger="click"
274
+ interactive={true}
275
+ additionalContent={getBuildContextAdditionalContent()}
276
+ >
277
+ <div className="icon-dim-16 fcn-5 ml-8 cursor">
278
+ <Question />
279
+ </div>
280
+ </TippyCustomized>
281
+ )
282
+ }
283
+
284
+ const getBuildContextAdditionalContent = () => {
285
+ return (
286
+ <div className="h-250 fs-13 dc__overflow-scroll p-12">
287
+ <div>Release tags allow you to tag container images with readable and relatable tags eg. v1.0.</div>
288
+ <ul className="pl-20 mt-8">
289
+ <li>
290
+ A release tag can only be added if a workflow has CD pipelines deploying to Production
291
+ environments.
292
+ </li>
293
+ <li>Multiple tags can be added to an image.</li>
294
+ <li>Multiple images in an application cannot have the same tag.</li>
295
+ <li>
296
+ Tags cannot be deleted once saved. Although, you can soft delete a tag if an unwanted tag has
297
+ been added.
298
+ </li>
299
+ </ul>
300
+ </div>
301
+ )
302
+ }
303
+
304
+ const creatableRef = useRef(null)
305
+
306
+ if (newDescription === '' && displayedTags.length === 0 && !isEditing) {
307
+ return tagsEditable ? (
308
+ <div className="bcn-0 mt-8 add-image-button">
309
+ <AddImageButton handleEditClick={handleEditClick} />
310
+ </div>
311
+ ) : (
312
+ <div></div>
313
+ )
314
+ }
315
+
316
+ const handleKeydown = (event) => {
317
+ if (event.key === 'Enter' && event.target.value.trim()) {
318
+ handleTagCreate(event.target.value.trim())
319
+ }
320
+ }
321
+
322
+ const setInputValue = (e) => {
323
+ e.target.value && validateTag(e.target.value)
324
+ setTextInput(e.target.value)
325
+ }
326
+
327
+ const enterTagCreate = (e) => {
328
+ stopPropagation(e)
329
+ if (textInput.trim()) {
330
+ handleTagCreate(textInput.trim())
331
+ }
332
+ }
333
+
334
+ const onClickSave = () => {
335
+ if (textInput.trim()) {
336
+ handleTagCreate(textInput)
337
+ }
338
+ handleSave()
339
+ }
340
+
341
+ return (
342
+ <div className="mt-8">
343
+ {isEditing && tagsEditable ? (
344
+ <div className="bcn-0 dc__border-top-n1 ">
345
+ <div className="cn-7 mt-12 flex left">
346
+ <span>Release tags (eg. v1.0)</span>
347
+ <div className="flex row ml-0">{renderInfoCard()}</div>
348
+ </div>
349
+ <div className="mt-6 dc__position-rel-imp" data-testid="add-tag-text-area">
350
+ <input
351
+ className="form__input"
352
+ value={textInput}
353
+ onChange={setInputValue}
354
+ placeholder="Type a tag and press enter"
355
+ onKeyDown={handleKeydown}
356
+ ref={creatableRef}
357
+ />
358
+ {textInput.length > 0 && (
359
+ <div
360
+ onClick={enterTagCreate}
361
+ className="dc__position-abs bcn-0 flex cursor pt-2 pb-2 pr-4 pl-4 br-2 cn-7 dc__border dc__border-bottom-2"
362
+ style={{
363
+ top: '50%',
364
+ fontFamily: 'monospace',
365
+ right: '8px',
366
+ transform: 'translateY(-50%)',
367
+ }}
368
+ >
369
+ <Enter className="icon-dim-16 mr-4" />
370
+ Enter
371
+ </div>
372
+ )}
373
+ </div>
374
+
375
+ {tagErrorMessage && (
376
+ <div className="flex left">
377
+ <Error className="form__icon form__icon--error" />
378
+ <div className="form__error">{tagErrorMessage}</div>
379
+ </div>
380
+ )}
381
+ <div className="flex-wrap mt-8 flex left">
382
+ {displayedTags?.map((tag, index) => (
383
+ <ImageTagButton
384
+ key={tag.id}
385
+ text={tag?.tagName}
386
+ isSoftDeleted={tag?.deleted}
387
+ isEditing={isEditing}
388
+ onSoftDeleteClick={(e) => {
389
+ stopPropagation(e)
390
+ handleTagSoftDelete(index)
391
+ }}
392
+ onHardDeleteClick={(e) => {
393
+ stopPropagation(e)
394
+ handleTagHardDelete(index)
395
+ }}
396
+ tagId={tag.id}
397
+ softDeleteTags={softDeleteTags}
398
+ isSuperAdmin={isSuperAdmin}
399
+ duplicateTag={tag?.duplicateTag}
400
+ hideHardDelete={hideHardDelete}
401
+ />
402
+ ))}
403
+ </div>
404
+ {showTagsWarning && (
405
+ <div className="cn-7 mb-8 flex left">
406
+ <Info className="form__icon--info icon-dim-16 mr-2" />
407
+ Tags cannot be edited/removed later
408
+ </div>
409
+ )}
410
+ <div className="cn-7 mt-12">Comment</div>
411
+ <div
412
+ className="flex left flex-wrap dc__gap-8 w-100 mt-6 mb-12"
413
+ data-testid="add-image-comment-text-area"
414
+ >
415
+ <textarea
416
+ value={newDescription}
417
+ onChange={handleDescriptionChange}
418
+ className="flex left flex-wrap dc__gap-8 dc__description-textarea"
419
+ style={{ height: '90px !important' }}
420
+ />
421
+ </div>
422
+ {descriptionValidationMessage !== '' && (
423
+ <div className="flex left">
424
+ <Error className="form__icon form__icon--error" />
425
+ <div className="form__error">{descriptionValidationMessage}</div>
426
+ </div>
427
+ )}
428
+ <div className="w-100 flex right mt-12">
429
+ <button
430
+ className="cta flex cancel h-32 lh-32-imp"
431
+ type="button"
432
+ onClick={(e) => {
433
+ stopPropagation(e)
434
+ handleCancel()
435
+ }}
436
+ >
437
+ Cancel
438
+ </button>
439
+ <button
440
+ className="cta flex h-32 lh-32-imp ml-12"
441
+ data-testid="save-tags-button"
442
+ type="button"
443
+ onClick={(e) => {
444
+ stopPropagation(e)
445
+ onClickSave()
446
+ }}
447
+ >
448
+ Save
449
+ </button>
450
+ </div>
451
+ </div>
452
+ ) : (
453
+ <div className="top br-4 bcn-0 image-tags-container flexbox">
454
+ <div
455
+ className="flex left image-tag-left-border w-100 mt-8 mb-8 pr-10 pl-10"
456
+ data-testid="image-tags-container-hover"
457
+ >
458
+ <div className='w-100'>
459
+ {initialDescription && <div className="mb-6 fs-13 dc__word-break-all">{initialDescription}</div>}
460
+ <div className="flex-wrap flex left">
461
+ {initialTags?.map((tag, index) => (
462
+ <ImageTagButton
463
+ key={tag?.id}
464
+ text={tag?.tagName}
465
+ isSoftDeleted={tag?.deleted}
466
+ isEditing={isEditing}
467
+ onSoftDeleteClick={(e) => {
468
+ stopPropagation(e)
469
+ handleTagSoftDelete(index)
470
+ }}
471
+ onHardDeleteClick={(e) => {
472
+ stopPropagation(e)
473
+ handleTagHardDelete(index)
474
+ }}
475
+ tagId={tag.id}
476
+ softDeleteTags={softDeleteTags}
477
+ isSuperAdmin={isSuperAdmin}
478
+ duplicateTag={tag?.duplicateTag}
479
+ hideHardDelete={hideHardDelete}
480
+ />
481
+ ))}
482
+ </div>
483
+ </div>
484
+ </div>
485
+ <div className="mt-8 mr-6 icon-dim-16">
486
+ {tagsEditable && (
487
+ <EditIcon
488
+ className="icon-dim-16 image-tags-container-edit__icon cursor"
489
+ data-testid="edit-tags-icon"
490
+ onClick={(e) => {
491
+ stopPropagation(e)
492
+ handleEditClick()
493
+ }}
494
+ />
495
+ )}
496
+ </div>
497
+ </div>
498
+ )}
499
+ </div>
500
+ )
501
+ }
502
+
503
+ export const ImageTagButton = ({
504
+ text,
505
+ isSoftDeleted,
506
+ isEditing,
507
+ onSoftDeleteClick,
508
+ onHardDeleteClick,
509
+ tagId,
510
+ softDeleteTags,
511
+ isSuperAdmin,
512
+ duplicateTag,
513
+ hideHardDelete,
514
+ }: ImageButtonType) => {
515
+ const IconComponent = isSoftDeleted ? Redo : Minus
516
+ const isInSoftDeleteTags = isSoftDeleted && softDeleteTags.some((tag) => tag.tagName === text)
517
+ const canTagBeHardDelete = tagId === 0 || (isSuperAdmin && !hideHardDelete)
518
+
519
+ const tabColor = () => {
520
+ if (duplicateTag) {
521
+ return 'cr-5 bcr-1 er-2'
522
+ } else if (isSoftDeleted) {
523
+ return 'cy-7 bcy-1 dc__strike-through ey-2'
524
+ } else {
525
+ return 'cn-9'
526
+ }
527
+ }
528
+
529
+ return (
530
+ <div
531
+ className={`br-4 en-2 bw-1 mr-8 bcn-0 mb-4 pt-2 pr-8 pb-2 pl-8 tag-class dc__word-break-all flex ${
532
+ isEditing ? 'icon-hover' : ''
533
+ } ${tabColor()}`}
534
+ >
535
+ {duplicateTag ? (
536
+ <Warning className="icon-dim-12 mr-4" />
537
+ ) : (
538
+ <Tippy
539
+ className="default-tt"
540
+ arrow={true}
541
+ placement="top"
542
+ content={isInSoftDeleteTags ? 'Restore tag' : 'Soft delete tag'}
543
+ >
544
+ <div
545
+ className={`action-icon mr-4 lh-16 pt-3 ${
546
+ isInSoftDeleteTags || (tagId !== 0 && !isSoftDeleted) ? 'show-icon' : ''
547
+ }`}
548
+ >
549
+ <IconComponent
550
+ className={`icon-dim-12 cursor${isSoftDeleted ? 'scn-6' : 'fcn-6'} `}
551
+ data-testid={`${text}-tag-soft-delete`}
552
+ onClick={onSoftDeleteClick}
553
+ />
554
+ </div>
555
+ </Tippy>
556
+ )}
557
+ {text}
558
+ <Tippy className="default-tt" arrow={true} placement="top" content="Remove tag">
559
+ <div className={`action-icon ml-4 lh-16 pt-3 ${canTagBeHardDelete ? 'show-icon' : ''}`}>
560
+ <Close
561
+ className="icon-dim-12 fcn-6 cursor"
562
+ data-testid={`${text}-tag-hard-delete`}
563
+ onClick={onHardDeleteClick}
564
+ />
565
+ </div>
566
+ </Tippy>
567
+ </div>
568
+ )
569
+ }
570
+
571
+ const AddImageButton = ({ handleEditClick }) => {
572
+ const handleClick = (e) => {
573
+ stopPropagation(e)
574
+ handleEditClick()
575
+ }
576
+
577
+ return (
578
+ <div className="add-tag-button flex pt-12 pr-12" data-testid="add-tags-button" onClick={handleClick}>
579
+ <div className="lh-16 flex">
580
+ <Add className="icon-dim-16 cn-6" />
581
+ <span className="cn-7">Add tags/comment</span>
582
+ </div>
583
+ </div>
584
+ )
585
+ }
@@ -26,6 +26,7 @@ export interface DefinitionSourceType {
26
26
  }
27
27
  export interface MandatoryPluginDetailType {
28
28
  id: number
29
+ icon: string
29
30
  name: string
30
31
  description?: string
31
32
  requiredIn: PluginRequiredStage
@@ -35,7 +35,7 @@ const RadioGroup: React.FC<RadioGroupInterface> & RadioGroupComposition = React.
35
35
  className = '',
36
36
  initialTab,
37
37
  disabled = false,
38
- dataTestId
38
+ dataTestId,
39
39
  }) {
40
40
  const [selected, select] = useState(null)
41
41
 
@@ -51,23 +51,38 @@ const RadioGroup: React.FC<RadioGroupInterface> & RadioGroupComposition = React.
51
51
  )
52
52
  })
53
53
 
54
- function Radio({ value, children, className = '', showTippy = false, tippyContent = '', canSelect = true, isDisabled = false, tippyClass = '', dataTestId }) {
54
+ function Radio({
55
+ value,
56
+ children,
57
+ className = '',
58
+ showTippy = false,
59
+ tippyContent = '',
60
+ tippyPlacement = 'bottom',
61
+ canSelect = true,
62
+ isDisabled = false,
63
+ tippyClass = '',
64
+ dataTestId,
65
+ }) {
55
66
  const { name, selected, select, disabled, onChange } = useRadioContext()
56
67
  return (
57
68
  <ConditionalWrap
58
69
  condition={showTippy}
59
70
  wrap={(children) => (
60
- <Tippy className={`default-tt w-250 ${tippyClass}`} arrow={false} placement="bottom" content={tippyContent}>
71
+ <Tippy
72
+ className={`default-tt w-250 ${tippyClass}`}
73
+ arrow={false}
74
+ placement={tippyPlacement as any}
75
+ content={tippyContent}
76
+ >
61
77
  {children}
62
78
  </Tippy>
63
79
  )}
64
80
  >
65
- <label className={`${className} radio ${(isDisabled || disabled)? 'disabled': ''}`}>
81
+ <label className={`${className} radio ${isDisabled || disabled ? 'disabled' : ''}`}>
66
82
  <input
67
83
  type="checkbox"
68
84
  value={value}
69
85
  name={name}
70
-
71
86
  checked={value === selected}
72
87
  onChange={(e) => {
73
88
  e.persist()
@@ -78,7 +93,9 @@ function Radio({ value, children, className = '', showTippy = false, tippyConten
78
93
  }}
79
94
  disabled={isDisabled || disabled}
80
95
  />
81
- <span className="radio__item-label" data-testid={dataTestId}>{children}</span>
96
+ <span className="radio__item-label" data-testid={dataTestId}>
97
+ {children}
98
+ </span>
82
99
  </label>
83
100
  </ConditionalWrap>
84
101
  )