Skip to content

Commit 82fe1d4

Browse files
committed
Merge branch 'main' of github.com:devtron-labs/devtron-fe-common-lib into feat/release-track-apps
2 parents e595a14 + 9a3e6d3 commit 82fe1d4

32 files changed

+5021
-10859
lines changed

package-lock.json

Lines changed: 3688 additions & 10772 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devtron-labs/devtron-fe-common-lib",
3-
"version": "0.0.97",
3+
"version": "0.1.3",
44
"description": "Supporting common component library",
55
"type": "module",
66
"main": "dist/index.js",
@@ -60,7 +60,6 @@
6060
"moment": "^2.29.4",
6161
"monaco-editor": "0.44.0",
6262
"monaco-yaml": "5.1.1",
63-
"react-monaco-editor": "^0.55.0",
6463
"prettier": "^3.1.1",
6564
"react-ga4": "^1.4.1",
6665
"react-mde": "^11.5.0",
@@ -92,9 +91,19 @@
9291
"dependencies": {
9392
"ansi_up": "^5.2.1",
9493
"fast-json-patch": "^3.1.1",
95-
"jsonpath-plus": "^7.2.0",
9694
"react-monaco-editor": "^0.54.0",
95+
"@types/react-dates": "^21.8.6",
96+
"react-dates": "^21.8.0",
9797
"sass": "^1.69.7",
9898
"tslib": "^2.4.1"
99+
},
100+
"overrides": {
101+
"react-dates": {
102+
"react": "^17.0.2",
103+
"react-dom": "^17.0.2"
104+
},
105+
"react-monaco-editor": {
106+
"monaco-editor": "0.44.0"
107+
}
99108
}
100-
}
109+
}

src/Assets/Icon/ic-calender-blank.svg

Lines changed: 19 additions & 0 deletions
Loading

src/Assets/Icon/ic-clock.svg

Lines changed: 19 additions & 0 deletions
Loading
Lines changed: 20 additions & 0 deletions
Loading

src/Common/BreadCrumb/BreadcrumbStore.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,16 @@
1515
*/
1616

1717
import React, { createContext, useState } from 'react'
18+
import { BreadcrumbTextProps } from './Types'
1819

1920
const initialState = {
2021
alias: {},
2122
}
2223

24+
export const BreadcrumbText = ({ heading, isActive }: BreadcrumbTextProps) => (
25+
<h2 className={`m-0 fs-16 fw-6 lh-32 ${isActive ? 'cn-9' : 'cb-5'}`}>{heading}</h2>
26+
)
27+
2328
const Store = ({ children }) => {
2429
const [state, setState] = useState(initialState)
2530
return <BreadcrumbContext.Provider value={{ state, setState }}>{children}</BreadcrumbContext.Provider>

src/Common/BreadCrumb/Types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,8 @@ export interface Breadcrumbs {
4343
}
4444

4545
export type UseBreadcrumbOptionalProps = UseBreadcrumbProps | null
46+
47+
export interface BreadcrumbTextProps {
48+
heading: string
49+
isActive?: boolean
50+
}

src/Common/Constants.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -421,10 +421,6 @@ export enum FREQUENCY_ENUM {
421421
WEEKLY_RANGE = 'WEEKLY_RANGE',
422422
}
423423

424-
export const TIME_FORMAT = {
425-
DD_MMM_YYYY_HH_MM: 'DD MMM YYYY, hh:mm',
426-
}
427-
428424
export function getOrdinal(number) {
429425
if (number % 100 >= 11 && number % 100 <= 13) {
430426
return `${number}th`
@@ -512,4 +508,6 @@ export const DATE_TIME_FORMATS = {
512508
TWELVE_HOURS_FORMAT: 'ddd, DD MMM YYYY, hh:mm A',
513509
TWELVE_HOURS_FORMAT_WITHOUT_WEEKDAY: 'DD MMM YYYY, hh:mm A',
514510
TWELVE_HOURS_EXPORT_FORMAT: 'DD-MMM-YYYY hh.mm A',
511+
DD_MMM_YYYY_HH_MM: 'DD MMM YYYY, hh:mm',
512+
DD_MMM_YYYY: 'DD MMM YYYY',
515513
}

src/Common/CustomInput/CustomInput.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import { CustomInputProps } from './Types'
18-
import { ReactComponent as Info } from '../../Assets/Icon/ic-info-filled.svg'
18+
import { ReactComponent as Info } from '../../Assets/Icon/ic-info-filled-override.svg'
1919
import { ReactComponent as ErrorIcon } from '../../Assets/Icon/ic-warning.svg'
2020

2121
export const CustomInput = ({
@@ -135,9 +135,9 @@ export const CustomInput = ({
135135

136136
{getInputError()}
137137
{helperText && (
138-
<div className="form__text-field-info">
139-
<Info className="form__icon form__icon--info" />
140-
<p className="sentence-case">{helperText}</p>
138+
<div className="flex left top dc__gap-4 fs-11 lh-16 cn-7">
139+
<Info className="icon-dim-16" />
140+
<div>{helperText}</div>
141141
</div>
142142
)}
143143
</div>

src/Common/Helper.tsx

Lines changed: 98 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
import React, { SyntheticEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
1818
import { components } from 'react-select'
1919
import * as Sentry from '@sentry/browser'
20-
import * as jsonpatch from 'fast-json-patch'
21-
import { JSONPath } from 'jsonpath-plus'
2220
import moment from 'moment'
2321
import { useLocation } from 'react-router-dom'
2422
import { toast } from 'react-toastify'
@@ -622,79 +620,110 @@ export const getFilteredChartVersions = (charts, selectedChartType) =>
622620
chartRefId: item.chartRefId,
623621
}))
624622

625-
function removeEmptyObjectKeysAndNullValues(obj, originaljsonCopy) {
626-
// It recursively removes empty object keys and empty array keys
627-
for (const key in obj) {
628-
if (Array.isArray(obj[key])) {
629-
// Check if the array is empty
630-
if (obj[key].length !== 0) {
631-
obj[key].forEach((element, index) => {
632-
if (element === null || (typeof element === 'object' && Object.keys(element).length === 0))
633-
obj[key].splice(index, 1)
634-
else removeEmptyObjectKeysAndNullValues(element, originaljsonCopy[key][index])
635-
})
636-
}
637-
if (obj[key].length === 0 && originaljsonCopy[key].length !== 0) {
638-
delete obj[key]
639-
}
640-
} else if (obj[key] && typeof obj[key] === 'object') {
641-
if (
642-
removeEmptyObjectKeysAndNullValues(obj[key], originaljsonCopy[key]) &&
643-
Object.keys(originaljsonCopy[key]).length !== 0
644-
) {
645-
delete obj[key]
646-
}
647-
} else if (obj[key] === undefined) {
648-
delete obj[key]
649-
}
623+
/**
624+
* Removes nulls from the arrays in the provided object
625+
* @param {object} object from which we need to delete nulls in its arrays
626+
* @returns object after removing (in-place) the null items in arrays
627+
*/
628+
export const recursivelyRemoveNullsFromArraysInObject = (object: object) => {
629+
// NOTE: typeof null === 'object'
630+
if (typeof object !== 'object' || !object) {
631+
return object
632+
}
633+
if (Array.isArray(object)) {
634+
return object.filter((item) => {
635+
recursivelyRemoveNullsFromArraysInObject(item)
636+
return !!item
637+
})
650638
}
651-
return Object.keys(obj).length === 0
639+
Object.keys(object).forEach((key) => {
640+
object[key] = recursivelyRemoveNullsFromArraysInObject(object[key])
641+
})
642+
return object
652643
}
653644

654-
export function getUnlockedJSON(json, jsonPathArray, removeParentKeysAndEmptyArrays = false) {
655-
if (!jsonPathArray.length) return { newDocument: json, removedPatches: [] }
645+
const _joinObjects = (A: object, B: object) => {
646+
if (typeof A !== 'object' || typeof B !== 'object' || !B) {
647+
return
648+
}
649+
Object.keys(B).forEach((key) => {
650+
if (!A[key]) {
651+
A[key] = structuredClone(B[key])
652+
return
653+
}
654+
_joinObjects(A[key], B[key])
655+
})
656+
}
656657

657-
const jsonCopy = JSON.parse(JSON.stringify(json))
658-
const originaljsonCopy = JSON.parse(JSON.stringify(json))
659-
const removedPatches = []
660-
const patches = jsonPathArray.flatMap((jsonPath) => {
661-
const pathsToRemove = JSONPath({ path: jsonPath, json: jsonCopy, resultType: 'all' })
662-
// reversing patches to handle correct array index deletion
663-
pathsToRemove.reverse()
664-
return pathsToRemove.map((result) => {
665-
// storing removed patches to have functionality of undo
666-
removedPatches.push({ op: 'add', path: result.pointer, value: result.value })
667-
return { op: 'remove', path: result.pointer }
668-
})
658+
/**
659+
* Merges the objects into one object
660+
* @param {object[]} objects list of js objects
661+
* @returns object after the merge
662+
*/
663+
export const joinObjects = (objects: object[]) => {
664+
const join = {}
665+
objects.forEach((object) => {
666+
_joinObjects(join, object)
669667
})
670-
const { newDocument } = jsonpatch.applyPatch(jsonCopy, patches)
671-
if (removeParentKeysAndEmptyArrays) removeEmptyObjectKeysAndNullValues(newDocument, originaljsonCopy)
672-
return { newDocument, removedPatches: removedPatches.reverse() }
673-
}
674-
675-
export function getLockedJSON(json, jsonPathArray: string[]) {
676-
const jsonCopy = JSON.parse(JSON.stringify(json))
677-
const resultJson = {}
678-
jsonPathArray.forEach((jsonPath) => {
679-
const elements = JSONPath({ path: jsonPath, json: jsonCopy, resultType: 'all' })
680-
elements.forEach((element) => {
681-
const pathArray: string[] = JSONPath.toPathArray(element.path)
682-
const lastPath = pathArray.pop()
683-
let current = resultJson
684-
for (let i = 0; i < pathArray.length; i++) {
685-
const key = isNaN(Number(pathArray[i])) ? pathArray[i] : parseInt(pathArray[i])
686-
if (!current[key]) {
687-
current[key] = isNaN(Number(pathArray[i + 1] ?? lastPath)) ? {} : []
688-
}
689-
current = current[key]
690-
}
691-
const key = isNaN(Number(lastPath)) ? lastPath : parseInt(lastPath)
692-
current[key] = element.value
668+
return join
669+
}
670+
671+
const buildObjectFromPathTokens = (index: number, tokens: string[], value: any) => {
672+
if (index === tokens.length) {
673+
return value
674+
}
675+
const key = tokens[index]
676+
const numberKey = Number(key)
677+
const isKeyNumber = !Number.isNaN(numberKey)
678+
return isKeyNumber
679+
? [...Array(numberKey).fill(null), buildObjectFromPathTokens(index + 1, tokens, value)]
680+
: { [key]: buildObjectFromPathTokens(index + 1, tokens, value) }
681+
}
682+
683+
/**
684+
* Builds an object from the provided path
685+
* @param {string} path JSON pointer path of the form /path/to/1/...
686+
* @param {any} value property value
687+
* @returns final object formed from the path
688+
*/
689+
export const buildObjectFromPath = (path: string, value: any) => {
690+
const parts = path.split('/')
691+
// NOTE: since the path has a leading / we need to delete the first element
692+
parts.splice(0, 1)
693+
return buildObjectFromPathTokens(0, parts, value)
694+
}
695+
696+
/**
697+
* Returns the list of indices at which the regex matched
698+
* @param string the string to process
699+
* @param regex @RegExp
700+
* @returns array of indices where the @regex matched in @string
701+
*/
702+
export const getRegexMatchPositions = (string: string, regex: RegExp): number[] => {
703+
const pos = string.search(regex)
704+
if (pos < 0) {
705+
return []
706+
}
707+
const nextToken = string.slice(pos + 1)
708+
return [pos, ...getRegexMatchPositions(nextToken, regex).map((n) => n + pos + 1)]
709+
}
710+
711+
/**
712+
* Returns all the substrings from 0th index to progressively next regex match position
713+
* Eg. Suppose a string is /path/to/folder then it will return [/path, /path/to, /path/to/folder]
714+
* @param strings list of strings
715+
* @param regex that matches the separators for the substrings
716+
* @returns set of substrings
717+
*/
718+
export const powerSetOfSubstringsFromStart = (strings: string[], regex: RegExp) =>
719+
strings.flatMap((key) => {
720+
const _keys = [key]
721+
const positions = getRegexMatchPositions(key, regex)
722+
positions.forEach((position) => {
723+
_keys.push(key.slice(0, position))
693724
})
725+
return _keys
694726
})
695-
// eslint-disable-next-line dot-notation
696-
return resultJson['$']
697-
}
698727

699728
/**
700729
* Returns a debounced variant of the function
@@ -982,7 +1011,7 @@ export function useKeyDown() {
9821011
export const DropdownIndicator = (props) => {
9831012
return (
9841013
<components.DropdownIndicator {...props}>
985-
<ArrowDown className="icon-dim-20 icon-n5" />
1014+
<ArrowDown className="icon-dim-20 icon-n6" />
9861015
</components.DropdownIndicator>
9871016
)
9881017
}

0 commit comments

Comments
 (0)