@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.
- package/dist/Common/Constants.d.ts +6 -0
- package/dist/Common/CustomTagSelector/PropagateTagInfo.d.ts +2 -0
- package/dist/Common/CustomTagSelector/TagDetails.d.ts +8 -0
- package/dist/Common/CustomTagSelector/TagLabelSelect.d.ts +5 -0
- package/dist/Common/CustomTagSelector/TagLabelValueSelector.d.ts +10 -0
- package/dist/Common/CustomTagSelector/TagSelector.utils.d.ts +12 -0
- package/dist/Common/CustomTagSelector/ValidationRules.d.ts +10 -0
- package/dist/Common/CustomTagSelector/index.d.ts +2 -0
- package/dist/Common/Dialogs/ConfirmationDialog.d.ts +20 -0
- package/dist/Common/Dialogs/DeleteDialog.d.ts +5 -0
- package/dist/Common/Dialogs/DialogForm.d.ts +22 -0
- package/dist/Common/Dialogs/ForceDeleteDialog.d.ts +3 -0
- package/dist/Common/Dialogs/Types.d.ts +15 -0
- package/dist/Common/Dialogs/index.d.ts +4 -0
- package/dist/Common/PopupMenu.d.ts +23 -0
- package/dist/Common/RadioGroup.d.ts +17 -0
- package/dist/Common/TippyCustomized.d.ts +35 -0
- package/dist/Common/Types.d.ts +12 -0
- package/dist/Common/index.d.ts +4 -0
- package/dist/index.js +3959 -1
- package/package.json +2 -2
- package/rollup.config.js +1 -3
- package/src/Assets/Icon/ic-help-outline.svg +3 -0
- package/src/Assets/Icon/ic-info-outlined.svg +13 -0
- package/src/Assets/Icon/inject-tag.svg +5 -0
- package/src/Assets/Img/warning-medium.svg +15 -0
- package/src/Common/BreadCrumb/BreadCrumb.tsx +1 -2
- package/src/Common/Constants.ts +7 -0
- package/src/Common/CustomTagSelector/PropagateTagInfo.tsx +36 -0
- package/src/Common/CustomTagSelector/TagDetails.tsx +41 -0
- package/src/Common/CustomTagSelector/TagLabelSelect.tsx +49 -0
- package/src/Common/CustomTagSelector/TagLabelValueSelector.tsx +144 -0
- package/src/Common/CustomTagSelector/TagSelector.utils.ts +71 -0
- package/src/Common/CustomTagSelector/ValidationRules.ts +55 -0
- package/src/Common/CustomTagSelector/index.ts +2 -0
- package/src/Common/Dialogs/ConfirmationDialog.tsx +32 -0
- package/src/Common/Dialogs/DeleteDialog.tsx +35 -0
- package/src/Common/Dialogs/DialogForm.tsx +95 -0
- package/src/Common/Dialogs/ForceDeleteDialog.tsx +19 -0
- package/src/Common/Dialogs/Types.ts +16 -0
- package/src/Common/Dialogs/index.ts +4 -0
- package/src/Common/PopupMenu.tsx +131 -0
- package/src/Common/RadioGroup.tsx +63 -0
- package/src/Common/TippyCustomized.tsx +173 -0
- package/src/Common/Types.ts +14 -0
- package/src/Common/index.ts +5 -1
- package/tsconfig.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devtron-labs/devtron-fe-common-lib",
|
|
3
|
-
"version": "0.0.6-beta-
|
|
3
|
+
"version": "0.0.6-beta-13",
|
|
4
4
|
"description": "Supporting common component library",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@rollup/plugin-babel": "^6.0.3",
|
|
23
23
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
24
|
-
"@rollup/plugin-terser": "^0.2.0",
|
|
25
24
|
"@rollup/plugin-image": "3.0.1",
|
|
26
25
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
27
26
|
"rollup-plugin-postcss": "^4.0.2",
|
|
@@ -35,6 +34,7 @@
|
|
|
35
34
|
"@rollup/plugin-url": "8.0.1",
|
|
36
35
|
"typescript": "^4.9.4",
|
|
37
36
|
"react-toastify": "^8.2.0",
|
|
37
|
+
"@tippyjs/react": "^4.2.0",
|
|
38
38
|
"@sentry/browser": "^7.3.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
package/rollup.config.js
CHANGED
|
@@ -3,7 +3,6 @@ import resolve from '@rollup/plugin-node-resolve'
|
|
|
3
3
|
import typescript from 'rollup-plugin-typescript2'
|
|
4
4
|
import postcss from 'rollup-plugin-postcss'
|
|
5
5
|
import { babel } from '@rollup/plugin-babel'
|
|
6
|
-
import terser from '@rollup/plugin-terser'
|
|
7
6
|
import del from 'rollup-plugin-delete'
|
|
8
7
|
import pkg from './package.json'
|
|
9
8
|
import svgr from '@svgr/rollup';
|
|
@@ -19,13 +18,12 @@ export default {
|
|
|
19
18
|
postcss({
|
|
20
19
|
extensions: ['.css'],
|
|
21
20
|
}),
|
|
22
|
-
terser(),
|
|
23
21
|
babel({
|
|
24
22
|
exclude: 'node_modules/**',
|
|
25
23
|
}),
|
|
26
24
|
del({ targets: ['dist/*'] }),
|
|
27
25
|
url(),
|
|
28
|
-
svgr()
|
|
26
|
+
svgr()
|
|
29
27
|
],
|
|
30
28
|
external: Object.keys(pkg.peerDependencies || {}),
|
|
31
29
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 16">
|
|
2
|
+
<path fill="#767D84" fill-rule="evenodd" d="M9 1.333c3.682 0 6.667 2.985 6.667 6.667S12.682 14.667 9 14.667 2.333 11.682 2.333 8 5.318 1.333 9 1.333zm0 1.334C6.054 2.667 3.667 5.054 3.667 8S6.054 13.333 9 13.333 14.333 10.946 14.333 8 11.946 2.667 9 2.667zm-.137 7.245c.41 0 .747.337.747.747s-.337.747-.747.747-.747-.336-.747-.747c0-.41.338-.747.747-.747zm.046-5.245c1.166 0 2.313.537 2.313 1.82 0 1.185-1.357 1.64-1.648 2.068-.22.319-.146.766-.747.766-.392 0-.583-.319-.583-.61 0-1.085 1.593-1.33 1.593-2.223 0-.492-.327-.783-.873-.783-1.166 0-.711 1.202-1.594 1.202-.319 0-.592-.191-.592-.556 0-.893 1.02-1.684 2.13-1.684z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<g fill="#06C">
|
|
4
|
+
<g>
|
|
5
|
+
<g>
|
|
6
|
+
<g>
|
|
7
|
+
<path d="M10 1.667c4.6 0 8.333 3.733 8.333 8.333S14.6 18.333 10 18.333 1.667 14.6 1.667 10 5.4 1.667 10 1.667zm0 1.666C6.32 3.333 3.333 6.32 3.333 10c0 3.68 2.987 6.667 6.667 6.667 3.68 0 6.667-2.987 6.667-6.667 0-3.68-2.987-6.667-6.667-6.667zm0 5.834c.46 0 .833.373.833.833v3.333c0 .46-.373.834-.833.834-.46 0-.833-.373-.833-.834V10c0-.46.373-.833.833-.833zm0-3.334c.46 0 .833.373.833.834 0 .46-.373.833-.833.833-.46 0-.833-.373-.833-.833 0-.46.373-.834.833-.834z" transform="translate(-315.000000, -313.000000) translate(303.000000, 256.000000) translate(4.000000, 49.000000) translate(8.000000, 8.000000)"/>
|
|
8
|
+
</g>
|
|
9
|
+
</g>
|
|
10
|
+
</g>
|
|
11
|
+
</g>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M10 10H3.33333" stroke="#596168" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
+
<circle cx="9.99992" cy="9.99967" r="1.66667" fill="#596168"/>
|
|
4
|
+
<path d="M4.41455 6.40325L9.76177 3.39544C9.83454 3.35451 9.91661 3.33301 10.0001 3.33301C10.0836 3.33301 10.1657 3.35451 10.2384 3.39544L15.5856 6.40325C15.6608 6.44554 15.7234 6.50706 15.7669 6.58152C15.8105 6.65598 15.8334 6.74068 15.8334 6.82693V12.8208C15.8334 12.9071 15.8105 12.9918 15.7669 13.0662C15.7234 13.1407 15.6608 13.2022 15.5856 13.2445L10.2384 16.2523C10.1657 16.2933 10.0836 16.3148 10.0001 16.3148C9.91661 16.3148 9.83454 16.2933 9.76177 16.2523L4.41455 13.2445" stroke="#596168" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
|
|
3
|
+
<g fill="none" fill-rule="evenodd">
|
|
4
|
+
<path d="M0 0h48v48H0z"/>
|
|
5
|
+
<g fill="#FFEBEB" stroke="#F33E3E" stroke-linejoin="round" stroke-width="2">
|
|
6
|
+
<path d="M7 24h24v17a4 4 0 0 1-4 4H11a4 4 0 0 1-4-4V24z"/>
|
|
7
|
+
<path stroke-linecap="round" d="M19 29.5v10M24 29.5v10M14 29.5v10"/>
|
|
8
|
+
</g>
|
|
9
|
+
<path stroke="#F33E3E" stroke-dasharray="17,3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M42 10a3 3 0 0 1 0 6H31M38 23a3 3 0 0 0-3-3h-8"/>
|
|
10
|
+
<g stroke="#F33E3E" stroke-linejoin="round" stroke-width="2" transform="rotate(-10 36.29 -8.86)">
|
|
11
|
+
<rect width="30" height="5" y="6" fill="#FFEBEB" rx="1"/>
|
|
12
|
+
<path d="M11 0h8a2 2 0 0 1 2 2v4H9V2a2 2 0 0 1 2-2z"/>
|
|
13
|
+
</g>
|
|
14
|
+
</g>
|
|
15
|
+
</svg>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useMemo, useEffect } from 'react'
|
|
2
|
-
import { useRouteMatch, useParams } from 'react-router'
|
|
3
|
-
import { Link } from 'react-router-dom';
|
|
2
|
+
import { Link, useRouteMatch, useParams } from 'react-router-dom';
|
|
4
3
|
import { useBreadcrumbContext } from './BreadcrumbStore'
|
|
5
4
|
import { ConditionalWrap } from '../Helper';
|
|
6
5
|
export const BreadcrumbContext = React.createContext(null)
|
package/src/Common/Constants.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
export const RequestTimeout = 60000
|
|
2
2
|
export const Host = process.env.REACT_APP_ORCHESTRATOR_ROOT
|
|
3
|
+
|
|
4
|
+
export const PATTERNS = {
|
|
5
|
+
KUBERNETES_KEY_PREFIX: /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/,
|
|
6
|
+
KUBERNETES_KEY_NAME: /^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$/,
|
|
7
|
+
START_END_ALPHANUMERIC: /^([A-Za-z0-9].*[A-Za-z0-9])$|[A-Za-z0-9]$/,
|
|
8
|
+
ALPHANUMERIC_WITH_SPECIAL_CHAR: /^[A-Za-z0-9._-]+$/, // allow alphanumeric,(.) ,(-),(_)
|
|
9
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ReactComponent as InjectTag } from '../../Assets/Icon/inject-tag.svg'
|
|
3
|
+
import { ReactComponent as Help } from '../../Assets/Icon/ic-help-outline.svg'
|
|
4
|
+
import TippyCustomized, { TippyTheme } from '../TippyCustomized'
|
|
5
|
+
|
|
6
|
+
export default function PropagateTagInfo() {
|
|
7
|
+
const additionalInfo = () => {
|
|
8
|
+
return (
|
|
9
|
+
<div className="p-12 fs-13">
|
|
10
|
+
Use these tags to filter/identify resources via CLI or in other Kubernetes tools.
|
|
11
|
+
</div>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
return (
|
|
15
|
+
<div className="flexbox">
|
|
16
|
+
<InjectTag className="icon-dim-16 mt-2 mr-4" />
|
|
17
|
+
<span>Propagate tags</span>
|
|
18
|
+
<TippyCustomized
|
|
19
|
+
theme={TippyTheme.white}
|
|
20
|
+
className="w-300"
|
|
21
|
+
placement="top"
|
|
22
|
+
Icon={InjectTag}
|
|
23
|
+
heading={'Propagate tags to K8s resources'}
|
|
24
|
+
infoText={`Add a tag and click on the ⬡ icon to propagate tags as labels to Kubernetes resources.`}
|
|
25
|
+
additionalContent={additionalInfo()}
|
|
26
|
+
showCloseButton={true}
|
|
27
|
+
trigger="click"
|
|
28
|
+
interactive={true}
|
|
29
|
+
documentationLink={''}
|
|
30
|
+
documentationLinkText={'View Documentation'}
|
|
31
|
+
>
|
|
32
|
+
<Help className="icon-dim-16 mt-2 ml-4 cursor" />
|
|
33
|
+
</TippyCustomized>
|
|
34
|
+
</div>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ReactComponent as DeleteCross } from '../../Assets/Icon/ic-close.svg'
|
|
3
|
+
import { ReactComponent as InjectTag } from '../../Assets/Icon/inject-tag.svg'
|
|
4
|
+
import { TagType } from '../Types'
|
|
5
|
+
import TagLabelValueSelector from './TagLabelValueSelector'
|
|
6
|
+
|
|
7
|
+
export default function TagDetails({
|
|
8
|
+
index,
|
|
9
|
+
tagData,
|
|
10
|
+
setTagData,
|
|
11
|
+
removeTag,
|
|
12
|
+
}: {
|
|
13
|
+
index: number
|
|
14
|
+
tagData: TagType
|
|
15
|
+
setTagData: (index: number, tagData: TagType) => void
|
|
16
|
+
removeTag: (index: number) => void
|
|
17
|
+
}) {
|
|
18
|
+
const deleteTag = (): void => {
|
|
19
|
+
removeTag(index)
|
|
20
|
+
}
|
|
21
|
+
const propagateTagToResource = (): void => {
|
|
22
|
+
const _tagData = { ...tagData }
|
|
23
|
+
_tagData.propagate = !_tagData.propagate
|
|
24
|
+
setTagData(index, _tagData)
|
|
25
|
+
}
|
|
26
|
+
return (
|
|
27
|
+
<div className="flexbox mb-8">
|
|
28
|
+
<div
|
|
29
|
+
className={`dc__border pl-4 pr-4 br-4 mr-8 pointer ${tagData.propagate ? 'bcn-7' : ''}`}
|
|
30
|
+
onClick={propagateTagToResource}
|
|
31
|
+
>
|
|
32
|
+
<InjectTag className={`icon-dim-20 mt-4 ${tagData.propagate ? 'scn-0' : ''}`} />
|
|
33
|
+
</div>
|
|
34
|
+
<TagLabelValueSelector selectedTagIndex={index} tagData={tagData} setTagData={setTagData} type="key" />
|
|
35
|
+
<TagLabelValueSelector selectedTagIndex={index} tagData={tagData} setTagData={setTagData} type="value" />
|
|
36
|
+
<div className="dc__border pl-4 pr-4 dc__right-radius-4 pointer" onClick={deleteTag}>
|
|
37
|
+
<DeleteCross className="icon-dim-20 mt-4" />
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropagateTagInfo from './PropagateTagInfo'
|
|
3
|
+
import { ReactComponent as Add } from '../../Assets/Icon/ic-add.svg'
|
|
4
|
+
import TagDetails from './TagDetails'
|
|
5
|
+
|
|
6
|
+
export default function TagLabelSelect({ labelTags, setLabelTags }) {
|
|
7
|
+
const setTagData = (index, tagValue): void => {
|
|
8
|
+
const _tags = [...labelTags]
|
|
9
|
+
_tags[index] = tagValue
|
|
10
|
+
setLabelTags(_tags)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const addNewTag = (): void => {
|
|
14
|
+
const _tags = [...labelTags]
|
|
15
|
+
_tags.splice(0, 0, { key: '', value: '', propagate: false, isInvalidKey: false, isInvalidValue: false })
|
|
16
|
+
setLabelTags(_tags)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const removeTag = (index: number): void => {
|
|
20
|
+
const _tags = [...labelTags]
|
|
21
|
+
_tags.splice(index, 1)
|
|
22
|
+
setLabelTags(_tags)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div>
|
|
27
|
+
<div className="flexbox dc__content-space mb-8">
|
|
28
|
+
<span>Tags</span>
|
|
29
|
+
<PropagateTagInfo />
|
|
30
|
+
</div>
|
|
31
|
+
<div>
|
|
32
|
+
<div className="dc_width-max-content cb-5 fw-6 fs-13 flexbox mr-20 mb-8 cursor" onClick={addNewTag}>
|
|
33
|
+
<Add className="icon-dim-20 fcb-5" /> Add tag
|
|
34
|
+
</div>
|
|
35
|
+
<div className="mb-8">
|
|
36
|
+
{labelTags?.map((tagData, index) => (
|
|
37
|
+
<TagDetails
|
|
38
|
+
key={`tag-${index}`}
|
|
39
|
+
index={index}
|
|
40
|
+
tagData={tagData}
|
|
41
|
+
setTagData={setTagData}
|
|
42
|
+
removeTag={removeTag}
|
|
43
|
+
/>
|
|
44
|
+
))}
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react'
|
|
2
|
+
import { OptionType, TagType } from '../Types'
|
|
3
|
+
import PopupMenu from '../PopupMenu'
|
|
4
|
+
import { ValidationRules } from './ValidationRules'
|
|
5
|
+
import { ReactComponent as ErrorCross } from '../../Assets/Icon/ic-close.svg'
|
|
6
|
+
import { ReactComponent as Info } from '../../Assets/Icon/ic-info-outlined.svg'
|
|
7
|
+
|
|
8
|
+
export default function TagLabelValueSelector({
|
|
9
|
+
selectedTagIndex,
|
|
10
|
+
tagData,
|
|
11
|
+
setTagData,
|
|
12
|
+
tagOptions,
|
|
13
|
+
isRequired,
|
|
14
|
+
type,
|
|
15
|
+
}: {
|
|
16
|
+
selectedTagIndex: number
|
|
17
|
+
tagData: TagType
|
|
18
|
+
setTagData: (index: number, tagData: TagType) => void
|
|
19
|
+
tagOptions?: OptionType[]
|
|
20
|
+
isRequired?: boolean
|
|
21
|
+
type?: string
|
|
22
|
+
}) {
|
|
23
|
+
const [selectedValue, setSelectedValue] = useState<string>('')
|
|
24
|
+
const [isPopupOpen, togglePopup] = useState<boolean>(false)
|
|
25
|
+
|
|
26
|
+
const validationRules = new ValidationRules()
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const _tagData = { ...tagData }
|
|
30
|
+
setSelectedValue(_tagData[type])
|
|
31
|
+
}, [selectedTagIndex, tagData, type])
|
|
32
|
+
|
|
33
|
+
const handleOnBlur = (e) => {
|
|
34
|
+
if (
|
|
35
|
+
!e.relatedTarget ||
|
|
36
|
+
!e.relatedTarget.classList.value ||
|
|
37
|
+
e.relatedTarget.classList.value.indexOf(`tag-${selectedTagIndex}-class`) === -1
|
|
38
|
+
) {
|
|
39
|
+
const _tagData = { ...tagData }
|
|
40
|
+
_tagData[type] = selectedValue
|
|
41
|
+
if (type === 'key') {
|
|
42
|
+
_tagData.isInvalidKey = selectedValue
|
|
43
|
+
? !validationRules.propagateTagKey(selectedValue).isValid
|
|
44
|
+
: _tagData.value !== ''
|
|
45
|
+
} else {
|
|
46
|
+
if (selectedValue) {
|
|
47
|
+
_tagData.isInvalidValue = !validationRules.propagateTagValue(selectedValue).isValid
|
|
48
|
+
_tagData.isInvalidKey = !_tagData.key || _tagData.isInvalidKey
|
|
49
|
+
} else {
|
|
50
|
+
_tagData.isInvalidValue = false
|
|
51
|
+
_tagData.isInvalidKey = !_tagData.key ? false : _tagData.isInvalidKey
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
setTagData(selectedTagIndex, _tagData)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const handleInputChange = (e): void => {
|
|
59
|
+
setSelectedValue(e.target.value)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const onSelectValue = (e): void => {
|
|
63
|
+
const _tagData = { ...tagData }
|
|
64
|
+
_tagData[type] = e.currentTarget.dataset.key
|
|
65
|
+
setTagData(selectedTagIndex, _tagData)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const renderValidationsSuggestions = (): JSX.Element => {
|
|
69
|
+
let field = { isValid: true, messages: [] }
|
|
70
|
+
if (type === 'value') {
|
|
71
|
+
if (isRequired || selectedValue) {
|
|
72
|
+
field = validationRules.propagateTagValue(selectedValue)
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
field = validationRules.propagateTagKey(selectedValue)
|
|
76
|
+
}
|
|
77
|
+
if (!field.isValid) {
|
|
78
|
+
return (
|
|
79
|
+
<div className="p-4">
|
|
80
|
+
{field.messages.map((error) => (
|
|
81
|
+
<div key={error} className="flexbox p-4">
|
|
82
|
+
<span>
|
|
83
|
+
<ErrorCross className="icon-dim-14 scr-5 mt-3 mr-4" />
|
|
84
|
+
</span>
|
|
85
|
+
<span>{error}</span>
|
|
86
|
+
</div>
|
|
87
|
+
))}
|
|
88
|
+
{type === 'key' && (
|
|
89
|
+
<div className="flexbox p-4">
|
|
90
|
+
<span>
|
|
91
|
+
<Info className="icon-dim-14 mt-3 mr-4" />
|
|
92
|
+
</span>
|
|
93
|
+
<span className="dc__italic-font-style">Key format: prefix/name or name</span>
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
return null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const renderSuggestions = (): JSX.Element => {
|
|
103
|
+
if (tagOptions?.length) {
|
|
104
|
+
const filteredTags = tagOptions.filter((tag) => tag.value.indexOf(selectedValue) >= 0)
|
|
105
|
+
if (filteredTags.length) {
|
|
106
|
+
return (
|
|
107
|
+
<div className="p-8">
|
|
108
|
+
{tagOptions
|
|
109
|
+
.filter((tag) => tag.value.indexOf(selectedValue) >= 0)
|
|
110
|
+
.map((tag, index) => (
|
|
111
|
+
<div data-key={`${tag.value}-${index}`} className="cursor" onClick={onSelectValue}>
|
|
112
|
+
{tag.label}
|
|
113
|
+
</div>
|
|
114
|
+
))}
|
|
115
|
+
</div>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return renderValidationsSuggestions()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<PopupMenu onToggleCallback={(isOpen) => togglePopup(isOpen)} autoClose>
|
|
124
|
+
<PopupMenu.Button
|
|
125
|
+
rootClassName={`${
|
|
126
|
+
type === 'key'
|
|
127
|
+
? `dc__no-right-radius`
|
|
128
|
+
: `dc__no-border-radius dc__no-right-border dc__no-left-border`
|
|
129
|
+
} ${tagData[type === 'key' ? 'isInvalidKey' : 'isInvalidValue'] ? 'er-5 bw-1' : ''}`}
|
|
130
|
+
>
|
|
131
|
+
<input
|
|
132
|
+
type="text"
|
|
133
|
+
className="form__input pt-4-imp pb-4-imp dc__no-border"
|
|
134
|
+
value={selectedValue}
|
|
135
|
+
onChange={handleInputChange}
|
|
136
|
+
onBlur={handleOnBlur}
|
|
137
|
+
/>
|
|
138
|
+
</PopupMenu.Button>
|
|
139
|
+
<PopupMenu.Body rootClassName={`tag-${selectedTagIndex}-class`} autoWidth={true}>
|
|
140
|
+
{isPopupOpen && renderSuggestions()}
|
|
141
|
+
</PopupMenu.Body>
|
|
142
|
+
</PopupMenu>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const baseSelectStyles = {
|
|
2
|
+
control: (base, state) => ({
|
|
3
|
+
...base,
|
|
4
|
+
border: '1px solid var(--N200)',
|
|
5
|
+
boxShadow: 'none',
|
|
6
|
+
minHeight: 'auto',
|
|
7
|
+
borderRadius: 'none',
|
|
8
|
+
height: '32px',
|
|
9
|
+
fontSize: '12px',
|
|
10
|
+
borderRight: '0',
|
|
11
|
+
cursor: state.isDisabled ? 'not-allowed' : 'normal',
|
|
12
|
+
}),
|
|
13
|
+
singleValue: (base, state) => ({
|
|
14
|
+
...base,
|
|
15
|
+
fontWeight: '500',
|
|
16
|
+
}),
|
|
17
|
+
placeholder: (base, state) => ({
|
|
18
|
+
...base,
|
|
19
|
+
fontWeight: '500',
|
|
20
|
+
}),
|
|
21
|
+
option: (base, state) => ({
|
|
22
|
+
...base,
|
|
23
|
+
fontWeight: '500',
|
|
24
|
+
color: 'var(--N900)',
|
|
25
|
+
fontSize: '12px',
|
|
26
|
+
padding: '5px 10px',
|
|
27
|
+
}),
|
|
28
|
+
dropdownIndicator: (styles) => ({ ...styles, padding: 0 }),
|
|
29
|
+
valueContainer: (base, state) => ({
|
|
30
|
+
...base,
|
|
31
|
+
color: 'var(--N900)',
|
|
32
|
+
background: state.isDisabled ? 'var(--N100) !important' : 'var(--N50) !important',
|
|
33
|
+
padding: '0px 10px',
|
|
34
|
+
display: 'flex',
|
|
35
|
+
height: '30px',
|
|
36
|
+
fontSize: '12px',
|
|
37
|
+
cursor: state.isDisabled ? 'not-allowed' : 'normal',
|
|
38
|
+
pointerEvents: 'all',
|
|
39
|
+
width: '100px',
|
|
40
|
+
whiteSpace: 'nowrap',
|
|
41
|
+
}),
|
|
42
|
+
indicatorsContainer: (base, state) => ({
|
|
43
|
+
...base,
|
|
44
|
+
background: state.isDisabled ? 'var(--N100) !important' : 'var(--N50) !important',
|
|
45
|
+
}),
|
|
46
|
+
menu: (base, state) => ({
|
|
47
|
+
...base,
|
|
48
|
+
marginTop: '0',
|
|
49
|
+
}),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const validateKubernetesKey = (key: string) => {
|
|
53
|
+
const invalidMessageList = []
|
|
54
|
+
if (!key || key.split('/')) {
|
|
55
|
+
invalidMessageList.push('Name is required')
|
|
56
|
+
} else {
|
|
57
|
+
const re = new RegExp('/', 'g')
|
|
58
|
+
const noOfSlashInKey = key.match(re).length
|
|
59
|
+
if (noOfSlashInKey > 1) {
|
|
60
|
+
invalidMessageList.push('Key: Max 1 ( / ) allowed')
|
|
61
|
+
} else {
|
|
62
|
+
let name = key
|
|
63
|
+
if (noOfSlashInKey === 1) {
|
|
64
|
+
const [prefix, name]=key.split('/')
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const validateKubernetesValue = (value: string) => {}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { PATTERNS } from '../Constants'
|
|
2
|
+
|
|
3
|
+
const validateTagValue = (value: string): string[] => {
|
|
4
|
+
const errorList = []
|
|
5
|
+
if (value.length > 63) {
|
|
6
|
+
errorList.push('Can be max 63 characters')
|
|
7
|
+
}
|
|
8
|
+
let firstLastAlphanumeric = PATTERNS.START_END_ALPHANUMERIC.test(value)
|
|
9
|
+
if (!firstLastAlphanumeric) {
|
|
10
|
+
errorList.push('Must start and end with an alphanumeric character')
|
|
11
|
+
}
|
|
12
|
+
let validValue = PATTERNS.ALPHANUMERIC_WITH_SPECIAL_CHAR.test(value)
|
|
13
|
+
if (!validValue) {
|
|
14
|
+
errorList.push('Can only contain alphanumeric chars and (-), (_), (.)')
|
|
15
|
+
}
|
|
16
|
+
return errorList
|
|
17
|
+
}
|
|
18
|
+
export class ValidationRules {
|
|
19
|
+
|
|
20
|
+
propagateTagKey = (key: string): { isValid: boolean; messages: string[] } => {
|
|
21
|
+
const errorList = []
|
|
22
|
+
if (!key) {
|
|
23
|
+
errorList.push('Key is required')
|
|
24
|
+
} else {
|
|
25
|
+
const re = new RegExp('/', 'g')
|
|
26
|
+
const noOfSlashInKey = key.match(re)?.length
|
|
27
|
+
if (noOfSlashInKey > 1) {
|
|
28
|
+
errorList.push('Key: Max 1 ( / ) allowed')
|
|
29
|
+
} else if (noOfSlashInKey === 1) {
|
|
30
|
+
const [prefix, name] = key.split('/')
|
|
31
|
+
errorList.push(...validateTagValue(name).map((error) => `Name: ${error}`))
|
|
32
|
+
if (prefix.length > 253) {
|
|
33
|
+
errorList.push('Prefix: Can be max 253 characters')
|
|
34
|
+
}
|
|
35
|
+
let validPrefix = PATTERNS.KUBERNETES_KEY_PREFIX.test(prefix)
|
|
36
|
+
if (!validPrefix) {
|
|
37
|
+
errorList.push('Prefix: Must be a DNS subdomain (a series of DNS labels separated by dots (.)')
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
errorList.push(...validateTagValue(key).map((error) => `Name: ${error}`))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { isValid: errorList.length === 0, messages: errorList }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
propagateTagValue = (value: string): { isValid: boolean; messages: string[] } => {
|
|
47
|
+
const errorList = []
|
|
48
|
+
if (!value) {
|
|
49
|
+
errorList.push('Value is required')
|
|
50
|
+
} else {
|
|
51
|
+
errorList.push(...validateTagValue(value))
|
|
52
|
+
}
|
|
53
|
+
return { isValid: errorList.length === 0, messages: errorList }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { VisibleModal2 } from '../Modals/VisibleModal2'
|
|
3
|
+
function ConfirmationDialog({ className = '', children }) {
|
|
4
|
+
return (
|
|
5
|
+
<VisibleModal2 className="confirmation-dialog">
|
|
6
|
+
<div className={`confirmation-dialog__body ${className}`}>{children}</div>
|
|
7
|
+
</VisibleModal2>
|
|
8
|
+
)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function Icon({ src, className = '' }) {
|
|
12
|
+
return <img src={src} className={`confirmation-dialog__icon ${className}`} alt="" />
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Body({ title, subtitle = null, children = null }) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="flex left column ">
|
|
18
|
+
<h3 className="confirmation-dialog__title lh-1-25 dc__break-word w-100">{title}</h3>
|
|
19
|
+
{subtitle && <div className="confirmation-dialog__subtitle">{subtitle}</div>}
|
|
20
|
+
{children}
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ButtonGroup({ children }) {
|
|
26
|
+
return <div className="flex right confirmation-dialog__button-group">{children}</div>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
ConfirmationDialog.Icon = Icon
|
|
30
|
+
ConfirmationDialog.Body = Body
|
|
31
|
+
ConfirmationDialog.ButtonGroup = ButtonGroup
|
|
32
|
+
export default ConfirmationDialog
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import warn from '../../../Assets/Img/warning-medium.svg';
|
|
3
|
+
import { Progressing } from '../Progressing'
|
|
4
|
+
import ConfirmationDialog from './ConfirmationDialog';
|
|
5
|
+
import { DeleteDialogProps } from './Types';
|
|
6
|
+
|
|
7
|
+
export const DeleteDialog: React.FC<DeleteDialogProps> & { Description?: React.FC<any> } = function (props) {
|
|
8
|
+
|
|
9
|
+
return <ConfirmationDialog className="confirmation-dialog__body--w-400">
|
|
10
|
+
<ConfirmationDialog.Icon src={warn} />
|
|
11
|
+
<ConfirmationDialog.Body title={props.title}>
|
|
12
|
+
<div className="fs-13 cn-7 lh-1-54 w-100">
|
|
13
|
+
{props.description ? props.description : null}
|
|
14
|
+
{props.children}
|
|
15
|
+
</div>
|
|
16
|
+
</ConfirmationDialog.Body>
|
|
17
|
+
<ConfirmationDialog.ButtonGroup>
|
|
18
|
+
<div className="flex right">
|
|
19
|
+
<button type="button" className="cta cancel cta-cd-delete-modal ml-16" onClick={props.closeDelete} disabled={props.apiCallInProgress}>Cancel</button>
|
|
20
|
+
<button type="button" className="cta delete cta-cd-delete-modal ml-16" onClick={() => props.delete()} disabled={props.apiCallInProgress}>
|
|
21
|
+
{props.apiCallInProgress ? <Progressing /> : `${props.deletePrefix || ''}Delete${props.deletePostfix || ''}`}
|
|
22
|
+
</button>
|
|
23
|
+
</div>
|
|
24
|
+
</ConfirmationDialog.ButtonGroup>
|
|
25
|
+
</ConfirmationDialog >
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function DeleteDialogDescription(props) {
|
|
29
|
+
|
|
30
|
+
return <>
|
|
31
|
+
{props.children}
|
|
32
|
+
</>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
DeleteDialog.Description = DeleteDialogDescription;
|