Add background color for null cell in query editor result grid (#22370)

This commit is contained in:
Hai Cao
2023-03-20 15:55:57 -07:00
committed by GitHub
parent f9e72b0d93
commit ffc7f05c10
5 changed files with 35 additions and 9 deletions

View File

@@ -85,7 +85,7 @@ export function hyperLinkFormatter(row: number | undefined, cell: any | undefine
/**
* Format all text to replace all new lines with spaces and performs HTML entity encoding
*/
export function textFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined): string {
export function textFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined, addClasses?: string): string | { text: string, addClasses: string } {
let cellClasses = 'grid-cell-value-container';
let valueToDisplay = '';
let titleValue = '';
@@ -122,7 +122,13 @@ export function textFormatter(row: number | undefined, cell: any | undefined, va
titleValue = valueToDisplay;
}
return `<span title="${titleValue}" style="${cellStyle}" class="${cellClasses}">${valueToDisplay}</span>`;
const formattedValue = `<span title="${titleValue}" style="${cellStyle}" class="${cellClasses}">${valueToDisplay}</span>`;
if (addClasses) {
return { text: formattedValue, addClasses: addClasses };
}
return formattedValue;
}
function getCellDisplayValue(cellValue: string): string {
@@ -132,7 +138,7 @@ function getCellDisplayValue(cellValue: string): string {
}
export function iconCssFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined): string {
export function iconCssFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined): string | { text: string, addClasses: string } {
if (isCssIconCellValue(value)) {
return `<div role="image" title="${escape(value.title ?? '')}" aria-label="${escape(value.title ?? '')}" class="grid-cell-value-container icon codicon slick-icon-cell-content ${value.iconCssClass}"></div>`;
}

View File

@@ -16,6 +16,9 @@ suite('Grid shared services tests', () => {
isNull: false
};
let formattedHtml = SharedServices.textFormatter(undefined, undefined, cellValue, undefined, undefined);
if (typeof formattedHtml !== 'string') {
formattedHtml = formattedHtml.text;
}
// Then the result is HTML for a span element containing the cell value's display value as plain text
verifyFormattedHtml(formattedHtml, testText);
@@ -24,6 +27,9 @@ suite('Grid shared services tests', () => {
test('textFormatter should encode HTML when formatting a string', () => {
// If I format a string that contains HTML
let formattedHtml = SharedServices.textFormatter(undefined, undefined, testText, undefined, undefined);
if (typeof formattedHtml !== 'string') {
formattedHtml = formattedHtml.text;
}
// Then the result is HTML for a span element containing the given text as plain text
verifyFormattedHtml(formattedHtml, testText);

View File

@@ -91,3 +91,6 @@ export const calloutDialogShadowColor = registerColor('calloutDialog.shadow', {
// Designer
export const DesignerPaneSeparator = registerColor('designer.paneSeparator', { light: '#DDDDDD', dark: '#8A8886', hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('designer.paneSeparator', 'The pane separator color.'));
// Query Editor
export const queryEditorNullBackground = registerColor('queryEditor.nullBackground', { light: '#FFFFE1', dark: '#4B0082', hcDark: '#000000', hcLight: '#FFFFE1' }, nls.localize('queryEditorNullBackground', 'The background color for null values in the query editor results grid.'));

View File

@@ -17,14 +17,14 @@ import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAc
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { escape } from 'sql/base/common/strings';
import { hyperLinkFormatter, textFormatter } from 'sql/base/browser/ui/table/formatters';
import { DBCellValue, hyperLinkFormatter, textFormatter } from 'sql/base/browser/ui/table/formatters';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Emitter, Event } from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { Disposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { range } from 'vs/base/common/arrays';
@@ -56,6 +56,7 @@ import { CopyAction } from 'vs/editor/contrib/clipboard/browser/clipboard';
import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/browser/format';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { queryEditorNullBackground } from 'sql/platform/theme/common/colorRegistry';
const ROW_HEIGHT = 29;
const HEADER_HEIGHT = 26;
@@ -80,6 +81,9 @@ const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ES
// or '{', and there must be a '}' or ']' to close it.
const IsJsonRegex = /^\s*[\{|\[][\S\s]*[\}\]]\s*$/g;
// The css class for null cell
const NULL_CELL_CSS_CLASS = 'cell-null';
export class GridPanel extends Disposable {
private container = document.createElement('div');
private scrollableView: ScrollableView;
@@ -431,7 +435,7 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
? localize('xmlShowplan', "XML Showplan")
: escape(c.columnName),
field: i.toString(),
formatter: c.isXml ? hyperLinkFormatter : (row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string => {
formatter: c.isXml ? hyperLinkFormatter : (row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string | { text: string, addClasses: string } => {
return queryResultTextFormatter(this.gridConfig.showJsonAsLink, row, cell, value, columnDef, dataContext);
},
width: this.state.columnSizes && this.state.columnSizes[i] ? this.state.columnSizes[i] : undefined
@@ -567,6 +571,13 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
refreshColumns: !autoSizeOnRender // The auto size columns plugin refreshes the columns so we don't need to refresh twice if both plugins are on.
});
this._register(attachTableFilterStyler(this.filterPlugin, this.themeService));
this._register(registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
const nullBackground = theme.getColor(queryEditorNullBackground);
if (nullBackground) {
collector.addRule(`.${NULL_CELL_CSS_CLASS} { background: ${nullBackground};}`);
}
}));
this.table.registerPlugin(this.filterPlugin);
if (this.styles) {
this.table.style(this.styles);
@@ -1004,10 +1015,10 @@ function isJsonCell(value: ICellValue): boolean {
return !!(value && !value.isNull && value.displayValue?.match(IsJsonRegex));
}
function queryResultTextFormatter(showJsonAsLink: boolean, row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string {
function queryResultTextFormatter(showJsonAsLink: boolean, row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string | { text: string, addClasses: string } {
if (showJsonAsLink && isJsonCell(value)) {
return hyperLinkFormatter(row, cell, value, columnDef, dataContext);
} else {
return textFormatter(row, cell, value, columnDef, dataContext);
return textFormatter(row, cell, value, columnDef, dataContext, (DBCellValue.isDBCellValue(value) && value.isNull) ? NULL_CELL_CSS_CLASS : undefined);
}
}