Skip to content

Commit d6268d7

Browse files
authored
Merge pull request #746 from devtron-labs/feat/revamp-edit-taints-modal
feat: add taint documentation link to constants
2 parents 07f5fb8 + 803de19 commit d6268d7

File tree

11 files changed

+66
-81
lines changed

11 files changed

+66
-81
lines changed

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devtron-labs/devtron-fe-common-lib",
3-
"version": "1.14.1-pre-2",
3+
"version": "1.14.1-pre-3",
44
"description": "Supporting common component library",
55
"type": "module",
66
"main": "dist/index.js",

src/Assets/IconV2/ic-spray-can.svg

Lines changed: 3 additions & 0 deletions
Loading

src/Shared/Components/DynamicDataTable/DynamicDataTable.tsx

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { useMemo, useState } from 'react'
17+
import { useMemo } from 'react'
1818

1919
import { DynamicDataTableHeader } from './DynamicDataTableHeader'
2020
import { DynamicDataTableRow } from './DynamicDataTableRow'
@@ -24,30 +24,15 @@ import './styles.scss'
2424

2525
export const DynamicDataTable = <K extends string, CustomStateType = Record<string, unknown>>({
2626
headers,
27-
onRowAdd,
2827
...props
2928
}: DynamicDataTableProps<K, CustomStateType>) => {
30-
// STATES
31-
const [isAddRowButtonClicked, setIsAddRowButtonClicked] = useState(false)
32-
3329
// CONSTANTS
3430
const filteredHeaders = useMemo(() => headers.filter(({ isHidden }) => !isHidden), [headers])
3531

36-
// HANDLERS
37-
const handleRowAdd = () => {
38-
setIsAddRowButtonClicked(true)
39-
onRowAdd()
40-
}
41-
4232
return (
4333
<div className="w-100">
44-
<DynamicDataTableHeader headers={filteredHeaders} onRowAdd={handleRowAdd} {...props} />
45-
<DynamicDataTableRow
46-
headers={filteredHeaders}
47-
isAddRowButtonClicked={isAddRowButtonClicked}
48-
setIsAddRowButtonClicked={setIsAddRowButtonClicked}
49-
{...props}
50-
/>
34+
<DynamicDataTableHeader headers={filteredHeaders} {...props} />
35+
<DynamicDataTableRow headers={filteredHeaders} {...props} />
5136
</div>
5237
)
5338
}

src/Shared/Components/DynamicDataTable/DynamicDataTableRow.tsx

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,18 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {
18-
createElement,
19-
createRef,
20-
Fragment,
21-
ReactElement,
22-
RefObject,
23-
useEffect,
24-
useMemo,
25-
useRef,
26-
useState,
27-
} from 'react'
17+
import { createElement, createRef, Fragment, ReactElement, RefObject, useEffect, useMemo, useRef } from 'react'
2818
// eslint-disable-next-line import/no-extraneous-dependencies
2919
import { followCursor } from 'tippy.js'
3020

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

3726
import { Button, ButtonStyleType, ButtonVariantType } from '../Button'
3827
import { FileUpload } from '../FileUpload'
28+
import { Icon } from '../Icon'
3929
import {
4030
getSelectPickerOptionByValue,
4131
SelectPicker,
@@ -73,8 +63,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
7363
trailingCellIcon,
7464
buttonCellWrapComponent,
7565
focusableFieldKey,
76-
isAddRowButtonClicked,
77-
setIsAddRowButtonClicked,
66+
shouldAutoFocusOnMount = false,
7867
}: DynamicDataTableRowProps<K, CustomStateType>) => {
7968
// CONSTANTS
8069
const isFirstRowEmpty = headers.every(({ key }) => !rows[0]?.data[key].value)
@@ -88,25 +77,18 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
8877
isDeletionNotAllowed || readOnly,
8978
)
9079

91-
// STATES
92-
const [isRowAdded, setIsRowAdded] = useState(false)
93-
9480
// CELL REFS
95-
const cellRef = useRef<Record<string | number, Record<K, RefObject<HTMLTextAreaElement>>>>()
81+
const shouldAutoFocusNewRowRef = useRef(shouldAutoFocusOnMount)
82+
const cellRef = useRef<Record<string | number, Record<K, RefObject<HTMLTextAreaElement>>>>(null)
9683
if (!cellRef.current) {
97-
cellRef.current = rows.reduce(
98-
(acc, curr) => ({
99-
...acc,
100-
[curr.id]: headers.reduce((headerAcc, { key }) => ({ ...headerAcc, [key]: createRef() }), {}),
101-
}),
102-
{},
103-
)
84+
cellRef.current = rows.reduce((acc, curr) => {
85+
acc[curr.id] = headers.reduce((headerAcc, { key }) => ({ ...headerAcc, [key]: createRef() }), {})
86+
return acc
87+
}, {})
10488
}
10589
const rowIds = useMemo(() => rows.map(({ id }) => id), [rows])
10690

10791
useEffect(() => {
108-
setIsRowAdded(rows.length > 0 && Object.keys(cellRef.current).length < rows.length)
109-
11092
// When a new row is added, we create references for its cells.
11193
// This logic ensures that references are created only for the newly added row, while retaining the existing references.
11294
const updatedCellRef = rowIds.reduce((acc, curr) => {
@@ -121,18 +103,6 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
121103
cellRef.current = updatedCellRef
122104
}, [JSON.stringify(rowIds)])
123105

124-
useEffect(() => {
125-
if (isAddRowButtonClicked && isRowAdded) {
126-
// Using the below logic to ensure the cell is focused after row addition.
127-
const cell = cellRef.current[rows[0].id][focusableFieldKey || headers[0].key].current
128-
if (cell) {
129-
cell.focus()
130-
setIsRowAdded(false)
131-
setIsAddRowButtonClicked(false)
132-
}
133-
}
134-
}, [isRowAdded, isAddRowButtonClicked])
135-
136106
// METHODS
137107
const onChange =
138108
(row: DynamicDataTableRowType<K, CustomStateType>, key: K) =>
@@ -163,14 +133,30 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
163133
}
164134

165135
// RENDERERS
166-
const renderCellContent = (row: DynamicDataTableRowType<K, CustomStateType>, key: K) => {
136+
const renderCellContent = (row: DynamicDataTableRowType<K, CustomStateType>, key: K, index: number) => {
167137
const isDisabled = readOnly || row.data[key].disabled
138+
const autoFocus =
139+
shouldAutoFocusNewRowRef.current && key === (focusableFieldKey ?? headers[0].key) && index === 0
140+
141+
// This logic ensures only newly added rows get autofocus.
142+
// On the initial mount, all rows are treated as new, so autofocus is enabled.
143+
// After the first render, when cellRef is set (DOM rendered), we set shouldAutoFocusNewRowRef to true,
144+
// so autofocus is applied only to the correct cell in any subsequently added row.
145+
if (
146+
!shouldAutoFocusOnMount &&
147+
!shouldAutoFocusNewRowRef.current &&
148+
index === 0 &&
149+
cellRef?.current?.[row.id]?.[key].current
150+
) {
151+
shouldAutoFocusNewRowRef.current = true
152+
}
168153

169154
switch (row.data[key].type) {
170155
case DynamicDataTableRowDataType.DROPDOWN:
171156
return (
172157
<div className="w-100 h-100 flex top dc__align-self-start">
173158
<SelectPicker<string, false>
159+
autoFocus={autoFocus}
174160
{...row.data[key].props}
175161
inputId={`data-table-${row.id}-${key}-cell`}
176162
classNamePrefix="dynamic-data-table__cell__select-picker"
@@ -193,6 +179,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
193179
return (
194180
<div className="w-100 h-100 flex top dc__align-self-start">
195181
<SelectPickerTextArea
182+
autoFocus={autoFocus}
196183
isCreatable={isCreatable}
197184
isClearable
198185
{...props}
@@ -248,6 +235,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
248235
default:
249236
return (
250237
<ResizableTagTextArea
238+
autoFocus={autoFocus}
251239
{...row.data[key].props}
252240
id={`data-table-${row.id}-${key}-cell`}
253241
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' : ''}`}
@@ -283,8 +271,8 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
283271
}
284272

285273
const renderErrorMessage = (errorMessage: string) => (
286-
<div key={errorMessage} className="flexbox align-items-center dc__gap-4">
287-
<ICClose className="icon-dim-16 fcr-5 dc__align-self-start dc__no-shrink" />
274+
<div key={errorMessage} className="flexbox dc__gap-4">
275+
<Icon name="ic-close-small" color="R500" />
288276
<p className="fs-12 lh-16 cn-7 m-0">{errorMessage}</p>
289277
</div>
290278
)
@@ -329,7 +317,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
329317
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' : ''}`}
330318
>
331319
{renderCellIcon(row, key, true)}
332-
{renderCellContent(row, key)}
320+
{renderCellContent(row, key, index)}
333321
{renderAsterisk(row, key)}
334322
{renderCellIcon(row, key)}
335323
{renderErrorMessages(row, key)}
@@ -383,7 +371,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
383371
dataTestId="dynamic-data-table-row-delete-btn"
384372
ariaLabel="Delete Row"
385373
showAriaLabelInTippy={false}
386-
icon={<ICClose />}
374+
icon={<Icon name="ic-close-large" color={null} />}
387375
disabled={disableDeleteRow || row.disableDelete}
388376
onClick={onDelete(row)}
389377
variant={ButtonVariantType.borderLess}

src/Shared/Components/DynamicDataTable/styles.scss

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,21 @@
6969
height: 36px;
7070
width: 100%;
7171
background: inherit;
72-
73-
&--add {
74-
resize: none;
75-
border-radius: 4px;
76-
outline: none;
77-
}
7872
}
7973

8074
&__cell {
8175
min-width: 0;
8276

83-
&__select-picker__control {
84-
gap: 6px !important;
85-
padding: 8px !important;
86-
max-height: 160px !important;
77+
&__select-picker {
78+
&__control {
79+
gap: 6px !important;
80+
padding: 8px !important;
81+
max-height: 160px !important;
82+
}
83+
84+
&__single-value {
85+
font-weight: 400 !important;
86+
}
8787
}
8888

8989
&__select-picker-text-area {

src/Shared/Components/DynamicDataTable/types.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { DetailedHTMLProps, Dispatch, ReactElement, ReactNode, SetStateAction } from 'react'
17+
import { DetailedHTMLProps, ReactElement, ReactNode } from 'react'
1818

1919
import { ResizableTagTextAreaProps } from '@Common/CustomTagSelector'
2020
import { UseStateFiltersReturnType } from '@Common/Hooks'
@@ -228,6 +228,12 @@ export type DynamicDataTableProps<K extends string, CustomStateType = Record<str
228228
* @default 'first column key'
229229
*/
230230
focusableFieldKey?: K
231+
/**
232+
* When true, the table will automatically focus the first focusable cell
233+
* or cell key denoted by `focusableFieldKey` when the component mounts.
234+
* @default false
235+
*/
236+
shouldAutoFocusOnMount?: boolean
231237
}
232238

233239
export interface DynamicDataTableHeaderProps<K extends string, CustomStateType = Record<string, unknown>>
@@ -261,7 +267,5 @@ export interface DynamicDataTableRowProps<K extends string, CustomStateType = Re
261267
| 'trailingCellIcon'
262268
| 'buttonCellWrapComponent'
263269
| 'focusableFieldKey'
264-
> {
265-
isAddRowButtonClicked: boolean
266-
setIsAddRowButtonClicked: Dispatch<SetStateAction<boolean>>
267-
}
270+
| 'shouldAutoFocusOnMount'
271+
> {}

src/Shared/Components/Icon/Icon.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ import { ReactComponent as ICSortDescending } from '@IconsV2/ic-sort-descending.
136136
import { ReactComponent as ICSortable } from '@IconsV2/ic-sortable.svg'
137137
import { ReactComponent as ICSparkleColor } from '@IconsV2/ic-sparkle-color.svg'
138138
import { ReactComponent as ICSpinny } from '@IconsV2/ic-spinny.svg'
139+
import { ReactComponent as ICSprayCan } from '@IconsV2/ic-spray-can.svg'
139140
import { ReactComponent as ICStack } from '@IconsV2/ic-stack.svg'
140141
import { ReactComponent as ICStamp } from '@IconsV2/ic-stamp.svg'
141142
import { ReactComponent as ICSuccess } from '@IconsV2/ic-success.svg'
@@ -298,6 +299,7 @@ export const iconMap = {
298299
'ic-sortable': ICSortable,
299300
'ic-sparkle-color': ICSparkleColor,
300301
'ic-spinny': ICSpinny,
302+
'ic-spray-can': ICSprayCan,
301303
'ic-stack': ICStack,
302304
'ic-stamp': ICStamp,
303305
'ic-success': ICSuccess,

src/Shared/Components/KeyValueTable/KeyValueTable.component.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const KeyValueTable = ({
4141
headerComponent,
4242
onChange,
4343
isAdditionNotAllowed,
44+
shouldAutoFocusOnMount,
4445
readOnly,
4546
showError,
4647
validationSchema: parentValidationSchema,
@@ -180,6 +181,7 @@ export const KeyValueTable = ({
180181
headerComponent={headerComponent}
181182
readOnly={readOnly}
182183
isAdditionNotAllowed={isAdditionNotAllowed}
184+
shouldAutoFocusOnMount={shouldAutoFocusOnMount}
183185
sortingConfig={{
184186
sortBy,
185187
sortOrder,

src/Shared/Components/KeyValueTable/KeyValueTable.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export interface KeyValueTableData extends Pick<KeyValueTableRowType, 'id'> {
8585
*/
8686
export type KeyValueTableProps = Pick<
8787
DynamicDataTableProps<KeyValueTableDataType>,
88-
'isAdditionNotAllowed' | 'readOnly' | 'headerComponent'
88+
'isAdditionNotAllowed' | 'readOnly' | 'headerComponent' | 'shouldAutoFocusOnMount'
8989
> & {
9090
/**
9191
* The label for the table header.

src/Shared/DocLink/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export const DOCUMENTATION = {
3333
EXTERNAL_SECRET: 'usage/applications/creating-application/secrets#external-secrets',
3434
HOME_PAGE: 'https://devtron.ai',
3535
KUBE_CONFIG: 'usage/resource-browser#running-kubectl-commands-locally',
36+
TAINT: 'usage/resource-browser#taint-a-node',
3637

3738
// Global Configurations
3839
GLOBAL_CONFIG_API_TOKEN: 'getting-started/global-configurations/authorization/api-tokens',

0 commit comments

Comments
 (0)