Skip to content

Commit f031076

Browse files
RohitRaj011AbhishekA1509
authored andcommitted
refactor: KeyValueTable - refactor variable names, replace index with id based data update, delete, refactor scss
1 parent 9def841 commit f031076

File tree

3 files changed

+165
-196
lines changed

3 files changed

+165
-196
lines changed

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

Lines changed: 135 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,12 @@
1515
* limitations under the License.
1616
*/
1717

18-
/*
19-
* Copyright (c) 2024 Devtron Inc.
20-
* All rights reserved.
21-
22-
* Licensed under the Apache License, Version 2.0 (the "License");
23-
* you may not use this file except in compliance with the License.
24-
* You may obtain a copy of the License at
25-
26-
* http://www.apache.org/licenses/LICENSE-2.0
27-
28-
* Unless required by applicable law or agreed to in writing, software
29-
* distributed under the License is distributed on an "AS IS" BASIS,
30-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31-
* See the License for the specific language governing permissions and
32-
* limitations under the License.
33-
*/
34-
3518
import React, { createRef, useEffect, useRef, useState } from 'react'
3619

3720
import { ReactComponent as ICArrowDown } from '../../../Assets/Icon/ic-arrow-down.svg'
3821
import { ReactComponent as ICCross } from '../../../Assets/Icon/ic-cross.svg'
3922
import { ResizableTagTextArea, SortingOrder, useStateFilters } from '../../../Common'
23+
import { DEFAULT_SECRET_PLACEHOLDER } from '../../constants'
4024
import { stringComparatorBySortOrder } from '../../Helpers'
4125
import { KeyValueRow, KeyValueTableProps } from './KeyValueTable.types'
4226

@@ -58,7 +42,7 @@ export const KeyValueTable = <K extends string>({
5842
const secondHeaderKey = headers[1].key
5943

6044
// STATES
61-
const [_rows, setRows] = useState<KeyValueRow<K>[]>(rows)
45+
const [updatedRows, setUpdatedRows] = useState<KeyValueRow<K>[]>(rows)
6246
const [newRowAdded, setNewRowAdded] = useState(false)
6347

6448
// HOOKS
@@ -70,21 +54,21 @@ export const KeyValueTable = <K extends string>({
7054
const valueTextAreaRef = useRef<Record<string, React.RefObject<HTMLTextAreaElement>>>()
7155

7256
if (!keyTextAreaRef.current) {
73-
keyTextAreaRef.current = _rows.reduce((acc, curr) => ({ ...acc, [curr.id]: createRef() }), {})
57+
keyTextAreaRef.current = updatedRows.reduce((acc, curr) => ({ ...acc, [curr.id]: createRef() }), {})
7458
}
7559

7660
if (!valueTextAreaRef.current) {
77-
valueTextAreaRef.current = _rows.reduce((acc, curr) => ({ ...acc, [curr.id]: createRef() }), {})
61+
valueTextAreaRef.current = updatedRows.reduce((acc, curr) => ({ ...acc, [curr.id]: createRef() }), {})
7862
}
7963

8064
useEffect(() => {
81-
const sortedRows = [..._rows]
65+
const sortedRows = [...updatedRows]
8266
sortedRows.sort((a, b) => stringComparatorBySortOrder(a.data[sortBy].value, b.data[sortBy].value, sortOrder))
83-
setRows(sortedRows)
67+
setUpdatedRows(sortedRows)
8468
}, [sortOrder])
8569

8670
useEffect(() => {
87-
const firstRow = _rows?.[0]
71+
const firstRow = updatedRows?.[0]
8872
if (firstRow && newRowAdded) {
8973
setNewRowAdded(false)
9074

@@ -103,15 +87,15 @@ export const KeyValueTable = <K extends string>({
10387
valueTextAreaRef.current[firstRow.id].current.focus()
10488
}
10589
}
106-
}, [_rows, newRowAdded])
90+
}, [updatedRows, newRowAdded])
10791

10892
// METHODS
10993
const onSortBtnClick = () => handleSorting(sortBy)
11094

11195
const onNewRowAdd = (key: K) => (e: React.ChangeEvent<HTMLTextAreaElement>) => {
11296
const { value } = e.target
11397

114-
const id = Date.now().toString(16)
98+
const id = (Date.now() * Math.random()).toString(16)
11599
const data = {
116100
data: {
117101
[firstHeaderKey]: {
@@ -125,7 +109,8 @@ export const KeyValueTable = <K extends string>({
125109
} as KeyValueRow<K>
126110

127111
setNewRowAdded(true)
128-
setRows([data, ..._rows])
112+
setUpdatedRows([data, ...updatedRows])
113+
onChange?.(id, key, value)
129114

130115
keyTextAreaRef.current = {
131116
...keyTextAreaRef.current,
@@ -137,162 +122,150 @@ export const KeyValueTable = <K extends string>({
137122
}
138123
}
139124

140-
const onRowDataEdit =
141-
(row: KeyValueRow<K>, key: K, rowIndex: number) => (e: React.ChangeEvent<HTMLTextAreaElement>) => {
142-
const { value } = e.target
125+
const onRowDataEdit = (row: KeyValueRow<K>, key: K) => (e: React.ChangeEvent<HTMLTextAreaElement>) => {
126+
const { value } = e.target
143127

144-
let newRows = []
145-
if (!value && !row.data[key === firstHeaderKey ? secondHeaderKey : firstHeaderKey].value) {
146-
newRows = _rows.filter((_, idx) => idx !== rowIndex)
128+
let editedRows = []
129+
if (!value && !row.data[key === firstHeaderKey ? secondHeaderKey : firstHeaderKey].value) {
130+
editedRows = updatedRows.filter(({ id }) => id !== row.id)
147131

148-
delete keyTextAreaRef.current[row.id]
149-
delete valueTextAreaRef.current[row.id]
132+
delete keyTextAreaRef.current[row.id]
133+
delete valueTextAreaRef.current[row.id]
150134

151-
if (inputRowRef.current) {
152-
inputRowRef.current.focus()
153-
}
154-
} else {
155-
newRows = [
156-
..._rows.slice(0, rowIndex),
157-
{
158-
...row,
159-
data: {
160-
...row.data,
161-
[key]: {
162-
...row.data[key],
163-
value,
164-
},
165-
},
135+
if (inputRowRef.current) {
136+
inputRowRef.current.focus()
137+
}
138+
} else {
139+
const rowData = {
140+
...row,
141+
data: {
142+
...row.data,
143+
[key]: {
144+
...row.data[key],
145+
value,
166146
},
167-
..._rows.slice(rowIndex + 1),
168-
]
147+
},
169148
}
170-
171-
setRows(newRows)
172-
onChange?.(rowIndex, key, value)
149+
editedRows = updatedRows.map((_row) => (_row.id === row.id ? rowData : _row))
173150
}
174151

175-
const onRowDelete = (rowIndex: number, row: KeyValueRow<K>) => (e: React.MouseEvent<HTMLButtonElement>) => {
176-
const newRows = _rows.filter((_, idx) => idx !== rowIndex)
177-
setRows(newRows)
152+
setUpdatedRows(editedRows)
153+
onChange?.(row.id, key, value)
154+
}
155+
156+
const onRowDelete = (row: KeyValueRow<K>) => () => {
157+
const remainingRows = updatedRows.filter(({ id }) => id !== row.id)
158+
setUpdatedRows(remainingRows)
178159

179160
delete keyTextAreaRef.current[row.id]
180161
delete valueTextAreaRef.current[row.id]
181162

182-
onDelete?.(e, rowIndex)
163+
onDelete?.(row.id)
183164
}
184165

185166
return (
186-
<div style={{ minHeight: '500px', background: 'white', padding: '2px' }}>
187-
<div className="dc__border br-4 w-100 table-container">
188-
<div
189-
className={`table-row flexbox dc__align-items-center bcn-50 ${!isAdditionNotAllowed || _rows.length ? 'dc__border-bottom' : ''}`}
190-
style={{ borderColor: 'var(--N100)' }}
191-
>
192-
{headers.map(({ key, label, className }) =>
193-
isSortable && key === firstHeaderKey ? (
194-
<button
195-
key={key}
196-
type="button"
197-
className={`dc__unset-button-styles cn-9 fs-13 lh-20 py-8 px-12 fw-6 flexbox dc__align-items-center dc__gap-2 head__key ${className || ''}`}
198-
onClick={onSortBtnClick}
199-
>
200-
{label}
201-
<ICArrowDown
202-
className="icon-dim-16 fcn-7 rotate cursor"
203-
style={{
204-
['--rotateBy' as string]: sortOrder === SortingOrder.ASC ? '0deg' : '180deg',
205-
}}
206-
/>
207-
</button>
208-
) : (
209-
<div
210-
key={key}
211-
className={`cn-9 fs-13 lh-20 py-8 px-12 fw-6 flexbox dc__align-items-center dc__gap-2 ${key === firstHeaderKey ? 'head__key' : 'flex-grow-1'} ${className || ''}`}
212-
>
213-
{label}
214-
</div>
215-
),
216-
)}
217-
{!!headerComponent && <div className="px-12">{headerComponent}</div>}
218-
</div>
219-
{!isAdditionNotAllowed && (
220-
<div
221-
className={`table-row flexbox dc__align-items-center ${_rows.length ? 'dc__border-bottom' : ''}`}
222-
style={{ borderColor: 'var(--N100)' }}
223-
>
224-
{headers.map(({ key }) => (
225-
<div
226-
key={key}
227-
className={`cn-9 fs-13 lh-20 py-8 px-12 flex dc__overflow-auto ${key === firstHeaderKey ? 'head__key' : 'flex-grow-1'}`}
228-
>
229-
<textarea
230-
ref={key === firstHeaderKey ? inputRowRef : undefined}
231-
className="table-input table-input__text-area pt-8 pb-8 pl-10 pb-10 lh-20 fs-13 fw-4"
232-
value=""
233-
rows={1}
234-
placeholder={placeholder[key]}
235-
onChange={onNewRowAdd(key)}
236-
/>
237-
</div>
238-
))}
239-
</div>
240-
)}
241-
{_rows.map((row, index) => (
242-
<div
243-
key={row.id}
244-
className={`table-row flexbox dc__align-items-center ${index !== _rows.length - 1 ? 'dc__border-bottom' : ''}`}
245-
style={{ borderColor: 'var(--N100)' }}
246-
>
247-
{headers.map(({ key }) => (
248-
<div
249-
key={key}
250-
className={`cn-9 fs-13 lh-20 py-8 px-12 dc__overflow-auto flexbox dc__align-items-center dc__gap-4 ${key === firstHeaderKey ? 'head__key' : 'flex-grow-1'}`}
251-
>
252-
{maskValue?.[key] && row.data[key].value ? (
253-
'*****'
254-
) : (
255-
<>
256-
<ResizableTagTextArea
257-
{...row.data[key]}
258-
className="table-input"
259-
minHeight={20}
260-
maxHeight={144}
261-
value={row.data[key].value}
262-
placeholder={placeholder[key]}
263-
onChange={onRowDataEdit(row, key, index)}
264-
refVar={
265-
key === firstHeaderKey
266-
? keyTextAreaRef.current?.[row.id]
267-
: valueTextAreaRef.current?.[row.id]
268-
}
269-
dependentRef={
270-
key === firstHeaderKey
271-
? valueTextAreaRef.current?.[row.id]
272-
: keyTextAreaRef.current?.[row.id]
273-
}
274-
disableOnBlurResizeToMinHeight
275-
/>
276-
{row.data[key].showAsterisk && (
277-
<span className="cr-5 fs-16 dc__align-self-start px-6">*</span>
278-
)}
279-
</>
280-
)}
281-
</div>
282-
))}
167+
<div className="dc__border br-4 w-100 bcn-0">
168+
<div
169+
className={`key-value__row flexbox dc__align-items-center bcn-50 ${!isAdditionNotAllowed || updatedRows.length ? 'dc__border-bottom-n1' : ''}`}
170+
>
171+
{headers.map(({ key, label, className }) =>
172+
isSortable && key === firstHeaderKey ? (
283173
<button
174+
key={key}
284175
type="button"
285-
className=" dc__unset-button-styles icon flex dc__no-shrink py-10 px-8"
286-
onClick={onRowDelete(index, row)}
176+
className={`dc__unset-button-styles cn-9 fs-13 lh-20 py-8 px-12 fw-6 flexbox dc__align-items-center dc__gap-2 dc__align-self-stretch key-value__header__col-1 ${className || ''}`}
177+
onClick={onSortBtnClick}
287178
>
288-
<ICCross
289-
aria-label="delete-data"
290-
className="icon-dim-16 fcn-4 dc__align-self-start cursor"
179+
{label}
180+
<ICArrowDown
181+
className="icon-dim-16 scn-7 rotate cursor"
182+
style={{
183+
['--rotateBy' as string]: sortOrder === SortingOrder.ASC ? '0deg' : '180deg',
184+
}}
291185
/>
292186
</button>
293-
</div>
294-
))}
187+
) : (
188+
<div
189+
key={key}
190+
className={`cn-9 fs-13 lh-20 py-8 px-12 fw-6 flexbox dc__align-items-center dc__gap-2 ${key === firstHeaderKey ? 'dc__align-self-stretch dc__border-right--n1 key-value__header__col-1' : 'flex-grow-1 dc__border-left-n1'} ${className || ''}`}
191+
>
192+
{label}
193+
</div>
194+
),
195+
)}
196+
{!!headerComponent && <div className="px-12">{headerComponent}</div>}
295197
</div>
198+
{!isAdditionNotAllowed && (
199+
<div
200+
className={`key-value__row flexbox dc__align-items-center ${updatedRows.length ? 'dc__border-bottom-n1' : ''}`}
201+
>
202+
{headers.map(({ key }) => (
203+
<div
204+
key={key}
205+
className={`cn-9 fs-13 lh-20 py-8 px-12 flex dc__overflow-auto ${key === firstHeaderKey ? 'dc__align-self-stretch dc__border-right--n1 key-value__header__col-1' : 'flex-grow-1'}`}
206+
>
207+
<textarea
208+
ref={key === firstHeaderKey ? inputRowRef : undefined}
209+
className="key-value__row-input key-value__row-input--add placeholder-cn5 p-0 lh-20 fs-13 fw-4"
210+
value=""
211+
rows={1}
212+
placeholder={placeholder[key]}
213+
onChange={onNewRowAdd(key)}
214+
/>
215+
</div>
216+
))}
217+
</div>
218+
)}
219+
{updatedRows.map((row, index) => (
220+
<div
221+
key={row.id}
222+
className={`key-value__row flexbox dc__align-items-center ${index !== updatedRows.length - 1 ? 'dc__border-bottom-n1' : ''}`}
223+
>
224+
{headers.map(({ key }) => (
225+
<div
226+
key={key}
227+
className={`cn-9 fs-13 lh-20 px-12 dc__overflow-auto flexbox dc__align-items-center dc__gap-4 ${key === firstHeaderKey ? 'dc__align-self-stretch dc__border-right--n1 key-value__header__col-1' : 'flex-grow-1'}`}
228+
>
229+
{maskValue?.[key] && row.data[key].value ? (
230+
DEFAULT_SECRET_PLACEHOLDER
231+
) : (
232+
<>
233+
<ResizableTagTextArea
234+
{...row.data[key]}
235+
className="key-value__row-input placeholder-cn5 py-8 px-0"
236+
minHeight={20}
237+
maxHeight={160}
238+
value={row.data[key].value}
239+
placeholder={placeholder[key]}
240+
onChange={onRowDataEdit(row, key)}
241+
refVar={
242+
key === firstHeaderKey
243+
? keyTextAreaRef.current?.[row.id]
244+
: valueTextAreaRef.current?.[row.id]
245+
}
246+
dependentRef={
247+
key === firstHeaderKey
248+
? valueTextAreaRef.current?.[row.id]
249+
: keyTextAreaRef.current?.[row.id]
250+
}
251+
disableOnBlurResizeToMinHeight
252+
/>
253+
{row.data[key].showAsterisk && (
254+
<span className="cr-5 fs-16 dc__align-self-start px-6">*</span>
255+
)}
256+
</>
257+
)}
258+
</div>
259+
))}
260+
<button
261+
type="button"
262+
className="dc__unset-button-styles dc__align-self-stretch dc__no-shrink flex py-10 px-8 dc__border-left-n1--important"
263+
onClick={onRowDelete(row)}
264+
>
265+
<ICCross aria-label="delete-row" className="icon-dim-16 fcn-4 dc__align-self-start cursor" />
266+
</button>
267+
</div>
268+
))}
296269
</div>
297270
)
298271
}

0 commit comments

Comments
 (0)