Skip to content

Commit

Permalink
feat: DynamicDataTable - new cell type incorporated, code refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
RohitRaj011 committed Nov 27, 2024
1 parent 446feb4 commit c6c37c4
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 36 deletions.
18 changes: 12 additions & 6 deletions src/Shared/Components/DynamicDataTable/DynamicDataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,20 @@
* limitations under the License.
*/

import { useMemo } from 'react'

import { DynamicDataTableHeader } from './DynamicDataTableHeader'
import { DynamicDataTableRow } from './DynamicDataTableRow'
import { DynamicDataTableProps } from './types'
import './styles.scss'

export const DynamicDataTable = <K extends string>(props: DynamicDataTableProps<K>) => (
<div className="w-100">
<DynamicDataTableHeader {...props} />
<DynamicDataTableRow {...props} />
</div>
)
export const DynamicDataTable = <K extends string>({ headers, ...props }: DynamicDataTableProps<K>) => {
const filteredHeaders = useMemo(() => headers.filter(({ isHidden }) => !isHidden), [headers])

return (
<div className="w-100 dc__overflow-auto">
<DynamicDataTableHeader headers={filteredHeaders} {...props} />
<DynamicDataTableRow headers={filteredHeaders} {...props} />
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,10 @@ export const DynamicDataTableHeader = <K extends string>({
{label}
</div>
)}
{key === firstHeaderKey && (
{!isActionDisabled && key === firstHeaderKey && (
<Button
dataTestId="data-table-add-row-button"
ariaLabel="Add"
disabled={isActionDisabled}
onClick={onRowAdd}
icon={<ICAdd />}
variant={ButtonVariantType.borderLess}
Expand Down
40 changes: 31 additions & 9 deletions src/Shared/Components/DynamicDataTable/DynamicDataTableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { ReactComponent as ICClose } from '@Icons/ic-close.svg'
import { DEFAULT_SECRET_PLACEHOLDER } from '@Shared/constants'
import { Tooltip } from '@Common/Tooltip'
import { TippyCustomized } from '@Common/TippyCustomized'
import { TippyCustomizedProps } from '@Common/Types'

import { ConditionalWrap } from '@Common/Helper'
import { SelectTextArea } from '../SelectTextArea'
import {
getSelectPickerOptionByValue,
Expand All @@ -15,15 +17,20 @@ import {
SelectPickerVariantType,
} from '../SelectPicker'
import { MultipleResizableTextArea } from '../MultipleResizableTextArea'
import { getActionButtonPosition, getRowGridTemplateColumn } from './utils'
import { getActionButtonPosition, getRowGridTemplateColumn, rowTypeHasInputField } from './utils'
import { DynamicDataTableRowType, DynamicDataTableRowProps, DynamicDataTableRowDataType } from './types'

const renderWithTippyCustomized = (props: Omit<TippyCustomizedProps, 'children'>) => (children) => (
<TippyCustomized {...props}>{children}</TippyCustomized>
)

export const DynamicDataTableRow = <K extends string>({
rows,
headers,
maskValue,
readOnly,
isAdditionNotAllowed,
isDeletionNotAllowed,
validationSchema = () => true,
showError,
errorMessages = [],
Expand All @@ -39,7 +46,11 @@ export const DynamicDataTableRow = <K extends string>({
const hasRows = (!readOnly && !isAdditionNotAllowed) || !!rows.length
const disableDeleteRow = rows.length === 1 && isFirstRowEmpty
/** style: grid-template-columns */
const rowGridTemplateColumn = getRowGridTemplateColumn(headers, actionButtonConfig, readOnly)
const rowGridTemplateColumn = getRowGridTemplateColumn(
headers,
actionButtonConfig,
isDeletionNotAllowed || readOnly,
)

const cellRef = useRef<Record<string | number, Record<K, RefObject<HTMLTextAreaElement>>>>()
if (!cellRef.current) {
Expand Down Expand Up @@ -122,18 +133,29 @@ export const DynamicDataTableRow = <K extends string>({
dependentRefs={cellRef?.current?.[row.id]}
textAreaProps={{
...row.data[key].props?.textAreaProps,
className: 'dynamic-data-table__cell-input placeholder-cn5 py-8 cn-9 fs-13 lh-20',
className: 'dynamic-data-table__cell-input placeholder-cn5 p-8 cn-9 fs-13 lh-20',
disableOnBlurResizeToMinHeight: true,
minHeight: 20,
maxHeight: 160,
}}
/>
</div>
)
case DynamicDataTableRowDataType.TIPPY_CUSTOMIZED:
case DynamicDataTableRowDataType.BUTTON:
return (
<div className="w-100 h-100 flex top p-8">
<TippyCustomized {...row.data[key].props} />
<div className="w-100 h-100 flex top">
<ConditionalWrap
condition={row.data[key].props?.showTippyCustomized}
wrap={renderWithTippyCustomized(row.data[key].props?.tippyCustomizedProps)}
>
<button
type="button"
className={`dc__transparent w-100 p-8 flex left dc__gap-8 dc__hover-n50 cn-9 fs-13 lh-20 ${row.data[key].disabled ? 'dc__disabled' : ''}`}
>
{row.data[key].props?.icon || null}
{row.data[key].props.text}
</button>
</ConditionalWrap>
</div>
)
default:
Expand Down Expand Up @@ -205,7 +227,7 @@ export const DynamicDataTableRow = <K extends string>({
plugins={[followCursor]}
>
<div
className={`dynamic-data-table__cell bcn-0 flexbox dc__align-items-center dc__gap-4 dc__position-rel ${readOnly || row.data[key].disabled ? 'cursor-not-allowed no-hover' : ''} ${showError && !validationSchema(row.data[key].value, key, row.id) ? 'dynamic-data-table__cell--error no-hover' : ''}`}
className={`dynamic-data-table__cell bcn-0 flexbox dc__align-items-center dc__gap-4 dc__position-rel ${readOnly || row.data[key].disabled ? 'cursor-not-allowed no-hover' : ''} ${showError && !validationSchema(row.data[key].value, key, row.id) ? 'dynamic-data-table__cell--error no-hover' : ''} ${!rowTypeHasInputField(row.data[key].type) ? 'no-hover no-focus' : ''}`}
>
{maskValue?.[key] && row.data[key].value ? (
<div className="py-8 px-12 h-36 flex">{DEFAULT_SECRET_PLACEHOLDER}</div>
Expand All @@ -225,7 +247,7 @@ export const DynamicDataTableRow = <K extends string>({
const actionButtonIndex = getActionButtonPosition({ headers, actionButtonConfig })
if (actionButtonIndex === index) {
const { renderer, position = 'start' } = actionButtonConfig
const actionButtonNode = <div className="dynamic-data-table__cell flex top p-8 bcn-0">{renderer(row)}</div>
const actionButtonNode = <div className="dc__overflow-hidden flex top bcn-0">{renderer(row)}</div>

return position === 'start' ? (
<>
Expand Down Expand Up @@ -257,7 +279,7 @@ export const DynamicDataTableRow = <K extends string>({
{headers.map(({ key }, index) => (
<Fragment key={key}>{renderCell(row, key, index)}</Fragment>
))}
{!readOnly && (
{!isDeletionNotAllowed && !readOnly && (
<button
type="button"
className={`dynamic-data-table__row-delete-btn dc__unset-button-styles dc__align-self-stretch dc__no-shrink flex py-10 px-8 bcn-0 dc__hover-n50 dc__tab-focus ${disableDeleteRow ? 'dc__disabled' : ''}`}
Expand Down
8 changes: 2 additions & 6 deletions src/Shared/Components/DynamicDataTable/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,18 @@
padding: 8px;
}

&:not(:has(.select-picker-borderless-control)) .select-text-area-container {
padding-inline: 8px;
}

&:hover:not(:focus-within):not(.no-hover) {
outline: 1px solid var(--N200);
}

&:focus-within {
&:focus-within:not(.no-focus) {
position: relative;
z-index: 1;
outline: 1px solid var(--B500);
}

&--error,
&--error:focus-within,
&--error:focus-within:not(.no-focus),
&--error:hover {
outline: 1px solid var(--R500);
}
Expand Down
31 changes: 27 additions & 4 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 { ReactNode } from 'react'
import { DetailedHTMLProps, ReactNode } from 'react'

import { SortingOrder } from '@Common/Constants'

Expand All @@ -36,13 +36,15 @@ export type DynamicDataTableHeaderType<K extends string> = {
width: string
/** An optional boolean indicating if the column is sortable. */
isSortable?: boolean
/** An optional boolean to hide the column */
isHidden?: boolean
}

export enum DynamicDataTableRowDataType {
TEXT = 'text',
DROPDOWN = 'dropdown',
SELECT_TEXT = 'select-text',
TIPPY_CUSTOMIZED = 'tippy-customized',
BUTTON = 'button',
}

/**
Expand Down Expand Up @@ -92,12 +94,30 @@ export type DynamicDataTableRowType<K extends string> = {
}
}
| {
type?: DynamicDataTableRowDataType.TIPPY_CUSTOMIZED
props: TippyCustomizedProps
type?: DynamicDataTableRowDataType.BUTTON
props: (Pick<
DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>,
'onClick'
> & {
icon?: ReactNode
text: ReactNode
}) &
(
| {
showTippyCustomized: true
tippyCustomizedProps: Omit<TippyCustomizedProps, 'children'>
}
| {
showTippyCustomized?: false
tippyCustomizedProps?: never
}
)
}
)
}
id: string | number
/** */
customState?: Record<string, any>
}

/**
Expand Down Expand Up @@ -137,6 +157,8 @@ export type DynamicDataTableProps<K extends string> = {
headerComponent?: ReactNode
/** When true, data addition field will not be shown. */
isAdditionNotAllowed?: boolean
/** When true, data addition field will not be shown. */
isDeletionNotAllowed?: boolean
/** When true, data add or update is disabled. */
readOnly?: boolean
/** */
Expand Down Expand Up @@ -205,6 +227,7 @@ export interface DynamicDataTableRowProps<K extends string>
| 'headers'
| 'maskValue'
| 'isAdditionNotAllowed'
| 'isDeletionNotAllowed'
| 'readOnly'
| 'onRowEdit'
| 'onRowDelete'
Expand Down
28 changes: 20 additions & 8 deletions src/Shared/Components/DynamicDataTable/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DynamicDataTableHeaderType, DynamicDataTableProps } from './types'
import { DynamicDataTableHeaderType, DynamicDataTableProps, DynamicDataTableRowDataType } from './types'

export const getActionButtonPosition = <K extends string>({
headers,
Expand All @@ -11,14 +11,16 @@ export const getHeaderGridTemplateColumn = <K extends string>(
actionButtonConfig: DynamicDataTableProps<K>['actionButtonConfig'],
) => {
const actionButtonIndex = getActionButtonPosition({ headers, actionButtonConfig })
const actionButtonWidth = actionButtonConfig?.width || '32px'
const actionButtonWidth = actionButtonConfig?.width || '33px'
const isActionButtonAtTheStart = actionButtonIndex === 0 && actionButtonConfig.position !== 'end'
const gridWidthRegex = /^\d+fr$/

const columns = headers.map(({ width }, index) => {
if (!isActionButtonAtTheStart && index === actionButtonIndex) {
if (!width.match(/^\d+fr$/)) {
return `calc(${width} + ${actionButtonWidth})`
}
if (!isActionButtonAtTheStart && index === actionButtonIndex && !gridWidthRegex.test(width)) {
return `calc(${width} + ${actionButtonWidth})`
}
if (index === headers.length - 1 && !gridWidthRegex.test(width)) {
return `calc(${width} + 33px)`
}
return width
})
Expand All @@ -33,7 +35,7 @@ export const getHeaderGridTemplateColumn = <K extends string>(
export const getRowGridTemplateColumn = <K extends string>(
headers: DynamicDataTableHeaderType<K>[],
actionButtonConfig: DynamicDataTableProps<K>['actionButtonConfig'],
readOnly: boolean,
noDeleteBtn: boolean,
) => {
const actionButtonIndex = getActionButtonPosition({ headers, actionButtonConfig })
const actionButtonWidth = actionButtonConfig?.width || '32px'
Expand All @@ -46,9 +48,19 @@ export const getRowGridTemplateColumn = <K extends string>(
return width
})

if (!readOnly) {
if (!noDeleteBtn) {
columns.push('32px')
}

return columns.join(' ').trim()
}

export const rowTypeHasInputField = (type: DynamicDataTableRowDataType) => {
const inputFieldTypes = [
DynamicDataTableRowDataType.TEXT,
DynamicDataTableRowDataType.SELECT_TEXT,
DynamicDataTableRowDataType.DROPDOWN,
]

return inputFieldTypes.includes(type || DynamicDataTableRowDataType.TEXT)
}
6 changes: 5 additions & 1 deletion src/Shared/Components/SelectTextArea/SelectTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ export const SelectTextArea = ({
fullWidth
/>
) : (
<button type="button" className="flex dc__transparent" onClick={onClear}>
<button
type="button"
className="flex dc__transparent dc__position-abs dc__top-8 dc__right-8"
onClick={onClear}
>
<ICClearSquare className="icon-dim-16" />
</button>
)}
Expand Down

0 comments on commit c6c37c4

Please sign in to comment.