diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8cd9252d9b..176a846ef0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @amanmahajan7 @nstepien +* @grafana/dataviz-squad diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c00752d3c9..023ef422b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,14 +5,19 @@ on: - main pull_request: +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false - - uses: actions/setup-node@v7 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 with: node-version-file: 'package.json' check-latest: true @@ -48,7 +53,7 @@ jobs: - name: Upload test failure artifacts if: failure() - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: test-artifacts path: | @@ -58,21 +63,56 @@ jobs: if-no-files-found: ignore - name: Upload coverage - uses: codecov/codecov-action@v7 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7 with: token: ${{ secrets.CODECOV_TOKEN }} - - name: Deploy gh-pages - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + publish: + name: Publish to NPM + runs-on: ubuntu-latest + needs: [test] + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + permissions: + contents: write # push the release tag + id-token: write # npm trusted publishing + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + + # only publish when package.json version changes - https://github.com/EndBug/version-check + - name: Check version changes + uses: EndBug/version-check@36ff30f37c7deabe56a30caa043d127be658c425 # 2.1.5 + id: version_check + with: + diff-search: true + + - name: Setup Node for npm registry + if: steps.version_check.outputs.changed == 'true' + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version-file: 'package.json' + check-latest: true + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + if: steps.version_check.outputs.changed == 'true' + run: npm ci + + - name: Bundle + if: steps.version_check.outputs.changed == 'true' + run: node --run build + + - name: Publish to npm + if: steps.version_check.outputs.changed == 'true' + run: npm publish --tag latest + + - name: Push tag and create GitHub release + if: steps.version_check.outputs.changed == 'true' + env: + GH_TOKEN: ${{ github.token }} + NEW_VERSION: ${{ steps.version_check.outputs.version }} run: | - git config --global user.email 'action@github.com' - git config --global user.name 'GitHub Action' - git fetch origin gh-pages - git worktree add gh-pages gh-pages - cd gh-pages - git rm -r . - mv ../dist/* . - touch .nojekyll - git add . - git commit -m "gh-pages deployment" || echo "Nothing to commit" - git push -f https://comcast:${{secrets.GITHUB_TOKEN}}@github.com/Comcast/react-data-grid.git + git tag "v${NEW_VERSION}" + git push origin "v${NEW_VERSION}" + gh release create "v${NEW_VERSION}" --title "v${NEW_VERSION}" --generate-notes diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 59c00efc32..0000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: Publish - -on: - workflow_dispatch: - inputs: - version: - description: 'Version to publish (e.g., 0.1.1, 0.2.0, 1.0.0)' - required: true - type: string - -jobs: - publish: - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - contents: write - id-token: write - - steps: - - uses: actions/checkout@v7 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v7 - with: - node-version-file: 'package.json' - check-latest: true - registry-url: 'https://registry.npmjs.org' - - - name: Install dependencies - run: npm ci - - - name: Typecheck - run: node --run typecheck - - - name: ESLint - run: node --run eslint - - - name: Oxfmt - run: node --run format:check - - - name: Bundle - run: node --run build - - - name: Build website - run: node --run build:website - - - name: Check routeTree.gen.ts - run: git diff --exit-code website/routeTree.gen.ts - - - name: Install Playwright Browsers - run: npx playwright install chromium firefox - timeout-minutes: 2 - - - name: Test - run: node --run test:ci - timeout-minutes: 4 - - - name: Update version - run: | - git config user.name "${{ github.actor }}" - git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" - npm version ${{ inputs.version }} - - # https://docs.npmjs.com/trusted-publishers - - name: Publish to npm - run: npm publish --tag latest - - - name: Push tag - run: git push origin v${{ inputs.version }} - - - name: Create GitHub Release - run: gh release create v${{ inputs.version }} --title "v${{ inputs.version }}" --generate-notes - env: - GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/update-screenshots.yml b/.github/workflows/update-screenshots.yml deleted file mode 100644 index 5201002371..0000000000 --- a/.github/workflows/update-screenshots.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Update Screenshots - -on: - pull_request: - types: [labeled] - workflow_dispatch: - -jobs: - update-screenshots: - if: ${{ github.event.label.name == 'Update Screenshots' || github.event_name == 'workflow_dispatch' }} - runs-on: ubuntu-latest - timeout-minutes: 10 - - permissions: - contents: write - pull-requests: write - - # one at a time per branch - concurrency: - group: update-screenshots@${{ github.head_ref }} - cancel-in-progress: true - - steps: - - uses: actions/checkout@v7 - with: - ref: ${{ github.head_ref }} - - - uses: actions/setup-node@v7 - with: - node-version-file: 'package.json' - check-latest: true - - - name: Remove label - if: github.event_name == 'pull_request' - run: gh pr edit --remove-label 'Update Screenshots' - env: - GH_TOKEN: ${{ github.token }} - - - name: Install dependencies - run: npm ci - - name: Install Playwright Browsers - run: npx playwright install chromium firefox - timeout-minutes: 2 - - - name: Update screenshots - run: | - rm -r test/**/screenshots/** - node --run test:ci:update - timeout-minutes: 4 - - - name: Push new screenshots - run: | - git config user.name "${{ github.actor }}" - git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" - git add "test/**/screenshots/**" - git diff-index --quiet HEAD || git commit -m "Update screenshots" - git push diff --git a/README.md b/README.md index 7ad7807480..fe69903430 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ [![codecov-badge]][codecov-url] [![ci-badge]][ci-url] +_Note: This package is a Grafana-maintained fork of [react-data-grid](https://github.com/Comcast/react-data-grid), published as `@grafana/react-data-grid`. It adds auto-height support for non-virtualized rendering and a few accessibility improvements on top of upstream._ + The DataGrid component is designed to handle large datasets efficiently while offering a rich set of features for customization and interactivity. ## Table of contents @@ -881,7 +883,7 @@ function MyGrid() { Function to generate unique IDs for group rows. If not provided, a default implementation is used that concatenates parent and group keys with `__`. -###### `rowHeight?: Maybe) => number)>` +###### `rowHeight?: Maybe) => number)>` **Note:** Unlike `DataGrid`, the `rowHeight` function receives [`RowHeightArgs`](#rowheightargstrow) which includes a `type` property to distinguish between regular rows and group rows: diff --git a/package-lock.json b/package-lock.json index 75e8588587..4e8ac93794 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "react-data-grid", - "version": "7.0.0-beta.60", + "name": "@grafana/react-data-grid", + "version": "7.0.0-beta.59", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "react-data-grid", - "version": "7.0.0-beta.60", + "name": "@grafana/react-data-grid", + "version": "7.0.0-beta.59", "license": "MIT", "devDependencies": { "@eslint-react/eslint-plugin": "^5.6.0", diff --git a/package.json b/package.json index 0175302559..3e6e5218be 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { - "name": "react-data-grid", - "version": "7.0.0-beta.60", + "name": "@grafana/react-data-grid", + "version": "7.0.0-beta.59", "description": "Feature-rich and customizable data grid React component", "keywords": [ "data grid", "react" ], - "homepage": "https://github.com/Comcast/react-data-grid#readme", - "bugs": "https://github.com/Comcast/react-data-grid/issues", + "homepage": "https://github.com/grafana/react-data-grid#readme", + "bugs": "https://github.com/grafana/react-data-grid/issues", "license": "MIT", "repository": { "type": "git", - "url": "git+https://github.com/Comcast/react-data-grid.git" + "url": "git+https://github.com/grafana/react-data-grid.git" }, "files": [ "lib" @@ -31,6 +31,9 @@ "default": "./lib/index.js" } }, + "publishConfig": { + "access": "public" + }, "scripts": { "start": "vite serve --clearScreen false", "preview": "vite preview", diff --git a/src/DataGrid.tsx b/src/DataGrid.tsx index 5c750a00fc..402f944916 100644 --- a/src/DataGrid.tsx +++ b/src/DataGrid.tsx @@ -1,10 +1,13 @@ -import { useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'; import type { Key, KeyboardEvent } from 'react'; +import { useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { flushSync } from 'react-dom'; import { + type ActivePosition, HeaderRowSelectionChangeContext, HeaderRowSelectionContext, + type HeaderRowSelectionContextValue, + type PartialPosition, RowSelectionChangeContext, useActivePosition, useCalculatedColumns, @@ -14,10 +17,7 @@ import { useScrollState, useScrollToPosition, useViewportColumns, - useViewportRows, - type ActivePosition, - type HeaderRowSelectionContextValue, - type PartialPosition + useViewportRows } from './hooks'; import { assertIsValidKeyGetter, @@ -45,7 +45,6 @@ import type { CellMouseEventHandler, CellNavigationMode, CellPasteArgs, - PositionChangeArgs, Column, ColumnOrColumnGroup, ColumnWidths, @@ -53,11 +52,12 @@ import type { FillEvent, Maybe, Position, + PositionChangeArgs, Renderers, RowsChangeData, - SetActivePositionOptions, SelectHeaderRowEvent, SelectRowEvent, + SetActivePositionOptions, SortColumn } from './types'; import { defaultRenderCell } from './Cell'; @@ -138,7 +138,7 @@ export interface DataGridProps extends Sha * Height of each row in pixels * @default 35 */ - rowHeight?: Maybe) => number)>; + rowHeight?: Maybe) => number)>; /** * Height of the header row in pixels * @default 35 @@ -303,9 +303,13 @@ export function DataGrid(props: DataGridPr const renderCheckbox = renderers?.renderCheckbox ?? defaultRenderers?.renderCheckbox ?? defaultRenderCheckbox; const noRowsFallback = renderers?.noRowsFallback ?? defaultRenderers?.noRowsFallback; - const enableVirtualization = rawEnableVirtualization ?? true; + const enableVirtualization = rawEnableVirtualization ?? typeof rawRowHeight !== 'string'; const direction = rawDirection ?? 'ltr'; + if (enableVirtualization && typeof rowHeight === 'string') { + throw new Error('`rowHeight` cannot be a string when `enableVirtualization` is true.'); + } + /** * ref */ @@ -413,7 +417,9 @@ export function DataGrid(props: DataGridPr maxRowIdx, setDraggedOverRowIdx }); - const { setScrollToPosition, scrollToPositionElement } = useScrollToPosition({ gridRef }); + const { setScrollToPosition, scrollToPositionElement } = useScrollToPosition({ + gridRef + }); const defaultGridComponents = useMemo( () => ({ @@ -457,10 +463,16 @@ export function DataGrid(props: DataGridPr findRowIdx } = useViewportRows({ rows, - rowHeight, clientHeight, scrollTop, - enableVirtualization + enableVirtualization, + ...(typeof rowHeight === 'string' + ? { + rowHeight, + gridRef, + gridHeight + } + : { rowHeight }) }); const { @@ -690,7 +702,11 @@ export function DataGrid(props: DataGridPr if (isSelectable && shiftKey && key === ' ') { assertIsValidKeyGetter(rowKeyGetter); const rowKey = rowKeyGetter(row); - selectRow({ row, checked: !selectedRows.has(rowKey), isShiftClick: false }); + selectRow({ + row, + checked: !selectedRows.has(rowKey), + isShiftClick: false + }); // prevent scrolling event.preventDefault(); return; @@ -776,7 +792,11 @@ export function DataGrid(props: DataGridPr const indexes: number[] = []; for (let i = startRowIdx; i < endRowIdx; i++) { if (isCellEditable({ rowIdx: i, idx })) { - const updatedRow = onFill!({ columnKey: column.key, sourceRow, targetRow: rows[i] }); + const updatedRow = onFill!({ + columnKey: column.key, + sourceRow, + targetRow: rows[i] + }); if (updatedRow !== rows[i]) { updatedRows[i] = updatedRow; indexes.push(i); @@ -971,6 +991,7 @@ export function DataGrid(props: DataGridPr return (