Skip to content

Commit

Permalink
fix accessibility issues with query grid (#17479)
Browse files Browse the repository at this point in the history
* fix accessibility issues

* fix selection and set active grid

* fix linting error
  • Loading branch information
alanrenmsft committed Nov 18, 2022
1 parent ff4fbdf commit f0a77e1
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 102 deletions.
5 changes: 2 additions & 3 deletions gulpfile.js
Expand Up @@ -179,13 +179,12 @@ gulp.task('ext:copy-dependencies', (done) => {
]).pipe(gulp.dest('out/src/views/htmlcontent/src/js/lib'));

gulp.src([
config.paths.project.root + '/node_modules/angular2-slickgrid/components/css/SlickGrid.css',
config.paths.project.root + '/node_modules/angular2-slickgrid/out/css/SlickGrid.css',
config.paths.project.root + '/node_modules/slickgrid/slick.grid.css'
]).pipe(gulp.dest('out/src/views/htmlcontent/src/css'));

gulp.src([
config.paths.project.root + '/node_modules/angular2-slickgrid/index.js',
config.paths.project.root + '/node_modules/angular2-slickgrid/components/**/*.js'
config.paths.project.root + '/node_modules/angular2-slickgrid/out/**/*.js'
], { base: config.paths.project.root + '/node_modules/angular2-slickgrid' }).pipe(gulp.dest('out/src/views/htmlcontent/src/js/lib/angular2-slickgrid'));

return gulp.src([config.paths.project.root + '/node_modules/@angular/**/*'])
Expand Down
9 changes: 9 additions & 0 deletions localization/xliff/enu/constants/localizedConstants.enu.xlf
Expand Up @@ -536,6 +536,15 @@
<trans-unit id="msgMultipleSelectionModeNotSupported">
<source xml:lang="en">Running query is not supported when the editor is in multiple selection mode.</source>
</trans-unit>
<trans-unit id="newColumnWidthPrompt">
<source xml:lang="en">Enter new column width</source>
</trans-unit>
<trans-unit id="columnWidthInvalidNumberError">
<source xml:lang="en">Invalid column width</source>
</trans-unit>
<trans-unit id="columnWidthMustBePositiveError">
<source xml:lang="en">Width cannot be 0 or negative</source>
</trans-unit>
</body>
</file>
</xliff>
7 changes: 4 additions & 3 deletions package.json
Expand Up @@ -90,7 +90,7 @@
"@vscode/test-electron": "^2.1.5",
"@xmldom/xmldom": "0.8.4",
"angular-in-memory-web-api": "0.1.13",
"angular2-slickgrid": "github:microsoft/angular2-slickgrid#1.2.2-patch1",
"angular2-slickgrid": "github:microsoft/angular2-slickgrid#1.4.6",
"assert": "^1.4.1",
"chai": "^3.5.0",
"coveralls": "^3.0.2",
Expand All @@ -111,7 +111,7 @@
"remap-istanbul": "0.9.6",
"rxjs": "5.0.0-beta.12",
"sinon": "^14.0.0",
"slickgrid": "github:kburtram/SlickGrid#2.3.23-2",
"slickgrid": "github:Microsoft/SlickGrid.ADS#2.3.39",
"systemjs": "0.19.40",
"systemjs-builder": "^0.15.32",
"systemjs-plugin-json": "^0.2.0",
Expand Down Expand Up @@ -783,7 +783,8 @@
"event.selectAll": "ctrl+A",
"event.saveAsJSON": "",
"event.saveAsCSV": "",
"event.saveAsExcel": ""
"event.saveAsExcel": "",
"event.changeColumnWidth": "ctrl+alt+S"
},
"scope": "resource"
},
Expand Down
5 changes: 5 additions & 0 deletions src/models/interfaces.ts
Expand Up @@ -301,8 +301,13 @@ export enum FieldType {

export interface IColumnDefinition {
id?: string;
field?: string;
name: string;
type: FieldType;
width?: number;
cssClass?: string;
focusable?: boolean;
selectable?: boolean;
asyncPostRender?: (cellRef: string, row: number, dataContext: JSON, colDef: any) => void;
formatter?: (row: number, cell: any, value: any, columnDef: any, dataContext: any) => string;
}
Expand Down
20 changes: 17 additions & 3 deletions src/models/sqlOutputContentProvider.ts
Expand Up @@ -157,15 +157,29 @@ export class SqlOutputContentProvider {
this.copyRequestHandler(uri, batchId, resultsId, selection, includeHeaders),
getConfig: () => this.configRequestHandler(uri),
getLocalizedTexts: () => Promise.resolve(LocalizedConstants),
openLink: (content: string, columnName: string, linkType: string) =>
this.openLinkRequestHandler(content, columnName, linkType),
openLink: (content: string, columnName: string, linkType: string) => this.openLinkRequestHandler(content, columnName, linkType),
saveResults: (batchId: number, resultId: number, format: string, selection: ISlickRange[]) =>
this.saveResultsRequestHandler(uri, batchId, resultId, format, selection),
setEditorSelection: (selection: ISelectionData) => this.editorSelectionRequestHandler(uri, selection),
showError: (message: string) => this.showErrorRequestHandler(message),
showWarning: (message: string) => this.showWarningRequestHandler(message),
sendReadyEvent: async () => await this.sendReadyEvent(uri),
dispose: () => this._panels.delete(uri)
dispose: () => this._panels.delete(uri),
getNewColumnWidth: async (current: number): Promise<number | undefined> => {
const val = await vscode.window.showInputBox({
prompt: LocalizedConstants.newColumnWidthPrompt,
value: current.toString(),
validateInput: async (value: string) => {
if (!Number(value)) {
return LocalizedConstants.columnWidthInvalidNumberError;
} else if (parseInt(value, 10) <= 0) {
return LocalizedConstants.columnWidthMustBePositiveError;
}
return undefined;
}
});
return val === undefined ? undefined : parseInt(val, 10);
}
};
const controller = new WebviewPanelController(this._vscodeWrapper, uri, title, proxy, this.context.extensionPath, this._statusView);
this._panels.set(uri, controller);
Expand Down
1 change: 1 addition & 0 deletions src/protocol.ts
Expand Up @@ -21,6 +21,7 @@ export interface IServerProxy extends Disposable {
showError(message: string): void;
getLocalizedTexts(): Promise<{ [key: string]: any }>;
sendReadyEvent(uri: string): Promise<boolean>;
getNewColumnWidth(current: number): Promise<number | undefined>;
}

export interface IMessageProtocol {
Expand Down
114 changes: 79 additions & 35 deletions src/views/htmlcontent/src/js/components/app.component.ts
Expand Up @@ -61,12 +61,13 @@ const template = `
<slick-grid #slickgrid id="slickgrid_{{i}}" [columnDefinitions]="dataSet.columnDefinitions"
[ngClass]="i === activeGrid ? 'active' : ''"
[dataRows]="dataSet.dataRows"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
(onContextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
enableAsyncPostRender="true"
showDataTypeIcon="false"
showHeader="true"
[resized]="dataSet.resized"
(mousedown)="navigateToGrid(i)"
(focusin)="setActiveGrid(i)"
[selectionModel]="selectionModel"
[plugins]="slickgridPlugins"
class="boxCol content vertBox slickgrid">
Expand Down Expand Up @@ -195,14 +196,14 @@ export class AppComponent implements OnInit, AfterViewChecked {
} else {
let activeGrid = this.activeGrid;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges();
selection = this.tryCombineSelections(selection);
selection = this.tryCombineSelectionsForResults(selection);
this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId, this.renderedDataSets[activeGrid].resultId);
}
},
'event.copyWithHeaders': () => {
let activeGrid = this.activeGrid;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges();
selection = this.tryCombineSelections(selection);
selection = this.tryCombineSelectionsForResults(selection);
this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId,
this.renderedDataSets[activeGrid].resultId, true);
},
Expand All @@ -220,6 +221,22 @@ export class AppComponent implements OnInit, AfterViewChecked {
},
'event.saveAsExcel': () => {
this.sendSaveRequest('excel');
},
'event.changeColumnWidth': async () => {
const activeGrid = this.slickgrids.toArray()[this.activeGrid]['_grid'];
if (activeGrid) {
const activeCell = activeGrid.getActiveCell();
const columns = activeGrid.getColumns();
if (!columns[activeCell.cell]?.resizable) {
return;
}
const newWidth = await this.dataService.getNewColumnWidth(columns[activeCell.cell].width);
if (newWidth) {
columns[activeCell.cell].width = newWidth;
activeGrid.setColumns(columns);
activeGrid.setActiveCell(activeCell.row, activeCell.cell);
}
}
}
};

Expand All @@ -246,7 +263,7 @@ export class AppComponent implements OnInit, AfterViewChecked {
hoverText: () => { return Constants.saveCSVLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
selection = this.tryCombineSelections(selection);
selection = this.tryCombineSelectionsForResults(selection);
if (selection.length <= 1) {
this.handleContextClick({ type: 'savecsv', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
Expand All @@ -260,7 +277,7 @@ export class AppComponent implements OnInit, AfterViewChecked {
hoverText: () => { return Constants.saveJSONLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
selection = this.tryCombineSelections(selection);
selection = this.tryCombineSelectionsForResults(selection);
if (selection.length <= 1) {
this.handleContextClick({ type: 'savejson', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
Expand All @@ -274,7 +291,7 @@ export class AppComponent implements OnInit, AfterViewChecked {
hoverText: () => { return Constants.saveExcelLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
selection = this.tryCombineSelections(selection);
selection = this.tryCombineSelectionsForResults(selection);
if (selection.length <= 1) {
this.handleContextClick({ type: 'saveexcel', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
Expand Down Expand Up @@ -381,16 +398,12 @@ export class AppComponent implements OnInit, AfterViewChecked {
let resultSet = event.data;

// Setup a function for generating a promise to lookup result subsets
let loadDataFunction = (offset: number, count: number): Promise<IGridDataRow[]> => {
return self.dataService.getRows(offset, count, resultSet.batchId, resultSet.id).then(rows => {
let gridData: IGridDataRow[] = [];
for (let row = 0; row < rows.rows.length; row++) {
// Push row values onto end of gridData for slickgrid
gridData.push({
values: rows.rows[row]
});
let loadDataFunction = (offset: number, count: number): Promise<any[]> => {
return self.dataService.getRows(offset, count, resultSet.batchId, resultSet.id).then(response => {
if (!response) {
return [];
}
return gridData;
return response.rows;
});
};

Expand Down Expand Up @@ -421,6 +434,7 @@ export class AppComponent implements OnInit, AfterViewChecked {
let linkType = c.isXml ? 'xml' : 'json';
return {
id: i.toString(),
field: i.toString(),
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: Utils.htmlEntities(c.columnName),
Expand All @@ -430,6 +444,16 @@ export class AppComponent implements OnInit, AfterViewChecked {
};
})
};
dataSet.columnDefinitions.unshift({
id: 'rowNumber',
name: '',
field: 'rowNumber',
width: 22,
type: FieldType.Integer,
focusable: true,
selectable: false,
formatter: r => { return `<span class="row-number">${r + 1}</span>`; }
});
self.dataSets.push(dataSet);

// Create a dataSet to render without rows to reduce DOM size
Expand Down Expand Up @@ -556,10 +580,24 @@ export class AppComponent implements OnInit, AfterViewChecked {
}
}

openContextMenu(event: { x: number, y: number }, batchId, resultId, index): void {
let selection = this.slickgrids.toArray()[index].getSelectedRanges();
selection = this.tryCombineSelections(selection);
this.contextMenu.show(event.x, event.y, batchId, resultId, index, selection);
openContextMenu(event: MouseEvent, batchId, resultId, index): void {
let selection: ISlickRange[] = this.slickgrids.toArray()[index].getSelectedRanges();
selection = this.tryCombineSelectionsForResults(selection);
this.contextMenu.show(event.clientX, event.clientY, batchId, resultId, index, selection);
event.preventDefault();
event.stopPropagation();
}

private tryCombineSelectionsForResults(selections: ISlickRange[]): ISlickRange[] {
// need to take row number column in to consideration.
return this.tryCombineSelections(selections).map(range => {
return {
fromCell: range.fromCell - 1,
fromRow: range.fromRow,
toCell: range.toCell - 1,
toRow: range.toRow
};
});
}

private tryCombineSelections(selections: ISlickRange[]): ISlickRange[] {
Expand Down Expand Up @@ -598,7 +636,7 @@ export class AppComponent implements OnInit, AfterViewChecked {
let batchId = this.renderedDataSets[activeGrid].batchId;
let resultId = this.renderedDataSets[activeGrid].resultId;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges();
selection = this.tryCombineSelections(selection);
selection = this.tryCombineSelectionsForResults(selection);
this.dataService.sendSaveRequest(batchId, resultId, format, selection);
}

Expand Down Expand Up @@ -904,6 +942,27 @@ export class AppComponent implements OnInit, AfterViewChecked {
* @returns A boolean representing if the navigation was successful
*/
navigateToGrid(targetIndex: number): boolean {
const result = this.setActiveGrid(targetIndex);
if (!result) { return false; }

// scrolling logic
let resultsWindow = $('#results');
let scrollTop = resultsWindow.scrollTop();
let scrollBottom = scrollTop + resultsWindow.height();
let gridHeight = $(this._el.nativeElement).find('slick-grid').height();
if (scrollBottom < gridHeight * (targetIndex + 1)) {
scrollTop += (gridHeight * (targetIndex + 1)) - scrollBottom;
resultsWindow.scrollTop(scrollTop);
}
if (scrollTop > gridHeight * targetIndex) {
scrollTop = (gridHeight * targetIndex);
resultsWindow.scrollTop(scrollTop);
}

return true;
}

setActiveGrid(targetIndex: number): boolean {
// check if the target index is valid
if (targetIndex >= this.renderedDataSets.length || targetIndex < 0) {
return false;
Expand All @@ -921,21 +980,6 @@ export class AppComponent implements OnInit, AfterViewChecked {
this.slickgrids.toArray()[this.activeGrid].selection = false;
this.slickgrids.toArray()[targetIndex].setActive();
this.activeGrid = targetIndex;

// scrolling logic
let resultsWindow = $('#results');
let scrollTop = resultsWindow.scrollTop();
let scrollBottom = scrollTop + resultsWindow.height();
let gridHeight = $(this._el.nativeElement).find('slick-grid').height();
if (scrollBottom < gridHeight * (targetIndex + 1)) {
scrollTop += (gridHeight * (targetIndex + 1)) - scrollBottom;
resultsWindow.scrollTop(scrollTop);
}
if (scrollTop > gridHeight * targetIndex) {
scrollTop = (gridHeight * targetIndex);
resultsWindow.scrollTop(scrollTop);
}

return true;
}

Expand Down
4 changes: 4 additions & 0 deletions src/views/htmlcontent/src/js/services/data.service.ts
Expand Up @@ -163,4 +163,8 @@ export class DataService implements OnDestroy {
});
}
}

getNewColumnWidth(currentWidth: number): Promise<number | undefined> {
return this._proxy.getNewColumnWidth(currentWidth);
}
}

0 comments on commit f0a77e1

Please sign in to comment.