mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-29 09:35:38 -05:00
table component improvement (#13801)
* hyperlink column * fixed width for image only button - old behavior
This commit is contained in:
3
src/sql/azdata.d.ts
vendored
3
src/sql/azdata.d.ts
vendored
@@ -3222,6 +3222,9 @@ declare module 'azdata' {
|
||||
headerCssClass?: string;
|
||||
toolTip?: string;
|
||||
type?: ColumnType;
|
||||
/**
|
||||
* @deprecated options property is deprecated, use specific column types to access the options directly
|
||||
*/
|
||||
options?: CheckboxColumnOption | TextColumnOption;
|
||||
}
|
||||
|
||||
|
||||
63
src/sql/azdata.proposed.d.ts
vendored
63
src/sql/azdata.proposed.d.ts
vendored
@@ -842,21 +842,76 @@ declare module 'azdata' {
|
||||
}
|
||||
|
||||
export interface IconColumnCellValue {
|
||||
/**
|
||||
* The icon to be displayed.
|
||||
*/
|
||||
icon: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
|
||||
ariaLabel: string;
|
||||
/**
|
||||
* The title of the icon.
|
||||
*/
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface ButtonColumnCellValue {
|
||||
/**
|
||||
* The icon to be displayed.
|
||||
*/
|
||||
icon?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
|
||||
/**
|
||||
* The title of the button.
|
||||
*/
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface HyperlinkColumnCellValue {
|
||||
/**
|
||||
* The icon to be displayed.
|
||||
*/
|
||||
icon?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
|
||||
/**
|
||||
* The title of the hyperlink.
|
||||
*/
|
||||
title?: string;
|
||||
|
||||
/**
|
||||
* The url to open.
|
||||
*/
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export enum ColumnType {
|
||||
icon = 3
|
||||
icon = 3,
|
||||
hyperlink = 4
|
||||
}
|
||||
|
||||
export interface TableColumn {
|
||||
/**
|
||||
* The text to display on the column heading. 'value' property will be used, if not specified
|
||||
**/
|
||||
* The text to display on the column heading. 'value' property will be used, if not specified
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface IconColumnOptions {
|
||||
/**
|
||||
* The icon to use for all the cells in this column.
|
||||
*/
|
||||
icon?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
|
||||
}
|
||||
|
||||
export interface ButtonColumn extends IconColumnOptions, TableColumn {
|
||||
/**
|
||||
* Whether to show the text, default value is false.
|
||||
*/
|
||||
showText?: boolean;
|
||||
}
|
||||
|
||||
export interface HyperlinkColumn extends IconColumnOptions, TableColumn {
|
||||
}
|
||||
|
||||
export interface CheckboxColumn extends TableColumn {
|
||||
action: ActionOnCellCheckboxCheck;
|
||||
}
|
||||
|
||||
export enum AzureResource {
|
||||
/**
|
||||
* Microsoft Graph
|
||||
|
||||
@@ -38,7 +38,7 @@ export interface HyperlinkCellValue {
|
||||
|
||||
export interface CssIconCellValue {
|
||||
iconCssClass: string,
|
||||
ariaLabel: string
|
||||
title: string
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ export function textFormatter(row: number | undefined, cell: any | undefined, va
|
||||
|
||||
export function iconCssFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined): string {
|
||||
if (isCssIconCellValue(value)) {
|
||||
return `<div role='image' aria-label="${escape(value.ariaLabel)}" class="grid-cell-value-container icon codicon slick-icon-cell-content ${value.iconCssClass}"></div>`;
|
||||
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>`;
|
||||
}
|
||||
return textFormatter(row, cell, value, columnDef, dataContext);
|
||||
}
|
||||
|
||||
@@ -163,8 +163,7 @@
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.slick-icon-cell-content,
|
||||
.slick-button-cell-content {
|
||||
.slick-icon-cell-content {
|
||||
background-position: 5px center !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 16px !important;
|
||||
@@ -175,9 +174,3 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.slick-button-cell-content {
|
||||
cursor: pointer;
|
||||
border-width: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
@@ -3,133 +3,44 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextWithIconColumnDefinition } from 'sql/base/browser/ui/table/plugins/textWithIconColumn';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import 'vs/css!./media/buttonColumn.plugin';
|
||||
import 'vs/css!./media/iconColumn';
|
||||
import { BaseClickableColumn, getIconCellValue, IconColumnOptions } from 'sql/base/browser/ui/table/plugins/tableColumn';
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
|
||||
export interface ButtonColumnDefinition<T extends Slick.SlickData> extends TextWithIconColumnDefinition<T> {
|
||||
}
|
||||
|
||||
export interface ButtonColumnOptions {
|
||||
/**
|
||||
* The CSS class of the icon (either a common icon or one added by the user of this column) to display in the button
|
||||
*/
|
||||
export interface ButtonCellValue {
|
||||
iconCssClass?: string;
|
||||
/**
|
||||
* The aria-label title of the button
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* The unique ID used by SlickGrid
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* Whether the column is sortable or not
|
||||
*/
|
||||
sortable?: boolean;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface ButtonClickEventArgs<T extends Slick.SlickData> {
|
||||
item: T;
|
||||
position: { x: number, y: number };
|
||||
row: number;
|
||||
column: number;
|
||||
export interface ButtonColumnOptions extends IconColumnOptions {
|
||||
/**
|
||||
* Whether to show the text.
|
||||
*/
|
||||
showText?: boolean
|
||||
}
|
||||
|
||||
export class ButtonColumn<T extends Slick.SlickData> implements Slick.Plugin<T> {
|
||||
private _handler = new Slick.EventHandler();
|
||||
private _definition: ButtonColumnDefinition<T>;
|
||||
private _grid!: Slick.Grid<T>;
|
||||
private _onClick = new Emitter<ButtonClickEventArgs<T>>();
|
||||
public onClick = this._onClick.event;
|
||||
export class ButtonColumn<T extends Slick.SlickData> extends BaseClickableColumn<T> {
|
||||
|
||||
constructor(private options: ButtonColumnOptions) {
|
||||
this._definition = {
|
||||
id: options.id,
|
||||
resizable: false,
|
||||
name: '',
|
||||
super();
|
||||
}
|
||||
|
||||
public get definition(): Slick.Column<T> {
|
||||
return {
|
||||
id: this.options.id || this.options.title || this.options.field,
|
||||
width: this.options.showText === true ? this.options.width : 26,
|
||||
formatter: (row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string => {
|
||||
return this.formatter(row, cell, value, columnDef, dataContext);
|
||||
const iconValue = getIconCellValue(this.options, dataContext);
|
||||
const escapedTitle = escape(iconValue.title ?? '');
|
||||
const iconCssClasses = iconValue.iconCssClass ? `codicon icon slick-plugin-icon ${iconValue.iconCssClass}` : '';
|
||||
const buttonTypeCssClass = this.options.showText ? 'slick-plugin-button slick-plugin-text-button' : 'slick-plugin-button slick-plugin-image-only-button';
|
||||
const buttonText = this.options.showText ? escapedTitle : '';
|
||||
return `<button tabindex=-1 class="${iconCssClasses} ${buttonTypeCssClass}" title="${escapedTitle}" aria-label="${escapedTitle}">${buttonText}</button>`;
|
||||
},
|
||||
width: 30,
|
||||
selectable: false,
|
||||
iconCssClassField: options.iconCssClass,
|
||||
sortable: options.sortable
|
||||
name: this.options.name,
|
||||
resizable: this.options.showText === true, // Image only button has fixed width.
|
||||
selectable: false
|
||||
};
|
||||
}
|
||||
|
||||
public init(grid: Slick.Grid<T>): void {
|
||||
this._grid = grid;
|
||||
this._handler.subscribe(grid.onClick, (e: DOMEvent, args: Slick.OnClickEventArgs<T>) => this.handleClick(args));
|
||||
this._handler.subscribe(grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs<T>) => this.handleKeyboardEvent(e as KeyboardEvent, args));
|
||||
this._handler.subscribe(grid.onActiveCellChanged, (e: DOMEvent, args: Slick.OnActiveCellChangedEventArgs<T>) => { this.handleActiveCellChanged(args); });
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this._handler.unsubscribeAll();
|
||||
}
|
||||
|
||||
private handleActiveCellChanged(args: Slick.OnActiveCellChangedEventArgs<T>): void {
|
||||
if (this.isCurrentColumn(args.cell)) {
|
||||
const cellElement = this._grid.getActiveCellNode();
|
||||
if (cellElement && cellElement.children) {
|
||||
const button = cellElement.children[0] as HTMLButtonElement;
|
||||
button.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleClick(args: Slick.OnClickEventArgs<T>): void {
|
||||
if (this.isCurrentColumn(args.cell)) {
|
||||
// SlickGrid will automatically set active cell on mouse click event,
|
||||
// during the process of setting active cell, blur event will be triggered and handled in a setTimeout block,
|
||||
// on Windows platform, the context menu is html based which will respond the focus related events and hide the context menu.
|
||||
// If we call the fireClickEvent directly the menu will be set to hidden immediately, to workaround the issue we need to wrap it in a setTimeout block.
|
||||
setTimeout(() => {
|
||||
this.fireClickEvent();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private handleKeyboardEvent(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>): void {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) && this.isCurrentColumn(args.cell)) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.fireClickEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public get definition(): ButtonColumnDefinition<T> {
|
||||
return this._definition;
|
||||
}
|
||||
|
||||
private fireClickEvent(): void {
|
||||
const activeCell = this._grid.getActiveCell();
|
||||
const activeCellPosition = this._grid.getActiveCellPosition();
|
||||
if (activeCell && activeCellPosition) {
|
||||
this._onClick.fire({
|
||||
row: activeCell.row,
|
||||
column: activeCell.cell,
|
||||
item: this._grid.getDataItem(activeCell.row),
|
||||
position: {
|
||||
x: (activeCellPosition.left + activeCellPosition.right) / 2,
|
||||
y: (activeCellPosition.bottom + activeCellPosition.top) / 2
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private isCurrentColumn(columnIndex: number): boolean {
|
||||
return this._grid?.getColumns()[columnIndex]?.id === this.definition.id;
|
||||
}
|
||||
|
||||
private formatter(row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string {
|
||||
const buttonColumn = columnDef as ButtonColumnDefinition<T>;
|
||||
|
||||
// tabindex=-1 means it is only focusable programatically, when the button column cell becomes active, we will set to focus to the button inside it, the tab navigation experience is smooth.
|
||||
// Otherwise, if we set tabindex to 0, the focus will go to the button first and then the first cell of the table.
|
||||
return `<button tabindex=-1 class="codicon icon slick-button-cell-content ${buttonColumn.iconCssClassField}" aria-label="${this.options.title}"></button>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./media/hyperlinkColumn.plugin';
|
||||
import 'vs/css!./media/iconColumn';
|
||||
import { BaseClickableColumn, getIconCellValue, IconColumnOptions } from 'sql/base/browser/ui/table/plugins/tableColumn';
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
|
||||
export interface HyperlinkCellValue {
|
||||
iconCssClass?: string;
|
||||
title: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface HyperlinkColumnOptions extends IconColumnOptions {
|
||||
}
|
||||
|
||||
export class HyperlinkColumn<T extends Slick.SlickData> extends BaseClickableColumn<T> {
|
||||
constructor(private options: HyperlinkColumnOptions) {
|
||||
super();
|
||||
}
|
||||
|
||||
public get definition(): Slick.Column<T> {
|
||||
return {
|
||||
id: this.options.id || this.options.title || this.options.field,
|
||||
width: this.options.width,
|
||||
formatter: (row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string => {
|
||||
const iconValue = getIconCellValue(this.options, dataContext);
|
||||
const escapedTitle = escape(iconValue.title ?? '');
|
||||
const cellValue = dataContext[this.options.field] as HyperlinkCellValue;
|
||||
const cssClasses = iconValue.iconCssClass ? `codicon icon slick-plugin-icon ${iconValue.iconCssClass}` : '';
|
||||
const urlPart = cellValue?.url ? `href="${encodeURI(cellValue.url)}" target="blank"` : '';
|
||||
return `<a ${urlPart} class="slick-hyperlink-cell ${cssClasses}" tabindex=-1 title="${escapedTitle}" aria-label="${escapedTitle}">${escapedTitle}</a>`;
|
||||
},
|
||||
name: this.options.name,
|
||||
resizable: true,
|
||||
selectable: false
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.slick-plugin-button {
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
padding-right: 0px;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.slick-plugin-button.slick-plugin-text-button {
|
||||
border-width: 1px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.slick-plugin-icon.slick-plugin-button.slick-plugin-text-button {
|
||||
text-align: left;
|
||||
padding-left: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.slick-plugin-button.slick-plugin-image-only-button {
|
||||
border-width: 0px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.slick-hyperlink-cell {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
11
src/sql/base/browser/ui/table/plugins/media/iconColumn.css
Normal file
11
src/sql/base/browser/ui/table/plugins/media/iconColumn.css
Normal file
@@ -0,0 +1,11 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.slick-plugin-icon {
|
||||
padding-left: 26px;
|
||||
background-size: 16px !important;
|
||||
background-position: 5px 50% !important;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
173
src/sql/base/browser/ui/table/plugins/tableColumn.ts
Normal file
173
src/sql/base/browser/ui/table/plugins/tableColumn.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
export interface TableColumn<T extends Slick.SlickData> {
|
||||
readonly definition: Slick.Column<T>;
|
||||
}
|
||||
|
||||
export interface TableCellClickEventArgs<T extends Slick.SlickData> {
|
||||
item: T;
|
||||
position: { x: number, y: number };
|
||||
row: number;
|
||||
column: number;
|
||||
}
|
||||
|
||||
export abstract class BaseClickableColumn<T extends Slick.SlickData> implements Slick.Plugin<T>, TableColumn<T> {
|
||||
private _handler = new Slick.EventHandler();
|
||||
private _grid!: Slick.Grid<T>;
|
||||
private _onClick = new Emitter<TableCellClickEventArgs<T>>();
|
||||
public onClick = this._onClick.event;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public init(grid: Slick.Grid<T>): void {
|
||||
this._grid = grid;
|
||||
this._handler.subscribe(grid.onClick, (e: DOMEvent, args: Slick.OnClickEventArgs<T>) => this.handleClick(args));
|
||||
this._handler.subscribe(grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs<T>) => this.handleKeyboardEvent(e as KeyboardEvent, args));
|
||||
this._handler.subscribe(grid.onActiveCellChanged, (e: DOMEvent, args: Slick.OnActiveCellChangedEventArgs<T>) => { this.handleActiveCellChanged(args); });
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this._handler.unsubscribeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column definition.
|
||||
* Note when implementing this abstract getter:
|
||||
* Make sure to set the tabindex to -1 for the element returned by the formatter. tabindex=-1 means it is only focusable programatically, when the cell becomes active, we will set to focus to the element inside it, the tab navigation experience is smooth.
|
||||
* Otherwise, if we set tabindex to 0, the focus will go to the element first and then the first cell of the table.
|
||||
*/
|
||||
public abstract get definition(): Slick.Column<T>;
|
||||
|
||||
private handleActiveCellChanged(args: Slick.OnActiveCellChangedEventArgs<T>): void {
|
||||
if (this.isCurrentColumn(args.cell)) {
|
||||
const cellElement = this._grid.getActiveCellNode();
|
||||
if (cellElement && cellElement.children) {
|
||||
const element = cellElement.children[0] as HTMLElement;
|
||||
element.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleClick(args: Slick.OnClickEventArgs<T>): void {
|
||||
if (this.isCurrentColumn(args.cell)) {
|
||||
// SlickGrid will automatically set active cell on mouse click event,
|
||||
// during the process of setting active cell, blur event will be triggered and handled in a setTimeout block,
|
||||
// on Windows platform, the context menu is html based which will respond the focus related events and hide the context menu.
|
||||
// If we call the fireClickEvent directly the menu will be set to hidden immediately, to workaround the issue we need to wrap it in a setTimeout block.
|
||||
setTimeout(() => {
|
||||
this.fireClickEvent();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private handleKeyboardEvent(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>): void {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) && this.isCurrentColumn(args.cell)) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.fireClickEvent();
|
||||
}
|
||||
}
|
||||
|
||||
private fireClickEvent(): void {
|
||||
const activeCell = this._grid.getActiveCell();
|
||||
const activeCellPosition = this._grid.getActiveCellPosition();
|
||||
if (activeCell && activeCellPosition) {
|
||||
this._onClick.fire({
|
||||
row: activeCell.row,
|
||||
column: activeCell.cell,
|
||||
item: this._grid.getDataItem(activeCell.row),
|
||||
position: {
|
||||
x: (activeCellPosition.left + activeCellPosition.right) / 2,
|
||||
y: (activeCellPosition.bottom + activeCellPosition.top) / 2
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private isCurrentColumn(columnIndex: number): boolean {
|
||||
return this._grid.getColumns()[columnIndex]?.id === this.definition.id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Definition for table column.
|
||||
*/
|
||||
export interface BaseTableColumnOptions {
|
||||
/**
|
||||
* Id of the column.
|
||||
*/
|
||||
id?: string,
|
||||
/**
|
||||
* Width of the column in px.
|
||||
*/
|
||||
width?: number,
|
||||
/**
|
||||
* Column header text.
|
||||
*/
|
||||
name?: string,
|
||||
/**
|
||||
* The property name in the data object to pull content from. (This is assumed to be on the root of the data object.)
|
||||
*/
|
||||
field?: string,
|
||||
/**
|
||||
* Whether the column is resizable. Default is true.
|
||||
*/
|
||||
resizable?: boolean,
|
||||
/**
|
||||
* The CSS class for the column header.
|
||||
*/
|
||||
headerCssClass?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition for table column with icon
|
||||
*/
|
||||
export interface IconColumnOptions extends BaseTableColumnOptions {
|
||||
/**
|
||||
* The icon class to use for all the cells in this column. If the 'field' is provided, the cell values will overwrite this value.
|
||||
*/
|
||||
iconCssClass?: string;
|
||||
/**
|
||||
* The title for all the cells. If the 'field' is provided, the cell values will overwrite this value.
|
||||
*/
|
||||
title?: string
|
||||
}
|
||||
|
||||
export interface IconCellValue {
|
||||
/**
|
||||
* The icon css class.
|
||||
*/
|
||||
iconCssClass: string;
|
||||
/**
|
||||
* The title of the cell.
|
||||
*/
|
||||
title: string
|
||||
}
|
||||
|
||||
export function getIconCellValue(options: IconColumnOptions, dataContext: Slick.SlickData): IconCellValue {
|
||||
if (options.field && dataContext[options.field]) {
|
||||
const cellValue = dataContext[options.field];
|
||||
if (typeof cellValue === 'string') {
|
||||
return {
|
||||
iconCssClass: '',
|
||||
title: cellValue
|
||||
};
|
||||
} else {
|
||||
return cellValue as IconCellValue;
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
iconCssClass: options.iconCssClass!,
|
||||
title: options.title!
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -4,48 +4,28 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
import { getIconCellValue, IconColumnOptions, TableColumn } from 'sql/base/browser/ui/table/plugins/tableColumn';
|
||||
|
||||
/**
|
||||
* Definition for column with icon on the left of text.
|
||||
*/
|
||||
export interface TextWithIconColumnDefinition<T extends Slick.SlickData> extends Slick.Column<T> {
|
||||
iconCssClassField?: string;
|
||||
export interface TextWithIconColumnOptions extends IconColumnOptions {
|
||||
}
|
||||
|
||||
export interface TextWithIconColumnOptions<T extends Slick.SlickData> {
|
||||
iconCssClassField?: string;
|
||||
field?: string;
|
||||
width?: number;
|
||||
id?: string;
|
||||
resizable?: boolean;
|
||||
name?: string;
|
||||
headerCssClass?: string;
|
||||
formatter?: Slick.Formatter<T>
|
||||
}
|
||||
export class TextWithIconColumn<T extends Slick.SlickData> implements TableColumn<T> {
|
||||
constructor(private options: TextWithIconColumnOptions) {
|
||||
}
|
||||
|
||||
export class TextWithIconColumn<T extends Slick.SlickData> {
|
||||
|
||||
private _definition: TextWithIconColumnDefinition<T>;
|
||||
|
||||
constructor(options: TextWithIconColumnOptions<T>) {
|
||||
this._definition = {
|
||||
id: options.id,
|
||||
field: options.field,
|
||||
resizable: options.resizable,
|
||||
formatter: options.formatter ?? this.formatter,
|
||||
width: options.width,
|
||||
name: options.name,
|
||||
iconCssClassField: options.iconCssClassField,
|
||||
public get definition(): Slick.Column<T> {
|
||||
return {
|
||||
id: this.options.id || this.options.field,
|
||||
field: this.options.field,
|
||||
resizable: this.options.resizable,
|
||||
formatter: (row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string => {
|
||||
const iconValue = getIconCellValue(this.options, dataContext);
|
||||
return `<div class="icon codicon slick-icon-cell-content ${iconValue.iconCssClass ?? ''}">${escape(iconValue.title ?? '')}</div>`;
|
||||
},
|
||||
width: this.options.width,
|
||||
name: this.options.name,
|
||||
cssClass: 'slick-icon-cell',
|
||||
headerCssClass: options.headerCssClass
|
||||
headerCssClass: this.options.headerCssClass
|
||||
};
|
||||
}
|
||||
private formatter(row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string {
|
||||
const iconColumn = columnDef as TextWithIconColumnDefinition<T>;
|
||||
return `<div class="icon codicon slick-icon-cell-content ${iconColumn.iconCssClassField ? dataContext[iconColumn.iconCssClassField] : ''}">${escape(value)}</div>`;
|
||||
}
|
||||
|
||||
public get definition(): TextWithIconColumnDefinition<T> {
|
||||
return this._definition;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,7 +813,8 @@ export enum ColumnType {
|
||||
text = 0,
|
||||
checkBox = 1,
|
||||
button = 2,
|
||||
icon = 3
|
||||
icon = 3,
|
||||
hyperlink = 4
|
||||
}
|
||||
|
||||
export enum ActionOnCellCheckboxCheck {
|
||||
|
||||
@@ -44,8 +44,7 @@ export class ResourceViewerInput extends EditorInput {
|
||||
this.actionsColumn = new ButtonColumn<azdata.DataGridItem>({
|
||||
id: 'actions',
|
||||
iconCssClass: 'toggle-more',
|
||||
title: nls.localize('resourceViewer.showActions', "Show Actions"),
|
||||
sortable: false
|
||||
title: nls.localize('resourceViewer.showActions', "Show Actions")
|
||||
});
|
||||
this.refresh().catch(err => onUnexpectedError(err));
|
||||
}
|
||||
|
||||
@@ -21,7 +21,3 @@
|
||||
.display-none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modelview-table-button-icon {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { attachTableStyler, attachButtonStyler } from 'sql/platform/theme/common
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { getContentHeight, getContentWidth, Dimension, isAncestor } from 'vs/base/browser/dom';
|
||||
import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin';
|
||||
import { CheckboxSelectColumn, ICheckboxCellActionEventArgs } from 'sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin';
|
||||
import { ActionOnCheck, CheckboxSelectColumn, ICheckboxCellActionEventArgs } from 'sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin';
|
||||
import { Emitter, Event as vsEvent } from 'vs/base/common/event';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
@@ -27,11 +27,13 @@ import { slickGridDataItemColumnValueWithNoData, textFormatter, iconCssFormatter
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType, ModelViewAction } from 'sql/platform/dashboard/browser/interfaces';
|
||||
import { convertSizeToNumber } from 'sql/base/browser/dom';
|
||||
import { ButtonColumn, ButtonClickEventArgs } from 'sql/base/browser/ui/table/plugins/buttonColumn.plugin';
|
||||
import { ButtonCellValue, ButtonColumn } from 'sql/base/browser/ui/table/plugins/buttonColumn.plugin';
|
||||
import { IUserFriendlyIcon, createIconCssClass, getIconKey } from 'sql/workbench/browser/modelComponents/iconUtils';
|
||||
import { HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { TableCellClickEventArgs } from 'sql/base/browser/ui/table/plugins/tableColumn';
|
||||
import { HyperlinkCellValue, HyperlinkColumn } from 'sql/base/browser/ui/table/plugins/hyperlinkColumn.plugin';
|
||||
|
||||
export enum ColumnSizingMode {
|
||||
ForceFit = 0, // all columns will be sized to fit in viewable space, no horiz scroll bar
|
||||
@@ -43,9 +45,13 @@ enum ColumnType {
|
||||
text = 0,
|
||||
checkBox = 1,
|
||||
button = 2,
|
||||
icon = 3
|
||||
icon = 3,
|
||||
hyperlink = 4
|
||||
}
|
||||
|
||||
type TableCellInputDataType = string | azdata.IconColumnCellValue | azdata.ButtonColumnCellValue | azdata.HyperlinkColumnCellValue | undefined;
|
||||
type TableCellDataType = string | CssIconCellValue | ButtonCellValue | HyperlinkCellValue | undefined;
|
||||
|
||||
@Component({
|
||||
selector: 'modelview-table',
|
||||
template: `
|
||||
@@ -59,13 +65,14 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
private _tableData: TableDataView<Slick.SlickData>;
|
||||
private _tableColumns;
|
||||
private _checkboxColumns: CheckboxSelectColumn<{}>[] = [];
|
||||
private _buttonsColumns: ButtonColumn<{}>[] = [];
|
||||
private _buttonColumns: ButtonColumn<{}>[] = [];
|
||||
private _hyperlinkColumns: HyperlinkColumn<{}>[] = [];
|
||||
private _pluginsRegisterStatus: boolean[] = [];
|
||||
private _filterPlugin: HeaderFilter<Slick.SlickData>;
|
||||
private _onCheckBoxChanged = new Emitter<ICheckboxCellActionEventArgs>();
|
||||
private _onButtonClicked = new Emitter<ButtonClickEventArgs<{}>>();
|
||||
private _onButtonClicked = new Emitter<TableCellClickEventArgs<{}>>();
|
||||
public readonly onCheckBoxChanged: vsEvent<ICheckboxCellActionEventArgs> = this._onCheckBoxChanged.event;
|
||||
public readonly onButtonClicked: vsEvent<ButtonClickEventArgs<{}>> = this._onButtonClicked.event;
|
||||
public readonly onButtonClicked: vsEvent<TableCellClickEventArgs<{}>> = this._onButtonClicked.event;
|
||||
private _iconCssMap: { [iconKey: string]: string } = {};
|
||||
|
||||
@ViewChild('table', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||
@@ -80,7 +87,7 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
transformColumns(columns: string[] | azdata.TableColumn[]): Slick.Column<any>[] {
|
||||
let tableColumns: any[] = <any[]>columns;
|
||||
if (tableColumns) {
|
||||
let mycolumns: Slick.Column<any>[] = [];
|
||||
const mycolumns: Slick.Column<any>[] = [];
|
||||
let index: number = 0;
|
||||
|
||||
(<any[]>columns).map(col => {
|
||||
@@ -90,6 +97,8 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
this.createButtonPlugin(col);
|
||||
} else if (col.type === ColumnType.icon) {
|
||||
mycolumns.push(TableComponent.createIconColumn(col));
|
||||
} else if (col.type === ColumnType.hyperlink) {
|
||||
this.createHyperlinkPlugin(col);
|
||||
}
|
||||
else if (col.value) {
|
||||
mycolumns.push(TableComponent.createTextColumn(col as azdata.TableColumn));
|
||||
@@ -142,28 +151,52 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
};
|
||||
}
|
||||
|
||||
public transformData(rows: (string | azdata.IconColumnCellValue)[][], columns: any[]): { [key: string]: string | CssIconCellValue }[] {
|
||||
public transformData(rows: (TableCellInputDataType)[][], columns: string[] | azdata.TableColumn[]): { [key: string]: TableCellDataType }[] {
|
||||
if (rows && columns) {
|
||||
|
||||
return rows.map(row => {
|
||||
let object: { [key: string]: string | CssIconCellValue } = {};
|
||||
const object: { [key: string]: TableCellDataType } = {};
|
||||
if (!Array.isArray(row)) {
|
||||
return object;
|
||||
}
|
||||
|
||||
row.forEach((val, index) => {
|
||||
let columnName: string = (columns[index].value) ? columns[index].value : <string>columns[index];
|
||||
if (isIconColumnCellValue(val)) {
|
||||
const icon: IUserFriendlyIcon = val.icon;
|
||||
const iconKey: string = getIconKey(icon);
|
||||
const iconCssClass = this._iconCssMap[iconKey] ?? createIconCssClass(icon);
|
||||
if (!this._iconCssMap[iconKey]) {
|
||||
this._iconCssMap[iconKey] = iconCssClass;
|
||||
}
|
||||
|
||||
object[columnName] = { iconCssClass: iconCssClass, ariaLabel: val.ariaLabel };
|
||||
const column = columns[index];
|
||||
if (typeof column === 'string') {
|
||||
object[column] = <string>val;
|
||||
} else {
|
||||
object[columnName] = <string>val;
|
||||
const columnType = <ColumnType>column.type;
|
||||
let cellValue = undefined;
|
||||
switch (columnType) {
|
||||
case ColumnType.icon:
|
||||
const iconValue = <azdata.IconColumnCellValue>val;
|
||||
cellValue = <CssIconCellValue>{
|
||||
iconCssClass: this.createIconCssClassInternal(iconValue.icon),
|
||||
title: iconValue.title
|
||||
};
|
||||
break;
|
||||
case ColumnType.button:
|
||||
if (val) {
|
||||
const buttonValue = <azdata.ButtonColumnCellValue>val;
|
||||
cellValue = <ButtonCellValue>{
|
||||
iconCssClass: buttonValue.icon ? this.createIconCssClassInternal(buttonValue.icon) : undefined,
|
||||
title: buttonValue.title
|
||||
};
|
||||
}
|
||||
break;
|
||||
case ColumnType.hyperlink:
|
||||
if (val) {
|
||||
const hyperlinkValue = <azdata.HyperlinkColumnCellValue>val;
|
||||
cellValue = <HyperlinkCellValue>{
|
||||
iconCssClass: hyperlinkValue.icon ? this.createIconCssClassInternal(hyperlinkValue.icon) : undefined,
|
||||
title: hyperlinkValue.title,
|
||||
url: hyperlinkValue.url
|
||||
};
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cellValue = val;
|
||||
}
|
||||
object[column.value] = cellValue;
|
||||
}
|
||||
});
|
||||
return object;
|
||||
@@ -173,6 +206,15 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
}
|
||||
}
|
||||
|
||||
private createIconCssClassInternal(icon: IUserFriendlyIcon): string {
|
||||
const iconKey: string = getIconKey(icon);
|
||||
const iconCssClass = this._iconCssMap[iconKey] ?? createIconCssClass(icon);
|
||||
if (!this._iconCssMap[iconKey]) {
|
||||
this._iconCssMap[iconKey] = iconCssClass;
|
||||
}
|
||||
return iconCssClass;
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
if (this._inputContainer) {
|
||||
this._tableData = new TableDataView<Slick.SlickData>(
|
||||
@@ -307,7 +349,8 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
}
|
||||
|
||||
Object.keys(this._checkboxColumns).forEach(col => this.registerPlugins(col, this._checkboxColumns[col]));
|
||||
Object.keys(this._buttonsColumns).forEach(col => this.registerPlugins(col, this._buttonsColumns[col]));
|
||||
Object.keys(this._buttonColumns).forEach(col => this.registerPlugins(col, this._buttonColumns[col]));
|
||||
Object.keys(this._hyperlinkColumns).forEach(col => this.registerPlugins(col, this._hyperlinkColumns[col]));
|
||||
|
||||
if (this.headerFilter === true) {
|
||||
this.registerFilterPlugin();
|
||||
@@ -356,16 +399,17 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
});
|
||||
}
|
||||
|
||||
private createCheckBoxPlugin(col: any, index: number) {
|
||||
private createCheckBoxPlugin(col: azdata.CheckboxColumn, index: number) {
|
||||
let name = col.value;
|
||||
if (!this._checkboxColumns[col.value]) {
|
||||
const checkboxAction = <ActionOnCheck>(col.options ? (<any>col.options).actionOnCheckbox : col.action);
|
||||
this._checkboxColumns[col.value] = new CheckboxSelectColumn({
|
||||
title: col.value,
|
||||
toolTip: col.toolTip,
|
||||
width: col.width,
|
||||
cssClass: col.cssClass,
|
||||
headerCssClass: col.headerCssClass,
|
||||
actionOnCheck: col.options ? col.options.actionOnCheckbox : null
|
||||
actionOnCheck: checkboxAction,
|
||||
}, index);
|
||||
|
||||
this._register(this._checkboxColumns[col.value].onChange((state) => {
|
||||
@@ -382,15 +426,19 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
}
|
||||
}
|
||||
|
||||
private createButtonPlugin(col: any) {
|
||||
private createButtonPlugin(col: azdata.ButtonColumn) {
|
||||
let name = col.value;
|
||||
if (!this._buttonsColumns[col.value]) {
|
||||
this._buttonsColumns[col.value] = new ButtonColumn({
|
||||
title: col.title,
|
||||
iconCssClass: 'modelview-table-button-icon ' + (col.options ? createIconCssClass(col.options.icon) : '')
|
||||
if (!this._buttonColumns[col.value]) {
|
||||
const icon = <IUserFriendlyIcon>(col.options ? (<any>col.options).icon : col.icon);
|
||||
this._buttonColumns[col.value] = new ButtonColumn({
|
||||
title: col.value,
|
||||
iconCssClass: icon ? this.createIconCssClassInternal(icon) : undefined,
|
||||
field: col.value,
|
||||
showText: col.showText,
|
||||
name: col.name
|
||||
});
|
||||
|
||||
this._register(this._buttonsColumns[col.value].onClick((state) => {
|
||||
this._register(this._buttonColumns[col.value].onClick((state) => {
|
||||
this.fireEvent({
|
||||
eventType: ComponentEventType.onCellAction,
|
||||
args: {
|
||||
@@ -403,7 +451,33 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
}
|
||||
}
|
||||
|
||||
private registerPlugins(col: string, plugin: CheckboxSelectColumn<{}> | ButtonColumn<{}>): void {
|
||||
private createHyperlinkPlugin(col: azdata.HyperlinkColumn) {
|
||||
const name = col.value;
|
||||
if (!this._hyperlinkColumns[col.value]) {
|
||||
const hyperlinkColumn = new HyperlinkColumn({
|
||||
title: col.value,
|
||||
width: col.width,
|
||||
iconCssClass: col.icon ? this.createIconCssClassInternal(col.icon) : undefined,
|
||||
field: col.value,
|
||||
name: col.name
|
||||
});
|
||||
|
||||
this._hyperlinkColumns[col.value] = hyperlinkColumn;
|
||||
|
||||
this._register(hyperlinkColumn.onClick((state) => {
|
||||
this.fireEvent({
|
||||
eventType: ComponentEventType.onCellAction,
|
||||
args: {
|
||||
row: state.row,
|
||||
column: state.column,
|
||||
name: name
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private registerPlugins(col: string, plugin: CheckboxSelectColumn<{}> | ButtonColumn<{}> | HyperlinkColumn<{}>): void {
|
||||
|
||||
const index = 'index' in plugin ? plugin.index : this.columns?.findIndex(x => x === col || ('value' in x && x['value'] === col));
|
||||
if (index >= 0) {
|
||||
@@ -548,7 +622,3 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isIconColumnCellValue(obj: any | undefined): obj is azdata.IconColumnCellValue {
|
||||
return !!(<azdata.IconColumnCellValue>obj)?.icon;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { Router } from '@angular/router';
|
||||
import { ButtonColumn } from 'sql/base/browser/ui/table/plugins/buttonColumn.plugin';
|
||||
import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin';
|
||||
import { IconCellValue } from 'sql/base/browser/ui/table/plugins/tableColumn';
|
||||
import { TextWithIconColumn } from 'sql/base/browser/ui/table/plugins/textWithIconColumn';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
|
||||
@@ -33,7 +34,7 @@ import { IEditorProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
const ShowActionsText: string = nls.localize('dashboard.explorer.actions', "Show Actions");
|
||||
const IconClassProperty: string = 'iconClass';
|
||||
const NameWithIconProperty: string = 'NameWithIcon';
|
||||
export const ConnectionProfilePropertyName: string = 'connection_profile';
|
||||
|
||||
/**
|
||||
@@ -171,7 +172,10 @@ export class ExplorerTable extends Disposable {
|
||||
this._view.clear();
|
||||
this._view.clearFilter();
|
||||
items.forEach(item => {
|
||||
item[IconClassProperty] = this._explorerView.getIconClass(item);
|
||||
item[NameWithIconProperty] = <IconCellValue>{
|
||||
iconCssClass: this._explorerView.getIconClass(item),
|
||||
title: item[NameProperty]
|
||||
};
|
||||
});
|
||||
this._view.push(items);
|
||||
}
|
||||
@@ -190,9 +194,8 @@ export class ExplorerTable extends Disposable {
|
||||
if (property.value === NameProperty) {
|
||||
const nameColumn = new TextWithIconColumn({
|
||||
id: property.value,
|
||||
iconCssClassField: IconClassProperty,
|
||||
width: columnWidth,
|
||||
field: property.value,
|
||||
field: NameWithIconProperty,
|
||||
name: property.displayName
|
||||
});
|
||||
return nameColumn.definition;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import TableComponent from 'sql/workbench/browser/modelComponents/table.component';
|
||||
import { CssIconCellValue } from 'sql/base/browser/ui/table/formatters';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
suite('TableComponent Tests', () => {
|
||||
@@ -22,7 +21,7 @@ suite('TableComponent Tests', () => {
|
||||
let columns = ['c1', 'c2', 'c3'];
|
||||
const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService());
|
||||
|
||||
let actual: { [key: string]: string | CssIconCellValue }[] = tableComponent.transformData(data, columns);
|
||||
let actual = tableComponent.transformData(data, columns);
|
||||
let expected: { [key: string]: string }[] = [
|
||||
{
|
||||
'c1': '1',
|
||||
@@ -42,7 +41,7 @@ suite('TableComponent Tests', () => {
|
||||
let data = undefined;
|
||||
const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService());
|
||||
let columns = ['c1', 'c2', 'c3'];
|
||||
let actual: { [key: string]: string | CssIconCellValue }[] = tableComponent.transformData(data, columns);
|
||||
let actual = tableComponent.transformData(data, columns);
|
||||
let expected: { [key: string]: string }[] = [];
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
@@ -54,7 +53,7 @@ suite('TableComponent Tests', () => {
|
||||
];
|
||||
let columns;
|
||||
const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService());
|
||||
let actual: { [key: string]: string | CssIconCellValue }[] = tableComponent.transformData(data, columns);
|
||||
let actual = tableComponent.transformData(data, columns);
|
||||
let expected: { [key: string]: string }[] = [];
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
@@ -66,7 +65,7 @@ suite('TableComponent Tests', () => {
|
||||
];
|
||||
const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService());
|
||||
let columns = ['c1', 'c2', 'c3'];
|
||||
let actual: { [key: string]: string | CssIconCellValue }[] = tableComponent.transformData(data, columns);
|
||||
let actual = tableComponent.transformData(data, columns);
|
||||
let expected: { [key: string]: string }[] = [
|
||||
{
|
||||
'c1': '1',
|
||||
|
||||
Reference in New Issue
Block a user