@ -99,7 +99,6 @@ export function getColumns(
for ( const [ fieldIndex , field ] of data . fields . entries ( ) ) {
const fieldTableOptions = ( field . config . custom || { } ) as TableFieldOptions ;
if ( fieldTableOptions . hidden ) {
continue ;
}
@ -319,43 +318,73 @@ function toNumber(value: any): number {
}
export function getFooterItems (
filterFields : Array < { id : string ; field : Field } > ,
filterFields : Array < { id : string ; field? : Field } | undefined > ,
values : any [ number ] ,
options : TableFooterCalc ,
theme2 : GrafanaTheme2
) : FooterItem [ ] {
/ *
Here , ` filterFields ` is passed to as the ` headerGroups[0].headers ` array that was destrcutured from the ` useTable ` hook .
Unfortunately , since the ` headerGroups ` object is data based ONLY on the rendered "non-hidden" column headers ,
it will NOT include the Row Number column if it has been toggled off . This will shift the rendering of the footer left 1 column ,
creating an off - by - one issue . This is why we test for a ` field.id ` of "0" . If the condition is truthy , the togglable Row Number column is being rendered ,
and we can proceed normally . If not , we must add the field data in its place so that the footer data renders in the expected column .
Here , ` filterFields ` is passed as the ` headerGroups[0].headers ` array
that was destructured from the ` useTable ` hook . Unfortunately , since
the ` headerGroups ` object is data based ONLY on the rendered "non-hidden"
column headers , it will NOT include the Row Number column if it has been
toggled off . This will shift the rendering of the footer left 1 column ,
creating an off - by - one issue . This is why we test for a ` field.id ` of "0" .
If the condition is truthy , the togglable Row Number column is being rendered ,
and we can proceed normally . If not , we must add the field data in its place
so that the footer data renders in the expected column .
* /
if ( ! filterFields . some ( ( field ) = > field . id === '0' ) ) {
if ( ! filterFields . some ( ( field ) = > field ? . id === '0' ) ) {
const length = values . length ;
// Build the additional field that will correct the off-by-one footer issue.
const fieldToAdd = { id : '0' , field : buildFieldsForOptionalRowNums ( length ) } ;
filterFields = [ fieldToAdd , . . . filterFields ] ;
}
/ *
The FooterItems [ ] are calculated using both the ` headerGroups[0].headers `
( filterFields ) and ` rows ` ( values ) destructured from the useTable ( ) hook .
This cacluation is based on the data from each index in ` filterFields `
array as well as the corresponding index in the ` values ` array .
When the user hides a column through an override , the getColumns ( )
hook is invoked , removes said hidden column , sends the updated column
data to the useTable ( ) hook , which then builds ` headerGroups[0].headers `
without the hidden column . However , it doesn ' t remove the hidden column
from the ` row ` data , instead it substututes the hidden column row data
with an ` undefined ` value . Therefore , the ` row ` array length never changes ,
despite the ` headerGroups[0].headers ` length changing at every column removal .
This makes all footer reduce calculations AFTER the first hidden column
in the ` headerGroups[0].headers ` break , since the indexing of both
arrays is no longer in parity .
So , here we simply recursively test for the "hidden" columns
from ` headerGroups[0].headers ` . Each column has an ID property that corresponds
to its own index , therefore if ( ` filterField.id ` !== ` String(index) ` ) ,
we know there is one or more hidden columns ; at which point we update
the index with an ersatz placeholder with just an ` id ` property .
* /
addMissingColumnIndex ( filterFields ) ;
return filterFields . map ( ( data , i ) = > {
if ( data . field . type !== FieldType . number ) {
// Then test for numerical data - this will filter out placeholder `filterFields` as well.
if ( data ? . field ? . type !== FieldType . number ) {
// Show the reducer type ("Total", "Range", "Count", "Delta", etc) in the first non "Row Number" column, only if it cannot be numerically reduced.
if ( i === 1 && options . reducer && options . reducer . length > 0 ) {
const reducer = fieldReducers . get ( options . reducer [ 0 ] ) ;
return reducer . name ;
}
// Otherwise return `undefined`, which will render an <EmptyCell />.
// R ender an <EmptyCell />.
return undefined ;
}
let newField = clone ( data . field ) ;
newField . values = new ArrayVector ( values [ i ] ) ;
newField . values = new ArrayVector ( values [ data . id ] ) ;
newField . state = undefined ;
data . field = newField ;
if ( options . fields && options . fields . length > 0 ) {
const f = options . fields . find ( ( f ) = > f === data . field . name ) ;
const f = options . fields . find ( ( f ) = > f === data ? . field ? . name ) ;
if ( f ) {
return getFormattedValue ( data . field , options . reducer , theme2 ) ;
}
@ -477,3 +506,30 @@ export const defaultRowNumberColumnFieldData: Omit<Field, 'values'> = {
} ,
} ,
} ;
/ * *
* This recurses through an array of ` filterFields ` ( Array < { id : string ; field? : Field } | undefined > )
* and adds back the missing indecies that are removed due to hiding a column through an panel override .
* This is necessary to create Array . length parity between the ` filterFields ` array and the ` values ` array ( any [ number ] ) ,
* since the footer value calculations are based on the corresponding index values of both arrays .
*
* @remarks
* This function uses the splice ( ) method , and therefore mutates the array .
*
* @param columns - An array of ` filterFields ` ( Array < { id : string ; field? : Field } | undefined > ) .
* @returns void ; this function returns nothing ; it only mutates values as a side effect .
* /
function addMissingColumnIndex ( columns : Array < { id : string ; field? : Field } | undefined > ) : void {
const missingIndex = columns . findIndex ( ( field , index ) = > field ? . id !== String ( index ) ) ;
// Base case
if ( missingIndex === - 1 ) {
return ;
}
// Splice in missing column
columns . splice ( missingIndex , 0 , { id : String ( missingIndex ) } ) ;
// Recurse
addMissingColumnIndex ( columns ) ;
}