Skip to content
Open
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
20 changes: 20 additions & 0 deletions src/components/Common/DataView/DataCards/DataCards.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,29 @@ h5.columnTitle {

.columnData {
width: 100%;
display: flex;
flex-direction: column;
gap: 0.125rem;
color: var(--g-colorBodyContent);
}

.columnPrimary {
color: inherit;
}

.columnSecondary {
color: var(--g-colorBodySubContent);
font-size: var(--g-fontSizeSmall);
}

.footerItem {
width: 100%;
display: flex;
flex-direction: column;
gap: toRem(2);
}

.footerSecondary {
color: var(--g-colorBodySubContent);
font-size: var(--g-fontSizeSmall);
}
14 changes: 11 additions & 3 deletions src/components/Common/DataView/DataCards/DataCards.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ type MockData = {
id: number
name: string
age: number
department: string
}

// Sample test data
const testData: MockData[] = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 },
{ id: 1, name: 'Alice', age: 25, department: 'Engineering' },
{ id: 2, name: 'Bob', age: 30, department: 'Operations' },
]

// Sample columns
const testColumns = [
{ key: 'name', title: 'Name' },
{ key: 'name', title: 'Name', secondaryText: 'department' },
{ key: 'age', title: 'Age' },
] as const

Expand All @@ -40,6 +41,13 @@ describe('DataCards', () => {
expect(screen.getByText('Bob')).toBeInTheDocument()
})

test('should render secondary text when provided', () => {
renderWithProviders(<DataCards data={testData} columns={[...testColumns]} />)

expect(screen.getByText('Engineering')).toBeInTheDocument()
expect(screen.getByText('Operations')).toBeInTheDocument()
})

test('should render the component with column headers', () => {
renderWithProviders(<DataCards data={testData} columns={[...testColumns]} />)

Expand Down
41 changes: 27 additions & 14 deletions src/components/Common/DataView/DataCards/DataCards.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { getColumnContent } from '../getColumnContent'
import { getFooterContent } from '../getFooterContent'
import styles from './DataCards.module.scss'
import type { useDataViewPropReturn } from '@/components/Common/DataView/useDataView'
import { Flex } from '@/components/Common/Flex/Flex'
Expand Down Expand Up @@ -40,15 +42,20 @@ export const DataCards = <T,>({
: undefined
}
>
{columns.map((column, index) => (
<Flex key={index} flexDirection="column" gap={0}>
{column.title && <h5 className={styles.columnTitle}>{column.title}</h5>}
<div className={styles.columnData}>
{' '}
{column.render ? column.render(item) : String(item[column.key as keyof T])}
</div>
</Flex>
))}
{columns.map((column, index) => {
const { primary, secondary } = getColumnContent(item, column)
return (
<Flex key={index} flexDirection="column" gap={2}>
{column.title && <h5 className={styles.columnTitle}>{column.title}</h5>}
<div className={styles.columnData}>
<div className={styles.columnPrimary}>{primary}</div>
{secondary !== undefined && (
<div className={styles.columnSecondary}>{secondary}</div>
)}
</div>
</Flex>
)
})}
</Components.Card>
</div>
))}
Expand All @@ -59,11 +66,17 @@ export const DataCards = <T,>({
const footerContent = footer()

// Footer content is always an object with column keys
return Object.entries(footerContent).map(([key, content]) => (
<div key={key} className={styles.footerItem}>
{content}
</div>
))
return Object.entries(footerContent).map(([key, content]) => {
const { primary, secondary } = getFooterContent(content)
return (
<div key={key} className={styles.footerItem}>
<div>{primary}</div>
{secondary !== undefined && (
<div className={styles.footerSecondary}>{secondary}</div>
)}
</div>
)
})
})()}
</Components.Card>
</div>
Expand Down
56 changes: 56 additions & 0 deletions src/components/Common/DataView/DataTable/DataTable.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.cellWrapper {
width: 100%;
text-align: left;
}

.cellWrapper[data-align='center'] {
text-align: center;
}

.cellWrapper[data-align='right'] {
text-align: right;
}

.cellContent {
display: flex;
flex-direction: column;
gap: toRem(2);
}

.cellSecondary {
color: var(--g-colorBodySubContent);
font-weight: var(--g-fontWeightRegular);
}

.headerCell {
text-align: left;
}

.headerCell[data-align='center'] {
text-align: center;
}

.headerCell[data-align='right'] {
text-align: right;
}

.footerCell {
width: 100%;
text-align: left;
display: flex;
flex-direction: column;
gap: toRem(2);
}

.footerCell[data-align='center'] {
text-align: center;
}

.footerCell[data-align='right'] {
text-align: right;
}

.footerSecondary {
color: var(--g-colorBodySubContent);
font-weight: var(--g-fontWeightRegular);
}
28 changes: 26 additions & 2 deletions src/components/Common/DataView/DataTable/DataTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,27 @@ type MockData = {
id: number
name: string
age: number
department: string
}

// Sample test data
const testData: MockData[] = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 },
{ id: 1, name: 'Alice', age: 25, department: 'Engineering' },
{ id: 2, name: 'Bob', age: 30, department: 'Operations' },
]

// Sample columns
const testColumns: useDataViewPropReturn<MockData>['columns'] = [
{
key: 'name',
title: 'Name',
secondaryText: 'department',
render: (item: MockData) => item.name,
},
{
key: 'age',
title: 'Age',
align: 'right',
render: (item: MockData) => item.age.toString(),
},
]
Expand Down Expand Up @@ -67,6 +70,17 @@ describe('DataTable Component', () => {
expect(screen.getByText('Bob')).toBeInTheDocument()
})

test('should render secondary text when provided', () => {
renderTable<MockData>({
data: testData,
columns: testColumns,
label: 'Test Table',
})

expect(screen.getByText('Engineering')).toBeInTheDocument()
expect(screen.getByText('Operations')).toBeInTheDocument()
})

test('should render checkboxes and call onSelect when clicked', async () => {
const onSelectMock = vi.fn()
renderTable<MockData>({
Expand Down Expand Up @@ -119,6 +133,16 @@ describe('DataTable Component', () => {
expect(screen.getByText('55')).toBeInTheDocument()
})

test('should apply alignment when column specifies it', () => {
const { container } = renderTable<MockData>({
data: testData,
columns: testColumns,
label: 'Aligned Table',
})

expect(container.querySelectorAll('[data-align="right"]').length).toBeGreaterThan(0)
})

describe('accessibility', () => {
it('should not have any accessibility violations - empty table', async () => {
const { container } = renderTable<MockData>({ data: [], columns: [], label: 'Test Table' })
Expand Down
67 changes: 44 additions & 23 deletions src/components/Common/DataView/DataTable/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { useTranslation } from 'react-i18next'
import type { useDataViewPropReturn } from '../useDataView'
import type { TableData, TableRow, TableProps } from '../../UI/Table/TableTypes'
import { VisuallyHidden } from '../../VisuallyHidden'
import { getColumnContent } from '../getColumnContent'
import { getFooterContent } from '../getFooterContent'
import styles from './DataTable.module.scss'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'

export type DataTableProps<T> = {
Expand All @@ -15,22 +18,6 @@ export type DataTableProps<T> = {
variant?: TableProps['variant']
}

function getCellContent<T>(
item: T,
column: { key?: string | keyof T; render?: (item: T) => React.ReactNode },
) {
if (column.render) {
return column.render(item)
}

if (column.key) {
const key = column.key as keyof T
return String(item[key] ?? '')
}

return ''
}

export const DataTable = <T,>({
label,
data,
Expand All @@ -53,10 +40,17 @@ export const DataTable = <T,>({
},
]
: []),
...columns.map((column, index) => ({
key: typeof column.key === 'string' ? column.key : `header-${index}`,
content: column.title,
})),
...columns.map((column, index) => {
const alignment = column.align ?? 'left'
return {
key: typeof column.key === 'string' ? column.key : `header-${index}`,
content: (
<div className={styles.headerCell} data-align={alignment}>
{column.title}
</div>
),
}
}),
...(itemMenu
? [
{
Expand Down Expand Up @@ -86,9 +80,25 @@ export const DataTable = <T,>({
]
: []),
...columns.map((column, colIndex) => {
const alignment = column.align ?? 'left'
const { primary, secondary } = getColumnContent(item, column)
const cellContent =
secondary !== undefined ? (
<div className={styles.cellContent}>
<div>{primary}</div>
<div className={styles.cellSecondary}>{secondary}</div>
</div>
) : (
primary
)

return {
key: typeof column.key === 'string' ? column.key : `cell-${colIndex}`,
content: getCellContent(item, column),
content: (
<div className={styles.cellWrapper} data-align={alignment}>
{cellContent}
</div>
),
}
}),
...(itemMenu
Expand Down Expand Up @@ -124,17 +134,28 @@ export const DataTable = <T,>({
// Add data column footers
columns.forEach((column, index) => {
const columnKey = typeof column.key === 'string' ? column.key : `column-${index}`
const alignment = column.align ?? 'left'
const footerValue = footerContent[columnKey]
const { primary: footerPrimary, secondary: footerSecondary } = getFooterContent(footerValue)

footerCells.push({
key: `footer-${columnKey}`,
content: footerContent[columnKey] || '',
content: (
<div className={styles.footerCell} data-align={alignment}>
<div>{footerPrimary}</div>
{footerSecondary !== undefined && (
<div className={styles.footerSecondary}>{footerSecondary}</div>
)}
</div>
),
})
})

// Add actions column footer (empty)
if (itemMenu) {
footerCells.push({
key: 'footer-actions',
content: '',
content: <div className={styles.footerCell} data-align="left" />,
})
}

Expand Down
Loading