diff --git a/packages/ui/__stories__/Table.stories.tsx b/packages/ui/__stories__/Table.stories.tsx
index ec1ca5556..802811788 100644
--- a/packages/ui/__stories__/Table.stories.tsx
+++ b/packages/ui/__stories__/Table.stories.tsx
@@ -9,7 +9,7 @@ import { TextField } from '../src'
import storyStyles from './TextField.stories.module.scss'
import styles from './utils.scss'
-import { SortingRule } from 'react-table'
+import { SortingRule } from '../lib/Table/types'
export default {
title: 'Components/Table',
@@ -164,7 +164,7 @@ export const WithControlledSorting = () => {
// Only update the data if this is the latest fetch
if (fetchId === fetchIdRef.current) {
const { id, desc: isDescending } = sortBy[0]
- const sortColumn = id as keyof Person
+ const sortColumn = id
const newData = [...bigDataSet]
newData.sort((a, b) =>
isDescending ? Number(b[sortColumn]) - Number(a[sortColumn]) : Number(a[sortColumn]) - Number(b[sortColumn]),
diff --git a/packages/ui/__tests__/.eslintrc.js b/packages/ui/__tests__/.eslintrc.js
index 491bd2df4..3f610df7d 100644
--- a/packages/ui/__tests__/.eslintrc.js
+++ b/packages/ui/__tests__/.eslintrc.js
@@ -6,5 +6,13 @@ module.exports = {
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-empty-function': 'off',
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ {
+ caughtErrors: 'none',
+ argsIgnorePattern: '_',
+ ignoreRestSiblings: true,
+ },
+ ],
},
}
diff --git a/packages/ui/__tests__/Alert.test.tsx b/packages/ui/__tests__/Alert.test.tsx
index c47a8298d..baced569e 100644
--- a/packages/ui/__tests__/Alert.test.tsx
+++ b/packages/ui/__tests__/Alert.test.tsx
@@ -75,7 +75,7 @@ describe('Alert', () => {
expect(AlertElement.findDataTest('alert-content').props()).toMatchObject({
children: content,
})
- expect(wrapper.findDataTest('alert-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('alert-icon').props()).toMatchObject({
icon,
ariaLabel,
})
diff --git a/packages/ui/__tests__/Badge.test.tsx b/packages/ui/__tests__/Badge.test.tsx
index 27112420d..593b09807 100644
--- a/packages/ui/__tests__/Badge.test.tsx
+++ b/packages/ui/__tests__/Badge.test.tsx
@@ -12,7 +12,7 @@ describe('Badge', () => {
it('Renders Badge with all necessary components', async () => {
const wrapper = await mountAndCheckA11Y()
- expect(wrapper.findDataTest('badge-icon').prop('size')).toBe('medium')
+ expect(wrapper.findDataTestFirst('badge-icon').prop('size')).toBe('medium')
expect(wrapper.findDataTest('badge-content').text()).toBe(badgeContent)
})
@@ -63,7 +63,7 @@ describe('Badge', () => {
const wrapper = await mountAndCheckA11Y()
expect(wrapper.findDataTest('badge-container').prop('className')).toContain(className)
- expect(wrapper.findDataTest('badge-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('badge-icon').props()).toMatchObject({
ariaLabel,
icon,
size: 'small',
diff --git a/packages/ui/__tests__/Calendar/CalendarRange.test.tsx b/packages/ui/__tests__/Calendar/CalendarRange.test.tsx
index ccbbc7ee7..880ad3992 100644
--- a/packages/ui/__tests__/Calendar/CalendarRange.test.tsx
+++ b/packages/ui/__tests__/Calendar/CalendarRange.test.tsx
@@ -28,7 +28,7 @@ describe('CalendarRange', () => {
})
// Icon
- expect(wrapper.findDataTest('calendar-range-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('calendar-range-icon').props()).toMatchObject({
icon: ArrowRight,
ariaLabel: 'Arrow Right',
})
@@ -72,7 +72,7 @@ describe('CalendarRange', () => {
})
// Icon
- expect(wrapper.findDataTest('calendar-range-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('calendar-range-icon').props()).toMatchObject({
icon: ArrowRight,
ariaLabel: 'Arrow Right',
})
diff --git a/packages/ui/__tests__/Card.test.tsx b/packages/ui/__tests__/Card.test.tsx
index b2dc5b8b1..2e4e2f094 100644
--- a/packages/ui/__tests__/Card.test.tsx
+++ b/packages/ui/__tests__/Card.test.tsx
@@ -59,8 +59,7 @@ describe('Card', () => {
expect(content).toBeInTheDocument()
expect(content.className).toEqual(styles.content)
expect(within(content).queryByText(cardContent)).toBeInTheDocument()
-
- expect(screen.queryByTestId('card-heading-icon')).not.toBeInTheDocument()
+ expect(screen.queryByTestId('card-heading-icon')).toBeInTheDocument()
})
it('renders caption', async () => {
diff --git a/packages/ui/__tests__/EmptyState.test.tsx b/packages/ui/__tests__/EmptyState.test.tsx
index 3fff7b7f2..d99a2cdb5 100644
--- a/packages/ui/__tests__/EmptyState.test.tsx
+++ b/packages/ui/__tests__/EmptyState.test.tsx
@@ -31,7 +31,7 @@ describe('EmptyState', () => {
)
expect(wrapper.findDataTest('empty-state-container').prop('className')).toBe(cn(styles.container))
- expect(wrapper.findDataTest('empty-state-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('empty-state-icon').props()).toMatchObject({
icon,
ariaLabel: iconLabel,
size: 'large',
@@ -59,7 +59,7 @@ describe('EmptyState', () => {
)
expect(wrapper.findDataTest('empty-state-container').prop('className')).toBe(cn(styles.container))
- expect(wrapper.findDataTest('empty-state-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('empty-state-icon').props()).toMatchObject({
icon,
ariaLabel: iconLabel,
size: 'large',
@@ -80,7 +80,7 @@ describe('EmptyState', () => {
)
expect(wrapper.findDataTest('empty-state-container').prop('className')).toBe(cn(styles.container, styles.large))
- expect(wrapper.findDataTest('empty-state-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('empty-state-icon').props()).toMatchObject({
icon,
ariaLabel: iconLabel,
size: 'xlarge',
@@ -108,7 +108,7 @@ describe('EmptyState', () => {
)
expect(wrapper.findDataTest('empty-state-container').prop('className')).toBe(cn(styles.container, styles.horizontal))
- expect(wrapper.findDataTest('empty-state-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('empty-state-icon').props()).toMatchObject({
icon,
ariaLabel: iconLabel,
size: 'large',
diff --git a/packages/ui/__tests__/Modal.test.tsx b/packages/ui/__tests__/Modal.test.tsx
index f17c0ebc8..67e97d1a0 100644
--- a/packages/ui/__tests__/Modal.test.tsx
+++ b/packages/ui/__tests__/Modal.test.tsx
@@ -98,7 +98,7 @@ describe('Modal', () => {
onRequestClose: onClose,
})
- expect(wrapper.findDataTest('modal-header-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('modal-header-icon').props()).toMatchObject({
icon: CloudLightning,
ariaLabel: iconAriaLabel,
})
diff --git a/packages/ui/__tests__/Notification.test.tsx b/packages/ui/__tests__/Notification.test.tsx
index 2a9b9264e..4990fd295 100644
--- a/packages/ui/__tests__/Notification.test.tsx
+++ b/packages/ui/__tests__/Notification.test.tsx
@@ -51,7 +51,7 @@ describe('Notification', () => {
expect(wrapper.findDataTest('notification').props()).toMatchObject({
className: cn(styles.notification, className),
})
- expect(wrapper.findDataTest('notification-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('notification-icon').props()).toMatchObject({
ariaLabel,
icon,
})
@@ -72,7 +72,7 @@ describe('Notification', () => {
expect(wrapper.findDataTest('notification').props()).toMatchObject({
className: cn(styles.notification, styles.success),
})
- expect(wrapper.findDataTest('notification-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('notification-icon').props()).toMatchObject({
ariaLabel: 'Check circle icon',
icon: CheckCircle,
})
diff --git a/packages/ui/__tests__/Overlay.test.tsx b/packages/ui/__tests__/Overlay.test.tsx
index 06f968fae..5ff78e87a 100644
--- a/packages/ui/__tests__/Overlay.test.tsx
+++ b/packages/ui/__tests__/Overlay.test.tsx
@@ -32,7 +32,7 @@ describe('Overlay', () => {
const reactModal = wrapper.find(ReactModal)
const overlayWrapper = reactModal.findDataTest('overlay-wrapper')
const header = overlayWrapper.findDataTest('overlay-header')
- const headerIcon = header.findDataTest('overlay-header-icon')
+ const headerIcon = header.findDataTestFirst('overlay-header-icon')
const headerTitle = header.findDataTest('overlay-header-title')
const headerCancelButton = header.findDataTestFirst('overlay-header-cancel-button')
const overlayContent = overlayWrapper.findDataTest('overlay-content')
diff --git a/packages/ui/__tests__/Pagination.test.tsx b/packages/ui/__tests__/Pagination.test.tsx
index 59d32a9c0..271cd72b0 100644
--- a/packages/ui/__tests__/Pagination.test.tsx
+++ b/packages/ui/__tests__/Pagination.test.tsx
@@ -94,6 +94,7 @@ const nextPrevButtonPropsBase: IconButtonProps = {
icon: ChevronRight,
ariaLabel: 'Base',
className: styles.iconButton,
+ 'data-test': 'pagination-buttons-next-page',
onClick: expect.anything(),
}
@@ -139,6 +140,7 @@ describe('Pagination', () => {
label: 'Rows',
options: pageSizeOptions.map((opt) => ({ value: opt, label: opt.toString() })),
size: 'small',
+ 'data-test': 'pagination-rows-per-page-select',
onChange: expect.anything(),
})
@@ -158,30 +160,48 @@ describe('Pagination', () => {
const buttons = wrapper.findDataTest('pagination-buttons').find(Button)
expect(buttons).toHaveLength(6)
- expect(buttons.at(0).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
className: `${styles.button} ${styles.selected}`,
children: '1',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(0).prop(key)).toEqual(value)
})
- expect(buttons.at(1).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
children: '2',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(1).prop(key)).toEqual(value)
})
- expect(buttons.at(2).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
children: '3',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(2).prop(key)).toEqual(value)
})
- expect(buttons.at(3).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
children: '4',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(3).prop(key)).toEqual(value)
})
- expect(buttons.at(4).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
children: '5',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(4).prop(key)).toEqual(value)
})
- expect(buttons.at(5).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
children: '2000',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(5).prop(key)).toEqual(value)
})
expect(wrapper.find(NumberField).props()).toEqual({
@@ -194,7 +214,7 @@ describe('Pagination', () => {
max: pageCount,
size: 'small',
value: currentPage,
-
+ 'data-test': 'pagination-jump-from-input-field',
onChange: expect.anything(),
})
})
@@ -230,39 +250,61 @@ describe('Pagination', () => {
const iconButton = wrapper.findDataTest('pagination-buttons').find(IconButton)
expect(iconButton).toHaveLength(2)
- expect(iconButton.at(0).props()).toEqual({
+
+ Object.entries({
...nextPrevButtonPropsBase,
icon: ChevronLeft,
ariaLabel: 'Previous page',
+ 'data-test': 'pagination-buttons-previous-page',
+ }).forEach(([key, value]) => {
+ expect(iconButton.at(0).prop(key)).toEqual(value)
})
- expect(iconButton.at(1).props()).toEqual({
+
+ Object.entries({
...nextPrevButtonPropsBase,
icon: ChevronRight,
ariaLabel: 'Next page',
+ }).forEach(([key, value]) => {
+ expect(iconButton.at(1).prop(key)).toEqual(value)
})
const buttons = wrapper.find(Button)
expect(buttons).toHaveLength(5)
- expect(buttons.at(0).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
children: '1',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(0).prop(key)).toEqual(value)
})
- expect(buttons.at(1).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
children: '999',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(1).prop(key)).toEqual(value)
})
- expect(buttons.at(2).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
className: `${styles.button} ${styles.selected}`,
children: '1000',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(2).prop(key)).toEqual(value)
})
- expect(buttons.at(3).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
children: '1001',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(3).prop(key)).toEqual(value)
})
- expect(buttons.at(4).props()).toEqual({
+ Object.entries({
...buttonPropsBase,
children: '2000',
+ 'data-test': 'pagination-buttons-go-to-page',
+ }).forEach(([key, value]) => {
+ expect(buttons.at(4).prop(key)).toEqual(value)
})
})
@@ -300,6 +342,7 @@ describe('Pagination', () => {
...nextPrevButtonPropsBase,
icon: ChevronLeft,
ariaLabel: 'Previous page',
+ 'data-test': 'pagination-buttons-previous-page',
})
const buttons = wrapper.find(Button)
@@ -307,27 +350,33 @@ describe('Pagination', () => {
expect(buttons.at(0).props()).toEqual({
...buttonPropsBase,
children: '1',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(buttons.at(1).props()).toEqual({
...buttonPropsBase,
children: '1996',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(buttons.at(2).props()).toEqual({
...buttonPropsBase,
children: '1997',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(buttons.at(3).props()).toEqual({
...buttonPropsBase,
children: '1998',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(buttons.at(4).props()).toEqual({
...buttonPropsBase,
children: '1999',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(buttons.at(5).props()).toEqual({
...buttonPropsBase,
className: `${styles.button} ${styles.selected}`,
children: '2000',
+ 'data-test': 'pagination-buttons-go-to-page',
})
})
@@ -502,14 +551,17 @@ describe('Pagination', () => {
...buttonPropsBase,
className: `${styles.button} ${styles.selected}`,
children: '1',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(buttons.at(1).props()).toEqual({
...buttonPropsBase,
children: '2',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(buttons.at(2).props()).toEqual({
...buttonPropsBase,
children: '2000',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(wrapper.findDataTest('pagination-range-of-shown-items').exists()).toBe(false)
@@ -534,6 +586,7 @@ describe('Pagination', () => {
label: 'Rows',
size: 'small',
options: pageSizeOptions.map((opt) => ({ value: opt, label: opt.toString() })),
+ 'data-test': 'pagination-rows-per-page-select',
onChange: expect.anything(),
})
@@ -548,6 +601,7 @@ describe('Pagination', () => {
max: pageCount,
size: 'small',
value: currentPage,
+ 'data-test': 'pagination-jump-from-input-field',
onChange: expect.anything(),
})
@@ -570,14 +624,17 @@ describe('Pagination', () => {
...buttonPropsBase,
className: `${styles.button} ${styles.selected}`,
children: '1',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(buttons.at(1).props()).toEqual({
...buttonPropsBase,
children: '2',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(buttons.at(2).props()).toEqual({
...buttonPropsBase,
children: '2000',
+ 'data-test': 'pagination-buttons-go-to-page',
})
expect(wrapper.findDataTest('pagination-range-of-shown-items').exists()).toBe(false)
diff --git a/packages/ui/__tests__/Select/SelectField.test.tsx b/packages/ui/__tests__/Select/SelectField.test.tsx
index 136aa4fab..179a21a1c 100644
--- a/packages/ui/__tests__/Select/SelectField.test.tsx
+++ b/packages/ui/__tests__/Select/SelectField.test.tsx
@@ -199,7 +199,7 @@ describe('SelectField', () => {
expect(wrapper.find(ReactSelect).exists()).toBe(true)
- expect(wrapper.findDataTest('select-field-icon-left').props()).toEqual({
+ expect(wrapper.findDataTestFirst('select-field-icon-left').props()).toEqual({
'data-test': 'select-field-icon-left',
containerClassName: styles.iconLeftContainer,
icon: iconLeft,
diff --git a/packages/ui/__tests__/Toast.test.tsx b/packages/ui/__tests__/Toast.test.tsx
index dbfd7a3fa..803d9ac5c 100644
--- a/packages/ui/__tests__/Toast.test.tsx
+++ b/packages/ui/__tests__/Toast.test.tsx
@@ -47,7 +47,7 @@ describe('Toast', () => {
expect(AlertElement.exists()).toBeTruthy()
expect(AlertElement.findDataTest('toast-content').text()).toBe(content)
- expect(wrapper.findDataTest('toast-icon').props()).toMatchObject({
+ expect(wrapper.findDataTestFirst('toast-icon').props()).toMatchObject({
icon,
ariaLabel,
})
diff --git a/packages/ui/src/Button.tsx b/packages/ui/src/Button.tsx
index 17c59ca9e..37a94592b 100644
--- a/packages/ui/src/Button.tsx
+++ b/packages/ui/src/Button.tsx
@@ -10,6 +10,7 @@ import { LinkRel, LinkTarget } from './Link'
import { Loader } from './Loader'
import styles from './Button.module.scss'
+import { DataTestProp } from '@hazelcast/helpers'
export type ButtonKind = 'primary' | 'secondary' | 'danger' | 'transparent'
@@ -105,7 +106,11 @@ export type ButtonTypeButtonProps = {
export type ButtonTypeProps = ButtonTypeAnchorProps | ButtonTypeButtonProps
-export type ButtonProps = ButtonCommonProps & T & ButtonAccessibleIconLeftProps & ButtonAccessibleIconRightProps
+export type ButtonProps = ButtonCommonProps &
+ T &
+ ButtonAccessibleIconLeftProps &
+ ButtonAccessibleIconRightProps &
+ DataTestProp
const capitalizeFirstCharacter = (str: string) => `${str[0].toUpperCase()}${str.slice(1)}`
diff --git a/packages/ui/src/Table/types.ts b/packages/ui/src/Table/types.ts
index 08b17a1e0..8bfe6a459 100644
--- a/packages/ui/src/Table/types.ts
+++ b/packages/ui/src/Table/types.ts
@@ -9,6 +9,8 @@ export type CellProps = {
row: RowType
}
+export type ColumnId = Extract
+
export type Column = {
id: string
}
@@ -17,7 +19,7 @@ export type ColumnType = {
minWidth?: number
maxWidth?: number
Footer?: string | ((props: HeaderProps) => JSX.Element | string)
- accessor?: Extract | ((data: D, index: number) => string | number | JSX.Element)
+ accessor?: ColumnId | ((data: D, index: number) => string | number | JSX.Element)
Cell?: string | ((props: CellProps) => JSX.Element)
canHide?: boolean // true by default
align?: 'left' | 'center' | 'right'
@@ -48,7 +50,7 @@ export type CellType = {
}
export type SortingRule = {
- id: Extract
+ id: ColumnId
desc?: boolean | undefined
}
@@ -56,14 +58,14 @@ export type InitialState = {
paginationOptions?: { pageIndex?: number; pageSize?: number }
}
-export type TableState = {
- hiddenColumns: string[]
- columnOrder: string[]
+export type TableState = {
+ hiddenColumns: ColumnId[]
+ columnOrder: ColumnId[]
pageSize: number
columnResizing: {
- columnWidths: Record
+ columnWidths: Record, number>
}
- sortBy: { id: string; desc: boolean }[]
+ sortBy: SortingRule[]
}
declare module '@tanstack/react-table' {
diff --git a/packages/ui/src/Table/useTrackTableState.ts b/packages/ui/src/Table/useTrackTableState.ts
index 43e5bbce1..e113c2994 100644
--- a/packages/ui/src/Table/useTrackTableState.ts
+++ b/packages/ui/src/Table/useTrackTableState.ts
@@ -2,9 +2,9 @@ import { useEffect, useRef } from 'react'
import { Table } from '@tanstack/react-table'
import { useRefValue } from '../hooks'
-import { TableState } from './types'
+import { ColumnId, SortingRule, TableState } from './types'
-export const useTrackTableState = (tableInstance: Table, onChange?: (state: TableState) => void) => {
+export const useTrackTableState = (tableInstance: Table, onChange?: (state: TableState) => void) => {
const {
columnOrder,
columnVisibility,
@@ -19,15 +19,15 @@ export const useTrackTableState = (tableInstance: Table, onChange?: (state
const cb = getOnChange()
if (cb && skippedInitialStateChangeRef.current) {
cb({
- sortBy: sorting,
+ sortBy: sorting as SortingRule[],
pageSize,
- columnOrder,
+ columnOrder: columnOrder as ColumnId[],
columnResizing: {
- columnWidths: columnSizing,
+ columnWidths: columnSizing as Record, number>,
},
- hiddenColumns: Object.entries(columnVisibility).reduce((res, [id, visible]) => {
+ hiddenColumns: Object.entries(columnVisibility).reduce[]>((res, [id, visible]) => {
if (!visible) {
- return [...res, id]
+ return [...res, id as ColumnId]
}
return res