grafana/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts

1821 lines
52 KiB
TypeScript
Raw Normal View History

Table: Move library to react-data-grid (#102482) * Changes galore * Freedom 🗽 * Add feature flag * Latest changes * Basic auto cell type * Partially working bar-gauge * Brokenish but whatevs * Include the toggle doc * TableNG: Context menu (#94094) * feat(table-ng): context menu init commit * betterer * feat(table-ng): re-use contextmenu component * fix(table-ng): close context menu issue * TableNG: Sorting columns (#94200) feat(table-ng): sorting column * fix feature toggle conflict * TableNG: Sorting with custom table header (#95351) * TableNG: Header Toggle (#95310) * TableNG: Multi-column sorting (#95395) feat(table-ng): multi-sorting * TableNG: Column width options (#95426) * feat(table-ng): column width * mouse handle drag event * move resizing task * TableNG: Fix icon sorting direction (#95653) fix(table-ng): sorting icon direction * TableNG: Show table footer (#95313) * TableNG: Show table footer * Revert betterer * Update betterer * Incorporate reducer calculations into footer * Update imports in FooterRow * Use getFooterValue for summary cell render * TableNG: Min column width (#95657) * feat(table-ng): min column width * feat(table-ng): set a min width constant * TableNG: Column alignment (#95679) * feat(table-ng): column alignment * cleaning * feat(table-ng): header cell alignment * optimizations * feat(table-ng): footer cell alignment * calc counter * TableNG: use compiled fn for columns -> records conversion (#95914) * use compiled fn for columns -> records conversion * TableNG: Move key rev and fix width overrides (#95921) * meh * add index to records --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sparkline Cell Parity (#95690) * sparkline value * todo * Remove unsued shallowField * Pass justifyContent to sparkline --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: BarGauge cell updates (#95521) * fix bargauge cell * merge and fix props * cleanup imports * TableNG: Text wrapping (#96041) * feat(table-ng): fix long text cell width * feat(table-ng): fix long text cell width 2 * comment out column rowHeight * fix long text column width * fix types * fix types * naming * Check current header cell ref is defined for key * cleaning * make table re-render when data changed * eslint --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Text overflow (#96641) * feat(table-ng): text overflow * cleaning * TableNG: Fix footer for count (#96802) * TableNG: Table column filter (#96767) * feat(table-ng): add filter form --------- Co-authored-by: drew08t <drew08@gmail.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: On column resize trigger (#97004) chore(table-ng): trigger on resize on text wrap only * TableNG: Improve sort performance (#97767) * TableNG: Improve sort performance * clean a bit * a bit more * Remove const that was breaking sort --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Fix sorting (#98141) fix(table-ng): sorting * TableNG: fix multi sorting (#98668) fix(table-ng): multi sorting * TableNG: Column re-size handler (#98901) * feat(table-ng): column re-size handler * TableNG: Fix footer calcs with no reducer (#99347) * TableNG: Update renderHeaderCell with filter dep (#99483) * TableNG: Updated styles for demo (#99530) * style proposal: table ng * chore: revert gauge cell custom stuff * TableNG: Cross-filter (#99459) * feat(table-ng): cross-filter * fix filter update issue * fix filter reset issue * Fix spacebar for filter input --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Filter perfomance optimization (#99620) fix(table-ng): filter performance optimization * TableNG: Refine styling closer to original table (#99625) * TableNG: Support groupToNestedTableTransform (#97134) * TableNG: Support groupToNestedTableTransform * Fix merge issues * Force refresh for now * Remove log * Fix some conflicts * Fix more conflicts * Help avoid clash with compiled frameToRecords keys * Make subtable height unconstrained * Support show field names in nested tables toggle * TableNG: Fix footer + some other misc updates (#99846) fix: footer fixes huzzah * TableNG: Styling - Update styling for cells (#99851) * fix(table-ng): bargauge inner width issue * TableNG: Move header cell component (#99844) * fix(table-ng): move header cell into separate file * Fix sub table --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Auto cell feature parity (#100095) * feat(table-ng): auto cell feature parity * TableNG: JSON cell implementation + hover fixes (#100152) * feat: tableNG json cell + auto fixes * chore: add comment * add justify content to json cell --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Fix cell hover issue (#100207) * fix(table-ng): cell hover issue * better commenting * TableNG: Text color cell (#100120) feat(table-ng): text color cell feature parity * TableNG: Image cell implementation (#100132) * feat: tableNG image cell * fix: incorporate justify-content correctly * chore: pass down cell options from fieldConfig --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Cell height performance improvement (#100544) * chore: perf improvement * chore: minor fix * Update packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * chore: fix betterer --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Add pagination (#100165) * TableNG: Add pagination * TableNG: Get collapsed icon state correct + update `rowHeight` (#100556) * fix: get collapsed icon state correct + update condition for calculating row height * chore: some cleanup! * chore: naming to avoid confusion with local state name * TableNG: Add support for `DataLinksCell` (#100459) * TableNG: Improve sub table styling (#100772) * Move files temporarily to fix conflicts * Fix feature flag conflicts * Move files back to cell dir * TableNG: Update inner height of bar gauge cell (#100996) * fix: change inner height of bar gauge cell * chore: move function to utils, cleanup * Remove testing line * TableNG: Add bottom border to column headers + fix footer styling (#101016) * feat: add bottom border to column headers for table parity * feat: summary row style fix * chore: remove redundant style --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for `ActionsCell` (#101024) * TableNG: Cell hover styles + header resize handler indicator (#100770) * fix: tableNG styles * chore: clean up comments * chore: remove column header stuffz for now * fix: refactor to transform/translate + resize handler hover styling * chore: re-think approach - change a lot of things * chore: most recent iteration * chore: wait i like this better * chore: hoist into colors function + clean it up! * moar better * chore: define constants for clarity * chore: calculate rbga to rgb values given background color --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix scoll hover jumpy behavior (#101085) * fix(table-ng): hover scroll jumping * Account for panel padding during pagination --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix imports (#101059) * fix(table-ng): clean imports Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sorted rows dependent upon filtered rows (#100985) TableNG: Improve multi-sort performance * TableNG: Fix sparkline width (#101164) fix(table-ng): sparkline width * TableNG: Type TableNG (#101257) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * chore: remove unused rowIndex prop --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for datalinks (#100769) Co-authored-by: drew08t <drew08@gmail.com> * Chore: Remove unused import (#102064) remove unused import * Update betterer * BarGauge: Remove z-index (#102220) fix(bargauge): remove z-index * TableNG: Refactor + testing (#102045) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * feat: refactor + tests * chore: fix import lint errors * betterer * chore: fix image cell * chore: revert width function * add test * betterer * chore: fix sorting + add tests * chore: pr feedback --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix table suggestion (#102497) fix: defensively guard against missing cellOptions * TableNG: Footer fields calc fix (#102487) * fix: respect footer fields calc selection * chore: add test * TableNG: Image cell hover fix (#102489) fix: image cell hover * TableNG: Persist scrollbars during re render (#102559) * TableNG: Persist scrollbars during re render * Update improved betterer * TableNG: Fix column width override (#102474) * fix(table): column width override * TableNG: Add support for crosshair share (#102410) * TableNG: Add support for crosshair share * Add tests * TableNG: Fix table ng tests (#102645) fix: cellType causing tests to fail * Remove empty file * TableNG: Update util tests (#102646) * TableNG: Add column type icon (#102686) * chore(table-ng): add column type icon * chore(table-ng): clean styling * Use core internationalization outside grafana ui * Import popover directly * Add count to grafana-ui locales * TableNG: Change feature flag to tableNextGen (#102814) Change feature flag to tableNextGen * TableNG: Add row colors (#102706) * chore(table-ng): add row colors * clean up * fix params * fix(table-ng): cell color background indexing --------- Co-authored-by: Kyle Cunningham <kyle@codeincarnate.com> Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: Adela Almasan <adela.almasan@grafana.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Adela Almasan <88068998+adela-almasan@users.noreply.github.com> Co-authored-by: Alex Spencer <52186778+alexjonspencer1@users.noreply.github.com>
2025-03-26 11:57:57 +08:00
import { SortColumn } from 'react-data-grid';
import {
createDataFrame,
createTheme,
DataFrame,
DisplayProcessor,
DisplayValue,
Field,
FieldType,
GrafanaTheme2,
LinkModel,
ValueLinkConfig,
} from '@grafana/data';
import {
BarGaugeDisplayMode,
TableCellBackgroundDisplayMode,
TableCellDisplayMode,
TableCellHeight,
} from '@grafana/schema';
import { Trans } from '../../../utils/i18n';
import { PanelContext } from '../../PanelChrome';
import { mapFrameToDataGrid, myRowRenderer } from './TableNG';
import { COLUMN, TABLE } from './constants';
import { TableColumn } from './types';
import {
convertRGBAToHex,
extractPixelValue,
frameToRecords,
getAlignmentFactor,
getCellColors,
getCellHeight,
getCellLinks,
getCellOptions,
getComparator,
getDefaultRowHeight,
getFooterItemNG,
getFooterStyles,
getIsNestedTable,
getRowHeight,
getTextAlign,
handleSort,
isTextCell,
migrateTableDisplayModeToCellOptions,
shouldTextOverflow,
} from './utils';
const data = createDataFrame({
fields: [
{
name: 'Time',
type: FieldType.time,
values: [],
config: {
custom: {
width: undefined, // For width distribution testing
displayMode: 'auto',
},
},
},
{
name: 'Value',
type: FieldType.number,
values: [],
display: ((v: any) => ({
text: String(v),
numeric: v,
color: undefined,
prefix: undefined,
suffix: undefined,
})) as DisplayProcessor,
config: {
custom: {
width: 100,
displayMode: 'basic',
},
},
},
{
name: 'Message',
type: FieldType.string,
values: [],
config: {
custom: {
align: 'center',
},
},
},
],
meta: {
custom: {
noHeader: false, // For header rendering tests
},
},
});
const calcsRef = { current: [] };
const headerCellRefs = { current: {} };
const crossFilterOrder = { current: [] };
const crossFilterRows = { current: {} };
const sortColumnsRef = { current: [] };
const mockOptions = {
osContext: null,
rows: [],
sortedRows: [],
Table: Move library to react-data-grid (#102482) * Changes galore * Freedom 🗽 * Add feature flag * Latest changes * Basic auto cell type * Partially working bar-gauge * Brokenish but whatevs * Include the toggle doc * TableNG: Context menu (#94094) * feat(table-ng): context menu init commit * betterer * feat(table-ng): re-use contextmenu component * fix(table-ng): close context menu issue * TableNG: Sorting columns (#94200) feat(table-ng): sorting column * fix feature toggle conflict * TableNG: Sorting with custom table header (#95351) * TableNG: Header Toggle (#95310) * TableNG: Multi-column sorting (#95395) feat(table-ng): multi-sorting * TableNG: Column width options (#95426) * feat(table-ng): column width * mouse handle drag event * move resizing task * TableNG: Fix icon sorting direction (#95653) fix(table-ng): sorting icon direction * TableNG: Show table footer (#95313) * TableNG: Show table footer * Revert betterer * Update betterer * Incorporate reducer calculations into footer * Update imports in FooterRow * Use getFooterValue for summary cell render * TableNG: Min column width (#95657) * feat(table-ng): min column width * feat(table-ng): set a min width constant * TableNG: Column alignment (#95679) * feat(table-ng): column alignment * cleaning * feat(table-ng): header cell alignment * optimizations * feat(table-ng): footer cell alignment * calc counter * TableNG: use compiled fn for columns -> records conversion (#95914) * use compiled fn for columns -> records conversion * TableNG: Move key rev and fix width overrides (#95921) * meh * add index to records --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sparkline Cell Parity (#95690) * sparkline value * todo * Remove unsued shallowField * Pass justifyContent to sparkline --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: BarGauge cell updates (#95521) * fix bargauge cell * merge and fix props * cleanup imports * TableNG: Text wrapping (#96041) * feat(table-ng): fix long text cell width * feat(table-ng): fix long text cell width 2 * comment out column rowHeight * fix long text column width * fix types * fix types * naming * Check current header cell ref is defined for key * cleaning * make table re-render when data changed * eslint --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Text overflow (#96641) * feat(table-ng): text overflow * cleaning * TableNG: Fix footer for count (#96802) * TableNG: Table column filter (#96767) * feat(table-ng): add filter form --------- Co-authored-by: drew08t <drew08@gmail.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: On column resize trigger (#97004) chore(table-ng): trigger on resize on text wrap only * TableNG: Improve sort performance (#97767) * TableNG: Improve sort performance * clean a bit * a bit more * Remove const that was breaking sort --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Fix sorting (#98141) fix(table-ng): sorting * TableNG: fix multi sorting (#98668) fix(table-ng): multi sorting * TableNG: Column re-size handler (#98901) * feat(table-ng): column re-size handler * TableNG: Fix footer calcs with no reducer (#99347) * TableNG: Update renderHeaderCell with filter dep (#99483) * TableNG: Updated styles for demo (#99530) * style proposal: table ng * chore: revert gauge cell custom stuff * TableNG: Cross-filter (#99459) * feat(table-ng): cross-filter * fix filter update issue * fix filter reset issue * Fix spacebar for filter input --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Filter perfomance optimization (#99620) fix(table-ng): filter performance optimization * TableNG: Refine styling closer to original table (#99625) * TableNG: Support groupToNestedTableTransform (#97134) * TableNG: Support groupToNestedTableTransform * Fix merge issues * Force refresh for now * Remove log * Fix some conflicts * Fix more conflicts * Help avoid clash with compiled frameToRecords keys * Make subtable height unconstrained * Support show field names in nested tables toggle * TableNG: Fix footer + some other misc updates (#99846) fix: footer fixes huzzah * TableNG: Styling - Update styling for cells (#99851) * fix(table-ng): bargauge inner width issue * TableNG: Move header cell component (#99844) * fix(table-ng): move header cell into separate file * Fix sub table --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Auto cell feature parity (#100095) * feat(table-ng): auto cell feature parity * TableNG: JSON cell implementation + hover fixes (#100152) * feat: tableNG json cell + auto fixes * chore: add comment * add justify content to json cell --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Fix cell hover issue (#100207) * fix(table-ng): cell hover issue * better commenting * TableNG: Text color cell (#100120) feat(table-ng): text color cell feature parity * TableNG: Image cell implementation (#100132) * feat: tableNG image cell * fix: incorporate justify-content correctly * chore: pass down cell options from fieldConfig --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Cell height performance improvement (#100544) * chore: perf improvement * chore: minor fix * Update packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * chore: fix betterer --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Add pagination (#100165) * TableNG: Add pagination * TableNG: Get collapsed icon state correct + update `rowHeight` (#100556) * fix: get collapsed icon state correct + update condition for calculating row height * chore: some cleanup! * chore: naming to avoid confusion with local state name * TableNG: Add support for `DataLinksCell` (#100459) * TableNG: Improve sub table styling (#100772) * Move files temporarily to fix conflicts * Fix feature flag conflicts * Move files back to cell dir * TableNG: Update inner height of bar gauge cell (#100996) * fix: change inner height of bar gauge cell * chore: move function to utils, cleanup * Remove testing line * TableNG: Add bottom border to column headers + fix footer styling (#101016) * feat: add bottom border to column headers for table parity * feat: summary row style fix * chore: remove redundant style --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for `ActionsCell` (#101024) * TableNG: Cell hover styles + header resize handler indicator (#100770) * fix: tableNG styles * chore: clean up comments * chore: remove column header stuffz for now * fix: refactor to transform/translate + resize handler hover styling * chore: re-think approach - change a lot of things * chore: most recent iteration * chore: wait i like this better * chore: hoist into colors function + clean it up! * moar better * chore: define constants for clarity * chore: calculate rbga to rgb values given background color --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix scoll hover jumpy behavior (#101085) * fix(table-ng): hover scroll jumping * Account for panel padding during pagination --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix imports (#101059) * fix(table-ng): clean imports Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sorted rows dependent upon filtered rows (#100985) TableNG: Improve multi-sort performance * TableNG: Fix sparkline width (#101164) fix(table-ng): sparkline width * TableNG: Type TableNG (#101257) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * chore: remove unused rowIndex prop --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for datalinks (#100769) Co-authored-by: drew08t <drew08@gmail.com> * Chore: Remove unused import (#102064) remove unused import * Update betterer * BarGauge: Remove z-index (#102220) fix(bargauge): remove z-index * TableNG: Refactor + testing (#102045) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * feat: refactor + tests * chore: fix import lint errors * betterer * chore: fix image cell * chore: revert width function * add test * betterer * chore: fix sorting + add tests * chore: pr feedback --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix table suggestion (#102497) fix: defensively guard against missing cellOptions * TableNG: Footer fields calc fix (#102487) * fix: respect footer fields calc selection * chore: add test * TableNG: Image cell hover fix (#102489) fix: image cell hover * TableNG: Persist scrollbars during re render (#102559) * TableNG: Persist scrollbars during re render * Update improved betterer * TableNG: Fix column width override (#102474) * fix(table): column width override * TableNG: Add support for crosshair share (#102410) * TableNG: Add support for crosshair share * Add tests * TableNG: Fix table ng tests (#102645) fix: cellType causing tests to fail * Remove empty file * TableNG: Update util tests (#102646) * TableNG: Add column type icon (#102686) * chore(table-ng): add column type icon * chore(table-ng): clean styling * Use core internationalization outside grafana ui * Import popover directly * Add count to grafana-ui locales * TableNG: Change feature flag to tableNextGen (#102814) Change feature flag to tableNextGen * TableNG: Add row colors (#102706) * chore(table-ng): add row colors * clean up * fix params * fix(table-ng): cell color background indexing --------- Co-authored-by: Kyle Cunningham <kyle@codeincarnate.com> Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: Adela Almasan <adela.almasan@grafana.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Adela Almasan <88068998+adela-almasan@users.noreply.github.com> Co-authored-by: Alex Spencer <52186778+alexjonspencer1@users.noreply.github.com>
2025-03-26 11:57:57 +08:00
setContextMenuProps: () => {},
setFilter: () => {},
setIsInspecting: () => {},
data,
width: 800,
height: 600,
fieldConfig: {
defaults: {
custom: {
width: 'auto',
minWidth: COLUMN.MIN_WIDTH,
cellOptions: {
wrapText: false,
},
},
},
overrides: [
{
matcher: { id: 'byName', options: 'Value' },
properties: [{ id: 'width', value: 100 }],
},
],
},
columnTypes: {},
columnWidth: 'auto',
defaultLineHeight: 40,
defaultRowHeight: 40,
expandedRows: [],
filter: {},
headerCellRefs,
crossFilterOrder,
crossFilterRows,
isCountRowsSet: false,
styles: { cell: '' },
theme: createTheme(),
setSortColumns: () => {},
sortColumnsRef,
textWrap: false,
};
describe('TableNG utils', () => {
describe('mapFrameToDataGrid', () => {
it('take data frame and return array of columns', () => {
const columns = mapFrameToDataGrid({
frame: data,
calcsRef,
options: mockOptions,
handlers: { onCellExpand: () => {}, onColumnResize: () => {} },
availableWidth: mockOptions.width,
});
// Test column structure
expect(columns).toHaveLength(3);
// Test Time column
expect(columns[0]).toMatchObject({
key: 'Time',
name: 'Time',
field: expect.objectContaining({
name: 'Time',
type: FieldType.time,
}),
});
// Test Value column with custom width
expect(columns[1]).toMatchObject({
key: 'Value',
name: 'Value',
width: 100,
field: expect.objectContaining({
name: 'Value',
type: FieldType.number,
}),
});
// Test Message column alignment
expect(columns[2]).toMatchObject({
key: 'Message',
name: 'Message',
field: expect.objectContaining({
name: 'Message',
type: FieldType.string,
config: expect.objectContaining({
custom: expect.objectContaining({
align: 'center',
}),
}),
}),
});
});
});
describe('column building', () => {
it('should build basic column structure', () => {
const columns = mapFrameToDataGrid({
frame: data,
calcsRef,
options: mockOptions,
handlers: { onCellExpand: () => {}, onColumnResize: () => {} },
availableWidth: mockOptions.width,
});
expect(columns).toHaveLength(3);
columns.forEach((column: TableColumn) => {
expect(column).toHaveProperty('key');
expect(column).toHaveProperty('name');
expect(column).toHaveProperty('field');
expect(column).toHaveProperty('cellClass');
expect(column).toHaveProperty('renderCell');
expect(column).toHaveProperty('renderHeaderCell');
});
});
it('should handle column width configurations', () => {
const columns = mapFrameToDataGrid({
frame: data,
calcsRef,
options: mockOptions,
handlers: { onCellExpand: () => {}, onColumnResize: () => {} },
availableWidth: mockOptions.width,
});
// Default width
expect(columns[0].width).toBe(350);
// Explicit width from field config
expect(columns[1].width).toBe(100);
// Default width with min width
expect(columns[2].minWidth).toBe(COLUMN.MIN_WIDTH);
});
it('should handle cell alignment', () => {
const columns = mapFrameToDataGrid({
frame: data,
calcsRef,
options: mockOptions,
handlers: { onCellExpand: () => {}, onColumnResize: () => {} },
availableWidth: mockOptions.width,
});
const messageColumn = columns[2];
expect(messageColumn.field.config.custom.align).toBe('center');
});
it('should handle footer/summary rows', () => {
const options = {
...mockOptions,
isCountRowsSet: true,
};
const columns = mapFrameToDataGrid({
frame: data,
calcsRef: { current: ['3', '', ''] },
options,
handlers: { onCellExpand: () => {}, onColumnResize: () => {} },
availableWidth: mockOptions.width,
});
// First column should show count
const firstCell = columns[0].renderSummaryCell?.({
row: { __depth: 0, __index: 0 },
column: {
...columns[0],
frozen: false,
idx: 0,
parent: undefined,
level: 0,
sortable: true,
minWidth: 100,
draggable: true,
renderCell: () => null,
renderHeaderCell: () => null,
resizable: true,
width: 100,
maxWidth: undefined,
headerCellClass: undefined,
summaryCellClass: undefined,
},
tabIndex: 0,
});
expect(firstCell).toBeDefined();
// Check the div structure and content
const divElement = firstCell as JSX.Element;
expect(divElement.props.style).toEqual({ display: 'flex', justifyContent: 'space-between' });
// Check that we have two spans with correct content
const [countSpan, valueSpan] = divElement.props.children;
expect(countSpan.type).toBe('span');
expect(countSpan.props.children.type).toBe(Trans);
expect(countSpan.props.children.props.i18nKey).toBe('grafana-ui.table.count');
expect(valueSpan.props.children).toBe('3');
});
});
describe('nested frames', () => {
const nestedData = createDataFrame({
fields: [
{ name: 'Time', type: FieldType.time, values: [1, 2] },
{ name: 'Value', type: FieldType.number, values: [10, 20] },
{
name: 'Nested frames',
type: FieldType.nestedFrames,
values: [
[
createDataFrame({
fields: [
{ name: 'Nested Time', type: FieldType.time, values: [3] },
{ name: 'Nested Value', type: FieldType.number, values: [30] },
],
}),
],
],
},
],
});
it('should add expander column for nested frames', () => {
const columns = mapFrameToDataGrid({
frame: nestedData,
calcsRef,
options: mockOptions,
handlers: { onCellExpand: () => {}, onColumnResize: () => {} },
availableWidth: mockOptions.width,
});
// First column should be expander
expect(columns[0]).toMatchObject({
key: 'expanded',
name: '',
width: COLUMN.EXPANDER_WIDTH,
minWidth: COLUMN.EXPANDER_WIDTH,
});
});
it('should not render nested frame type fields', () => {
const columns = mapFrameToDataGrid({
frame: nestedData,
calcsRef,
options: mockOptions,
handlers: { onCellExpand: () => {}, onColumnResize: () => {} },
availableWidth: mockOptions.width,
});
// Should only have expander + Time + Value (not Nested frames column)
expect(columns).toHaveLength(3);
// No column should be of type nestedFrames
const hasNestedFrameColumn = columns.some((col: TableColumn) => col.field.type === FieldType.nestedFrames);
expect(hasNestedFrameColumn).toBe(false);
});
it('should render nested frame data when expanded', () => {
const expandedRows = [0];
const columns = mapFrameToDataGrid({
frame: nestedData,
calcsRef,
options: { ...mockOptions, expandedRows },
handlers: { onCellExpand: () => {}, onColumnResize: () => {} },
availableWidth: mockOptions.width,
});
// Get the rendered content of first row's expander cell
const expanderCell = columns[0].renderCell?.({
row: {
__depth: 1,
__index: 0,
data: nestedData.fields[2].values[0][0],
},
rowIdx: 0,
column: {
...columns[0],
frozen: false,
idx: 0,
parent: undefined,
level: 0,
sortable: true,
minWidth: 100,
draggable: true,
renderCell: () => null,
renderHeaderCell: () => null,
resizable: true,
width: 100,
maxWidth: undefined,
headerCellClass: undefined,
summaryCellClass: undefined,
},
isCellEditable: false,
tabIndex: 0,
onRowChange: () => {},
});
expect(expanderCell).toBeDefined();
});
});
describe('getFooterItemNG', () => {
const rows = [
{ Field1: 1, Text: 'a', __depth: 0, __index: 0 },
{ Field1: 2, Text: 'b', __depth: 0, __index: 1 },
{ Field1: 3, Text: 'c', __depth: 0, __index: 2 },
{ Field2: 3, Text: 'd', __depth: 0, __index: 3 },
{ Field2: 10, Text: 'e', __depth: 0, __index: 4 },
];
const numericField: Field = {
name: 'Field1',
type: FieldType.number,
values: [1, 2, 3],
config: {
custom: {},
},
display: (value: unknown) => ({
text: String(value),
numeric: Number(value),
color: undefined,
prefix: undefined,
suffix: undefined,
}),
state: {},
getLinks: undefined,
};
const numericField2: Field = {
name: 'Field2',
type: FieldType.number,
values: [3, 10],
config: { custom: {} },
display: (value: unknown) => ({
text: String(value),
numeric: Number(value),
color: undefined,
prefix: undefined,
suffix: undefined,
}),
state: {},
getLinks: undefined,
};
const textField: Field = {
name: 'Text',
type: FieldType.string,
values: ['a', 'b', 'c'],
config: { custom: {} },
display: (value: unknown) => ({
text: String(value),
numeric: 0,
color: undefined,
prefix: undefined,
suffix: undefined,
}),
state: {},
getLinks: undefined,
};
it('should calculate sum for numeric fields', () => {
const result = getFooterItemNG(rows, numericField, {
show: true,
reducer: ['sum'],
});
expect(result).toBe('6'); // 1 + 2 + 3
});
it('should calculate mean for numeric fields', () => {
const result = getFooterItemNG(rows, numericField, {
show: true,
reducer: ['mean'],
});
expect(result).toBe('2'); // (1 + 2 + 3) / 3
});
it('should return empty string for non-numeric fields', () => {
const result = getFooterItemNG(rows, textField, {
show: true,
reducer: ['sum'],
});
expect(result).toBe('');
});
it('should return empty string when footer not shown', () => {
const result = getFooterItemNG(rows, numericField, undefined);
expect(result).toBe('');
});
it('should return empty string when reducer is undefined', () => {
const result = getFooterItemNG(rows, numericField, {
show: true,
reducer: undefined,
});
expect(result).toBe('');
});
it('should correctly calculate sum for numeric fields based on selected fields', () => {
const numericField1Result = getFooterItemNG(rows, numericField, {
show: true,
reducer: ['sum'],
fields: ['Field1'],
});
const numericField2Result = getFooterItemNG(rows, numericField2, {
show: true,
reducer: ['sum'],
fields: ['Field2'],
});
expect(numericField1Result).toBe('6'); // 1 + 2 + 3
expect(numericField2Result).toBe('13'); // 3 + 10
});
});
describe('text alignment', () => {
it('should map alignment options to flex values', () => {
// Test 'left' alignment
const leftField = {
name: 'Value',
type: FieldType.string,
values: [],
config: {
custom: {
align: 'left',
},
},
};
expect(getTextAlign(leftField)).toBe('flex-start');
// Test 'center' alignment
const centerField = {
name: 'Value',
type: FieldType.string,
values: [],
config: {
custom: {
align: 'center',
},
},
};
expect(getTextAlign(centerField)).toBe('center');
// Test 'right' alignment
const rightField = {
name: 'Value',
type: FieldType.string,
values: [],
config: {
custom: {
align: 'right',
},
},
};
expect(getTextAlign(rightField)).toBe('flex-end');
});
it('should default to flex-start when no alignment specified', () => {
const field = {
name: 'Value',
type: FieldType.string,
values: [],
config: {
custom: {},
},
};
expect(getTextAlign(field)).toBe('flex-start');
});
it('should default to flex-start when no field is specified', () => {
expect(getTextAlign(undefined)).toBe('flex-start');
});
it('should default to flex-end for number types', () => {
const field = {
name: 'Value',
type: FieldType.number,
values: [],
config: {
custom: {},
},
};
expect(getTextAlign(field)).toBe('flex-end');
});
it('should default to flex-start for string types', () => {
const field = {
name: 'String',
type: FieldType.string,
values: [],
config: {
custom: {},
},
};
expect(getTextAlign(field)).toBe('flex-start');
});
it('should default to flex-start for enum types', () => {
const field = {
name: 'Enum',
type: FieldType.enum,
values: [],
config: {
custom: {},
},
};
expect(getTextAlign(field)).toBe('flex-start');
});
it('should default to flex-start for time types', () => {
const field = {
name: 'Time',
type: FieldType.time,
values: [],
config: {
custom: {},
},
};
expect(getTextAlign(field)).toBe('flex-start');
});
it('should default to flex-start for boolean types', () => {
const field = {
name: 'Active',
type: FieldType.boolean,
values: [],
config: {
custom: {},
},
};
expect(getTextAlign(field)).toBe('flex-start');
});
});
describe('cell display mode', () => {
const theme = {
colors: {
isDark: true,
mode: 'dark',
primary: {
text: '#FFFFFF',
main: '#FF0000',
},
background: {
canvas: '#000000',
primary: '#111111',
},
text: {
primary: '#FFFFFF',
},
action: {
hover: '#FF0000',
},
},
} as unknown as GrafanaTheme2;
it('should handle color background mode', () => {
const field = {
type: TableCellDisplayMode.ColorBackground as const,
mode: TableCellBackgroundDisplayMode.Basic,
};
const displayValue = {
text: '100',
numeric: 100,
color: '#ff0000',
};
const colors = getCellColors(theme, field, displayValue);
expect(colors.bgColor).toBe('rgb(255, 0, 0)');
expect(colors.textColor).toBe('rgb(247, 248, 250)');
expect(colors.bgHoverColor).toBe('rgb(255, 36, 36)');
});
it('should handle color background gradient mode', () => {
const field = {
type: TableCellDisplayMode.ColorBackground as const,
mode: TableCellBackgroundDisplayMode.Gradient,
};
const displayValue = {
text: '100',
numeric: 100,
color: '#ff0000',
};
const colors = getCellColors(theme, field, displayValue);
expect(colors.bgColor).toBe('linear-gradient(120deg, rgb(255, 54, 36), #ff0000)');
expect(colors.textColor).toBe('rgb(247, 248, 250)');
expect(colors.bgHoverColor).toBe('linear-gradient(120deg, rgb(255, 54, 36), rgb(255, 54, 54))');
});
});
describe('frame to records conversion', () => {
it('should convert DataFrame to TableRows', () => {
const frame = createDataFrame({
fields: [
{ name: 'time', values: [1, 2] },
{ name: 'value', values: [10, 20] },
],
});
const records = frameToRecords(frame);
expect(records).toHaveLength(2);
expect(records[0]).toEqual({
__depth: 0,
__index: 0,
time: 1,
value: 10,
});
});
});
describe('handleSort', () => {
const setSortColumns = jest.fn();
const sortColumnsRef: { current: SortColumn[] } = { current: [] };
beforeEach(() => {
setSortColumns.mockClear();
sortColumnsRef.current = [];
});
it('should set initial sort', () => {
handleSort('Value', 'ASC', false, setSortColumns, sortColumnsRef);
expect(setSortColumns).toHaveBeenCalledWith([{ columnKey: 'Value', direction: 'ASC' }]);
});
it('should toggle sort direction on same column', () => {
// Initial sort
sortColumnsRef.current = [{ columnKey: 'Value', direction: 'ASC' }] as const;
handleSort('Value', 'DESC', false, setSortColumns, sortColumnsRef);
expect(setSortColumns).toHaveBeenCalledWith([{ columnKey: 'Value', direction: 'DESC' }]);
});
it('should handle multi-sort with shift key', () => {
// Initial sort
sortColumnsRef.current = [{ columnKey: 'Time', direction: 'ASC' }] as const;
handleSort('Value', 'ASC', true, setSortColumns, sortColumnsRef);
expect(setSortColumns).toHaveBeenCalledWith([
{ columnKey: 'Time', direction: 'ASC' },
{ columnKey: 'Value', direction: 'ASC' },
]);
});
it('should remove sort when toggling through all states', () => {
// Initial ASC sort
sortColumnsRef.current = [{ columnKey: 'Value', direction: 'ASC' }] as const;
// Toggle to DESC
handleSort('Value', 'DESC', false, setSortColumns, sortColumnsRef);
expect(setSortColumns).toHaveBeenCalledWith([{ columnKey: 'Value', direction: 'DESC' }]);
// Toggle to no sort
handleSort('Value', 'DESC', false, setSortColumns, sortColumnsRef);
expect(setSortColumns).toHaveBeenCalledWith([]);
});
});
describe('getAlignmentFactor', () => {
it('should create a new alignment factor when none exists', () => {
// Create a field with no existing alignment factor
const field: Field = {
name: 'test',
type: FieldType.number,
config: {},
values: [1, 22, 333, 4444],
// No state property initially
display: (value: any) => ({
text: String(value),
numeric: Number(value),
}),
};
// Create a display value
const displayValue: DisplayValue = {
text: '1',
numeric: 1,
};
// Call getAlignmentFactor with the first row
const result = getAlignmentFactor(field, displayValue, 0);
// Verify the result has the text property
expect(result).toEqual(
expect.objectContaining({
text: '1',
})
);
// Verify that field.state was created and contains the alignment factor
expect(field.state).toBeDefined();
expect(field.state?.alignmentFactors).toBeDefined();
expect(field.state?.alignmentFactors).toEqual(
expect.objectContaining({
text: '1',
})
);
});
it('should update alignment factor when a longer value is found', () => {
// Create a field with an existing alignment factor
const field: Field = {
name: 'test',
type: FieldType.number,
config: {},
values: [1, 22, 333, 4444],
state: {
alignmentFactors: {
text: '1',
},
},
display: (value: any) => ({
text: String(value),
numeric: Number(value),
}),
};
// Create a display value that is longer than the existing alignment factor
const displayValue: DisplayValue = {
text: '4444',
numeric: 4444,
};
// Call getAlignmentFactor
const result = getAlignmentFactor(field, displayValue, 3);
// Verify the result is updated to the longer value
expect(result).toEqual(
expect.objectContaining({
text: '4444',
})
);
// Verify that field.state.alignmentFactors was updated
expect(field.state?.alignmentFactors).toEqual(
expect.objectContaining({
text: '4444',
})
);
});
it('should not update alignment factor when a shorter value is found', () => {
// Create a field with an existing alignment factor for a long value
const field: Field = {
name: 'test',
type: FieldType.number,
config: {},
values: [1, 22, 333, 4444],
state: {
alignmentFactors: {
text: '4444',
},
},
display: (value: any) => ({
text: String(value),
numeric: Number(value),
}),
};
// Create a display value that is shorter than the existing alignment factor
const displayValue: DisplayValue = {
text: '1',
numeric: 1,
};
// Call getAlignmentFactor
const result = getAlignmentFactor(field, displayValue, 0);
// Verify the result is still the longer value
expect(result).toEqual(
expect.objectContaining({
text: '4444',
})
);
// Verify that field.state.alignmentFactors was not changed
expect(field.state?.alignmentFactors).toEqual(
expect.objectContaining({
text: '4444',
})
);
});
it('should add alignment factor to existing field state', () => {
// Create a field with existing state but no alignment factors yet
const field: Field = {
name: 'test',
type: FieldType.number,
config: {},
values: [1, 22, 333, 4444],
// Field has state but no alignmentFactors
state: {
// Use a valid property for FieldState
// For example, if calcs is a valid property:
calcs: { sum: 4460 },
// Or if noValue is a valid property:
// noValue: true
},
display: (value: any) => ({
text: String(value),
numeric: Number(value),
}),
};
// Create a display value
const displayValue: DisplayValue = {
text: '1',
numeric: 1,
};
// Call getAlignmentFactor with the first row
const result = getAlignmentFactor(field, displayValue, 0);
// Verify the result has the text property
expect(result).toEqual(
expect.objectContaining({
text: '1',
})
);
// Verify that field.state was preserved and alignment factor was added
expect(field.state).toBeDefined();
// Check for the valid property we used
expect(field.state?.calcs).toBeDefined();
expect(field.state?.alignmentFactors).toBeDefined();
expect(field.state?.alignmentFactors).toEqual(
expect.objectContaining({
text: '1',
})
);
});
});
describe('getIsNestedTable', () => {
it('should detect nested frames', () => {
const frame: DataFrame = {
fields: [
{ type: FieldType.string, name: 'stringCol', config: {}, values: [] },
{ type: FieldType.nestedFrames, name: 'nestedCol', config: {}, values: [] },
],
length: 0,
name: 'test',
};
expect(getIsNestedTable(frame)).toBe(true);
});
it('should return false for regular frames', () => {
const frame: DataFrame = {
fields: [
{ type: FieldType.string, name: 'stringCol', config: {}, values: [] },
{ type: FieldType.number, name: 'numberCol', config: {}, values: [] },
],
length: 0,
name: 'test',
};
expect(getIsNestedTable(frame)).toBe(false);
});
});
describe('getComparator', () => {
it('should compare numbers correctly', () => {
const comparator = getComparator(FieldType.number);
expect(comparator(1, 2)).toBeLessThan(0);
expect(comparator(2, 1)).toBeGreaterThan(0);
expect(comparator(1, 1)).toBe(0);
});
it('should handle undefined values', () => {
const comparator = getComparator(FieldType.number);
expect(comparator(undefined, 1)).toBeLessThan(0);
expect(comparator(1, undefined)).toBeGreaterThan(0);
expect(comparator(undefined, undefined)).toBe(0);
});
it('should compare strings case-insensitively', () => {
const comparator = getComparator(FieldType.string);
expect(comparator('a', 'B')).toBeLessThan(0);
expect(comparator('B', 'a')).toBeGreaterThan(0);
expect(comparator('a', 'a')).toBe(0);
});
it('should handle time values', () => {
const comparator = getComparator(FieldType.time);
const t1 = 1672531200000; // 2023-01-01
const t2 = 1672617600000; // 2023-01-02
expect(comparator(t1, t2)).toBeLessThan(0);
expect(comparator(t2, t1)).toBeGreaterThan(0);
expect(comparator(t1, t1)).toBe(0);
});
it('should handle boolean values', () => {
const comparator = getComparator(FieldType.boolean);
expect(comparator(false, true)).toBeLessThan(0);
expect(comparator(true, false)).toBeGreaterThan(0);
expect(comparator(true, true)).toBe(0);
});
});
describe('shouldTextOverflow', () => {
const mockContext = {
font: '',
measureText: (text: string) => ({
// Each character is 8px wide in our mock context
width: text.length * 8,
}),
};
const osContext = mockContext as unknown as OffscreenCanvasRenderingContext2D;
const headerCellRefs = {
current: {
column1: {
getBoundingClientRect: () => ({ width: 100 }),
offsetWidth: 100,
},
} as unknown as Record<string, HTMLDivElement>,
};
it('should return true when text exceeds cell width', () => {
const row = {
__depth: 0,
__index: 0,
// 43*8 = 344px wide cell, it should overflow as it's greater than 100px
column1: 'This is a very long text that should overflow',
};
const columnTypes = { column1: FieldType.string };
const result = shouldTextOverflow(
'column1',
row,
columnTypes,
headerCellRefs,
osContext,
20, // lineHeight
40, // defaultRowHeight
8, // padding
false, // textWrap
{
config: {
custom: {
inspect: false,
},
},
} as Field,
Table: Move library to react-data-grid (#102482) * Changes galore * Freedom 🗽 * Add feature flag * Latest changes * Basic auto cell type * Partially working bar-gauge * Brokenish but whatevs * Include the toggle doc * TableNG: Context menu (#94094) * feat(table-ng): context menu init commit * betterer * feat(table-ng): re-use contextmenu component * fix(table-ng): close context menu issue * TableNG: Sorting columns (#94200) feat(table-ng): sorting column * fix feature toggle conflict * TableNG: Sorting with custom table header (#95351) * TableNG: Header Toggle (#95310) * TableNG: Multi-column sorting (#95395) feat(table-ng): multi-sorting * TableNG: Column width options (#95426) * feat(table-ng): column width * mouse handle drag event * move resizing task * TableNG: Fix icon sorting direction (#95653) fix(table-ng): sorting icon direction * TableNG: Show table footer (#95313) * TableNG: Show table footer * Revert betterer * Update betterer * Incorporate reducer calculations into footer * Update imports in FooterRow * Use getFooterValue for summary cell render * TableNG: Min column width (#95657) * feat(table-ng): min column width * feat(table-ng): set a min width constant * TableNG: Column alignment (#95679) * feat(table-ng): column alignment * cleaning * feat(table-ng): header cell alignment * optimizations * feat(table-ng): footer cell alignment * calc counter * TableNG: use compiled fn for columns -> records conversion (#95914) * use compiled fn for columns -> records conversion * TableNG: Move key rev and fix width overrides (#95921) * meh * add index to records --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sparkline Cell Parity (#95690) * sparkline value * todo * Remove unsued shallowField * Pass justifyContent to sparkline --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: BarGauge cell updates (#95521) * fix bargauge cell * merge and fix props * cleanup imports * TableNG: Text wrapping (#96041) * feat(table-ng): fix long text cell width * feat(table-ng): fix long text cell width 2 * comment out column rowHeight * fix long text column width * fix types * fix types * naming * Check current header cell ref is defined for key * cleaning * make table re-render when data changed * eslint --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Text overflow (#96641) * feat(table-ng): text overflow * cleaning * TableNG: Fix footer for count (#96802) * TableNG: Table column filter (#96767) * feat(table-ng): add filter form --------- Co-authored-by: drew08t <drew08@gmail.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: On column resize trigger (#97004) chore(table-ng): trigger on resize on text wrap only * TableNG: Improve sort performance (#97767) * TableNG: Improve sort performance * clean a bit * a bit more * Remove const that was breaking sort --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Fix sorting (#98141) fix(table-ng): sorting * TableNG: fix multi sorting (#98668) fix(table-ng): multi sorting * TableNG: Column re-size handler (#98901) * feat(table-ng): column re-size handler * TableNG: Fix footer calcs with no reducer (#99347) * TableNG: Update renderHeaderCell with filter dep (#99483) * TableNG: Updated styles for demo (#99530) * style proposal: table ng * chore: revert gauge cell custom stuff * TableNG: Cross-filter (#99459) * feat(table-ng): cross-filter * fix filter update issue * fix filter reset issue * Fix spacebar for filter input --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Filter perfomance optimization (#99620) fix(table-ng): filter performance optimization * TableNG: Refine styling closer to original table (#99625) * TableNG: Support groupToNestedTableTransform (#97134) * TableNG: Support groupToNestedTableTransform * Fix merge issues * Force refresh for now * Remove log * Fix some conflicts * Fix more conflicts * Help avoid clash with compiled frameToRecords keys * Make subtable height unconstrained * Support show field names in nested tables toggle * TableNG: Fix footer + some other misc updates (#99846) fix: footer fixes huzzah * TableNG: Styling - Update styling for cells (#99851) * fix(table-ng): bargauge inner width issue * TableNG: Move header cell component (#99844) * fix(table-ng): move header cell into separate file * Fix sub table --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Auto cell feature parity (#100095) * feat(table-ng): auto cell feature parity * TableNG: JSON cell implementation + hover fixes (#100152) * feat: tableNG json cell + auto fixes * chore: add comment * add justify content to json cell --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Fix cell hover issue (#100207) * fix(table-ng): cell hover issue * better commenting * TableNG: Text color cell (#100120) feat(table-ng): text color cell feature parity * TableNG: Image cell implementation (#100132) * feat: tableNG image cell * fix: incorporate justify-content correctly * chore: pass down cell options from fieldConfig --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Cell height performance improvement (#100544) * chore: perf improvement * chore: minor fix * Update packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * chore: fix betterer --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Add pagination (#100165) * TableNG: Add pagination * TableNG: Get collapsed icon state correct + update `rowHeight` (#100556) * fix: get collapsed icon state correct + update condition for calculating row height * chore: some cleanup! * chore: naming to avoid confusion with local state name * TableNG: Add support for `DataLinksCell` (#100459) * TableNG: Improve sub table styling (#100772) * Move files temporarily to fix conflicts * Fix feature flag conflicts * Move files back to cell dir * TableNG: Update inner height of bar gauge cell (#100996) * fix: change inner height of bar gauge cell * chore: move function to utils, cleanup * Remove testing line * TableNG: Add bottom border to column headers + fix footer styling (#101016) * feat: add bottom border to column headers for table parity * feat: summary row style fix * chore: remove redundant style --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for `ActionsCell` (#101024) * TableNG: Cell hover styles + header resize handler indicator (#100770) * fix: tableNG styles * chore: clean up comments * chore: remove column header stuffz for now * fix: refactor to transform/translate + resize handler hover styling * chore: re-think approach - change a lot of things * chore: most recent iteration * chore: wait i like this better * chore: hoist into colors function + clean it up! * moar better * chore: define constants for clarity * chore: calculate rbga to rgb values given background color --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix scoll hover jumpy behavior (#101085) * fix(table-ng): hover scroll jumping * Account for panel padding during pagination --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix imports (#101059) * fix(table-ng): clean imports Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sorted rows dependent upon filtered rows (#100985) TableNG: Improve multi-sort performance * TableNG: Fix sparkline width (#101164) fix(table-ng): sparkline width * TableNG: Type TableNG (#101257) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * chore: remove unused rowIndex prop --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for datalinks (#100769) Co-authored-by: drew08t <drew08@gmail.com> * Chore: Remove unused import (#102064) remove unused import * Update betterer * BarGauge: Remove z-index (#102220) fix(bargauge): remove z-index * TableNG: Refactor + testing (#102045) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * feat: refactor + tests * chore: fix import lint errors * betterer * chore: fix image cell * chore: revert width function * add test * betterer * chore: fix sorting + add tests * chore: pr feedback --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix table suggestion (#102497) fix: defensively guard against missing cellOptions * TableNG: Footer fields calc fix (#102487) * fix: respect footer fields calc selection * chore: add test * TableNG: Image cell hover fix (#102489) fix: image cell hover * TableNG: Persist scrollbars during re render (#102559) * TableNG: Persist scrollbars during re render * Update improved betterer * TableNG: Fix column width override (#102474) * fix(table): column width override * TableNG: Add support for crosshair share (#102410) * TableNG: Add support for crosshair share * Add tests * TableNG: Fix table ng tests (#102645) fix: cellType causing tests to fail * Remove empty file * TableNG: Update util tests (#102646) * TableNG: Add column type icon (#102686) * chore(table-ng): add column type icon * chore(table-ng): clean styling * Use core internationalization outside grafana ui * Import popover directly * Add count to grafana-ui locales * TableNG: Change feature flag to tableNextGen (#102814) Change feature flag to tableNextGen * TableNG: Add row colors (#102706) * chore(table-ng): add row colors * clean up * fix params * fix(table-ng): cell color background indexing --------- Co-authored-by: Kyle Cunningham <kyle@codeincarnate.com> Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: Adela Almasan <adela.almasan@grafana.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Adela Almasan <88068998+adela-almasan@users.noreply.github.com> Co-authored-by: Alex Spencer <52186778+alexjonspencer1@users.noreply.github.com>
2025-03-26 11:57:57 +08:00
TableCellDisplayMode.Auto // cellType
);
expect(result).toBe(true);
});
it('should return false when text fits cell width', () => {
const row = {
__depth: 0,
__index: 0,
// 9*8 = 72px wide cell, it should fit as it's less than 100px
column1: 'Short text',
};
const columnTypes = { column1: FieldType.string };
const result = shouldTextOverflow(
'column1',
row,
columnTypes,
headerCellRefs,
osContext,
20, // lineHeight
40, // defaultRowHeight
8, // padding
false, // textWrap
{
config: {
custom: {
inspect: false,
},
},
} as Field,
Table: Move library to react-data-grid (#102482) * Changes galore * Freedom 🗽 * Add feature flag * Latest changes * Basic auto cell type * Partially working bar-gauge * Brokenish but whatevs * Include the toggle doc * TableNG: Context menu (#94094) * feat(table-ng): context menu init commit * betterer * feat(table-ng): re-use contextmenu component * fix(table-ng): close context menu issue * TableNG: Sorting columns (#94200) feat(table-ng): sorting column * fix feature toggle conflict * TableNG: Sorting with custom table header (#95351) * TableNG: Header Toggle (#95310) * TableNG: Multi-column sorting (#95395) feat(table-ng): multi-sorting * TableNG: Column width options (#95426) * feat(table-ng): column width * mouse handle drag event * move resizing task * TableNG: Fix icon sorting direction (#95653) fix(table-ng): sorting icon direction * TableNG: Show table footer (#95313) * TableNG: Show table footer * Revert betterer * Update betterer * Incorporate reducer calculations into footer * Update imports in FooterRow * Use getFooterValue for summary cell render * TableNG: Min column width (#95657) * feat(table-ng): min column width * feat(table-ng): set a min width constant * TableNG: Column alignment (#95679) * feat(table-ng): column alignment * cleaning * feat(table-ng): header cell alignment * optimizations * feat(table-ng): footer cell alignment * calc counter * TableNG: use compiled fn for columns -> records conversion (#95914) * use compiled fn for columns -> records conversion * TableNG: Move key rev and fix width overrides (#95921) * meh * add index to records --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sparkline Cell Parity (#95690) * sparkline value * todo * Remove unsued shallowField * Pass justifyContent to sparkline --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: BarGauge cell updates (#95521) * fix bargauge cell * merge and fix props * cleanup imports * TableNG: Text wrapping (#96041) * feat(table-ng): fix long text cell width * feat(table-ng): fix long text cell width 2 * comment out column rowHeight * fix long text column width * fix types * fix types * naming * Check current header cell ref is defined for key * cleaning * make table re-render when data changed * eslint --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Text overflow (#96641) * feat(table-ng): text overflow * cleaning * TableNG: Fix footer for count (#96802) * TableNG: Table column filter (#96767) * feat(table-ng): add filter form --------- Co-authored-by: drew08t <drew08@gmail.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: On column resize trigger (#97004) chore(table-ng): trigger on resize on text wrap only * TableNG: Improve sort performance (#97767) * TableNG: Improve sort performance * clean a bit * a bit more * Remove const that was breaking sort --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Fix sorting (#98141) fix(table-ng): sorting * TableNG: fix multi sorting (#98668) fix(table-ng): multi sorting * TableNG: Column re-size handler (#98901) * feat(table-ng): column re-size handler * TableNG: Fix footer calcs with no reducer (#99347) * TableNG: Update renderHeaderCell with filter dep (#99483) * TableNG: Updated styles for demo (#99530) * style proposal: table ng * chore: revert gauge cell custom stuff * TableNG: Cross-filter (#99459) * feat(table-ng): cross-filter * fix filter update issue * fix filter reset issue * Fix spacebar for filter input --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Filter perfomance optimization (#99620) fix(table-ng): filter performance optimization * TableNG: Refine styling closer to original table (#99625) * TableNG: Support groupToNestedTableTransform (#97134) * TableNG: Support groupToNestedTableTransform * Fix merge issues * Force refresh for now * Remove log * Fix some conflicts * Fix more conflicts * Help avoid clash with compiled frameToRecords keys * Make subtable height unconstrained * Support show field names in nested tables toggle * TableNG: Fix footer + some other misc updates (#99846) fix: footer fixes huzzah * TableNG: Styling - Update styling for cells (#99851) * fix(table-ng): bargauge inner width issue * TableNG: Move header cell component (#99844) * fix(table-ng): move header cell into separate file * Fix sub table --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Auto cell feature parity (#100095) * feat(table-ng): auto cell feature parity * TableNG: JSON cell implementation + hover fixes (#100152) * feat: tableNG json cell + auto fixes * chore: add comment * add justify content to json cell --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Fix cell hover issue (#100207) * fix(table-ng): cell hover issue * better commenting * TableNG: Text color cell (#100120) feat(table-ng): text color cell feature parity * TableNG: Image cell implementation (#100132) * feat: tableNG image cell * fix: incorporate justify-content correctly * chore: pass down cell options from fieldConfig --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Cell height performance improvement (#100544) * chore: perf improvement * chore: minor fix * Update packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * chore: fix betterer --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Add pagination (#100165) * TableNG: Add pagination * TableNG: Get collapsed icon state correct + update `rowHeight` (#100556) * fix: get collapsed icon state correct + update condition for calculating row height * chore: some cleanup! * chore: naming to avoid confusion with local state name * TableNG: Add support for `DataLinksCell` (#100459) * TableNG: Improve sub table styling (#100772) * Move files temporarily to fix conflicts * Fix feature flag conflicts * Move files back to cell dir * TableNG: Update inner height of bar gauge cell (#100996) * fix: change inner height of bar gauge cell * chore: move function to utils, cleanup * Remove testing line * TableNG: Add bottom border to column headers + fix footer styling (#101016) * feat: add bottom border to column headers for table parity * feat: summary row style fix * chore: remove redundant style --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for `ActionsCell` (#101024) * TableNG: Cell hover styles + header resize handler indicator (#100770) * fix: tableNG styles * chore: clean up comments * chore: remove column header stuffz for now * fix: refactor to transform/translate + resize handler hover styling * chore: re-think approach - change a lot of things * chore: most recent iteration * chore: wait i like this better * chore: hoist into colors function + clean it up! * moar better * chore: define constants for clarity * chore: calculate rbga to rgb values given background color --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix scoll hover jumpy behavior (#101085) * fix(table-ng): hover scroll jumping * Account for panel padding during pagination --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix imports (#101059) * fix(table-ng): clean imports Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sorted rows dependent upon filtered rows (#100985) TableNG: Improve multi-sort performance * TableNG: Fix sparkline width (#101164) fix(table-ng): sparkline width * TableNG: Type TableNG (#101257) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * chore: remove unused rowIndex prop --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for datalinks (#100769) Co-authored-by: drew08t <drew08@gmail.com> * Chore: Remove unused import (#102064) remove unused import * Update betterer * BarGauge: Remove z-index (#102220) fix(bargauge): remove z-index * TableNG: Refactor + testing (#102045) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * feat: refactor + tests * chore: fix import lint errors * betterer * chore: fix image cell * chore: revert width function * add test * betterer * chore: fix sorting + add tests * chore: pr feedback --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix table suggestion (#102497) fix: defensively guard against missing cellOptions * TableNG: Footer fields calc fix (#102487) * fix: respect footer fields calc selection * chore: add test * TableNG: Image cell hover fix (#102489) fix: image cell hover * TableNG: Persist scrollbars during re render (#102559) * TableNG: Persist scrollbars during re render * Update improved betterer * TableNG: Fix column width override (#102474) * fix(table): column width override * TableNG: Add support for crosshair share (#102410) * TableNG: Add support for crosshair share * Add tests * TableNG: Fix table ng tests (#102645) fix: cellType causing tests to fail * Remove empty file * TableNG: Update util tests (#102646) * TableNG: Add column type icon (#102686) * chore(table-ng): add column type icon * chore(table-ng): clean styling * Use core internationalization outside grafana ui * Import popover directly * Add count to grafana-ui locales * TableNG: Change feature flag to tableNextGen (#102814) Change feature flag to tableNextGen * TableNG: Add row colors (#102706) * chore(table-ng): add row colors * clean up * fix params * fix(table-ng): cell color background indexing --------- Co-authored-by: Kyle Cunningham <kyle@codeincarnate.com> Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: Adela Almasan <adela.almasan@grafana.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Adela Almasan <88068998+adela-almasan@users.noreply.github.com> Co-authored-by: Alex Spencer <52186778+alexjonspencer1@users.noreply.github.com>
2025-03-26 11:57:57 +08:00
TableCellDisplayMode.Auto // cellType
);
expect(result).toBe(false);
});
it('should return false when text wrapping is enabled', () => {
const row = {
__depth: 0,
__index: 0,
column1: 'This is a very long text that should wrap instead of overflow',
};
const columnTypes = { column1: FieldType.string };
const result = shouldTextOverflow(
'column1',
row,
columnTypes,
headerCellRefs,
osContext,
20, // lineHeight
40, // defaultRowHeight
8, // padding
true, // textWrap ENABLED
{
config: {
custom: {
inspect: true,
},
},
} as Field,
Table: Move library to react-data-grid (#102482) * Changes galore * Freedom 🗽 * Add feature flag * Latest changes * Basic auto cell type * Partially working bar-gauge * Brokenish but whatevs * Include the toggle doc * TableNG: Context menu (#94094) * feat(table-ng): context menu init commit * betterer * feat(table-ng): re-use contextmenu component * fix(table-ng): close context menu issue * TableNG: Sorting columns (#94200) feat(table-ng): sorting column * fix feature toggle conflict * TableNG: Sorting with custom table header (#95351) * TableNG: Header Toggle (#95310) * TableNG: Multi-column sorting (#95395) feat(table-ng): multi-sorting * TableNG: Column width options (#95426) * feat(table-ng): column width * mouse handle drag event * move resizing task * TableNG: Fix icon sorting direction (#95653) fix(table-ng): sorting icon direction * TableNG: Show table footer (#95313) * TableNG: Show table footer * Revert betterer * Update betterer * Incorporate reducer calculations into footer * Update imports in FooterRow * Use getFooterValue for summary cell render * TableNG: Min column width (#95657) * feat(table-ng): min column width * feat(table-ng): set a min width constant * TableNG: Column alignment (#95679) * feat(table-ng): column alignment * cleaning * feat(table-ng): header cell alignment * optimizations * feat(table-ng): footer cell alignment * calc counter * TableNG: use compiled fn for columns -> records conversion (#95914) * use compiled fn for columns -> records conversion * TableNG: Move key rev and fix width overrides (#95921) * meh * add index to records --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sparkline Cell Parity (#95690) * sparkline value * todo * Remove unsued shallowField * Pass justifyContent to sparkline --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: BarGauge cell updates (#95521) * fix bargauge cell * merge and fix props * cleanup imports * TableNG: Text wrapping (#96041) * feat(table-ng): fix long text cell width * feat(table-ng): fix long text cell width 2 * comment out column rowHeight * fix long text column width * fix types * fix types * naming * Check current header cell ref is defined for key * cleaning * make table re-render when data changed * eslint --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Text overflow (#96641) * feat(table-ng): text overflow * cleaning * TableNG: Fix footer for count (#96802) * TableNG: Table column filter (#96767) * feat(table-ng): add filter form --------- Co-authored-by: drew08t <drew08@gmail.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: On column resize trigger (#97004) chore(table-ng): trigger on resize on text wrap only * TableNG: Improve sort performance (#97767) * TableNG: Improve sort performance * clean a bit * a bit more * Remove const that was breaking sort --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Fix sorting (#98141) fix(table-ng): sorting * TableNG: fix multi sorting (#98668) fix(table-ng): multi sorting * TableNG: Column re-size handler (#98901) * feat(table-ng): column re-size handler * TableNG: Fix footer calcs with no reducer (#99347) * TableNG: Update renderHeaderCell with filter dep (#99483) * TableNG: Updated styles for demo (#99530) * style proposal: table ng * chore: revert gauge cell custom stuff * TableNG: Cross-filter (#99459) * feat(table-ng): cross-filter * fix filter update issue * fix filter reset issue * Fix spacebar for filter input --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Filter perfomance optimization (#99620) fix(table-ng): filter performance optimization * TableNG: Refine styling closer to original table (#99625) * TableNG: Support groupToNestedTableTransform (#97134) * TableNG: Support groupToNestedTableTransform * Fix merge issues * Force refresh for now * Remove log * Fix some conflicts * Fix more conflicts * Help avoid clash with compiled frameToRecords keys * Make subtable height unconstrained * Support show field names in nested tables toggle * TableNG: Fix footer + some other misc updates (#99846) fix: footer fixes huzzah * TableNG: Styling - Update styling for cells (#99851) * fix(table-ng): bargauge inner width issue * TableNG: Move header cell component (#99844) * fix(table-ng): move header cell into separate file * Fix sub table --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Auto cell feature parity (#100095) * feat(table-ng): auto cell feature parity * TableNG: JSON cell implementation + hover fixes (#100152) * feat: tableNG json cell + auto fixes * chore: add comment * add justify content to json cell --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Fix cell hover issue (#100207) * fix(table-ng): cell hover issue * better commenting * TableNG: Text color cell (#100120) feat(table-ng): text color cell feature parity * TableNG: Image cell implementation (#100132) * feat: tableNG image cell * fix: incorporate justify-content correctly * chore: pass down cell options from fieldConfig --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Cell height performance improvement (#100544) * chore: perf improvement * chore: minor fix * Update packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * chore: fix betterer --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Add pagination (#100165) * TableNG: Add pagination * TableNG: Get collapsed icon state correct + update `rowHeight` (#100556) * fix: get collapsed icon state correct + update condition for calculating row height * chore: some cleanup! * chore: naming to avoid confusion with local state name * TableNG: Add support for `DataLinksCell` (#100459) * TableNG: Improve sub table styling (#100772) * Move files temporarily to fix conflicts * Fix feature flag conflicts * Move files back to cell dir * TableNG: Update inner height of bar gauge cell (#100996) * fix: change inner height of bar gauge cell * chore: move function to utils, cleanup * Remove testing line * TableNG: Add bottom border to column headers + fix footer styling (#101016) * feat: add bottom border to column headers for table parity * feat: summary row style fix * chore: remove redundant style --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for `ActionsCell` (#101024) * TableNG: Cell hover styles + header resize handler indicator (#100770) * fix: tableNG styles * chore: clean up comments * chore: remove column header stuffz for now * fix: refactor to transform/translate + resize handler hover styling * chore: re-think approach - change a lot of things * chore: most recent iteration * chore: wait i like this better * chore: hoist into colors function + clean it up! * moar better * chore: define constants for clarity * chore: calculate rbga to rgb values given background color --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix scoll hover jumpy behavior (#101085) * fix(table-ng): hover scroll jumping * Account for panel padding during pagination --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix imports (#101059) * fix(table-ng): clean imports Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sorted rows dependent upon filtered rows (#100985) TableNG: Improve multi-sort performance * TableNG: Fix sparkline width (#101164) fix(table-ng): sparkline width * TableNG: Type TableNG (#101257) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * chore: remove unused rowIndex prop --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for datalinks (#100769) Co-authored-by: drew08t <drew08@gmail.com> * Chore: Remove unused import (#102064) remove unused import * Update betterer * BarGauge: Remove z-index (#102220) fix(bargauge): remove z-index * TableNG: Refactor + testing (#102045) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * feat: refactor + tests * chore: fix import lint errors * betterer * chore: fix image cell * chore: revert width function * add test * betterer * chore: fix sorting + add tests * chore: pr feedback --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix table suggestion (#102497) fix: defensively guard against missing cellOptions * TableNG: Footer fields calc fix (#102487) * fix: respect footer fields calc selection * chore: add test * TableNG: Image cell hover fix (#102489) fix: image cell hover * TableNG: Persist scrollbars during re render (#102559) * TableNG: Persist scrollbars during re render * Update improved betterer * TableNG: Fix column width override (#102474) * fix(table): column width override * TableNG: Add support for crosshair share (#102410) * TableNG: Add support for crosshair share * Add tests * TableNG: Fix table ng tests (#102645) fix: cellType causing tests to fail * Remove empty file * TableNG: Update util tests (#102646) * TableNG: Add column type icon (#102686) * chore(table-ng): add column type icon * chore(table-ng): clean styling * Use core internationalization outside grafana ui * Import popover directly * Add count to grafana-ui locales * TableNG: Change feature flag to tableNextGen (#102814) Change feature flag to tableNextGen * TableNG: Add row colors (#102706) * chore(table-ng): add row colors * clean up * fix params * fix(table-ng): cell color background indexing --------- Co-authored-by: Kyle Cunningham <kyle@codeincarnate.com> Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: Adela Almasan <adela.almasan@grafana.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Adela Almasan <88068998+adela-almasan@users.noreply.github.com> Co-authored-by: Alex Spencer <52186778+alexjonspencer1@users.noreply.github.com>
2025-03-26 11:57:57 +08:00
TableCellDisplayMode.Auto // cellType
);
expect(result).toBe(false);
});
it('should return false when cell inspection is enabled', () => {
const row = {
__depth: 0,
__index: 0,
column1: 'This is a very long text',
};
const columnTypes = { column1: FieldType.string };
const result = shouldTextOverflow(
'column1',
row,
columnTypes,
headerCellRefs,
osContext,
20, // lineHeight
40, // defaultRowHeight
8, // padding
false, // textWrap
{
config: {
custom: {
inspect: true,
},
},
} as Field,
Table: Move library to react-data-grid (#102482) * Changes galore * Freedom 🗽 * Add feature flag * Latest changes * Basic auto cell type * Partially working bar-gauge * Brokenish but whatevs * Include the toggle doc * TableNG: Context menu (#94094) * feat(table-ng): context menu init commit * betterer * feat(table-ng): re-use contextmenu component * fix(table-ng): close context menu issue * TableNG: Sorting columns (#94200) feat(table-ng): sorting column * fix feature toggle conflict * TableNG: Sorting with custom table header (#95351) * TableNG: Header Toggle (#95310) * TableNG: Multi-column sorting (#95395) feat(table-ng): multi-sorting * TableNG: Column width options (#95426) * feat(table-ng): column width * mouse handle drag event * move resizing task * TableNG: Fix icon sorting direction (#95653) fix(table-ng): sorting icon direction * TableNG: Show table footer (#95313) * TableNG: Show table footer * Revert betterer * Update betterer * Incorporate reducer calculations into footer * Update imports in FooterRow * Use getFooterValue for summary cell render * TableNG: Min column width (#95657) * feat(table-ng): min column width * feat(table-ng): set a min width constant * TableNG: Column alignment (#95679) * feat(table-ng): column alignment * cleaning * feat(table-ng): header cell alignment * optimizations * feat(table-ng): footer cell alignment * calc counter * TableNG: use compiled fn for columns -> records conversion (#95914) * use compiled fn for columns -> records conversion * TableNG: Move key rev and fix width overrides (#95921) * meh * add index to records --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sparkline Cell Parity (#95690) * sparkline value * todo * Remove unsued shallowField * Pass justifyContent to sparkline --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: BarGauge cell updates (#95521) * fix bargauge cell * merge and fix props * cleanup imports * TableNG: Text wrapping (#96041) * feat(table-ng): fix long text cell width * feat(table-ng): fix long text cell width 2 * comment out column rowHeight * fix long text column width * fix types * fix types * naming * Check current header cell ref is defined for key * cleaning * make table re-render when data changed * eslint --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Text overflow (#96641) * feat(table-ng): text overflow * cleaning * TableNG: Fix footer for count (#96802) * TableNG: Table column filter (#96767) * feat(table-ng): add filter form --------- Co-authored-by: drew08t <drew08@gmail.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: On column resize trigger (#97004) chore(table-ng): trigger on resize on text wrap only * TableNG: Improve sort performance (#97767) * TableNG: Improve sort performance * clean a bit * a bit more * Remove const that was breaking sort --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Fix sorting (#98141) fix(table-ng): sorting * TableNG: fix multi sorting (#98668) fix(table-ng): multi sorting * TableNG: Column re-size handler (#98901) * feat(table-ng): column re-size handler * TableNG: Fix footer calcs with no reducer (#99347) * TableNG: Update renderHeaderCell with filter dep (#99483) * TableNG: Updated styles for demo (#99530) * style proposal: table ng * chore: revert gauge cell custom stuff * TableNG: Cross-filter (#99459) * feat(table-ng): cross-filter * fix filter update issue * fix filter reset issue * Fix spacebar for filter input --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Filter perfomance optimization (#99620) fix(table-ng): filter performance optimization * TableNG: Refine styling closer to original table (#99625) * TableNG: Support groupToNestedTableTransform (#97134) * TableNG: Support groupToNestedTableTransform * Fix merge issues * Force refresh for now * Remove log * Fix some conflicts * Fix more conflicts * Help avoid clash with compiled frameToRecords keys * Make subtable height unconstrained * Support show field names in nested tables toggle * TableNG: Fix footer + some other misc updates (#99846) fix: footer fixes huzzah * TableNG: Styling - Update styling for cells (#99851) * fix(table-ng): bargauge inner width issue * TableNG: Move header cell component (#99844) * fix(table-ng): move header cell into separate file * Fix sub table --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Auto cell feature parity (#100095) * feat(table-ng): auto cell feature parity * TableNG: JSON cell implementation + hover fixes (#100152) * feat: tableNG json cell + auto fixes * chore: add comment * add justify content to json cell --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Fix cell hover issue (#100207) * fix(table-ng): cell hover issue * better commenting * TableNG: Text color cell (#100120) feat(table-ng): text color cell feature parity * TableNG: Image cell implementation (#100132) * feat: tableNG image cell * fix: incorporate justify-content correctly * chore: pass down cell options from fieldConfig --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> * TableNG: Cell height performance improvement (#100544) * chore: perf improvement * chore: minor fix * Update packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * chore: fix betterer --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * TableNG: Add pagination (#100165) * TableNG: Add pagination * TableNG: Get collapsed icon state correct + update `rowHeight` (#100556) * fix: get collapsed icon state correct + update condition for calculating row height * chore: some cleanup! * chore: naming to avoid confusion with local state name * TableNG: Add support for `DataLinksCell` (#100459) * TableNG: Improve sub table styling (#100772) * Move files temporarily to fix conflicts * Fix feature flag conflicts * Move files back to cell dir * TableNG: Update inner height of bar gauge cell (#100996) * fix: change inner height of bar gauge cell * chore: move function to utils, cleanup * Remove testing line * TableNG: Add bottom border to column headers + fix footer styling (#101016) * feat: add bottom border to column headers for table parity * feat: summary row style fix * chore: remove redundant style --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for `ActionsCell` (#101024) * TableNG: Cell hover styles + header resize handler indicator (#100770) * fix: tableNG styles * chore: clean up comments * chore: remove column header stuffz for now * fix: refactor to transform/translate + resize handler hover styling * chore: re-think approach - change a lot of things * chore: most recent iteration * chore: wait i like this better * chore: hoist into colors function + clean it up! * moar better * chore: define constants for clarity * chore: calculate rbga to rgb values given background color --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix scoll hover jumpy behavior (#101085) * fix(table-ng): hover scroll jumping * Account for panel padding during pagination --------- Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix imports (#101059) * fix(table-ng): clean imports Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> * TableNG: Sorted rows dependent upon filtered rows (#100985) TableNG: Improve multi-sort performance * TableNG: Fix sparkline width (#101164) fix(table-ng): sparkline width * TableNG: Type TableNG (#101257) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * chore: remove unused rowIndex prop --------- Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Add support for datalinks (#100769) Co-authored-by: drew08t <drew08@gmail.com> * Chore: Remove unused import (#102064) remove unused import * Update betterer * BarGauge: Remove z-index (#102220) fix(bargauge): remove z-index * TableNG: Refactor + testing (#102045) * feat: type tableNG * chore: push betterer * chore: fix linter + why can't I have inline if statements... GRR! * fix: linter - props name got changed at some point... * feedback: data links prop consistency + json cell robustness * feat: refactor + tests * chore: fix import lint errors * betterer * chore: fix image cell * chore: revert width function * add test * betterer * chore: fix sorting + add tests * chore: pr feedback --------- Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: drew08t <drew08@gmail.com> * TableNG: Fix table suggestion (#102497) fix: defensively guard against missing cellOptions * TableNG: Footer fields calc fix (#102487) * fix: respect footer fields calc selection * chore: add test * TableNG: Image cell hover fix (#102489) fix: image cell hover * TableNG: Persist scrollbars during re render (#102559) * TableNG: Persist scrollbars during re render * Update improved betterer * TableNG: Fix column width override (#102474) * fix(table): column width override * TableNG: Add support for crosshair share (#102410) * TableNG: Add support for crosshair share * Add tests * TableNG: Fix table ng tests (#102645) fix: cellType causing tests to fail * Remove empty file * TableNG: Update util tests (#102646) * TableNG: Add column type icon (#102686) * chore(table-ng): add column type icon * chore(table-ng): clean styling * Use core internationalization outside grafana ui * Import popover directly * Add count to grafana-ui locales * TableNG: Change feature flag to tableNextGen (#102814) Change feature flag to tableNextGen * TableNG: Add row colors (#102706) * chore(table-ng): add row colors * clean up * fix params * fix(table-ng): cell color background indexing --------- Co-authored-by: Kyle Cunningham <kyle@codeincarnate.com> Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: Adela Almasan <adela.almasan@grafana.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Adela Almasan <88068998+adela-almasan@users.noreply.github.com> Co-authored-by: Alex Spencer <52186778+alexjonspencer1@users.noreply.github.com>
2025-03-26 11:57:57 +08:00
TableCellDisplayMode.Auto // cellType
);
expect(result).toBe(false);
});
});
describe('getRowHeight', () => {
const mockContext = {
font: '',
measureText: (text: string) => ({
width: text.length * 8,
}),
};
const osContext = mockContext as unknown as OffscreenCanvasRenderingContext2D;
const headerCellRefs = {
current: {
stringCol: { offsetWidth: 100 },
numberCol: { offsetWidth: 100 },
} as unknown as Record<string, HTMLDivElement>,
};
it('should return default height when no text cells present', () => {
const row = {
__depth: 0,
__index: 0,
numberCol: 123,
};
const columnTypes = { numberCol: FieldType.number };
const height = getRowHeight(
row,
columnTypes,
headerCellRefs,
osContext,
20, // lineHeight
40, // defaultRowHeight
8 // padding
);
expect(height).toBe(40);
});
it('should calculate height based on longest text cell', () => {
const row = {
__depth: 0,
__index: 0,
stringCol: 'This is a very long text that should wrap',
numberCol: 123,
};
const columnTypes = {
stringCol: FieldType.string,
numberCol: FieldType.number,
};
const height = getRowHeight(row, columnTypes, headerCellRefs, osContext, 20, 40, 8);
expect(height).toBeGreaterThan(40);
expect(height).toBe(112);
});
it('should handle empty header cell refs', () => {
const row = {
__depth: 0,
__index: 0,
stringCol: 'Some text',
};
const columnTypes = { stringCol: FieldType.string };
const emptyRefs = { current: {} } as unknown as React.MutableRefObject<Record<string, HTMLDivElement>>;
const height = getRowHeight(row, columnTypes, emptyRefs, osContext, 20, 40, 8);
expect(height).toBe(40);
});
});
describe('isTextCell', () => {
it('should return true for string fields', () => {
expect(isTextCell('column', { column: FieldType.string })).toBe(true);
});
it('should return false for non-string fields', () => {
expect(isTextCell('column', { column: FieldType.number })).toBe(false);
expect(isTextCell('column', { column: FieldType.time })).toBe(false);
expect(isTextCell('column', { column: FieldType.boolean })).toBe(false);
});
it('should handle unknown fields', () => {
expect(isTextCell('unknown', { column: FieldType.string })).toBe(false);
});
});
describe('migrateTableDisplayModeToCellOptions', () => {
it('should migrate basic to gauge mode', () => {
const result = migrateTableDisplayModeToCellOptions(TableCellDisplayMode.BasicGauge);
expect(result).toEqual({
type: TableCellDisplayMode.Gauge,
mode: BarGaugeDisplayMode.Basic,
});
});
it('should migrate gradient-gauge to gauge mode with gradient', () => {
const result = migrateTableDisplayModeToCellOptions(TableCellDisplayMode.GradientGauge);
expect(result).toEqual({
type: TableCellDisplayMode.Gauge,
mode: BarGaugeDisplayMode.Gradient,
});
});
it('should migrate color-background to color background with gradient', () => {
const result = migrateTableDisplayModeToCellOptions(TableCellDisplayMode.ColorBackground);
expect(result).toEqual({
type: TableCellDisplayMode.ColorBackground,
mode: TableCellBackgroundDisplayMode.Gradient,
});
});
it('should handle other display modes', () => {
const result = migrateTableDisplayModeToCellOptions(TableCellDisplayMode.ColorText);
expect(result).toEqual({
type: TableCellDisplayMode.ColorText,
});
});
});
describe('getCellOptions', () => {
it('should return default options when no custom config is provided', () => {
const field: Field = {
name: 'test',
type: FieldType.string,
config: {},
values: [],
};
const options = getCellOptions(field);
// Check that default options are returned
expect(options).toEqual({ type: TableCellDisplayMode.Auto });
});
it('should extract cell options from field config', () => {
const field: Field = {
name: 'test',
type: FieldType.string,
config: {
custom: {
cellOptions: {
type: TableCellDisplayMode.ColorText,
inspectEnabled: false,
wrapText: true,
},
},
},
values: [],
};
const options = getCellOptions(field);
expect(options).toEqual({
type: TableCellDisplayMode.ColorText,
inspectEnabled: false,
wrapText: true,
});
});
it('should handle legacy displayMode property', () => {
const field: Field = {
name: 'test',
type: FieldType.string,
config: {
custom: {
displayMode: 'color-background',
},
},
values: [],
};
const options = getCellOptions(field);
// The legacy displayMode should be converted to the new format
expect(options.type).toBe(TableCellDisplayMode.ColorBackground);
});
it('should prioritize cellOptions over legacy displayMode', () => {
const field: Field = {
name: 'test',
type: FieldType.string,
config: {
custom: {
displayMode: 'color-background',
cellOptions: {
type: TableCellDisplayMode.ColorText,
},
},
},
values: [],
};
const options = getCellOptions(field);
expect(options.type).toBe(TableCellDisplayMode.ColorBackground);
});
it('should handle image display mode', () => {
const field: Field = {
name: 'test',
type: FieldType.string,
config: {
custom: {
cellOptions: {
type: TableCellDisplayMode.Image,
// Add image-specific options if they exist
},
},
},
values: [],
};
const options = getCellOptions(field);
expect(options.type).toBe(TableCellDisplayMode.Image);
});
it('should handle JSON display mode', () => {
const field: Field = {
name: 'test',
type: FieldType.string,
config: {
custom: {
cellOptions: {
type: TableCellDisplayMode.JSONView,
},
},
},
values: [],
};
const options = getCellOptions(field);
expect(options.type).toBe(TableCellDisplayMode.JSONView);
});
});
describe('getCellLinks', () => {
it('should return undefined when field has no getLinks function', () => {
const field: Field = {
name: 'test',
type: FieldType.string,
config: {},
values: ['value'],
};
const links = getCellLinks(field, 0);
expect(links).toEqual(undefined);
});
it('should return links from field getLinks function', () => {
const mockLinks: LinkModel[] = [
{ title: 'Link 1', href: 'http://example.com/1', target: '_blank', origin: { datasourceUid: 'test' } },
{ title: 'Link 2', href: 'http://example.com/2', target: '_self', origin: { datasourceUid: 'test' } },
];
const field: Field = {
name: 'test',
type: FieldType.string,
config: {},
values: ['value1', 'value2'],
getLinks: (config: ValueLinkConfig) => {
return config.valueRowIndex === 0 ? mockLinks : [];
},
};
const links = getCellLinks(field, 0);
expect(links).toEqual(mockLinks);
});
it('should return empty array for out of bounds index', () => {
const mockLinks: LinkModel[] = [
{ title: 'Link 1', href: 'http://example.com/1', target: '_blank', origin: { datasourceUid: 'test' } },
];
const field: Field = {
name: 'test',
type: FieldType.string,
config: {},
values: ['value1'],
getLinks: (config: ValueLinkConfig) => {
return config.valueRowIndex === 0 ? mockLinks : [];
},
};
// Index out of bounds
const links = getCellLinks(field, 1);
expect(links).toEqual([]);
});
it('should handle getLinks returning undefined', () => {
const field: Field = {
name: 'test',
type: FieldType.string,
config: {},
values: ['value1'],
getLinks: (config: ValueLinkConfig) => {
return [];
},
};
const links = getCellLinks(field, 0);
expect(links).toEqual([]);
});
it('should handle different link configurations', () => {
// Create links with different valid configurations
const mockLinks: LinkModel[] = [
// Standard link with href
{
title: 'External Link',
href: 'http://example.com/full',
target: '_blank',
origin: { datasourceUid: 'test' },
},
// Internal link with onClick handler
{
title: 'Internal Link',
href: '', // Empty href for internal links
onClick: jest.fn(),
target: '_self',
origin: { datasourceUid: 'test' },
},
];
const field: Field = {
name: 'test',
type: FieldType.string,
config: {},
values: ['value1'],
getLinks: () => mockLinks,
};
const links = getCellLinks(field, 0);
// Verify links are returned unmodified
expect(links).toEqual(mockLinks);
// Verify we have both types of links
expect(links?.find((link) => link.onClick !== undefined)).toBeDefined();
expect(links?.find((link) => link.href === 'http://example.com/full')).toBeDefined();
});
});
describe('getCellHeight', () => {
// Create a mock OffscreenCanvasRenderingContext2D
const createMockContext = () => {
return {
measureText: jest.fn((text) => {
// Simple mock that returns width based on text length
// This is a simplification - real browser would be more complex
return { width: text.length * 8 }; // Assume 8px per character
}),
} as unknown as OffscreenCanvasRenderingContext2D;
};
it('should return default row height when osContext is null', () => {
const defaultRowHeight = 40;
const height = getCellHeight('Some text', 100, null, 20, defaultRowHeight);
expect(height).toBe(defaultRowHeight);
});
it('should return default row height for text that fits in one line', () => {
const mockContext = createMockContext();
const defaultRowHeight = 40;
const cellWidth = 100; // 100px width
const text = 'Short'; // 5 chars * 8px = 40px, fits in cellWidth
const height = getCellHeight(text, cellWidth, mockContext, 20, defaultRowHeight);
// Since text fits in one line, should return default height
expect(height).toBe(defaultRowHeight);
expect(mockContext.measureText).toHaveBeenCalled();
});
it('should calculate height for text that wraps to multiple lines', () => {
const mockContext = createMockContext();
const defaultRowHeight = 40;
const lineHeight = 20;
const cellWidth = 100; // 100px width
// This text is long enough to wrap to multiple lines
const text = 'This is a very long text that will definitely need to wrap to multiple lines in our table cell';
const height = getCellHeight(text, cellWidth, mockContext, lineHeight, defaultRowHeight);
// Should be greater than default height since text wraps
expect(height).toBeGreaterThan(defaultRowHeight);
expect(height).toBe(180);
// Height should be a multiple of line height plus padding
expect(height % lineHeight).toBe(0);
expect(mockContext.measureText).toHaveBeenCalled();
});
it('should account for padding when calculating height', () => {
const mockContext = createMockContext();
const defaultRowHeight = 40;
const lineHeight = 20;
const cellWidth = 100;
const padding = 10;
const text = 'This is a very long text that will wrap to multiple lines';
const heightWithoutPadding = getCellHeight(text, cellWidth, mockContext, lineHeight, defaultRowHeight);
const heightWithPadding = getCellHeight(text, cellWidth, mockContext, lineHeight, defaultRowHeight, padding);
// Height with padding should be greater than without padding
expect(heightWithPadding).toBeGreaterThan(heightWithoutPadding);
// The difference should be related to the padding (padding is applied twice in the function)
expect(heightWithPadding - heightWithoutPadding).toBe(padding * 2 * 2);
});
it('should handle empty text', () => {
const mockContext = createMockContext();
const defaultRowHeight = 40;
const height = getCellHeight('', 100, mockContext, 20, defaultRowHeight);
// Empty text should return default height
expect(height).toBe(defaultRowHeight);
});
});
describe('getFooterStyles', () => {
it('should create an emotion css class', () => {
const styles = getFooterStyles('flex-start');
// Check that the footerCell style has been created
expect(styles.footerCell).toBeDefined();
// Get the CSS string representation
const cssString = styles.footerCell.toString();
// Verify it's an Emotion CSS class
expect(cssString).toContain('css-');
});
it('should use the provided justification value', () => {
const styles = getFooterStyles('center');
// Create a DOM element and apply the CSS class
document.body.innerHTML = `<div class="${styles.footerCell}">Test</div>`;
const element = document.querySelector('div');
// Get the computed style
const computedStyle = window.getComputedStyle(element!);
// Check the CSS property
expect(computedStyle.justifyContent).toBe('center');
});
it('should default to space-between when no justification is provided', () => {
const styles = getFooterStyles(undefined as any);
// Create a DOM element and apply the CSS class
document.body.innerHTML = `<div class="${styles.footerCell}">Test</div>`;
const element = document.querySelector('div');
// Get the computed style
const computedStyle = window.getComputedStyle(element!);
// Check the CSS property
expect(computedStyle.justifyContent).toBe('space-between');
});
// Clean up after all tests
afterAll(() => {
document.body.innerHTML = '';
});
});
describe('extractPixelValue', () => {
it('should extract numeric value from pixel string', () => {
expect(extractPixelValue('100px')).toBe(100);
expect(extractPixelValue('42px')).toBe(42);
expect(extractPixelValue('0px')).toBe(0);
});
it('should handle numeric input', () => {
expect(extractPixelValue(100)).toBe(100);
expect(extractPixelValue(42)).toBe(42);
expect(extractPixelValue(0)).toBe(0);
});
it('should handle string numbers without units', () => {
expect(extractPixelValue('100')).toBe(100);
expect(extractPixelValue('42')).toBe(42);
expect(extractPixelValue('0')).toBe(0);
});
it('should handle decimal values', () => {
expect(extractPixelValue('100.5px')).toBe(100.5);
expect(extractPixelValue('42.75px')).toBe(42.75);
expect(extractPixelValue(100.5)).toBe(100.5);
});
it('should handle negative values', () => {
expect(extractPixelValue('-100px')).toBe(-100);
expect(extractPixelValue('-42px')).toBe(-42);
expect(extractPixelValue(-100)).toBe(-100);
});
it('should handle other CSS units by removing them', () => {
expect(extractPixelValue('100em')).toBe(100);
expect(extractPixelValue('42rem')).toBe(42);
expect(extractPixelValue('10vh')).toBe(10);
expect(extractPixelValue('20vw')).toBe(20);
});
it('should handle whitespace', () => {
expect(extractPixelValue(' 100px ')).toBe(100);
expect(extractPixelValue(' 42 px ')).toBe(42);
});
it('should return 0 for invalid input when no default is provided', () => {
expect(extractPixelValue('not-a-number')).toBe(0);
expect(extractPixelValue('px')).toBe(0);
expect(extractPixelValue('')).toBe(0);
expect(extractPixelValue(null as any)).toBe(0);
expect(extractPixelValue(undefined as any)).toBe(0);
});
});
describe('convertRGBAToHex', () => {
it('should convert RGBA format to hex with alpha', () => {
expect(convertRGBAToHex('#181b1f', 'rgba(255, 0, 0, 1)')).toBe('#ff0000');
expect(convertRGBAToHex('#181b1f', 'rgba(0, 255, 0, 0.5)')).toBe('#0c8d10');
expect(convertRGBAToHex('#181b1f', 'rgba(0, 0, 255, 0)')).toBe('#181b1f');
});
});
describe('getDefaultRowHeight', () => {
const theme = createTheme();
it('returns correct height for TableCellHeight.Sm', () => {
const result = getDefaultRowHeight(theme, TableCellHeight.Sm);
expect(result).toBe(36);
});
it('returns correct height for TableCellHeight.Md', () => {
const result = getDefaultRowHeight(theme, TableCellHeight.Md);
expect(result).toBe(42);
});
it('returns correct height for TableCellHeight.Lg', () => {
const result = getDefaultRowHeight(theme, TableCellHeight.Lg);
expect(result).toBe(TABLE.MAX_CELL_HEIGHT);
});
it('calculates height based on theme when cellHeight is undefined', () => {
const result = getDefaultRowHeight(theme, undefined as unknown as TableCellHeight);
// Calculate the expected result based on the theme values
const expected = TABLE.CELL_PADDING * 2 + theme.typography.fontSize * theme.typography.body.lineHeight;
expect(result).toBe(expected);
});
});
describe('myRowRenderer', () => {
// Create mock props for testing
const createMockProps = (depth: number, hasData: boolean, index: number) => {
return {
row: {
__depth: depth,
__index: index,
data: hasData ? { length: 2 } : undefined,
},
viewportColumns: [],
rowIdx: 0,
isRowSelected: false,
onRowClick: jest.fn(),
onRowDoubleClick: jest.fn(),
rowClass: '',
top: 0,
height: 40,
'aria-rowindex': 1,
'aria-selected': false,
gridRowStart: 1,
isLastRow: false,
selectedCellIdx: undefined,
selectCell: jest.fn(),
lastFrozenColumnIndex: -1,
copiedCellIdx: undefined,
draggedOverCellIdx: undefined,
setDraggedOverRowIdx: jest.fn(),
onRowChange: jest.fn(),
rowArray: [],
selectedPosition: { idx: 0, rowIdx: 0, mode: 'SELECT' },
} as any;
};
const mockPanelContext = {
id: 1,
title: 'Test Panel',
description: 'Test Description',
width: 800,
height: 600,
timeRange: { from: 'now-6h', to: 'now' },
timeZone: 'browser',
onTimeRangeChange: jest.fn(),
onOptionsChange: jest.fn(),
onFieldConfigChange: jest.fn(),
onInstanceStateChange: jest.fn(),
replaceVariables: jest.fn(),
eventBus: {
publish: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
},
} as unknown as PanelContext;
const mockData = createDataFrame({
fields: [
{ name: 'Time', type: FieldType.time, values: [] },
{ name: 'Value', type: FieldType.number, values: [] },
],
});
it('returns null for non-expanded child rows', () => {
const props = createMockProps(1, false, 0);
const expandedRows: number[] = []; // No expanded rows
const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false);
expect(result).toBeNull();
});
it('renders child rows when parent is expanded', () => {
const props = createMockProps(1, false, 0);
const expandedRows: number[] = [0]; // Row 0 is expanded
const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false);
expect(result).not.toBeNull();
});
it('adds aria-expanded attribute to parent rows with nested data', () => {
const props = createMockProps(0, true, 0);
const expandedRows: number[] = [0]; // Row 0 is expanded
const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false) as JSX.Element;
expect(result.props['aria-expanded']).toBe(true);
});
it('sets aria-expanded to false when parent row is not expanded', () => {
const props = createMockProps(0, true, 0);
const expandedRows: number[] = []; // No expanded rows
const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false) as JSX.Element;
expect(result.props['aria-expanded']).toBe(false);
});
it('renders regular rows without aria-expanded attribute', () => {
const props = createMockProps(0, false, 0);
const expandedRows: number[] = [];
const result = myRowRenderer('key-0', props, expandedRows, mockPanelContext, mockData, false) as JSX.Element;
expect(result.props['aria-expanded']).toBeUndefined();
});
});
});