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
258 changes: 248 additions & 10 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,38 +409,276 @@ export default defineConfig([
'react-x/prefer-use-state-lazy-initialization': 1,

// SonarJS rules
// https://github.com/SonarSource/eslint-plugin-sonarjs#rules
'sonarjs/no-all-duplicated-branches': 1,
'sonarjs/no-element-overwrite': 1,
'sonarjs/no-empty-collection': 1,
'sonarjs/no-extra-arguments': 0,
'sonarjs/no-identical-conditions': 1,
'sonarjs/no-identical-expressions': 1,
'sonarjs/no-ignored-return': 1,
'sonarjs/no-use-of-empty-return-value': 1,
'sonarjs/non-existent-operator': 1,
// https://github.com/SonarSource/SonarJS/blob/master/packages/jsts/src/rules/README.md#rules
'sonarjs/anchor-precedence': 1,
'sonarjs/argument-type': 1,
'sonarjs/arguments-order': 1,
'sonarjs/arguments-usage': 1,
'sonarjs/array-callback-without-return': 1,
'sonarjs/array-constructor': 1,
'sonarjs/arrow-function-convention': 0,
'sonarjs/assertions-in-tests': 1,
'sonarjs/aws-apigateway-public-api': 0,
'sonarjs/aws-ec2-rds-dms-public': 0,
'sonarjs/aws-ec2-unencrypted-ebs-volume': 0,
'sonarjs/aws-efs-unencrypted': 0,
'sonarjs/aws-iam-all-privileges': 0,
'sonarjs/aws-iam-all-resources-accessible': 0,
'sonarjs/aws-iam-privilege-escalation': 0,
'sonarjs/aws-iam-public-access': 0,
'sonarjs/aws-opensearchservice-domain': 0,
'sonarjs/aws-rds-unencrypted-databases': 0,
'sonarjs/aws-restricted-ip-admin-access': 0,
'sonarjs/aws-s3-bucket-granted-access': 0,
'sonarjs/aws-s3-bucket-insecure-http': 0,
'sonarjs/aws-s3-bucket-public-access': 0,
'sonarjs/aws-s3-bucket-server-encryption': 0,
'sonarjs/aws-s3-bucket-versioning': 0,
'sonarjs/aws-sagemaker-unencrypted-notebook': 0,
'sonarjs/aws-sns-unencrypted-topics': 0,
'sonarjs/aws-sqs-unencrypted-queue': 0,
'sonarjs/bitwise-operators': 1,
'sonarjs/block-scoped-var': 1,
'sonarjs/bool-param-default': 0,
'sonarjs/call-argument-line': 1,
'sonarjs/certificate-transparency': 1,
'sonarjs/chai-determinate-assertion': 1,
'sonarjs/class-name': 1,
'sonarjs/class-prototype': 1,
'sonarjs/code-eval': 1,
'sonarjs/cognitive-complexity': 0,
'sonarjs/comma-or-logical-or-case': 1,
'sonarjs/comment-regex': 1,
'sonarjs/concise-regex': 1,
'sonarjs/conditional-indentation': 1,
'sonarjs/confidential-information-logging': 1,
'sonarjs/constructor-for-side-effects': 1,
'sonarjs/content-length': 1,
'sonarjs/content-security-policy': 1,
'sonarjs/cookie-no-httponly': 1,
'sonarjs/cookies': 1,
'sonarjs/cors': 1,
'sonarjs/csrf': 1,
'sonarjs/cyclomatic-complexity': 0,
'sonarjs/declarations-in-global-scope': 0,
'sonarjs/deprecation': 0,
'sonarjs/destructuring-assignment-syntax': 1,
'sonarjs/different-types-comparison': 0,
'sonarjs/disabled-auto-escaping': 1,
'sonarjs/disabled-resource-integrity': 1,
'sonarjs/disabled-timeout': 1,
'sonarjs/dns-prefetching': 1,
'sonarjs/duplicates-in-character-class': 1,
'sonarjs/dynamically-constructed-templates': 1,
'sonarjs/elseif-without-else': 0,
'sonarjs/empty-string-repetition': 1,
'sonarjs/encryption': 1,
'sonarjs/encryption-secure-mode': 1,
'sonarjs/enforce-trailing-comma': 0,
'sonarjs/existing-groups': 1,
'sonarjs/expression-complexity': 0,
'sonarjs/file-header': 0,
'sonarjs/file-name-differ-from-class': 1,
'sonarjs/file-permissions': 1,
'sonarjs/file-uploads': 1,
'sonarjs/fixme-tag': 1,
'sonarjs/for-in': 1,
'sonarjs/for-loop-increment-sign': 1,
'sonarjs/frame-ancestors': 1,
'sonarjs/function-inside-loop': 1,
'sonarjs/function-name': 0,
'sonarjs/function-return-type': 0,
'sonarjs/future-reserved-words': 1,
'sonarjs/generator-without-yield': 1,
'sonarjs/hardcoded-secret-signatures': 1,
'sonarjs/hashing': 1,
'sonarjs/hidden-files': 1,
'sonarjs/in-operator-type-error': 1,
'sonarjs/inconsistent-function-call': 1,
'sonarjs/index-of-compare-to-positive-number': 1,
'sonarjs/insecure-cookie': 1,
'sonarjs/insecure-jwt-token': 1,
'sonarjs/inverted-assertion-arguments': 1,
'sonarjs/jsx-no-leaked-render': 1,
'sonarjs/label-position': 1,
'sonarjs/link-with-target-blank': 1,
'sonarjs/max-lines': 0,
'sonarjs/max-lines-per-function': 0,
'sonarjs/max-switch-cases': 0,
'sonarjs/max-union-size': 0,
'sonarjs/misplaced-loop-counter': 1,
'sonarjs/nested-control-flow': 0,
'sonarjs/new-operator-misuse': 1,
'sonarjs/no-all-duplicated-branches': 1,
'sonarjs/no-alphabetical-sort': 1,
'sonarjs/no-angular-bypass-sanitization': 1,
'sonarjs/no-array-delete': 1,
'sonarjs/no-associative-arrays': 1,
'sonarjs/no-async-constructor': 1,
'sonarjs/no-built-in-override': 1,
'sonarjs/no-case-label-in-switch': 1,
'sonarjs/no-clear-text-protocols': 1,
'sonarjs/no-code-after-done': 1,
'sonarjs/no-collapsible-if': 1,
'sonarjs/no-collection-size-mischeck': 1,
'sonarjs/no-commented-code': 1,
'sonarjs/no-control-regex': 1,
'sonarjs/no-dead-store': 1,
'sonarjs/no-delete-var': 1,
'sonarjs/no-duplicate-in-composite': 1,
'sonarjs/no-duplicate-string': 0,
'sonarjs/no-duplicated-branches': 1,
'sonarjs/no-element-overwrite': 1,
'sonarjs/no-empty-after-reluctant': 1,
'sonarjs/no-empty-alternatives': 1,
'sonarjs/no-empty-character-class': 1,
'sonarjs/no-empty-collection': 1,
'sonarjs/no-empty-group': 1,
'sonarjs/no-empty-test-file': 1,
'sonarjs/no-equals-in-for-termination': 1,
'sonarjs/no-exclusive-tests': 1,
'sonarjs/no-extra-arguments': 0,
'sonarjs/no-fallthrough': 1,
'sonarjs/no-for-in-iterable': 1,
'sonarjs/no-function-declaration-in-block': 1,
'sonarjs/no-global-this': 1,
'sonarjs/no-globals-shadowing': 1,
'sonarjs/no-gratuitous-expressions': 1,
'sonarjs/no-hardcoded-ip': 1,
'sonarjs/no-hardcoded-passwords': 1,
'sonarjs/no-hardcoded-secrets': 1,
'sonarjs/no-hook-setter-in-body': 1,
'sonarjs/no-identical-conditions': 1,
'sonarjs/no-identical-expressions': 1,
'sonarjs/no-identical-functions': 1,
'sonarjs/no-ignored-exceptions': 1,
'sonarjs/no-ignored-return': 1,
'sonarjs/no-implicit-dependencies': 1,
'sonarjs/no-implicit-global': 1,
'sonarjs/no-in-misuse': 1,
'sonarjs/no-incomplete-assertions': 1,
'sonarjs/no-inconsistent-returns': 0,
'sonarjs/no-incorrect-string-concat': 1,
'sonarjs/no-internal-api-use': 1,
'sonarjs/no-intrusive-permissions': 1,
'sonarjs/no-invalid-regexp': 1,
'sonarjs/no-invariant-returns': 1,
'sonarjs/no-inverted-boolean-check': 1,
'sonarjs/no-ip-forward': 1,
'sonarjs/no-labels': 1,
'sonarjs/no-literal-call': 1,
'sonarjs/no-mime-sniff': 1,
'sonarjs/no-misleading-array-reverse': 1,
'sonarjs/no-misleading-character-class': 1,
'sonarjs/no-mixed-content': 1,
'sonarjs/no-nested-assignment': 1,
'sonarjs/no-nested-conditional': 0,
'sonarjs/no-nested-functions': 0,
'sonarjs/no-nested-incdec': 1,
'sonarjs/no-nested-switch': 1,
'sonarjs/no-nested-template-literals': 1,
'sonarjs/no-os-command-from-path': 1,
'sonarjs/no-parameter-reassignment': 1,
'sonarjs/no-primitive-wrappers': 1,
'sonarjs/no-redundant-assignments': 1,
'sonarjs/no-redundant-boolean': 1,
'sonarjs/no-redundant-jump': 1,
'sonarjs/no-redundant-optional': 1,
'sonarjs/no-redundant-parentheses': 1,
'sonarjs/no-reference-error': 0,
'sonarjs/no-referrer-policy': 1,
'sonarjs/no-regex-spaces': 1,
'sonarjs/no-require-or-define': 1,
'sonarjs/no-return-type-any': 1,
'sonarjs/no-same-argument-assert': 1,
'sonarjs/no-same-line-conditional': 1,
'sonarjs/no-selector-parameter': 0,
'sonarjs/no-skipped-tests': 1,
'sonarjs/no-small-switch': 1,
'sonarjs/no-sonar-comments': 1,
'sonarjs/no-tab': 1,
'sonarjs/no-table-as-layout': 1,
'sonarjs/no-try-promise': 1,
'sonarjs/no-undefined-argument': 1,
'sonarjs/no-undefined-assignment': 0,
'sonarjs/no-unenclosed-multiline-block': 1,
'sonarjs/no-uniq-key': 1,
'sonarjs/no-unsafe-unzip': 1,
'sonarjs/no-unthrown-error': 1,
'sonarjs/no-unused-collection': 1,
'sonarjs/no-unused-function-argument': 1,
'sonarjs/no-unused-vars': 1,
'sonarjs/no-use-of-empty-return-value': 1,
'sonarjs/no-useless-catch': 1,
'sonarjs/no-useless-increment': 1,
'sonarjs/no-useless-intersection': 1,
'sonarjs/no-useless-react-setstate': 1,
'sonarjs/no-variable-usage-before-declaration': 1,
'sonarjs/no-vue-bypass-sanitization': 1,
'sonarjs/no-weak-cipher': 1,
'sonarjs/no-weak-keys': 1,
'sonarjs/no-wildcard-import': 0,
'sonarjs/non-existent-operator': 1,
'sonarjs/non-number-in-arithmetic-expression': 1,
'sonarjs/null-dereference': 1,
'sonarjs/object-alt-content': 1,
'sonarjs/operation-returning-nan': 1,
'sonarjs/os-command': 1,
'sonarjs/post-message': 1,
'sonarjs/prefer-default-last': 1,
'sonarjs/prefer-immediate-return': 1,
'sonarjs/prefer-object-literal': 1,
'sonarjs/prefer-promise-shorthand': 1,
'sonarjs/prefer-read-only-props': 0,
'sonarjs/prefer-regexp-exec': 1,
'sonarjs/prefer-single-boolean-return': 1,
'sonarjs/prefer-type-guard': 1,
'sonarjs/prefer-while': 1,
'sonarjs/process-argv': 1,
'sonarjs/production-debug': 1,
'sonarjs/pseudo-random': 0,
'sonarjs/public-static-readonly': 1,
'sonarjs/publicly-writable-directories': 1,
'sonarjs/reduce-initial-value': 1,
'sonarjs/redundant-type-aliases': 0,
'sonarjs/regex-complexity': 1,
'sonarjs/regular-expr': 1,
'sonarjs/review-blockchain-mnemonic': 1,
'sonarjs/session-regeneration': 1,
'sonarjs/shorthand-property-grouping': 0,
'sonarjs/single-char-in-character-classes': 1,
'sonarjs/single-character-alternation': 1,
'sonarjs/slow-regex': 1,
'sonarjs/sockets': 1,
'sonarjs/sql-queries': 1,
'sonarjs/stable-tests': 1,
'sonarjs/standard-input': 1,
'sonarjs/stateful-regex': 1,
'sonarjs/strict-transport-security': 1,
'sonarjs/strings-comparison': 1,
'sonarjs/super-invocation': 1,
'sonarjs/table-header': 1,
'sonarjs/table-header-reference': 1,
'sonarjs/test-check-exception': 1,
'sonarjs/todo-tag': 0,
'sonarjs/too-many-break-or-continue-in-loop': 0,
'sonarjs/unicode-aware-regex': 1,
'sonarjs/unused-import': 1,
'sonarjs/unused-named-groups': 1,
'sonarjs/unverified-certificate': 1,
'sonarjs/unverified-hostname': 1,
'sonarjs/updated-const-var': 1,
'sonarjs/updated-loop-counter': 0,
'sonarjs/use-type-alias': 1,
'sonarjs/useless-string-operation': 1,
'sonarjs/values-not-convertible-to-numbers': 1,
'sonarjs/variable-name': 1,
'sonarjs/void-use': 1,
'sonarjs/weak-ssl': 1,
'sonarjs/web-sql-database': 1,
'sonarjs/x-powered-by': 1,
'sonarjs/xml-parser-xxe': 1,
'sonarjs/xpath': 1,

// @typescript-eslint/eslint-plugin rules
// https://typescript-eslint.io/rules/#supported-rules
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"eslint-plugin-jest-dom": "^5.5.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-sonarjs": "^3.0.5",
"eslint-plugin-sonarjs": "^3.0.6",
"eslint-plugin-testing-library": "^7.13.5",
"jspdf": "^4.0.0",
"jspdf-autotable": "^5.0.2",
Expand Down
2 changes: 1 addition & 1 deletion src/DataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
previousRowIdx < rows.length
) {
const step = sign(rowIdx - previousRowIdx);
for (let i = previousRowIdx + step; i !== rowIdx; i += step) {
for (let i = previousRowIdx + step; i < rowIdx; i += step) {
const row = rows[i];
if (isRowSelectionDisabled?.(row) === true) continue;
if (checked) {
Expand Down
2 changes: 1 addition & 1 deletion src/GroupRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function GroupedRow<R, SR>({
gridRowStart,
groupBy,
toggleGroup,
isRowSelectionDisabled,
isRowSelectionDisabled: _isRowSelectionDisabled,
...props
}: GroupRowRendererProps<R, SR>) {
// Select is always the first column
Expand Down
17 changes: 10 additions & 7 deletions test/browser/keyboardNavigation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import { getSelectedCell, setup, tabIntoGrid, testCount, validateCellPosition }

type Row = undefined;

const rows: readonly Row[] = new Array(100);
const rows: readonly Row[] = Array.from({ length: 100 });
const topSummaryRows: readonly Row[] = [undefined];
const bottomSummaryRows: readonly Row[] = [undefined, undefined];

const columns = [
const columns: readonly Column<Row, Row>[] = [
SelectColumn,
{ key: 'col2', name: 'col2' },
{ key: 'col3', name: 'col3' },
{ key: 'col4', name: 'col4' },
{ key: 'col5', name: 'col5' },
{ key: 'col6', name: 'col6' },
{ key: 'col7', name: 'col7' }
] as const satisfies Column<Row, Row>[];
];

test('keyboard navigation', async () => {
await setup({ columns, rows, topSummaryRows, bottomSummaryRows }, true);
Expand Down Expand Up @@ -122,7 +122,7 @@ test('arrow and tab navigation', async () => {
});

test('grid enter/exit', async () => {
await setup({ columns, rows: new Array(5), bottomSummaryRows }, true);
await setup<Row, Row>({ columns, rows: Array.from({ length: 5 }), bottomSummaryRows }, true);

const beforeButton = page.getByRole('button', { name: 'Before' });
const afterButton = page.getByRole('button', { name: 'After' });
Expand Down Expand Up @@ -164,7 +164,7 @@ test('grid enter/exit', async () => {
});

test('navigation with focusable cell renderer', async () => {
await setup({ columns, rows: new Array(1), bottomSummaryRows }, true);
await setup<Row, Row>({ columns, rows: Array.from({ length: 1 }), bottomSummaryRows }, true);
await tabIntoGrid();
await userEvent.keyboard('{arrowdown}');
await validateCellPosition(0, 1);
Expand Down Expand Up @@ -205,7 +205,10 @@ test('navigation when header and summary rows have focusable elements', async ()
}
];

await setup({ columns, rows: new Array(2), bottomSummaryRows: [1, 2] }, true);
await setup<Row, number>(
{ columns, rows: Array.from({ length: 2 }), bottomSummaryRows: [1, 2] },
true
);
await tabIntoGrid();

// should set focus on the header filter
Expand Down Expand Up @@ -320,7 +323,7 @@ test('reset selected cell when row is removed', async () => {
});

test('should not change the left and right arrow behavior for right to left languages', async () => {
await setup({ rows, columns, direction: 'rtl' }, true);
await setup<Row, Row>({ columns, rows, direction: 'rtl' }, true);
await tabIntoGrid();
await validateCellPosition(0, 0);
await userEvent.tab();
Expand Down
8 changes: 4 additions & 4 deletions test/browser/virtualization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ function setupGrid(
frozenColumnCount = 0,
summaryRowCount = 0
) {
const columns: Column<unknown>[] = [];
const rows = new Array(rowCount);
const topSummaryRows = new Array(summaryRowCount).fill(null);
const bottomSummaryRows = new Array(summaryRowCount).fill(null);
const columns: Column<unknown, null>[] = [];
const rows = Array.from({ length: rowCount });
const topSummaryRows = Array.from({ length: summaryRowCount }, () => null);
const bottomSummaryRows = Array.from({ length: summaryRowCount }, () => null);

for (let i = 0; i < columnCount; i++) {
const key = String(i);
Expand Down