diff --git a/CHANGELOG.md b/CHANGELOG.md index c86739a136..b702615f21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### 🐞 Bug Fixes * Fixed transparent background for popup inline editors. +* Exceptions that occur in custom application tooltips will now be caught and logged to console, + rather than crashing grid. + ### ⚙️ Technical * Improvements to exception handling during app initialization diff --git a/cmp/grid/columns/Column.ts b/cmp/grid/columns/Column.ts index afced6b965..396b7707ac 100644 --- a/cmp/grid/columns/Column.ts +++ b/cmp/grid/columns/Column.ts @@ -42,7 +42,7 @@ import { } from 'react'; import {GridModel} from '../GridModel'; import {GridSorter} from '../GridSorter'; -import {managedRenderer} from '../impl/Utils'; +import {getAgHeaderClassFn, managedRenderer} from '../impl/Utils'; import { ColumnCellClassFn, ColumnCellClassRuleFn, @@ -62,10 +62,8 @@ import { } from '../Types'; import {ExcelFormat} from '../enums/ExcelFormat'; import {FunctionComponent} from 'react'; -import {ColumnGroup} from './ColumnGroup'; import type { ColDef, - HeaderClassParams, ITooltipParams, ValueGetterParams, ValueSetterParams @@ -817,9 +815,15 @@ export class Column { store }); - ret = isFunction(tooltip) - ? tooltip(val, {record, column: this, gridModel, agParams}) - : val; + if (isFunction(tooltip)) { + try { + ret = tooltip(val, {record, gridModel, agParams, column: this}); + } catch (e) { + logWarn([`Failure in tooltip for '${this.displayName}'`, e], 'Column'); + } + } else { + ret = val; + } } const isElement = isValidElement(ret); @@ -1083,34 +1087,3 @@ export class Column { : record?.data[sortValue] ?? v; } } - -export function getAgHeaderClassFn( - column: Column | ColumnGroup -): (params: HeaderClassParams) => string[] { - // Generate CSS classes for headers. - // Default alignment classes are mixed in with any provided custom classes. - const {headerClass, headerAlign, gridModel} = column; - - return agParams => { - let r = []; - if (headerClass) { - r = castArray( - isFunction(headerClass) ? headerClass({column, gridModel, agParams}) : headerClass - ); - } - - if (headerAlign === 'center' || headerAlign === 'right') { - r.push('xh-column-header-align-' + headerAlign); - } - - if (column instanceof Column && column.isTreeColumn && column.headerHasExpandCollapse) { - r.push('xh-column-header--with-expand-collapse'); - } - - if (gridModel.headerMenuDisplay === 'hover') { - r.push('xh-column-header--hoverable'); - } - - return r; - }; -} diff --git a/cmp/grid/columns/ColumnGroup.ts b/cmp/grid/columns/ColumnGroup.ts index fa34c57e40..895f04bffd 100644 --- a/cmp/grid/columns/ColumnGroup.ts +++ b/cmp/grid/columns/ColumnGroup.ts @@ -4,6 +4,7 @@ * * Copyright © 2023 Extremely Heavy Industries Inc. */ +import {getAgHeaderClassFn} from '@xh/hoist/cmp/grid/impl/Utils'; import {HAlign, PlainObject, Some, Thunkable, XH} from '@xh/hoist/core'; import {genDisplayName} from '@xh/hoist/data'; @@ -13,7 +14,7 @@ import {clone, isEmpty, isFunction, isString, keysIn} from 'lodash'; import {ReactNode} from 'react'; import {GridModel} from '../GridModel'; import {ColumnHeaderClassFn, ColumnHeaderNameFn} from '../Types'; -import {Column, ColumnSpec, getAgHeaderClassFn} from './Column'; +import {Column, ColumnSpec} from './Column'; export interface ColumnGroupSpec { /** Column or ColumnGroup configs for children of this group.*/ diff --git a/cmp/grid/impl/Utils.ts b/cmp/grid/impl/Utils.ts index f8396a3d75..48b178a545 100644 --- a/cmp/grid/impl/Utils.ts +++ b/cmp/grid/impl/Utils.ts @@ -4,12 +4,11 @@ * * Copyright © 2023 Extremely Heavy Industries Inc. */ -import {ColumnRenderer, GroupRowRenderer} from '@xh/hoist/cmp/grid'; -import {isFunction} from 'lodash'; +import {Column, ColumnGroup, ColumnRenderer, GroupRowRenderer} from '@xh/hoist/cmp/grid'; +import {HeaderClassParams} from '@xh/hoist/kit/ag-grid'; +import {castArray, isFunction} from 'lodash'; -/** - * @internal - */ +/** @internal */ export function managedRenderer( fn: T, identifier: string @@ -24,3 +23,38 @@ export function managedRenderer( } } as unknown as T; } + +/** + * Generate CSS classes for headers. + * Default alignment classes are mixed in with any provided custom classes. + * + * @internal + */ +export function getAgHeaderClassFn( + column: Column | ColumnGroup +): (params: HeaderClassParams) => string[] { + const {headerClass, headerAlign, gridModel} = column; + + return agParams => { + let r = []; + if (headerClass) { + r = castArray( + isFunction(headerClass) ? headerClass({column, gridModel, agParams}) : headerClass + ); + } + + if (headerAlign === 'center' || headerAlign === 'right') { + r.push('xh-column-header-align-' + headerAlign); + } + + if (column instanceof Column && column.isTreeColumn && column.headerHasExpandCollapse) { + r.push('xh-column-header--with-expand-collapse'); + } + + if (gridModel.headerMenuDisplay === 'hover') { + r.push('xh-column-header--hoverable'); + } + + return r; + }; +}