@devtron-labs/devtron-fe-common-lib 0.0.7 → 0.0.9-beta-2
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.
- package/dist/Common/BreadCrumb/BreadCrumb.d.ts +1 -29
- package/dist/Common/BreadCrumb/Types.d.ts +28 -0
- package/dist/Common/CIPipeline.Types.d.ts +264 -0
- package/dist/Common/Checkbox.d.ts +1 -14
- package/dist/Common/Constants.d.ts +18 -0
- package/dist/Common/CustomTagSelector/ResizableTagTextArea.d.ts +1 -1
- package/dist/Common/CustomTagSelector/TagLabelValueSelector.d.ts +1 -1
- package/dist/Common/CustomTagSelector/Types.d.ts +11 -3
- package/dist/Common/CustomTagSelector/ValidationRules.d.ts +1 -1
- package/dist/Common/DeleteComponentModal/DeleteComponent.d.ts +4 -0
- package/dist/Common/DeleteComponentModal/types.d.ts +15 -0
- package/dist/Common/Dialogs/ConfirmationDialog.d.ts +5 -16
- package/dist/Common/Dialogs/DialogForm.d.ts +1 -9
- package/dist/Common/Dialogs/Types.d.ts +28 -0
- package/dist/Common/EmptyState/EmptyState.d.ts +2 -1
- package/dist/Common/EmptyState/GenericEmptyState.d.ts +5 -0
- package/dist/Common/Error.d.ts +2 -2
- package/dist/Common/Helper.d.ts +31 -1
- package/dist/Common/InfoColorBar/InfoColourbar.d.ts +2 -15
- package/dist/Common/Modals/Modal.d.ts +2 -9
- package/dist/Common/MultiSelectCustomization.d.ts +1 -2
- package/dist/Common/PopupMenu.d.ts +4 -21
- package/dist/Common/Progressing.d.ts +4 -13
- package/dist/Common/RadioGroup/RadioGroup.d.ts +17 -0
- package/dist/Common/RadioGroup.d.ts +1 -10
- package/dist/Common/Reload.d.ts +2 -4
- package/dist/Common/Security/ScanVulnerabilitiesTable.d.ts +4 -0
- package/dist/Common/TippyCustomized.d.ts +2 -30
- package/dist/Common/Toggle/Toggle.d.ts +12 -0
- package/dist/Common/Types.d.ts +293 -0
- package/dist/Common/index.d.ts +7 -1
- package/dist/index.js +2472 -1948
- package/package.json +2 -2
- package/rollup.config.js +3 -3
- package/src/Assets/Icon/ic-help.svg +3 -0
- package/src/Assets/Icon/ic-info-filled.svg +7 -0
- package/src/Assets/Icon/ic-warning.svg +7 -0
- package/src/Assets/Img/app-not-deployed.png +0 -0
- package/src/Common/Api.ts +7 -1
- package/src/Common/BreadCrumb/BreadCrumb.tsx +1 -30
- package/src/Common/BreadCrumb/Types.ts +30 -0
- package/src/Common/CIPipeline.Types.ts +276 -0
- package/src/Common/Checkbox.tsx +3 -16
- package/src/Common/Constants.ts +21 -0
- package/src/Common/CustomTagSelector/ResizableTagTextArea.tsx +6 -4
- package/src/Common/CustomTagSelector/TagDetails.tsx +15 -2
- package/src/Common/CustomTagSelector/TagLabelSelect.tsx +7 -3
- package/src/Common/CustomTagSelector/TagLabelValueSelector.tsx +66 -26
- package/src/Common/CustomTagSelector/Types.ts +11 -3
- package/src/Common/CustomTagSelector/ValidationRules.ts +21 -18
- package/src/Common/DeleteComponentModal/DeleteComponent.tsx +95 -0
- package/src/Common/DeleteComponentModal/types.ts +15 -0
- package/src/Common/Dialogs/ConfirmationDialog.tsx +6 -4
- package/src/Common/Dialogs/DeleteDialog.tsx +45 -27
- package/src/Common/Dialogs/DialogForm.tsx +2 -11
- package/src/Common/Dialogs/Types.ts +29 -0
- package/src/Common/Drawer/Drawer.scss +12 -11
- package/src/Common/EmptyState/EmptyState.tsx +5 -5
- package/src/Common/EmptyState/GenericEmptyState.tsx +41 -0
- package/src/Common/EmptyState/emptyState.scss +4 -15
- package/src/Common/Error.tsx +13 -16
- package/src/Common/ErrorScreenNotFound.tsx +23 -30
- package/src/Common/Helper.tsx +258 -2
- package/src/Common/InfoColorBar/InfoColourbar.tsx +6 -18
- package/src/Common/Modals/Modal.tsx +7 -1
- package/src/Common/Modals/OpaqueModal.tsx +2 -2
- package/src/Common/Modals/VisibleModal.tsx +1 -1
- package/src/Common/Modals/VisibleModal2.tsx +1 -1
- package/src/Common/MultiSelectCustomization.tsx +3 -3
- package/src/Common/PopupMenu.tsx +23 -6
- package/src/Common/Progressing.tsx +8 -15
- package/src/Common/RadioGroup/RadioGroup.tsx +88 -0
- package/src/Common/RadioGroup/radioGroup.scss +46 -0
- package/src/Common/RadioGroup.tsx +3 -13
- package/src/Common/Reload.tsx +15 -10
- package/src/Common/Security/ScanVulnerabilitiesTable.tsx +55 -0
- package/src/Common/Security/scanVulnerabilities.css +67 -0
- package/src/Common/TippyCustomized.tsx +29 -31
- package/src/Common/Toggle/Toggle.scss +70 -0
- package/src/Common/Toggle/Toggle.tsx +38 -0
- package/src/Common/Types.ts +319 -0
- package/src/Common/index.ts +7 -1
- package/dist/27bfa7a87d0b7039.png +0 -0
- package/dist/b5d52f036bdcd3ed.png +0 -0
- package/src/Assets/Icon/ic-close.svg +0 -9
- /package/src/Assets/Img/{warning-medium.svg → delete-medium.svg} +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import AppNotDeployed from '../../Assets/Img/app-not-deployed.png'
|
|
3
|
+
import { GenericEmptyStateType, ImageType } from '../Types'
|
|
4
|
+
import './emptyState.scss'
|
|
5
|
+
|
|
6
|
+
function GenericEmptyState({
|
|
7
|
+
title,
|
|
8
|
+
image,
|
|
9
|
+
subTitle,
|
|
10
|
+
isButtonAvailable,
|
|
11
|
+
classname,
|
|
12
|
+
styles,
|
|
13
|
+
heightToDeduct,
|
|
14
|
+
imageType,
|
|
15
|
+
renderButton,
|
|
16
|
+
imageClassName,
|
|
17
|
+
children,
|
|
18
|
+
noImage
|
|
19
|
+
}: GenericEmptyStateType): JSX.Element {
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className={`flex column empty-state dc__align-reload-center ${classname ? classname : ''}`}
|
|
23
|
+
style={styles}
|
|
24
|
+
data-testid="generic-empty-state"
|
|
25
|
+
{...(heightToDeduct >= 0 && { style: { ...styles, height: `calc(100vh - ${heightToDeduct}px)` } })}
|
|
26
|
+
>
|
|
27
|
+
{!noImage && <img className={imageClassName ? imageClassName : ''}
|
|
28
|
+
src={image || AppNotDeployed}
|
|
29
|
+
width={imageType === ImageType.Medium ? '200' : '250'}
|
|
30
|
+
height={imageType === ImageType.Medium ? '160' : '200'}
|
|
31
|
+
alt="empty-state"
|
|
32
|
+
/>}
|
|
33
|
+
<h4 className="title fw-6 cn-9 mb-8 mt-20">{title}</h4>
|
|
34
|
+
{subTitle && <p className="subtitle">{subTitle}</p>}
|
|
35
|
+
{isButtonAvailable && renderButton()}
|
|
36
|
+
{children}
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default GenericEmptyState
|
|
@@ -19,12 +19,9 @@
|
|
|
19
19
|
h6 {
|
|
20
20
|
color: var(--N900);
|
|
21
21
|
}
|
|
22
|
-
.title {
|
|
23
|
-
font-weight: 600;
|
|
24
|
-
}
|
|
25
22
|
.subtitle {
|
|
26
23
|
font-size: 13px;
|
|
27
|
-
width:
|
|
24
|
+
width: 300px;
|
|
28
25
|
font-weight: 400;
|
|
29
26
|
text-align: center;
|
|
30
27
|
line-height: 1.5;
|
|
@@ -34,7 +31,7 @@
|
|
|
34
31
|
height: 36px;
|
|
35
32
|
}
|
|
36
33
|
p {
|
|
37
|
-
width:
|
|
34
|
+
width: 300px;
|
|
38
35
|
text-align: center;
|
|
39
36
|
font-size: 13px;
|
|
40
37
|
color: var(--N700);
|
|
@@ -68,16 +65,8 @@
|
|
|
68
65
|
color: var(--N900);
|
|
69
66
|
}
|
|
70
67
|
.button__icon {
|
|
71
|
-
width:16px;
|
|
72
|
-
height:16px;
|
|
68
|
+
width: 16px;
|
|
69
|
+
height: 16px;
|
|
73
70
|
margin-left: 8px;
|
|
74
71
|
}
|
|
75
|
-
//TODO: use name
|
|
76
|
-
// .subtitle a {
|
|
77
|
-
// color: inherit;
|
|
78
|
-
// }
|
|
79
|
-
// .subtitle a:focus,
|
|
80
|
-
// .subtitle a:hover {
|
|
81
|
-
// color: inherit;
|
|
82
|
-
// }
|
|
83
72
|
}
|
package/src/Common/Error.tsx
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React, { Component } from 'react'
|
|
2
2
|
import EmptyState from './EmptyState/EmptyState'
|
|
3
3
|
import notAuthorized from '../Assets/Img/ic-not-authorized.svg'
|
|
4
|
-
import Reload from './Reload'
|
|
5
4
|
import ErrorScreenNotFound from './ErrorScreenNotFound'
|
|
5
|
+
import { ERROR_EMPTY_SCREEN } from './Constants'
|
|
6
|
+
import GenericEmptyState from './EmptyState/GenericEmptyState'
|
|
7
|
+
import Reload from './Reload'
|
|
6
8
|
|
|
7
9
|
export class ErrorScreenManager extends Component<{
|
|
8
10
|
code?: number
|
|
@@ -19,7 +21,7 @@ export class ErrorScreenManager extends Component<{
|
|
|
19
21
|
return 'Unauthorized'
|
|
20
22
|
case 403:
|
|
21
23
|
return (
|
|
22
|
-
<ErrorScreenNotAuthorized subtitle={this.props.subtitle}
|
|
24
|
+
<ErrorScreenNotAuthorized subtitle={this.props.subtitle} />
|
|
23
25
|
)
|
|
24
26
|
case 404:
|
|
25
27
|
return <ErrorScreenNotFound />
|
|
@@ -43,22 +45,17 @@ export class ErrorScreenManager extends Component<{
|
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
export class ErrorScreenNotAuthorized extends Component<{
|
|
48
|
+
export class ErrorScreenNotAuthorized extends Component<{
|
|
49
|
+
subtitle?: React.ReactChild
|
|
50
|
+
title?: string
|
|
51
|
+
}> {
|
|
47
52
|
render() {
|
|
48
53
|
return (
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<h3 className="title">Not authorized</h3>
|
|
55
|
-
</EmptyState.Title>
|
|
56
|
-
<EmptyState.Subtitle className={this.props.subtitleClass}>
|
|
57
|
-
{this.props.subtitle
|
|
58
|
-
? this.props.subtitle
|
|
59
|
-
: "Looks like you don't have access to information on this page. Please contact your manager to request access."}
|
|
60
|
-
</EmptyState.Subtitle>
|
|
61
|
-
</EmptyState>
|
|
54
|
+
<GenericEmptyState
|
|
55
|
+
image={notAuthorized}
|
|
56
|
+
title={this.props.title ?? ERROR_EMPTY_SCREEN.NOT_AUTHORIZED}
|
|
57
|
+
subTitle={this.props.subtitle ?? ERROR_EMPTY_SCREEN.ONLY_FOR_SUPERADMIN}
|
|
58
|
+
/>
|
|
62
59
|
)
|
|
63
60
|
}
|
|
64
61
|
}
|
|
@@ -1,38 +1,31 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import notFound from '../Assets/Img/ic-empty-error@2x.png'
|
|
3
|
-
import { useHistory } from 'react-router'
|
|
4
|
-
import EmptyState from './EmptyState/EmptyState'
|
|
3
|
+
import { useHistory } from 'react-router-dom'
|
|
5
4
|
import { ERROR_EMPTY_SCREEN } from './Constants'
|
|
5
|
+
import GenericEmptyState from './EmptyState/GenericEmptyState'
|
|
6
6
|
|
|
7
7
|
function ErrorScreenNotFound() {
|
|
8
|
-
|
|
8
|
+
const {push} = useHistory()
|
|
9
|
+
const redirectToHome = () => {
|
|
10
|
+
push('/app/list')
|
|
11
|
+
}
|
|
12
|
+
const renderGenerateButton = () => {
|
|
13
|
+
return (
|
|
14
|
+
<button className="flex cta h-32" onClick={redirectToHome} data-testid="empty-screen-take-me-home">
|
|
15
|
+
{ERROR_EMPTY_SCREEN.TAKE_BACK_HOME}
|
|
16
|
+
</button>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
</EmptyState.Image>
|
|
20
|
-
<EmptyState.Title>
|
|
21
|
-
<h3 className="title">{ERROR_EMPTY_SCREEN.PAGE_NOT_FOUND}</h3>
|
|
22
|
-
</EmptyState.Title>
|
|
23
|
-
<EmptyState.Subtitle >
|
|
24
|
-
<p>{ERROR_EMPTY_SCREEN.PAGE_NOT_EXIST}</p>
|
|
25
|
-
</EmptyState.Subtitle>
|
|
26
|
-
<EmptyState.Button>
|
|
27
|
-
<button className="flex cta h-32"
|
|
28
|
-
onClick={redirectToHome}
|
|
29
|
-
>
|
|
30
|
-
{ERROR_EMPTY_SCREEN.TAKE_BACK_HOME}
|
|
31
|
-
</button>
|
|
32
|
-
</EmptyState.Button>
|
|
33
|
-
</EmptyState>
|
|
34
|
-
</div>
|
|
35
|
-
)
|
|
20
|
+
return (
|
|
21
|
+
<GenericEmptyState
|
|
22
|
+
image={notFound}
|
|
23
|
+
title={ERROR_EMPTY_SCREEN.PAGE_NOT_FOUND}
|
|
24
|
+
subTitle={ERROR_EMPTY_SCREEN.PAGE_NOT_EXIST}
|
|
25
|
+
isButtonAvailable={true}
|
|
26
|
+
renderButton={renderGenerateButton}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
36
29
|
}
|
|
37
30
|
|
|
38
|
-
export default ErrorScreenNotFound
|
|
31
|
+
export default ErrorScreenNotFound
|
package/src/Common/Helper.tsx
CHANGED
|
@@ -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 {
|
|
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 (
|
|
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
|
|
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-
|
|
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-
|
|
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=
|
|
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={
|
|
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
|