Skip to content

Commit 9def841

Browse files
RohitRaj011AbhishekA1509
authored andcommitted
refactor: KeyValueTable - map keys, refactor refs of textarea, sorting fn, delete row functionality
1 parent 498b99b commit 9def841

File tree

2 files changed

+71
-54
lines changed

2 files changed

+71
-54
lines changed

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

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import React, { createRef, useEffect, useRef, useState } from 'react'
3737
import { ReactComponent as ICArrowDown } from '../../../Assets/Icon/ic-arrow-down.svg'
3838
import { ReactComponent as ICCross } from '../../../Assets/Icon/ic-cross.svg'
3939
import { ResizableTagTextArea, SortingOrder, useStateFilters } from '../../../Common'
40+
import { stringComparatorBySortOrder } from '../../Helpers'
4041
import { KeyValueRow, KeyValueTableProps } from './KeyValueTable.types'
4142

4243
import './KeyValueTable.scss'
@@ -48,6 +49,7 @@ export const KeyValueTable = <K extends string>({
4849
headerComponent,
4950
onChange,
5051
onDelete,
52+
placeholder,
5153
isAdditionNotAllowed,
5254
}: KeyValueTableProps<K>) => {
5355
// CONSTANTS
@@ -64,33 +66,20 @@ export const KeyValueTable = <K extends string>({
6466
initialSortKey: firstHeaderKey,
6567
})
6668
const inputRowRef = useRef<HTMLTextAreaElement>()
67-
const keyTextAreaRef = useRef<React.RefObject<HTMLTextAreaElement>[]>([])
68-
const valueTextAreaRef = useRef<React.RefObject<HTMLTextAreaElement>[]>([])
69+
const keyTextAreaRef = useRef<Record<string, React.RefObject<HTMLTextAreaElement>>>()
70+
const valueTextAreaRef = useRef<Record<string, React.RefObject<HTMLTextAreaElement>>>()
6971

70-
useEffect(() => {
71-
if (keyTextAreaRef.current.length !== _rows.length) {
72-
keyTextAreaRef.current = new Array(_rows.length)
73-
.fill(0)
74-
.map((_, i) => keyTextAreaRef.current[i] || createRef<HTMLTextAreaElement>())
75-
}
72+
if (!keyTextAreaRef.current) {
73+
keyTextAreaRef.current = _rows.reduce((acc, curr) => ({ ...acc, [curr.id]: createRef() }), {})
74+
}
7675

77-
if (valueTextAreaRef.current.length !== _rows.length) {
78-
valueTextAreaRef.current = new Array(_rows.length)
79-
.fill(0)
80-
.map((_, i) => valueTextAreaRef.current[i] || createRef<HTMLTextAreaElement>())
81-
}
82-
}, [])
76+
if (!valueTextAreaRef.current) {
77+
valueTextAreaRef.current = _rows.reduce((acc, curr) => ({ ...acc, [curr.id]: createRef() }), {})
78+
}
8379

8480
useEffect(() => {
85-
const sortFn = (a: KeyValueRow<K>, b: KeyValueRow<K>) => {
86-
if (sortOrder === SortingOrder.ASC) {
87-
return b.data[sortBy].value.localeCompare(a.data[sortBy].value)
88-
}
89-
return a.data[sortBy].value.localeCompare(b.data[sortBy].value)
90-
}
91-
9281
const sortedRows = [..._rows]
93-
sortedRows.sort(sortFn)
82+
sortedRows.sort((a, b) => stringComparatorBySortOrder(a.data[sortBy].value, b.data[sortBy].value, sortOrder))
9483
setRows(sortedRows)
9584
}, [sortOrder])
9685

@@ -101,45 +90,51 @@ export const KeyValueTable = <K extends string>({
10190

10291
if (
10392
!firstRow.data[secondHeaderKey].value &&
104-
keyTextAreaRef.current[0].current &&
105-
valueTextAreaRef.current[0].current
93+
keyTextAreaRef.current[firstRow.id].current &&
94+
valueTextAreaRef.current[firstRow.id].current
10695
) {
107-
keyTextAreaRef.current[0].current.focus()
96+
keyTextAreaRef.current[firstRow.id].current.focus()
10897
}
10998
if (
11099
!firstRow.data[firstHeaderKey].value &&
111-
valueTextAreaRef.current[0].current &&
112-
valueTextAreaRef.current[0].current
100+
keyTextAreaRef.current[firstRow.id].current &&
101+
valueTextAreaRef.current[firstRow.id].current
113102
) {
114-
valueTextAreaRef.current[0].current.focus()
103+
valueTextAreaRef.current[firstRow.id].current.focus()
115104
}
116105
}
117106
}, [_rows, newRowAdded])
118107

119108
// METHODS
120109
const onSortBtnClick = () => handleSorting(sortBy)
121110

122-
const onNewRowEdit = (key: K) => (e: React.ChangeEvent<HTMLTextAreaElement>) => {
111+
const onNewRowAdd = (key: K) => (e: React.ChangeEvent<HTMLTextAreaElement>) => {
123112
const { value } = e.target
124113

114+
const id = Date.now().toString(16)
125115
const data = {
126116
data: {
127117
[firstHeaderKey]: {
128118
value: key === firstHeaderKey ? value : '',
129-
placeholder: 'Enter Key',
130119
},
131120
[secondHeaderKey]: {
132121
value: key === secondHeaderKey ? value : '',
133-
placeholder: 'Enter Value',
134122
},
135123
},
136-
id: Date.now() * Math.random(),
124+
id,
137125
} as KeyValueRow<K>
138126

139127
setNewRowAdded(true)
140128
setRows([data, ..._rows])
141-
keyTextAreaRef.current = [createRef(), ...keyTextAreaRef.current]
142-
valueTextAreaRef.current = [createRef(), ...valueTextAreaRef.current]
129+
130+
keyTextAreaRef.current = {
131+
...keyTextAreaRef.current,
132+
[id]: createRef(),
133+
}
134+
valueTextAreaRef.current = {
135+
...valueTextAreaRef.current,
136+
[id]: createRef(),
137+
}
143138
}
144139

145140
const onRowDataEdit =
@@ -150,8 +145,9 @@ export const KeyValueTable = <K extends string>({
150145
if (!value && !row.data[key === firstHeaderKey ? secondHeaderKey : firstHeaderKey].value) {
151146
newRows = _rows.filter((_, idx) => idx !== rowIndex)
152147

153-
keyTextAreaRef.current = keyTextAreaRef.current.filter((_, idx) => idx !== rowIndex)
154-
valueTextAreaRef.current = valueTextAreaRef.current.filter((_, idx) => idx !== rowIndex)
148+
delete keyTextAreaRef.current[row.id]
149+
delete valueTextAreaRef.current[row.id]
150+
155151
if (inputRowRef.current) {
156152
inputRowRef.current.focus()
157153
}
@@ -171,15 +167,26 @@ export const KeyValueTable = <K extends string>({
171167
..._rows.slice(rowIndex + 1),
172168
]
173169
}
170+
174171
setRows(newRows)
175172
onChange?.(rowIndex, key, value)
176173
}
177174

175+
const onRowDelete = (rowIndex: number, row: KeyValueRow<K>) => (e: React.MouseEvent<HTMLButtonElement>) => {
176+
const newRows = _rows.filter((_, idx) => idx !== rowIndex)
177+
setRows(newRows)
178+
179+
delete keyTextAreaRef.current[row.id]
180+
delete valueTextAreaRef.current[row.id]
181+
182+
onDelete?.(e, rowIndex)
183+
}
184+
178185
return (
179186
<div style={{ minHeight: '500px', background: 'white', padding: '2px' }}>
180187
<div className="dc__border br-4 w-100 table-container">
181188
<div
182-
className="table-row flexbox dc__align-items-center bcn-50 dc__border-bottom"
189+
className={`table-row flexbox dc__align-items-center bcn-50 ${!isAdditionNotAllowed || _rows.length ? 'dc__border-bottom' : ''}`}
183190
style={{ borderColor: 'var(--N100)' }}
184191
>
185192
{headers.map(({ key, label, className }) =>
@@ -211,7 +218,7 @@ export const KeyValueTable = <K extends string>({
211218
</div>
212219
{!isAdditionNotAllowed && (
213220
<div
214-
className="table-row flexbox dc__align-items-center dc__border-bottom"
221+
className={`table-row flexbox dc__align-items-center ${_rows.length ? 'dc__border-bottom' : ''}`}
215222
style={{ borderColor: 'var(--N100)' }}
216223
>
217224
{headers.map(({ key }) => (
@@ -224,22 +231,22 @@ export const KeyValueTable = <K extends string>({
224231
className="table-input table-input__text-area pt-8 pb-8 pl-10 pb-10 lh-20 fs-13 fw-4"
225232
value=""
226233
rows={1}
227-
placeholder={key === firstHeaderKey ? 'Enter Key' : 'Enter Value'}
228-
onChange={onNewRowEdit(key)}
234+
placeholder={placeholder[key]}
235+
onChange={onNewRowAdd(key)}
229236
/>
230237
</div>
231238
))}
232239
</div>
233240
)}
234-
{_rows?.map((row, index) => (
241+
{_rows.map((row, index) => (
235242
<div
236-
key={`${index.toString()}`}
243+
key={row.id}
237244
className={`table-row flexbox dc__align-items-center ${index !== _rows.length - 1 ? 'dc__border-bottom' : ''}`}
238245
style={{ borderColor: 'var(--N100)' }}
239246
>
240-
{headers.map(({ key }, i) => (
247+
{headers.map(({ key }) => (
241248
<div
242-
key={`${index.toString()}-${i.toString()}`}
249+
key={key}
243250
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'}`}
244251
>
245252
{maskValue?.[key] && row.data[key].value ? (
@@ -252,16 +259,17 @@ export const KeyValueTable = <K extends string>({
252259
minHeight={20}
253260
maxHeight={144}
254261
value={row.data[key].value}
262+
placeholder={placeholder[key]}
255263
onChange={onRowDataEdit(row, key, index)}
256264
refVar={
257265
key === firstHeaderKey
258-
? keyTextAreaRef.current[index]
259-
: valueTextAreaRef.current[index]
266+
? keyTextAreaRef.current?.[row.id]
267+
: valueTextAreaRef.current?.[row.id]
260268
}
261269
dependentRef={
262270
key === firstHeaderKey
263-
? valueTextAreaRef.current[index]
264-
: keyTextAreaRef.current[index]
271+
? valueTextAreaRef.current?.[row.id]
272+
: keyTextAreaRef.current?.[row.id]
265273
}
266274
disableOnBlurResizeToMinHeight
267275
/>
@@ -272,12 +280,16 @@ export const KeyValueTable = <K extends string>({
272280
)}
273281
</div>
274282
))}
275-
<div className="icon flex dc__no-shrink py-10 px-8">
283+
<button
284+
type="button"
285+
className=" dc__unset-button-styles icon flex dc__no-shrink py-10 px-8"
286+
onClick={onRowDelete(index, row)}
287+
>
276288
<ICCross
277-
onClick={(e) => onDelete?.(e, index)}
278-
className="icon-dim-16 fcn-4 dc__align-self-start"
289+
aria-label="delete-data"
290+
className="icon-dim-16 fcn-4 dc__align-self-start cursor"
279291
/>
280-
</div>
292+
</button>
281293
</div>
282294
))}
283295
</div>

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export interface KeyValueHeader<K extends string> {
3737
*/
3838
export type KeyValueRow<K extends string> = {
3939
data: {
40-
[key in K]: ResizableTagTextAreaProps & {
40+
[key in K]: Pick<ResizableTagTextAreaProps, 'value' | 'dataTestId' | 'disabled' | 'tabIndex'> & {
4141
/** An optional boolean indicating if an asterisk should be shown. */
4242
showAsterisk?: boolean
4343
}
@@ -64,6 +64,10 @@ export type KeyValueMask<K extends string> = {
6464
[key in K]?: boolean
6565
}
6666

67+
export type KeyValuePlaceholder<K extends string> = {
68+
[key in K]: string
69+
}
70+
6771
/**
6872
* Interface representing the properties for a key-value table component.
6973
* @template K - A string representing the key type.
@@ -73,6 +77,7 @@ export interface KeyValueTableProps<K extends string> {
7377
config: KeyValueConfig<K>
7478
/** An optional mask for the key-value pairs. */
7579
maskValue?: KeyValueMask<K>
80+
placeholder?: KeyValuePlaceholder<K>
7681
/** An optional boolean indicating if the table is sortable. */
7782
isSortable?: boolean
7883
/** An optional React node for a custom header component. */
@@ -91,5 +96,5 @@ export interface KeyValueTableProps<K extends string> {
9196
* @param e - The event triggered by the delete action.
9297
* @param deletedRowIndex - The index of the row that was deleted.
9398
*/
94-
onDelete?: (e: SyntheticEvent<SVGSVGElement, MouseEvent>, deletedRowIndex: number) => void
99+
onDelete?: (e: SyntheticEvent<HTMLButtonElement, MouseEvent>, deletedRowIndex: number) => void
95100
}

0 commit comments

Comments
 (0)