diff --git a/src/sql/media/icons/common-icons.css b/src/sql/media/icons/common-icons.css index c2c2e40b6a..8651f3092d 100644 --- a/src/sql/media/icons/common-icons.css +++ b/src/sql/media/icons/common-icons.css @@ -3,6 +3,15 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.vs .codicon.settings { + background-image: url('settings.svg'); +} + +.vs-dark .codicon.settings, +.hc-black .codicon.settings { + background-image: url('settings_inverse.svg'); +} + .vs .codicon.backup { background: url("backup.svg") center center no-repeat; } diff --git a/src/sql/media/icons/settings.svg b/src/sql/media/icons/settings.svg new file mode 100644 index 0000000000..cdaf3ceffb --- /dev/null +++ b/src/sql/media/icons/settings.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/media/icons/settings_inverse.svg b/src/sql/media/icons/settings_inverse.svg new file mode 100644 index 0000000000..cea3f45e79 --- /dev/null +++ b/src/sql/media/icons/settings_inverse.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/workbench/browser/modal/modal.ts b/src/sql/workbench/browser/modal/modal.ts index 02640eabea..5eba61c79a 100644 --- a/src/sql/workbench/browser/modal/modal.ts +++ b/src/sql/workbench/browser/modal/modal.ts @@ -339,7 +339,7 @@ export abstract class Modal extends Disposable implements IThemable { // Try to find focusable element in dialog pane rather than overall container. _modalBodySection contains items in the pane for a wizard. // This ensures that we are setting the focus on a useful element in the form when possible. const focusableElements = this._modalBodySection ? - this._modalBodySection.querySelectorAll('input') : + this._modalBodySection.querySelectorAll(tabbableElementsQuerySelector) : this._bodyContainer.querySelectorAll(tabbableElementsQuerySelector); this._focusedElementBeforeOpen = document.activeElement; diff --git a/src/sql/workbench/contrib/charts/browser/actions.ts b/src/sql/workbench/contrib/charts/browser/actions.ts index 6066477338..67a51d5a66 100644 --- a/src/sql/workbench/contrib/charts/browser/actions.ts +++ b/src/sql/workbench/contrib/charts/browser/actions.ts @@ -23,6 +23,8 @@ import { assign } from 'vs/base/common/objects'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; +import { ChartView } from 'sql/workbench/contrib/charts/browser/chartView'; +import { ConfigureChartDialog } from 'sql/workbench/contrib/charts/browser/configureChartDialog'; export interface IChartActionContext { options: IInsightOptions; @@ -106,6 +108,28 @@ export class CreateInsightAction extends Action { } } +export class ConfigureChartAction extends Action { + public static ID = 'chartview.configureChart'; + public static LABEL = localize('configureChartLabel', "Configure Chart"); + public static ICON = 'settings'; + + private dialog: ConfigureChartDialog; + + constructor(private _chart: ChartView, + @IInstantiationService private readonly instantiationService: IInstantiationService) { + super(ConfigureChartAction.ID, ConfigureChartAction.LABEL, ConfigureChartAction.ICON); + } + + public run(context: IChartActionContext): Promise { + if (!this.dialog) { + this.dialog = this.instantiationService.createInstance(ConfigureChartDialog, ConfigureChartAction.LABEL, ConfigureChartAction.ID, this._chart); + this.dialog.render(); + } + this.dialog.open(); + return Promise.resolve(true); + } +} + export class CopyAction extends Action { public static ID = 'chartview.copy'; public static LABEL = localize('copyChartLabel', "Copy as image"); diff --git a/src/sql/workbench/contrib/charts/browser/chartTab.ts b/src/sql/workbench/contrib/charts/browser/chartTab.ts index aa1a2aca87..c4b7562f19 100644 --- a/src/sql/workbench/contrib/charts/browser/chartTab.ts +++ b/src/sql/workbench/contrib/charts/browser/chartTab.ts @@ -16,7 +16,7 @@ export class ChartTab implements IPanelTab { public readonly view: ChartView; constructor(@IInstantiationService instantiationService: IInstantiationService) { - this.view = instantiationService.createInstance(ChartView); + this.view = instantiationService.createInstance(ChartView, true); } public set queryRunner(runner: QueryRunner) { diff --git a/src/sql/workbench/contrib/charts/browser/chartView.ts b/src/sql/workbench/contrib/charts/browser/chartView.ts index a67365e4ba..44d08986bf 100644 --- a/src/sql/workbench/contrib/charts/browser/chartView.ts +++ b/src/sql/workbench/contrib/charts/browser/chartView.ts @@ -20,8 +20,8 @@ import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/c import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { isUndefinedOrNull } from 'vs/base/common/types'; -import { CreateInsightAction, CopyAction, SaveImageAction, IChartActionContext } from 'sql/workbench/contrib/charts/browser/actions'; -import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; +import { CreateInsightAction, CopyAction, SaveImageAction, IChartActionContext, ConfigureChartAction } from 'sql/workbench/contrib/charts/browser/actions'; +import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar'; import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox'; import { IInsightOptions, ChartType } from 'sql/workbench/contrib/charts/common/interfaces'; import { ChartState } from 'sql/workbench/common/editor/query/chartState'; @@ -57,6 +57,7 @@ export class ChartView extends Disposable implements IPanelView { private _createInsightAction: CreateInsightAction; private _copyAction: CopyAction; private _saveAction: SaveImageAction; + private _configureChartAction: ConfigureChartAction; private _state: ChartState; @@ -68,7 +69,7 @@ export class ChartView extends Disposable implements IPanelView { /** parent container */ private container: HTMLElement; /** container for the options controls */ - private optionsControl: HTMLElement; + public readonly optionsControl: HTMLElement; /** container for type specific controls */ private typeControls: HTMLElement; /** container for the insight */ @@ -82,6 +83,7 @@ export class ChartView extends Disposable implements IPanelView { private optionMap: { [x: string]: { element: HTMLElement; set: (val) => void } } = {}; constructor( + private readonly _renderOptionsInline: boolean, @IContextViewService private _contextViewService: IContextViewService, @IThemeService private _themeService: IThemeService, @IInstantiationService private _instantiationService: IInstantiationService, @@ -90,6 +92,7 @@ export class ChartView extends Disposable implements IPanelView { super(); this.taskbarContainer = DOM.$('div.taskbar-container'); this.taskbar = new Taskbar(this.taskbarContainer); + this.optionsControl = DOM.$('div.options-container'); const generalControls = DOM.$('div.general-controls'); this.optionsControl.appendChild(generalControls); @@ -100,7 +103,12 @@ export class ChartView extends Disposable implements IPanelView { this._copyAction = this._instantiationService.createInstance(CopyAction); this._saveAction = this._instantiationService.createInstance(SaveImageAction); - this.taskbar.setContent([{ action: this._createInsightAction }]); + if (this._renderOptionsInline) { + this.taskbar.setContent([{ action: this._createInsightAction }]); + } else { + this._configureChartAction = this._instantiationService.createInstance(ConfigureChartAction, this); + this.taskbar.setContent([{ action: this._createInsightAction }, { action: this._configureChartAction }]); + } const self = this; this.options = new Proxy(this.options, { @@ -165,7 +173,9 @@ export class ChartView extends Disposable implements IPanelView { this.container.appendChild(this.taskbarContainer); this.container.appendChild(this.chartingContainer); this.chartingContainer.appendChild(this.insightContainer); - this.chartingContainer.appendChild(this.optionsControl); + if (this._renderOptionsInline) { + this.chartingContainer.appendChild(this.optionsControl); + } this.insight = new Insight(this.insightContainer, this.options, this._instantiationService); } @@ -275,16 +285,21 @@ export class ChartView extends Disposable implements IPanelView { } private updateActionbar() { + let actions: ITaskbarContent[]; if (this.insight && this.insight.isCopyable) { this.taskbar.context = { insight: this.insight.insight, options: this.options }; - this.taskbar.setContent([ + actions = [ { action: this._createInsightAction }, { action: this._copyAction }, { action: this._saveAction } - ]); + ]; } else { - this.taskbar.setContent([{ action: this._createInsightAction }]); + actions = [{ action: this._createInsightAction }]; } + if (!this._renderOptionsInline) { + actions.push({ action: this._configureChartAction }); + } + this.taskbar.setContent(actions); } private createOption(option: IChartOption, container: HTMLElement) { @@ -318,6 +333,7 @@ export class ChartView extends Disposable implements IPanelView { case ControlType.combo: //pass options into changeAltNames in order for SelectBox to show user-friendly names. let dropdown = new SelectBox(option.displayableOptions || this.changeToAltNames(option.options), undefined, this._contextViewService); + dropdown.setAriaLabel(option.label); dropdown.select(option.options.indexOf(value)); dropdown.render(optionInput); dropdown.onDidSelect(e => { @@ -337,6 +353,7 @@ export class ChartView extends Disposable implements IPanelView { break; case ControlType.input: let input = new InputBox(optionInput, this._contextViewService); + input.setAriaLabel(option.label); input.value = value || ''; input.onDidChange(e => { if (this.options[option.configEntry] !== e) { @@ -355,6 +372,7 @@ export class ChartView extends Disposable implements IPanelView { break; case ControlType.numberInput: let numberInput = new InputBox(optionInput, this._contextViewService, { type: 'number' }); + numberInput.setAriaLabel(option.label); numberInput.value = value || ''; numberInput.onDidChange(e => { if (this.options[option.configEntry] !== Number(e)) { @@ -373,6 +391,7 @@ export class ChartView extends Disposable implements IPanelView { break; case ControlType.dateInput: let dateInput = new InputBox(optionInput, this._contextViewService, { type: 'datetime-local' }); + dateInput.setAriaLabel(option.label); dateInput.value = value || ''; dateInput.onDidChange(e => { if (this.options[option.configEntry] !== e) { diff --git a/src/sql/workbench/contrib/charts/browser/configureChartDialog.ts b/src/sql/workbench/contrib/charts/browser/configureChartDialog.ts new file mode 100644 index 0000000000..85145aeecf --- /dev/null +++ b/src/sql/workbench/contrib/charts/browser/configureChartDialog.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { localize } from 'vs/nls'; +import { ChartView } from 'sql/workbench/contrib/charts/browser/chartView'; +import { Modal } from 'sql/workbench/browser/modal/modal'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService'; +import { attachModalDialogStyler } from 'sql/workbench/common/styler'; +import { attachButtonStyler } from 'vs/platform/theme/common/styler'; + +export class ConfigureChartDialog extends Modal { + constructor( + title: string, + name: string, + private _chart: ChartView, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + @IThemeService themeService: IThemeService, + @IAdsTelemetryService telemetryService: IAdsTelemetryService, + @IContextKeyService contextKeyService: IContextKeyService, + @IClipboardService clipboardService: IClipboardService, + @ILogService logService: ILogService, + @ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService + ) { + super(title, name, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, undefined); + } + + public open() { + this.show(); + } + + public render() { + super.render(); + attachModalDialogStyler(this, this._themeService); + + let closeButton = this.addFooterButton(localize('configureChartDialog.close', "Close"), () => this.close()); + attachButtonStyler(closeButton, this._themeService); + } + + protected renderBody(container: HTMLElement) { + container.appendChild(this._chart.optionsControl); + } + + protected layout(height?: number): void { + } + + public close() { + this.hide(); + } +} diff --git a/src/sql/workbench/contrib/charts/browser/media/chartView.css b/src/sql/workbench/contrib/charts/browser/media/chartView.css index 26f21b895d..3d82fa99c4 100644 --- a/src/sql/workbench/contrib/charts/browser/media/chartView.css +++ b/src/sql/workbench/contrib/charts/browser/media/chartView.css @@ -9,6 +9,7 @@ display: flex; flex-direction: column; overflow: scroll; + min-height: 400px; } .actionbar-container { @@ -26,6 +27,7 @@ } .options-container { + padding: 20px; min-width: 250px; padding-right: 10px; } diff --git a/src/sql/workbench/contrib/charts/test/browser/chartView.test.ts b/src/sql/workbench/contrib/charts/test/browser/chartView.test.ts index c839a3fc34..ca6974c359 100644 --- a/src/sql/workbench/contrib/charts/test/browser/chartView.test.ts +++ b/src/sql/workbench/contrib/charts/test/browser/chartView.test.ts @@ -14,22 +14,32 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te suite('Chart View', () => { test('initializes without error', () => { - const chartview = createChartView(); + const chartview = createChartView(true); assert(chartview); }); test('renders without error', () => { - const chartview = createChartView(); + const chartview = createChartView(true); + chartview.render(document.createElement('div')); + }); + + test('initializes without error - without options', () => { + const chartview = createChartView(false); + assert(chartview); + }); + + test('renders without error - without options', () => { + const chartview = createChartView(false); chartview.render(document.createElement('div')); }); }); -function createChartView(): ChartView { +function createChartView(renderOptions: boolean): ChartView { const layoutService = new TestLayoutService(); const contextViewService = new ContextViewService(layoutService); const themeService = new TestThemeService(); const instantiationService = new TestInstantiationService(); const notificationService = new TestNotificationService(); instantiationService.stub(IThemeService, themeService); - return new ChartView(contextViewService, themeService, instantiationService, notificationService); + return new ChartView(renderOptions, contextViewService, themeService, instantiationService, notificationService); } diff --git a/src/sql/workbench/contrib/notebook/browser/outputs/gridOutput.component.ts b/src/sql/workbench/contrib/notebook/browser/outputs/gridOutput.component.ts index b6e501bbf7..735399b797 100644 --- a/src/sql/workbench/contrib/notebook/browser/outputs/gridOutput.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/outputs/gridOutput.component.ts @@ -126,7 +126,7 @@ class DataResourceTable extends GridTableBase { ) { super(state, createResultSet(source), contextMenuService, instantiationService, editorService, untitledEditorService, configurationService); this._gridDataProvider = this.instantiationService.createInstance(DataResourceDataProvider, source, this.resultSet, this.documentUri); - this._chart = this.instantiationService.createInstance(ChartView); + this._chart = this.instantiationService.createInstance(ChartView, false); } get gridDataProvider(): IGridDataProvider { @@ -389,13 +389,13 @@ export class NotebookChartAction extends ToggleableAction { public static SHOWCHART_LABEL = localize('notebook.showChart', "Show chart"); public static SHOWCHART_ICON = 'viewChart'; - public static HIDECHART_LABEL = localize('notebook.hideChart', "Hide chart"); - public static HIDECHART_ICON = 'close'; + public static SHOWTABLE_LABEL = localize('notebook.showTable', "Show table"); + public static SHOWTABLE_ICON = 'table'; constructor(private resourceTable: DataResourceTable) { super(NotebookChartAction.ID, { - toggleOnLabel: NotebookChartAction.HIDECHART_LABEL, - toggleOnClass: NotebookChartAction.HIDECHART_ICON, + toggleOnLabel: NotebookChartAction.SHOWTABLE_LABEL, + toggleOnClass: NotebookChartAction.SHOWTABLE_ICON, toggleOffLabel: NotebookChartAction.SHOWCHART_LABEL, toggleOffClass: NotebookChartAction.SHOWCHART_ICON, isOn: false diff --git a/src/sql/workbench/services/objectExplorer/browser/media/objectTypes/objecttypes.css b/src/sql/workbench/services/objectExplorer/browser/media/objectTypes/objecttypes.css index 90b362beab..047b85635a 100644 --- a/src/sql/workbench/services/objectExplorer/browser/media/objectTypes/objecttypes.css +++ b/src/sql/workbench/services/objectExplorer/browser/media/objectTypes/objecttypes.css @@ -5,7 +5,10 @@ .vs .icon.table, .vs-dark .icon.table, -.hc-black .icon.table { +.hc-black .icon.table, +.vs .codicon.table, +.vs-dark .codicon.table, +.hc-black .codicon.table { background: url("Table.svg") center center no-repeat; }