Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
6 changes: 0 additions & 6 deletions src/contexts/ColumnParametersContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,3 @@ export interface ColumnParameters extends ColumnConfig, Pick<ColumnDescriptor, '
*/
type ColumnParametersContextType = ColumnParameters[]
export const ColumnParametersContext = createContext<ColumnParametersContextType>([])

/**
* A set of the names of the sortable columns. Used to check if a column is sortable, and to provide the toggle function in the OrderByContext.
*/
type SortableColumnsContextType = Set<string>
export const SortableColumnsContext = createContext<SortableColumnsContextType>(new Set())
13 changes: 10 additions & 3 deletions src/contexts/DataContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContext } from 'react'

import type { ColumnDescriptor, DataFrame } from '../helpers/dataframe/types.js'
import type { DataFrame } from '../helpers/dataframe/types.js'

/**
* The data frame, limited to the getRowNumber, getCell, and fetch methods.
Expand All @@ -9,11 +9,18 @@ import type { ColumnDescriptor, DataFrame } from '../helpers/dataframe/types.js'
*/
export type DataFrameMethods = Pick<DataFrame, 'getRowNumber' | 'getCell' | 'fetch'>
export type DataFrameWithoutMethods = Omit<DataFrame, 'getRowNumber' | 'getCell' | 'fetch'>
/**
* A set of the names of the sortable columns. Used to check if a column is sortable, and to provide the toggle function in the OrderByContext.
*/
type SortableColumnsContextType = Set<string>

Comment thread
severo marked this conversation as resolved.
Outdated
export const DataVersionContext = createContext<number>(0)
export const NumRowsContext = createContext<number>(0)
export const ColumnDescriptorsContext = createContext<Pick<ColumnDescriptor, 'name' | 'sortable'>[]>([])
export const NumColumnsContext = createContext<number>(0)
/**
* The list of column names, in the order they are defined in the data frame.
*/
export const ColumnNamesContext = createContext<string[]>([])
export const SortableColumnsContext = createContext<SortableColumnsContextType>(new Set())
Comment thread
severo marked this conversation as resolved.
Outdated
export const ExclusiveSortContext = createContext<boolean>(false)
export const DataFrameMethodsContext = createContext<DataFrameMethods>({
getRowNumber: () => undefined,
Expand Down
21 changes: 7 additions & 14 deletions src/providers/ColumnParametersProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type ReactNode, useContext, useMemo } from 'react'

import { type ColumnParameters, ColumnParametersContext, SortableColumnsContext } from '../contexts/ColumnParametersContext.js'
import { ColumnDescriptorsContext } from '../contexts/DataContext.js'
import { type ColumnParameters, ColumnParametersContext } from '../contexts/ColumnParametersContext.js'
import { ColumnNamesContext } from '../contexts/DataContext.js'
import type { HighTableProps } from '../types.js'

type Props = Pick<HighTableProps, 'columnConfiguration'> & {
Expand All @@ -15,18 +15,13 @@ type Props = Pick<HighTableProps, 'columnConfiguration'> & {
* It merges the column descriptors from the data frame with the user-provided configuration.
Comment thread
severo marked this conversation as resolved.
Outdated
*/
export function ColumnParametersProvider({ columnConfiguration, children }: Props) {
const columnDescriptors = useContext(ColumnDescriptorsContext)

// A column is sortable iif it's marked as sortable in the column descriptors from the data frame. The user configuration can't change that.
const sortableColumns = useMemo(() => {
return new Set(columnDescriptors.filter(({ sortable }) => sortable).map(({ name }) => name))
}, [columnDescriptors])
const columnNames = useContext(ColumnNamesContext)

const columnParameters = useMemo(() => {
const inHeader = new Set(columnDescriptors.map(c => c.name))
const inHeader = new Set(columnNames)

// Build descriptors following DataFrame columns order
const cols: ColumnParameters[] = columnDescriptors.map(({ name }, index) => ({
const cols: ColumnParameters[] = columnNames.map((name, index) => ({
Comment thread
severo marked this conversation as resolved.
Outdated
name,
index,
...columnConfiguration?.[name] ?? {},
Expand All @@ -43,13 +38,11 @@ export function ColumnParametersProvider({ columnConfiguration, children }: Prop
}

return cols
}, [columnDescriptors, columnConfiguration])
}, [columnNames, columnConfiguration])

return (
<ColumnParametersContext.Provider value={columnParameters}>
<SortableColumnsContext.Provider value={sortableColumns}>
{children}
</SortableColumnsContext.Provider>
{children}
</ColumnParametersContext.Provider>
)
}
4 changes: 2 additions & 2 deletions src/providers/ColumnWidthsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'r

import { ColumnParametersContext } from '../contexts/ColumnParametersContext.js'
import { ColumnWidthsContext } from '../contexts/ColumnWidthsContext.js'
import { NumColumnsContext } from '../contexts/DataContext.js'
import { ColumnNamesContext } from '../contexts/DataContext.js'
import { TableCornerWidthContext } from '../contexts/TableCornerSizeContext.js'
import { ViewportWidthContext } from '../contexts/ViewportSizeContext.js'
import { cellStyle } from '../helpers/width.js'
Expand Down Expand Up @@ -77,7 +77,7 @@ export function ColumnWidthsProvider({ children, localStorageKey, minWidth }: Co
/** Current table corner width (used to compute the maximum total width) */
const tableCornerWidth = useContext(TableCornerWidthContext)
/** Number of columns (used to initialize the widths array, and compute the widths) */
const numColumns = useContext(NumColumnsContext)
const numColumns = useContext(ColumnNamesContext).length

// Number of columns
if (!Number.isInteger(numColumns) || numColumns < 0) {
Expand Down
14 changes: 7 additions & 7 deletions src/providers/DataProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type ReactNode, useEffect, useState } from 'react'

import type { DataFrameWithoutMethods } from '../contexts/DataContext.js'
import { ColumnDescriptorsContext, DataFrameMethodsContext, DataKeyContext, DataVersionContext, ExclusiveSortContext, NumColumnsContext, NumRowsContext } from '../contexts/DataContext.js'
import { ColumnNamesContext, DataFrameMethodsContext, DataKeyContext, DataVersionContext, ExclusiveSortContext, NumRowsContext, SortableColumnsContext } from '../contexts/DataContext.js'
import type { HighTableProps } from '../types.js'

// Assign stable numeric ids to data instances without triggering state
Expand Down Expand Up @@ -63,9 +63,9 @@ function KeyedDataProvider({ children, data }: KeyedDataProviderProps) {

// Some dataframe properties are expected to be stable for a given data frame.
// We keep their initial value, no setter.
const [columnDescriptors] = useState(() => data.columnDescriptors.map(({ name, sortable }) => ({ name, sortable })))
const numColumns = columnDescriptors.length
const [exclusiveSort] = useState(() => data.exclusiveSort === true)
const [columnNames] = useState(() => data.columnDescriptors.map(({ name }) => name))
const [sortableColumns] = useState(() => new Set(data.columnDescriptors.filter(({ sortable }) => sortable).map(({ name }) => name)))
Comment thread
severo marked this conversation as resolved.

// Synchronize version and numRows with data frame events (external system - useEffect is needed)
useEffect(() => {
Expand All @@ -88,15 +88,15 @@ function KeyedDataProvider({ children, data }: KeyedDataProviderProps) {
// Multiple contexts, to avoid unnecessary re-renders of the components consuming the API when only the data changes, and vice-versa. See https://react.dev/reference/react/useContext#caveats for more details.
return (
<DataVersionContext.Provider value={version}>
<ColumnDescriptorsContext.Provider value={columnDescriptors}>
<NumColumnsContext.Provider value={numColumns}>
<ColumnNamesContext.Provider value={columnNames}>
<SortableColumnsContext.Provider value={sortableColumns}>
<ExclusiveSortContext.Provider value={exclusiveSort}>
<NumRowsContext.Provider value={numRows}>
{children}
</NumRowsContext.Provider>
</ExclusiveSortContext.Provider>
</NumColumnsContext.Provider>
</ColumnDescriptorsContext.Provider>
</SortableColumnsContext.Provider>
</ColumnNamesContext.Provider>
</DataVersionContext.Provider>
)
}
3 changes: 1 addition & 2 deletions src/providers/OrderByProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { type ReactNode, useContext, useMemo } from 'react'

import { SortableColumnsContext } from '../contexts/ColumnParametersContext.js'
import { ExclusiveSortContext } from '../contexts/DataContext.js'
import { ExclusiveSortContext, SortableColumnsContext } from '../contexts/DataContext.js'
import { OrderByContext, SortInfoAndActionsByColumnContext } from '../contexts/OrderByContext.js'
import { type OrderBy, toggleColumn, toggleColumnExclusive } from '../helpers/sort.js'
import { useInputState } from '../hooks/useInputState.js'
Expand Down
50 changes: 25 additions & 25 deletions test/components/ColumnHeader.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { act, fireEvent } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'

import ColumnHeader from '../../src/components/ColumnHeader.js'
import { ColumnDescriptorsContext, NumColumnsContext } from '../../src/contexts/DataContext.js'
import { ColumnNamesContext } from '../../src/contexts/DataContext.js'
import { SortInfoAndActionsByColumnContext } from '../../src/contexts/OrderByContext.js'
import { getOffsetWidth } from '../../src/helpers/width.js'
import { ColumnParametersProvider } from '../../src/providers/ColumnParametersProvider.js'
Expand Down Expand Up @@ -62,11 +62,11 @@ describe('ColumnHeader', () => {

it('measures the width if canMeasureWidth is true', () => {
render(
<NumColumnsContext.Provider value={1}>
<ColumnNamesContext.Provider value={['test']}>
<ColumnWidthsProvider minWidth={10}>
<table><thead><tr><ColumnHeader columnName="test" {...defaultProps} canMeasureWidth={true} /></tr></thead></table>
</ColumnWidthsProvider>
</NumColumnsContext.Provider>
</ColumnNamesContext.Provider>
)
expect(getOffsetWidth).toHaveBeenCalled()
})
Expand Down Expand Up @@ -95,11 +95,11 @@ describe('ColumnHeader', () => {
localStorage.setItem(cacheKey, JSON.stringify([savedWidth]))

const { getByRole } = render(
<NumColumnsContext.Provider value={1}>
<ColumnNamesContext.Provider value={['test']}>
<ColumnWidthsProvider localStorageKey={cacheKey} minWidth={10}>
<table><thead><tr><ColumnHeader columnName="test" {...defaultProps} /></tr></thead></table>
</ColumnWidthsProvider>
</NumColumnsContext.Provider>
</ColumnNamesContext.Provider>
)
const header = getByRole('columnheader')
expect(header.style.maxWidth).toEqual(`${savedWidth}px`)
Expand All @@ -113,11 +113,11 @@ describe('ColumnHeader', () => {
localStorage.setItem(cacheKey, JSON.stringify([savedWidth]))

const { getByRole } = render(
<NumColumnsContext.Provider value={1}>
<ColumnNamesContext.Provider value={['test']}>
<ColumnWidthsProvider localStorageKey={cacheKey} minWidth={minWidth}>
<table><thead><tr><ColumnHeader columnName="test" {...defaultProps} /></tr></thead></table>
</ColumnWidthsProvider>
</NumColumnsContext.Provider>
</ColumnNamesContext.Provider>
)
const header = getByRole('columnheader')
expect(header.style.maxWidth).toEqual(expected)
Expand All @@ -130,11 +130,11 @@ describe('ColumnHeader', () => {
localStorage.setItem(cacheKey, JSON.stringify([savedWidth]))

const { user, getByRole } = render(
<NumColumnsContext.Provider value={1}>
<ColumnNamesContext.Provider value={['test']}>
<ColumnWidthsProvider localStorageKey={cacheKey} minWidth={minWidth}>
<table><thead><tr><ColumnHeader columnName="test" canMeasureWidth={true} {...defaultProps} /></tr></thead></table>
</ColumnWidthsProvider>
</NumColumnsContext.Provider>
</ColumnNamesContext.Provider>
)
const header = getByRole('columnheader')
const resizeHandle = getByRole('spinbutton')
Expand All @@ -156,11 +156,11 @@ describe('ColumnHeader', () => {
localStorage.setItem(cacheKey, JSON.stringify([savedWidth]))

const { user, getByRole } = render(
<NumColumnsContext.Provider value={1}>
<ColumnNamesContext.Provider value={['test']}>
<ColumnWidthsProvider localStorageKey={cacheKey} minWidth={10}>
<table><thead><tr><ColumnHeader columnName="test" {...defaultProps} /></tr></thead></table>
</ColumnWidthsProvider>
</NumColumnsContext.Provider>
</ColumnNamesContext.Provider>
)

// Simulate resizing the column
Expand All @@ -187,18 +187,18 @@ describe('ColumnHeader', () => {
localStorage.setItem(cacheKey, JSON.stringify([savedWidth]))

const columnConfiguration = { test: { minWidth: columnMinWidth } }
const columnDescriptors = [{ name: 'test' }]
const columnNames = ['test']

const { user, getByRole } = render(
<ColumnDescriptorsContext.Provider value={columnDescriptors}>
<NumColumnsContext.Provider value={1}>
<ColumnNamesContext.Provider value={columnNames}>
<ColumnNamesContext.Provider value={['test']}>
<ColumnParametersProvider columnConfiguration={columnConfiguration}>
Comment thread
severo marked this conversation as resolved.
Outdated
<ColumnWidthsProvider localStorageKey={cacheKey} minWidth={10}>
<table><thead><tr><ColumnHeader columnName="test" canMeasureWidth={true} {...defaultProps} /></tr></thead></table>
</ColumnWidthsProvider>
</ColumnParametersProvider>
</NumColumnsContext.Provider>
</ColumnDescriptorsContext.Provider>
</ColumnNamesContext.Provider>
</ColumnNamesContext.Provider>
)

const header = getByRole('columnheader')
Expand All @@ -225,18 +225,18 @@ describe('ColumnHeader', () => {
localStorage.setItem(cacheKey, JSON.stringify([savedWidth]))

const columnConfiguration = { test: { minWidth: columnMinWidth } }
const columnDescriptors = [{ name: 'test' }]
const columnNames = ['test']

const { user, getByRole } = render(
<ColumnDescriptorsContext.Provider value={columnDescriptors}>
<NumColumnsContext.Provider value={1}>
<ColumnNamesContext.Provider value={columnNames}>
<ColumnNamesContext.Provider value={['test']}>
<ColumnParametersProvider columnConfiguration={columnConfiguration}>
Comment thread
severo marked this conversation as resolved.
Outdated
<ColumnWidthsProvider localStorageKey={cacheKey} minWidth={globalMinWidth}>
<table><thead><tr><ColumnHeader columnName="test" canMeasureWidth={true} {...defaultProps} /></tr></thead></table>
</ColumnWidthsProvider>
</ColumnParametersProvider>
</NumColumnsContext.Provider>
</ColumnDescriptorsContext.Provider>
</ColumnNamesContext.Provider>
</ColumnNamesContext.Provider>
)

const header = getByRole('columnheader')
Expand All @@ -263,20 +263,20 @@ describe('ColumnHeader', () => {
localStorage.setItem(cacheKey2, JSON.stringify([width2]))

const { rerender, getByRole } = render(
<NumColumnsContext.Provider value={1}>
<ColumnNamesContext.Provider value={['test']}>
<ColumnWidthsProvider localStorageKey={cacheKey} minWidth={10}>
<table><thead><tr><ColumnHeader columnName="test" {...defaultProps} /></tr></thead></table>
</ColumnWidthsProvider>
</NumColumnsContext.Provider>
</ColumnNamesContext.Provider>
)
const header = getByRole('columnheader')
expect(header.style.maxWidth).toEqual(`${width1}px`)
rerender(
<NumColumnsContext.Provider value={1}>
<ColumnNamesContext.Provider value={['test']}>
<ColumnWidthsProvider localStorageKey={cacheKey2} minWidth={10}>
<table><thead><tr><ColumnHeader columnName="test" {...defaultProps} /></tr></thead></table>
</ColumnWidthsProvider>
</NumColumnsContext.Provider>
</ColumnNamesContext.Provider>
)
expect(header.style.maxWidth).toEqual(`${width2}px`)
})
Expand Down
Loading