Skip to content

feat: add taint documentation link to constants #746

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@devtron-labs/devtron-fe-common-lib",
"version": "1.14.1-pre-2",
"version": "1.14.1-pre-3",
"description": "Supporting common component library",
"type": "module",
"main": "dist/index.js",
Expand Down
3 changes: 3 additions & 0 deletions src/Assets/IconV2/ic-spray-can.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 3 additions & 18 deletions src/Shared/Components/DynamicDataTable/DynamicDataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { useMemo, useState } from 'react'
import { useMemo } from 'react'

import { DynamicDataTableHeader } from './DynamicDataTableHeader'
import { DynamicDataTableRow } from './DynamicDataTableRow'
Expand All @@ -24,30 +24,15 @@ import './styles.scss'

export const DynamicDataTable = <K extends string, CustomStateType = Record<string, unknown>>({
headers,
onRowAdd,
...props
}: DynamicDataTableProps<K, CustomStateType>) => {
// STATES
const [isAddRowButtonClicked, setIsAddRowButtonClicked] = useState(false)

// CONSTANTS
const filteredHeaders = useMemo(() => headers.filter(({ isHidden }) => !isHidden), [headers])

// HANDLERS
const handleRowAdd = () => {
setIsAddRowButtonClicked(true)
onRowAdd()
}

return (
<div className="w-100">
<DynamicDataTableHeader headers={filteredHeaders} onRowAdd={handleRowAdd} {...props} />
<DynamicDataTableRow
headers={filteredHeaders}
isAddRowButtonClicked={isAddRowButtonClicked}
setIsAddRowButtonClicked={setIsAddRowButtonClicked}
{...props}
/>
<DynamicDataTableHeader headers={filteredHeaders} {...props} />
<DynamicDataTableRow headers={filteredHeaders} {...props} />
</div>
)
}
76 changes: 32 additions & 44 deletions src/Shared/Components/DynamicDataTable/DynamicDataTableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,18 @@
* limitations under the License.
*/

import {
createElement,
createRef,
Fragment,
ReactElement,
RefObject,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import { createElement, createRef, Fragment, ReactElement, RefObject, useEffect, useMemo, useRef } from 'react'
// eslint-disable-next-line import/no-extraneous-dependencies
import { followCursor } from 'tippy.js'

import { ReactComponent as ICClose } from '@Icons/ic-close.svg'
import { ResizableTagTextArea } from '@Common/CustomTagSelector'
import { ConditionalWrap } from '@Common/Helper'
import { Tooltip } from '@Common/Tooltip'
import { ComponentSizeType } from '@Shared/constants'

import { Button, ButtonStyleType, ButtonVariantType } from '../Button'
import { FileUpload } from '../FileUpload'
import { Icon } from '../Icon'
import {
getSelectPickerOptionByValue,
SelectPicker,
Expand Down Expand Up @@ -73,8 +63,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
trailingCellIcon,
buttonCellWrapComponent,
focusableFieldKey,
isAddRowButtonClicked,
setIsAddRowButtonClicked,
shouldAutoFocusOnMount = false,
}: DynamicDataTableRowProps<K, CustomStateType>) => {
// CONSTANTS
const isFirstRowEmpty = headers.every(({ key }) => !rows[0]?.data[key].value)
Expand All @@ -88,25 +77,18 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
isDeletionNotAllowed || readOnly,
)

// STATES
const [isRowAdded, setIsRowAdded] = useState(false)

// CELL REFS
const cellRef = useRef<Record<string | number, Record<K, RefObject<HTMLTextAreaElement>>>>()
const shouldAutoFocusNewRowRef = useRef(shouldAutoFocusOnMount)
const cellRef = useRef<Record<string | number, Record<K, RefObject<HTMLTextAreaElement>>>>(null)
if (!cellRef.current) {
cellRef.current = rows.reduce(
(acc, curr) => ({
...acc,
[curr.id]: headers.reduce((headerAcc, { key }) => ({ ...headerAcc, [key]: createRef() }), {}),
}),
{},
)
cellRef.current = rows.reduce((acc, curr) => {
acc[curr.id] = headers.reduce((headerAcc, { key }) => ({ ...headerAcc, [key]: createRef() }), {})
return acc
}, {})
}
const rowIds = useMemo(() => rows.map(({ id }) => id), [rows])

useEffect(() => {
setIsRowAdded(rows.length > 0 && Object.keys(cellRef.current).length < rows.length)

// When a new row is added, we create references for its cells.
// This logic ensures that references are created only for the newly added row, while retaining the existing references.
const updatedCellRef = rowIds.reduce((acc, curr) => {
Expand All @@ -121,18 +103,6 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
cellRef.current = updatedCellRef
}, [JSON.stringify(rowIds)])

useEffect(() => {
if (isAddRowButtonClicked && isRowAdded) {
// Using the below logic to ensure the cell is focused after row addition.
const cell = cellRef.current[rows[0].id][focusableFieldKey || headers[0].key].current
if (cell) {
cell.focus()
setIsRowAdded(false)
setIsAddRowButtonClicked(false)
}
}
}, [isRowAdded, isAddRowButtonClicked])

// METHODS
const onChange =
(row: DynamicDataTableRowType<K, CustomStateType>, key: K) =>
Expand Down Expand Up @@ -163,14 +133,30 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
}

// RENDERERS
const renderCellContent = (row: DynamicDataTableRowType<K, CustomStateType>, key: K) => {
const renderCellContent = (row: DynamicDataTableRowType<K, CustomStateType>, key: K, index: number) => {
const isDisabled = readOnly || row.data[key].disabled
const autoFocus =
shouldAutoFocusNewRowRef.current && key === (focusableFieldKey ?? headers[0].key) && index === 0

// This logic ensures only newly added rows get autofocus.
// On the initial mount, all rows are treated as new, so autofocus is enabled.
// After the first render, when cellRef is set (DOM rendered), we set shouldAutoFocusNewRowRef to true,
// so autofocus is applied only to the correct cell in any subsequently added row.
if (
!shouldAutoFocusOnMount &&
!shouldAutoFocusNewRowRef.current &&
index === 0 &&
cellRef?.current?.[row.id]?.[key].current
) {
shouldAutoFocusNewRowRef.current = true
}

switch (row.data[key].type) {
case DynamicDataTableRowDataType.DROPDOWN:
return (
<div className="w-100 h-100 flex top dc__align-self-start">
<SelectPicker<string, false>
autoFocus={autoFocus}
{...row.data[key].props}
inputId={`data-table-${row.id}-${key}-cell`}
classNamePrefix="dynamic-data-table__cell__select-picker"
Expand All @@ -193,6 +179,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
return (
<div className="w-100 h-100 flex top dc__align-self-start">
<SelectPickerTextArea
autoFocus={autoFocus}
isCreatable={isCreatable}
isClearable
{...props}
Expand Down Expand Up @@ -248,6 +235,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
default:
return (
<ResizableTagTextArea
autoFocus={autoFocus}
{...row.data[key].props}
id={`data-table-${row.id}-${key}-cell`}
className={`dynamic-data-table__cell-input placeholder-cn5 p-8 cn-9 fs-13 lh-20 dc__align-self-start dc__no-border-radius ${isDisabled ? 'cursor-not-allowed' : ''}`}
Expand Down Expand Up @@ -283,8 +271,8 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
}

const renderErrorMessage = (errorMessage: string) => (
<div key={errorMessage} className="flexbox align-items-center dc__gap-4">
<ICClose className="icon-dim-16 fcr-5 dc__align-self-start dc__no-shrink" />
<div key={errorMessage} className="flexbox dc__gap-4">
<Icon name="ic-close-small" color="R500" />
<p className="fs-12 lh-16 cn-7 m-0">{errorMessage}</p>
</div>
)
Expand Down Expand Up @@ -329,7 +317,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
className={`dynamic-data-table__cell bg__primary flexbox dc__align-items-center dc__gap-4 dc__position-rel ${isDisabled ? 'cursor-not-allowed no-hover' : ''} ${!isDisabled && hasError ? 'dynamic-data-table__cell--error no-hover' : ''} ${!rowTypeHasInputField(row.data[key].type) ? 'no-hover no-focus' : ''}`}
>
{renderCellIcon(row, key, true)}
{renderCellContent(row, key)}
{renderCellContent(row, key, index)}
{renderAsterisk(row, key)}
{renderCellIcon(row, key)}
{renderErrorMessages(row, key)}
Expand Down Expand Up @@ -383,7 +371,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
dataTestId="dynamic-data-table-row-delete-btn"
ariaLabel="Delete Row"
showAriaLabelInTippy={false}
icon={<ICClose />}
icon={<Icon name="ic-close-large" color={null} />}
disabled={disableDeleteRow || row.disableDelete}
onClick={onDelete(row)}
variant={ButtonVariantType.borderLess}
Expand Down
20 changes: 10 additions & 10 deletions src/Shared/Components/DynamicDataTable/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,21 @@
height: 36px;
width: 100%;
background: inherit;

&--add {
resize: none;
border-radius: 4px;
outline: none;
}
}

&__cell {
min-width: 0;

&__select-picker__control {
gap: 6px !important;
padding: 8px !important;
max-height: 160px !important;
&__select-picker {
&__control {
gap: 6px !important;
padding: 8px !important;
max-height: 160px !important;
}

&__single-value {
font-weight: 400 !important;
}
}

&__select-picker-text-area {
Expand Down
14 changes: 9 additions & 5 deletions src/Shared/Components/DynamicDataTable/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { DetailedHTMLProps, Dispatch, ReactElement, ReactNode, SetStateAction } from 'react'
import { DetailedHTMLProps, ReactElement, ReactNode } from 'react'

import { ResizableTagTextAreaProps } from '@Common/CustomTagSelector'
import { UseStateFiltersReturnType } from '@Common/Hooks'
Expand Down Expand Up @@ -228,6 +228,12 @@ export type DynamicDataTableProps<K extends string, CustomStateType = Record<str
* @default 'first column key'
*/
focusableFieldKey?: K
/**
* When true, the table will automatically focus the first focusable cell
* or cell key denoted by `focusableFieldKey` when the component mounts.
* @default false
*/
shouldAutoFocusOnMount?: boolean
}

export interface DynamicDataTableHeaderProps<K extends string, CustomStateType = Record<string, unknown>>
Expand Down Expand Up @@ -261,7 +267,5 @@ export interface DynamicDataTableRowProps<K extends string, CustomStateType = Re
| 'trailingCellIcon'
| 'buttonCellWrapComponent'
| 'focusableFieldKey'
> {
isAddRowButtonClicked: boolean
setIsAddRowButtonClicked: Dispatch<SetStateAction<boolean>>
}
| 'shouldAutoFocusOnMount'
> {}
2 changes: 2 additions & 0 deletions src/Shared/Components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ import { ReactComponent as ICSortDescending } from '@IconsV2/ic-sort-descending.
import { ReactComponent as ICSortable } from '@IconsV2/ic-sortable.svg'
import { ReactComponent as ICSparkleColor } from '@IconsV2/ic-sparkle-color.svg'
import { ReactComponent as ICSpinny } from '@IconsV2/ic-spinny.svg'
import { ReactComponent as ICSprayCan } from '@IconsV2/ic-spray-can.svg'
import { ReactComponent as ICStack } from '@IconsV2/ic-stack.svg'
import { ReactComponent as ICStamp } from '@IconsV2/ic-stamp.svg'
import { ReactComponent as ICSuccess } from '@IconsV2/ic-success.svg'
Expand Down Expand Up @@ -298,6 +299,7 @@ export const iconMap = {
'ic-sortable': ICSortable,
'ic-sparkle-color': ICSparkleColor,
'ic-spinny': ICSpinny,
'ic-spray-can': ICSprayCan,
'ic-stack': ICStack,
'ic-stamp': ICStamp,
'ic-success': ICSuccess,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const KeyValueTable = ({
headerComponent,
onChange,
isAdditionNotAllowed,
shouldAutoFocusOnMount,
readOnly,
showError,
validationSchema: parentValidationSchema,
Expand Down Expand Up @@ -180,6 +181,7 @@ export const KeyValueTable = ({
headerComponent={headerComponent}
readOnly={readOnly}
isAdditionNotAllowed={isAdditionNotAllowed}
shouldAutoFocusOnMount={shouldAutoFocusOnMount}
sortingConfig={{
sortBy,
sortOrder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export interface KeyValueTableData extends Pick<KeyValueTableRowType, 'id'> {
*/
export type KeyValueTableProps = Pick<
DynamicDataTableProps<KeyValueTableDataType>,
'isAdditionNotAllowed' | 'readOnly' | 'headerComponent'
'isAdditionNotAllowed' | 'readOnly' | 'headerComponent' | 'shouldAutoFocusOnMount'
> & {
/**
* The label for the table header.
Expand Down
1 change: 1 addition & 0 deletions src/Shared/DocLink/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const DOCUMENTATION = {
EXTERNAL_SECRET: 'usage/applications/creating-application/secrets#external-secrets',
HOME_PAGE: 'https://devtron.ai',
KUBE_CONFIG: 'usage/resource-browser#running-kubectl-commands-locally',
TAINT: 'usage/resource-browser#taint-a-node',

// Global Configurations
GLOBAL_CONFIG_API_TOKEN: 'getting-started/global-configurations/authorization/api-tokens',
Expand Down