@devtron-labs/devtron-fe-common-lib 0.0.6-beta-10 → 0.0.6-beta-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.
Files changed (47) hide show
  1. package/dist/Common/Constants.d.ts +6 -0
  2. package/dist/Common/CustomTagSelector/PropagateTagInfo.d.ts +2 -0
  3. package/dist/Common/CustomTagSelector/TagDetails.d.ts +8 -0
  4. package/dist/Common/CustomTagSelector/TagLabelSelect.d.ts +5 -0
  5. package/dist/Common/CustomTagSelector/TagLabelValueSelector.d.ts +10 -0
  6. package/dist/Common/CustomTagSelector/TagSelector.utils.d.ts +12 -0
  7. package/dist/Common/CustomTagSelector/ValidationRules.d.ts +10 -0
  8. package/dist/Common/CustomTagSelector/index.d.ts +2 -0
  9. package/dist/Common/Dialogs/ConfirmationDialog.d.ts +20 -0
  10. package/dist/Common/Dialogs/DeleteDialog.d.ts +5 -0
  11. package/dist/Common/Dialogs/DialogForm.d.ts +22 -0
  12. package/dist/Common/Dialogs/ForceDeleteDialog.d.ts +3 -0
  13. package/dist/Common/Dialogs/Types.d.ts +15 -0
  14. package/dist/Common/Dialogs/index.d.ts +4 -0
  15. package/dist/Common/PopupMenu.d.ts +23 -0
  16. package/dist/Common/RadioGroup.d.ts +17 -0
  17. package/dist/Common/TippyCustomized.d.ts +35 -0
  18. package/dist/Common/Types.d.ts +12 -0
  19. package/dist/Common/index.d.ts +4 -0
  20. package/dist/index.js +3959 -1
  21. package/package.json +2 -2
  22. package/rollup.config.js +1 -3
  23. package/src/Assets/Icon/ic-help-outline.svg +3 -0
  24. package/src/Assets/Icon/ic-info-outlined.svg +13 -0
  25. package/src/Assets/Icon/inject-tag.svg +5 -0
  26. package/src/Assets/Img/warning-medium.svg +15 -0
  27. package/src/Common/BreadCrumb/BreadCrumb.tsx +1 -2
  28. package/src/Common/Constants.ts +7 -0
  29. package/src/Common/CustomTagSelector/PropagateTagInfo.tsx +36 -0
  30. package/src/Common/CustomTagSelector/TagDetails.tsx +41 -0
  31. package/src/Common/CustomTagSelector/TagLabelSelect.tsx +49 -0
  32. package/src/Common/CustomTagSelector/TagLabelValueSelector.tsx +144 -0
  33. package/src/Common/CustomTagSelector/TagSelector.utils.ts +71 -0
  34. package/src/Common/CustomTagSelector/ValidationRules.ts +55 -0
  35. package/src/Common/CustomTagSelector/index.ts +2 -0
  36. package/src/Common/Dialogs/ConfirmationDialog.tsx +32 -0
  37. package/src/Common/Dialogs/DeleteDialog.tsx +35 -0
  38. package/src/Common/Dialogs/DialogForm.tsx +95 -0
  39. package/src/Common/Dialogs/ForceDeleteDialog.tsx +19 -0
  40. package/src/Common/Dialogs/Types.ts +16 -0
  41. package/src/Common/Dialogs/index.ts +4 -0
  42. package/src/Common/PopupMenu.tsx +131 -0
  43. package/src/Common/RadioGroup.tsx +63 -0
  44. package/src/Common/TippyCustomized.tsx +173 -0
  45. package/src/Common/Types.ts +14 -0
  46. package/src/Common/index.ts +5 -1
  47. package/tsconfig.json +1 -1
@@ -0,0 +1,95 @@
1
+ import React, { Component, createContext } from 'react';
2
+ import { VisibleModal } from '../Modals/VisibleModal';
3
+ import close from '../../../Assets/Icons/ic-close.svg';
4
+ import { Progressing } from '../Progressing'
5
+ //TODO: may not need context
6
+ const DialogFormContext = createContext({ title: '', isLoading: false, close: (event) => {}, onSave: (event) => {} });
7
+
8
+ export interface DialogFormProps {
9
+ className: string;
10
+ title: string;
11
+ isLoading: boolean;
12
+ closeOnESC?: boolean;
13
+ close: (event) => void;
14
+ onSave: (event) => void;
15
+ headerClassName?: string;
16
+ }
17
+
18
+ export class DialogForm extends Component<DialogFormProps> {
19
+ constructor(props) {
20
+ super(props);
21
+ this.escFunction = this.escFunction.bind(this);
22
+ }
23
+
24
+ componentDidMount() {
25
+ document.addEventListener('keydown', this.escFunction);
26
+ }
27
+
28
+ componentWillUnmount() {
29
+ document.removeEventListener('keydown', this.escFunction);
30
+ }
31
+
32
+ escFunction(event) {
33
+ if (event.keyCode === 27 && this.props.closeOnESC) {
34
+ this.props.close(event);
35
+ }
36
+ }
37
+
38
+ render() {
39
+ return (
40
+ <DialogFormContext.Provider
41
+ value={{
42
+ title: this.props.title,
43
+ isLoading: this.props.isLoading,
44
+ close: this.props.close,
45
+ onSave: this.props.onSave,
46
+ }}
47
+ >
48
+ <VisibleModal className="">
49
+ <div className={`modal__body ${this.props.className || ''}`}>
50
+ <div className={`modal__header ${this.props.headerClassName || ''}`}>
51
+ <h1 className="modal__title">{this.props.title}</h1>
52
+ <button type="button" className="dc__transparent" onClick={this.props.close}>
53
+ {' '}
54
+ <img src={close} alt="close" />
55
+ </button>
56
+ </div>
57
+ <DialogFormContext.Consumer>
58
+ {(context) => {
59
+ return (
60
+ <form
61
+ noValidate
62
+ onSubmit={(e) => {
63
+ e.preventDefault();
64
+ if (!context.isLoading) {
65
+ context.onSave(e);
66
+ }
67
+ }}
68
+ >
69
+ {this.props.children}
70
+ </form>
71
+ );
72
+ }}
73
+ </DialogFormContext.Consumer>
74
+ </div>
75
+ </VisibleModal>
76
+ </DialogFormContext.Provider>
77
+ );
78
+ }
79
+ }
80
+
81
+ export class DialogFormSubmit extends Component<{ tabIndex: number }> {
82
+ render() {
83
+ return (
84
+ <DialogFormContext.Consumer>
85
+ {(context) => {
86
+ return (
87
+ <button type="submit" className="cta dc__align-right" tabIndex={this.props.tabIndex}>
88
+ {context.isLoading ? <Progressing /> : this.props.children}
89
+ </button>
90
+ );
91
+ }}
92
+ </DialogFormContext.Consumer>
93
+ );
94
+ }
95
+ }
@@ -0,0 +1,19 @@
1
+ import React from 'react'
2
+ import { ForceDeleteDialogType } from './Types';
3
+ import { DeleteDialog } from './DeleteDialog';
4
+
5
+ export default function ForceDeleteDialog({ onClickDelete, closeDeleteModal, forceDeleteDialogTitle, forceDeleteDialogMessage }: ForceDeleteDialogType) {
6
+ return (
7
+ <div>
8
+ <DeleteDialog title={forceDeleteDialogTitle}
9
+ delete={onClickDelete}
10
+ closeDelete={closeDeleteModal}
11
+ deletePrefix="Force ">
12
+ <DeleteDialog.Description >
13
+ <p className="mt-12 mb-12 p-8 dc__break-word dc__window-bg">Error: {forceDeleteDialogMessage}</p>
14
+ <p>Do you want to force delete?</p>
15
+ </DeleteDialog.Description>
16
+ </DeleteDialog>
17
+ </div>
18
+ )
19
+ }
@@ -0,0 +1,16 @@
1
+ export interface DeleteDialogProps {
2
+ title: string
3
+ description?: string
4
+ closeDelete: () => void
5
+ delete: () => any
6
+ deletePrefix?: string
7
+ deletePostfix?: string
8
+ apiCallInProgress?: boolean
9
+ }
10
+
11
+ export interface ForceDeleteDialogType {
12
+ onClickDelete: () => void
13
+ closeDeleteModal: () => void
14
+ forceDeleteDialogTitle: string
15
+ forceDeleteDialogMessage: string
16
+ }
@@ -0,0 +1,4 @@
1
+ export * from './ConfirmationDialog'
2
+ export * from './DeleteDialog'
3
+ export * from './DialogForm'
4
+ export * from './ForceDeleteDialog'
@@ -0,0 +1,131 @@
1
+ import React, { useEffect } from 'react';
2
+ import {Modal} from './Modals/Modal';
3
+ const PopupContext = React.createContext(null);
4
+
5
+ function usePopupContext() {
6
+ const context = React.useContext(PopupContext);
7
+ if (!context) {
8
+ throw new Error(
9
+ `Select compound components cannot be rendered outside popupmenup`,
10
+ )
11
+ }
12
+ return context
13
+ }
14
+
15
+ function PopupMenu({ children = null, onToggleCallback = null, autoClose = false}) {
16
+ const [popupPosition, setPopupPosition]=React.useState(null)
17
+ const [opacity, setOpacity] = React.useState(0)
18
+ const observer = React.useRef<IntersectionObserver | null>(null);
19
+ const buttonRef = React.useRef(null);
20
+ const buttonWidth = React.useRef(null);
21
+
22
+ React.useEffect(()=>{
23
+ observer.current = window.IntersectionObserver ? new IntersectionObserver(intersectionCallback, options) : null
24
+ return ()=>{
25
+ if(observer && observer.current && observer.current.disconnect) observer.current.disconnect();
26
+ }
27
+ },[])
28
+
29
+ useEffect(() => {
30
+ if (typeof onToggleCallback === 'function') {
31
+ if (popupPosition) {
32
+ onToggleCallback(true)
33
+ }
34
+ else {
35
+ onToggleCallback(false)
36
+ }
37
+ }
38
+ }, [popupPosition])
39
+
40
+ let options = {
41
+ root: null,
42
+ rootMargin: '0px',
43
+ threshold: 1.0
44
+ }
45
+
46
+ if(!children) return children
47
+
48
+ function intersectionCallback(entries, observer) {
49
+ entries.forEach(entry => {
50
+ if (entry.isIntersecting && entry.intersectionRatio >= 1) {
51
+ setOpacity(1)
52
+ }
53
+ else {
54
+ const {boundingClientRect, rootBounds} = entry
55
+ const {x, y, height, width} = boundingClientRect
56
+ const {x: buttonX, y: buttonY, height: buttonHeight, width: buttonWidth, left: buttonLeft, right: buttonRight, top: buttonTop, bottom: buttonBottom} = buttonRef.current.getBoundingClientRect();
57
+ if(y + height > rootBounds.height){
58
+ setPopupPosition({ left: buttonX, bottom: document.documentElement.clientHeight - buttonY})
59
+ setOpacity(1)
60
+ }
61
+ if(x + width > rootBounds.width){
62
+ // setPopupPosition(position => ({ right: document.documentElement.clientWidth - buttonLeft,...position, left:'unset' }))
63
+ setPopupPosition(position=>({...position, right: '20px', left:'unset'}))
64
+ setOpacity(1)
65
+ }
66
+ }
67
+ });
68
+ }
69
+
70
+ const handleOpen = (e)=>{
71
+ e.stopPropagation();
72
+ setOpacity(0)
73
+ const { bottom,height,left,right,top,width,x,y } = e.currentTarget.getBoundingClientRect()
74
+ setPopupPosition({left: x, top: y + height})
75
+ }
76
+
77
+ const handleClose = (e, inOrOut) =>{
78
+ if(autoClose || inOrOut === 'out'){
79
+ if(observer && observer.current && observer.current.disconnect) observer.current.disconnect()
80
+ setOpacity(0)
81
+ setPopupPosition(null)
82
+ }
83
+ else{
84
+ e.stopPropagation();
85
+ }
86
+ }
87
+
88
+
89
+ function callbackRef(element) {
90
+ if (element && observer?.current?.observe){
91
+ observer.current.observe(element)
92
+ }
93
+ else observer.current.disconnect();
94
+ }
95
+
96
+ function initialiseButtonWidth(buttonEl){
97
+ if(!buttonEl) return
98
+ buttonRef.current = buttonEl
99
+ const { bottom, height, left, right, top, width, x, y } = buttonEl.getBoundingClientRect()
100
+ buttonWidth.current=width;
101
+ }
102
+
103
+ return <PopupContext.Provider value={{handleOpen, popupPosition, handleClose, opacity, setPopupPosition, callbackRef, buttonRef, initialiseButtonWidth, buttonWidth}}>
104
+ {children}
105
+ </PopupContext.Provider>
106
+
107
+ }
108
+
109
+ function Button({ children = null, disabled = false, rootClassName = "", tabIndex = 0, onHover = false, isKebab = false }) {
110
+ const { handleOpen, popupPosition, buttonRef, initialiseButtonWidth } = usePopupContext()
111
+ return <button ref={initialiseButtonWidth} type="button"
112
+ tabIndex={tabIndex}
113
+ disabled={disabled}
114
+ className={`${rootClassName} ${popupPosition ? 'focused' : ''} ${isKebab ? 'popup-button-kebab':"popup-button" }`}
115
+ onMouseEnter={onHover ? (disabled ? null : handleOpen) : () => { }}
116
+ onClick={disabled ? null : handleOpen} >
117
+ {children}
118
+ </button>
119
+ }
120
+
121
+ function Body({children=null, rootClassName="", style={}, autoWidth=false}){
122
+ const {handleClose, popupPosition, opacity, callbackRef, buttonWidth} = usePopupContext()
123
+ return popupPosition ? <Modal callbackRef={callbackRef} onClick={handleClose} rootClassName={`${rootClassName} popup-body ${!children ? 'popup-body--empty' : ''} ${Object.keys(popupPosition).join(" ")}`} style={{...popupPosition, ...style,...(autoWidth ? {width: buttonWidth.current} : {}), opacity:opacity}}>
124
+ {children}
125
+ </Modal> : null
126
+ }
127
+
128
+ PopupMenu.Button = Button
129
+ PopupMenu.Body = Body
130
+
131
+ export default PopupMenu
@@ -0,0 +1,63 @@
1
+ import React, { Component, createContext } from 'react';
2
+ const RadioGroupContext = createContext({ name: '', value: '', disabled: false, onChange: (event) => {} });
3
+
4
+ export interface RadioGroupItemProps {
5
+ value: string;
6
+ }
7
+
8
+ export class RadioGroupItem extends Component<RadioGroupItemProps> {
9
+ render() {
10
+ return (
11
+ <RadioGroupContext.Consumer>
12
+ {(context) => {
13
+ return (
14
+ <>
15
+ <label className={context.disabled ? 'form__radio-item disabled' : 'form__radio-item'}>
16
+ <input
17
+ type="radio"
18
+ className="form__checkbox"
19
+ name={context.name}
20
+ disabled={context.disabled}
21
+ onChange={context.onChange}
22
+ value={this.props.value}
23
+ checked={context.value === this.props.value}
24
+ />
25
+ <span className="form__radio-item-content">
26
+ <span className="radio__button"></span>
27
+ <span className="radio__title">{this.props.children}</span>
28
+ </span>
29
+ </label>
30
+ </>
31
+ );
32
+ }}
33
+ </RadioGroupContext.Consumer>
34
+ );
35
+ }
36
+ }
37
+
38
+ export interface RadioGroupProps {
39
+ value: string;
40
+ name: string;
41
+ disabled?: boolean;
42
+ onChange: (event) => void;
43
+ className?: string;
44
+ }
45
+
46
+ export class RadioGroup extends Component<RadioGroupProps> {
47
+ render() {
48
+ return (
49
+ <div className={`form__radio-group ${this.props.className || ''}`}>
50
+ <RadioGroupContext.Provider
51
+ value={{
52
+ name: this.props.name,
53
+ value: this.props.value,
54
+ disabled: this.props.disabled,
55
+ onChange: this.props.onChange,
56
+ }}
57
+ >
58
+ {this.props.children}
59
+ </RadioGroupContext.Provider>
60
+ </div>
61
+ );
62
+ }
63
+ }
@@ -0,0 +1,173 @@
1
+ import React, { ReactNode, useEffect, useRef } from 'react'
2
+ import Tippy from '@tippyjs/react'
3
+ import { Placement } from 'tippy.js'
4
+ import { ReactComponent as CloseIcon } from '../Assets/Icon/ic-close.svg'
5
+ import 'tippy.js/animations/shift-toward-subtle.css'
6
+
7
+ export enum TippyTheme {
8
+ black = 'black',
9
+ white = 'white',
10
+ }
11
+
12
+ interface TippyCustomizedProps {
13
+ theme: TippyTheme
14
+ visible?: boolean
15
+ heading?: string
16
+ infoTextHeading?: string
17
+ placement: Placement
18
+ className?: string
19
+ Icon?: React.FunctionComponent<React.SVGProps<SVGSVGElement>>
20
+ iconPath?: string
21
+ iconClass?: string
22
+ 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-`).
23
+ onImageLoadError?: (e) => void
24
+ onClose?: () => void
25
+ infoText?: string
26
+ showCloseButton?: boolean
27
+ arrow?: boolean
28
+ interactive?: boolean
29
+ showOnCreate?: boolean
30
+ trigger?: string
31
+ animation?: string
32
+ duration?: number
33
+ additionalContent?: ReactNode
34
+ documentationLink?: string
35
+ documentationLinkText?: string
36
+ children: React.ReactElement<any>
37
+ }
38
+
39
+ // This component will handle some of the new tippy designs and interactions
40
+ // So this can be updated to support further for new features or interactions
41
+ export default function TippyCustomized(props: TippyCustomizedProps) {
42
+ const tippyRef = useRef(null)
43
+ const isWhiteTheme = props.theme === TippyTheme.white
44
+
45
+ const onTippyMount = (tippyInstance) => {
46
+ tippyRef.current = tippyInstance
47
+ document.addEventListener('keydown', closeOnEsc)
48
+ }
49
+
50
+ const closeTippy = () => {
51
+ if (tippyRef.current?.hide) {
52
+ tippyRef.current.hide()
53
+ tippyRef.current = null
54
+
55
+ if (props.onClose) {
56
+ props.onClose()
57
+ }
58
+ }
59
+ }
60
+
61
+ const closeOnEsc = (e) => {
62
+ if (e.keyCode === 27) {
63
+ closeTippy()
64
+ }
65
+ }
66
+
67
+ const getTippyContent = () => {
68
+ const {
69
+ Icon,
70
+ iconPath,
71
+ iconClass,
72
+ iconSize,
73
+ onImageLoadError,
74
+ heading,
75
+ infoTextHeading,
76
+ infoText,
77
+ showCloseButton,
78
+ additionalContent,
79
+ documentationLink,
80
+ documentationLinkText,
81
+ } = props
82
+ return (
83
+ <>
84
+ <div
85
+ className={`dc__word-break dc__hyphens-auto flex left ${
86
+ isWhiteTheme ? 'p-12 dc__border-bottom-n1 cn-9' : 'pt-20 pb-12 pr-20 pl-20 cn-0'
87
+ }`}
88
+ >
89
+ {iconPath ? (
90
+ <img
91
+ className={`icon-dim-${iconSize || 20} mr-6 ${iconClass || ''}`}
92
+ src={iconPath}
93
+ alt={heading}
94
+ onError={onImageLoadError}
95
+ />
96
+ ) : (
97
+ Icon && (
98
+ <div className={`icon-dim-${iconSize || 20} mr-6`}>
99
+ <Icon className={`icon-dim-${iconSize || 20} ${iconClass || ''}`} />
100
+ </div>
101
+ )
102
+ )}
103
+ {heading && <span className={`fs-14 fw-6 lh-20 ${showCloseButton ? 'mr-6' : ''}`}>{heading}</span>}
104
+ {showCloseButton && (
105
+ <div className="icon-dim-16 ml-auto">
106
+ <CloseIcon
107
+ className={`icon-dim-16 cursor ${isWhiteTheme ? 'fcn-9' : 'fcn-0'}`}
108
+ onClick={closeTippy}
109
+ />
110
+ </div>
111
+ )}
112
+ </div>
113
+ {infoTextHeading && (
114
+ <div
115
+ className={`dc__word-break dc__hyphens-auto fs-14 fw-6 lh-20 ${
116
+ isWhiteTheme ? 'pl-12 pr-12' : 'pl-20 pr-20'
117
+ }`}
118
+ >
119
+ {infoTextHeading}
120
+ </div>
121
+ )}
122
+ {infoText && (
123
+ <div
124
+ className={`dc__word-break dc__hyphens-auto fs-13 fw-4 lh-20 ${
125
+ isWhiteTheme
126
+ ? 'p-12'
127
+ : `pl-20 pr-20 pt-4 ${additionalContent && documentationLink ? 'pb-12' : 'pb-20'}`
128
+ }`}
129
+ >
130
+ {infoText}
131
+ </div>
132
+ )}
133
+ {additionalContent}
134
+ {documentationLink && (
135
+ <div className="pl-12 pb-12">
136
+ <a
137
+ href={documentationLink}
138
+ target="_blank"
139
+ rel="noreferrer noopener"
140
+ className="cb-5"
141
+ onClick={closeTippy}
142
+ >
143
+ {documentationLinkText || 'Learn more'}
144
+ </a>
145
+ </div>
146
+ )}
147
+ </>
148
+ )
149
+ }
150
+
151
+ const { className, placement, arrow, interactive, showOnCreate, trigger, animation, duration, children } = props
152
+
153
+ return (
154
+ <Tippy
155
+ className={`${
156
+ isWhiteTheme ? 'tippy-white-container default-white' : 'tippy-black-container default-black'
157
+ } no-content-padding tippy-shadow ${className}`}
158
+ arrow={arrow || false}
159
+ interactive={interactive || false}
160
+ placement={placement || 'top'}
161
+ content={getTippyContent()}
162
+ trigger={trigger || 'mouseenter'}
163
+ onMount={onTippyMount}
164
+ onClickOutside={closeTippy}
165
+ showOnCreate={showOnCreate || false}
166
+ animation={animation || 'fade'}
167
+ duration={duration || 300}
168
+ visible={props.visible}
169
+ >
170
+ {children}
171
+ </Tippy>
172
+ )
173
+ }
@@ -10,3 +10,17 @@ export interface APIOptions {
10
10
  signal?: AbortSignal
11
11
  preventAutoLogout?: boolean
12
12
  }
13
+
14
+ export interface TagType {
15
+ key:string
16
+ value?: string
17
+ description?: string
18
+ propagate: boolean
19
+ isInvalidKey?: boolean
20
+ isInvalidValue?: boolean
21
+ }
22
+
23
+ export interface OptionType {
24
+ label: string;
25
+ value: string;
26
+ }
@@ -14,4 +14,8 @@ export * from './Modals/OpaqueModal'
14
14
  export * from './Modals/VisibleModal'
15
15
  export * from './Modals/VisibleModal2'
16
16
  export { BreadCrumb, useBreadcrumb } from './BreadCrumb/BreadCrumb'
17
- export { default as BreadcrumbStore } from './BreadCrumb/BreadcrumbStore'
17
+ export { default as BreadcrumbStore } from './BreadCrumb/BreadcrumbStore'
18
+ export * from './RadioGroup'
19
+ export * from './PopupMenu'
20
+ export * from './TippyCustomized'
21
+ export * from './CustomTagSelector'
package/tsconfig.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "es5",
3
+ "target": "es6",
4
4
  "downlevelIteration": true,
5
5
  "lib": [
6
6
  "dom",