mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Fix loading and clickable div screen reader issues (#9088)
* Fix loading and clickable div screen reader issues * Change back to default clickable to false
This commit is contained in:
@@ -68,7 +68,7 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage {
|
|||||||
this.propertiesErrorMessage = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component();
|
this.propertiesErrorMessage = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component();
|
||||||
rootContainer.addItem(this.propertiesErrorMessage, { flex: '0 0 auto' });
|
rootContainer.addItem(this.propertiesErrorMessage, { flex: '0 0 auto' });
|
||||||
|
|
||||||
this.propertiesContainer = view.modelBuilder.divContainer().component();
|
this.propertiesContainer = view.modelBuilder.divContainer().withProperties<azdata.DivContainerProperties>({ clickable: false }).component();
|
||||||
|
|
||||||
// Row 1
|
// Row 1
|
||||||
const row1 = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '30px', alignItems: 'center' }).component();
|
const row1 = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '30px', alignItems: 'center' }).component();
|
||||||
@@ -76,14 +76,20 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage {
|
|||||||
// Cluster State
|
// Cluster State
|
||||||
const clusterStateLabel = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.clusterState }).component();
|
const clusterStateLabel = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.clusterState }).component();
|
||||||
const clusterStateValue = view.modelBuilder.text().component();
|
const clusterStateValue = view.modelBuilder.text().component();
|
||||||
this.clusterStateLoadingComponent = view.modelBuilder.loadingComponent().withItem(clusterStateValue).component();
|
this.clusterStateLoadingComponent = view.modelBuilder.loadingComponent()
|
||||||
|
.withItem(clusterStateValue)
|
||||||
|
.withProperties<azdata.LoadingComponentProperties>({ loadingCompletedText: loc.loadingClusterStateCompleted })
|
||||||
|
.component();
|
||||||
row1.addItem(clusterStateLabel, { CSSStyles: { 'width': `${clusterStateLabelColumnWidth}px`, 'min-width': `${clusterStateLabelColumnWidth}px`, 'user-select': 'none', 'font-weight': 'bold' } });
|
row1.addItem(clusterStateLabel, { CSSStyles: { 'width': `${clusterStateLabelColumnWidth}px`, 'min-width': `${clusterStateLabelColumnWidth}px`, 'user-select': 'none', 'font-weight': 'bold' } });
|
||||||
row1.addItem(this.clusterStateLoadingComponent, { CSSStyles: { 'width': `${clusterStateValueColumnWidth}px`, 'min-width': `${clusterStateValueColumnWidth}px` } });
|
row1.addItem(this.clusterStateLoadingComponent, { CSSStyles: { 'width': `${clusterStateValueColumnWidth}px`, 'min-width': `${clusterStateValueColumnWidth}px` } });
|
||||||
|
|
||||||
// Health Status
|
// Health Status
|
||||||
const healthStatusLabel = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.healthStatusWithColon }).component();
|
const healthStatusLabel = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.healthStatusWithColon }).component();
|
||||||
const healthStatusValue = view.modelBuilder.text().component();
|
const healthStatusValue = view.modelBuilder.text().component();
|
||||||
this.clusterHealthStatusLoadingComponent = view.modelBuilder.loadingComponent().withItem(healthStatusValue).component();
|
this.clusterHealthStatusLoadingComponent = view.modelBuilder.loadingComponent()
|
||||||
|
.withItem(healthStatusValue)
|
||||||
|
.withProperties<azdata.LoadingComponentProperties>({ loadingCompletedText: loc.loadingHealthStatusCompleted })
|
||||||
|
.component();
|
||||||
row1.addItem(healthStatusLabel, { CSSStyles: { 'width': `${healthStatusColumnWidth}px`, 'min-width': `${healthStatusColumnWidth}px`, 'user-select': 'none', 'font-weight': 'bold' } });
|
row1.addItem(healthStatusLabel, { CSSStyles: { 'width': `${healthStatusColumnWidth}px`, 'min-width': `${healthStatusColumnWidth}px`, 'user-select': 'none', 'font-weight': 'bold' } });
|
||||||
row1.addItem(this.clusterHealthStatusLoadingComponent, { CSSStyles: { 'width': `${healthStatusColumnWidth}px`, 'min-width': `${healthStatusColumnWidth}px` } });
|
row1.addItem(this.clusterHealthStatusLoadingComponent, { CSSStyles: { 'width': `${healthStatusColumnWidth}px`, 'min-width': `${healthStatusColumnWidth}px` } });
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ export const credentials = localize('mount.credentials.title', "Credentials");
|
|||||||
export const credentialsInfo = localize('mount.credentials.info', "Mount credentials for authentication to remote data source for reads");
|
export const credentialsInfo = localize('mount.credentials.info', "Mount credentials for authentication to remote data source for reads");
|
||||||
export const refreshMount = localize('refreshmount.dialog.title', "Refresh Mount");
|
export const refreshMount = localize('refreshmount.dialog.title', "Refresh Mount");
|
||||||
export const deleteMount = localize('deleteMount.dialog.title', "Delete Mount");
|
export const deleteMount = localize('deleteMount.dialog.title', "Delete Mount");
|
||||||
|
export const loadingClusterStateCompleted = localize('bdc.dashboard.loadingClusterStateCompleted', "Loading cluster state completed");
|
||||||
|
export const loadingHealthStatusCompleted = localize('bdc.dashboard.loadingHealthStatusCompleted', "Loading health status completed");
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
export const usernameRequired = localize('err.controller.username.required', "Username is required");
|
export const usernameRequired = localize('err.controller.username.required', "Username is required");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'vs/css!./media/divContainer';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Component, Input, Inject, ChangeDetectorRef, forwardRef,
|
Component, Input, Inject, ChangeDetectorRef, forwardRef,
|
||||||
ViewChild, ElementRef, OnDestroy,
|
ViewChild, ElementRef, OnDestroy, Renderer2, AfterViewInit
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
@@ -24,7 +24,7 @@ class DivItem {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<div #divContainer *ngIf="items" class="divContainer" [style.height]="height" [style.width]="width" [style.display]="display" (click)="onClick()" (keyup)="onKey($event)" [attr.role]="ariaRole" [attr.aria-selected]="ariaSelected">
|
<div #divContainer *ngIf="items" class="divContainer" [style.height]="height" [style.width]="width" [style.display]="display" (keyup)="onKey($event)" [attr.role]="ariaRole" [attr.aria-selected]="ariaSelected">
|
||||||
<div *ngFor="let item of items" [style.order]="getItemOrder(item)" [ngStyle]="getItemStyles(item)">
|
<div *ngFor="let item of items" [style.order]="getItemOrder(item)" [ngStyle]="getItemStyles(item)">
|
||||||
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
|
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
|
||||||
</model-component-wrapper>
|
</model-component-wrapper>
|
||||||
@@ -32,22 +32,30 @@ class DivItem {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export default class DivContainer extends ContainerBase<azdata.DivItemLayout> implements IComponent, OnDestroy {
|
export default class DivContainer extends ContainerBase<azdata.DivItemLayout> implements IComponent, OnDestroy, AfterViewInit {
|
||||||
@Input() descriptor: IComponentDescriptor;
|
@Input() descriptor: IComponentDescriptor;
|
||||||
@Input() modelStore: IModelStore;
|
@Input() modelStore: IModelStore;
|
||||||
@ViewChild('divContainer', { read: ElementRef }) divContainer;
|
@ViewChild('divContainer', { read: ElementRef }) divContainer;
|
||||||
private _height: string;
|
private _height: string;
|
||||||
private _width: string;
|
private _width: string;
|
||||||
private _overflowY: string;
|
private _overflowY: string;
|
||||||
|
private viewInitialized: boolean;
|
||||||
|
private cancelClick: Function;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef
|
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
||||||
|
@Inject(forwardRef(() => Renderer2)) private renderer: Renderer2
|
||||||
) {
|
) {
|
||||||
super(changeRef, el);
|
super(changeRef, el);
|
||||||
this._overflowY = ''; // default
|
this._overflowY = ''; // default
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.viewInitialized = true;
|
||||||
|
this.updateClickListener();
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.baseInit();
|
this.baseInit();
|
||||||
}
|
}
|
||||||
@@ -97,6 +105,7 @@ export default class DivContainer extends ContainerBase<azdata.DivItemLayout> im
|
|||||||
element.removeAttribute('tabIndex');
|
element.removeAttribute('tabIndex');
|
||||||
element.style.cursor = 'default';
|
element.style.cursor = 'default';
|
||||||
}
|
}
|
||||||
|
this.updateClickListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onClick() {
|
private onClick() {
|
||||||
@@ -148,4 +157,17 @@ export default class DivContainer extends ContainerBase<azdata.DivItemLayout> im
|
|||||||
public getItemStyles(item: DivItem): { [key: string]: string } {
|
public getItemStyles(item: DivItem): { [key: string]: string } {
|
||||||
return item.config && item.config.CSSStyles ? item.config.CSSStyles : {};
|
return item.config && item.config.CSSStyles ? item.config.CSSStyles : {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateClickListener(): void {
|
||||||
|
// We can't hook into the listener until the view is initialized
|
||||||
|
if (!this.viewInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.clickable && !this.cancelClick) {
|
||||||
|
this.cancelClick = this.renderer.listen(this.divContainer.nativeElement, 'click', () => this.onClick());
|
||||||
|
} else if (!this.clickable) {
|
||||||
|
this.cancelClick();
|
||||||
|
this.cancelClick = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,11 @@ import * as azdata from 'azdata';
|
|||||||
import { ComponentBase } from 'sql/workbench/browser/modelComponents/componentBase';
|
import { ComponentBase } from 'sql/workbench/browser/modelComponents/componentBase';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/platform/dashboard/browser/interfaces';
|
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/platform/dashboard/browser/interfaces';
|
||||||
|
import { status } from 'vs/base/browser/ui/aria/aria';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-loadingComponent',
|
selector: 'modelview-loadingComponent',
|
||||||
template: `
|
template: `
|
||||||
<div role="status" aria-live="polite" class="modelview-loading-component-status-message">
|
|
||||||
{{getStatusText()}}
|
|
||||||
</div>
|
|
||||||
<div class="modelview-loadingComponent-container" aria-busy="true" *ngIf="loading">
|
<div class="modelview-loadingComponent-container" aria-busy="true" *ngIf="loading">
|
||||||
<div class="modelview-loadingComponent-spinner" [title]="getStatusText()" #spinnerElement></div>
|
<div class="modelview-loadingComponent-spinner" [title]="getStatusText()" #spinnerElement></div>
|
||||||
<div *ngIf="showText" class="modelview-loadingComponent-status-text">{{getStatusText()}}</div>
|
<div *ngIf="showText" class="modelview-loadingComponent-status-text">{{getStatusText()}}</div>
|
||||||
@@ -66,7 +64,11 @@ export default class LoadingComponent extends ComponentBase implements IComponen
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setProperties(properties: { [key: string]: any; }): void {
|
public setProperties(properties: { [key: string]: any; }): void {
|
||||||
|
const wasLoading = this.loading;
|
||||||
super.setProperties(properties);
|
super.setProperties(properties);
|
||||||
|
if (wasLoading && !this.loading) {
|
||||||
|
status(this.getStatusText());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get loading(): boolean {
|
public get loading(): boolean {
|
||||||
|
|||||||
@@ -32,14 +32,3 @@
|
|||||||
.modelview-loadingComponent-content-loading {
|
.modelview-loadingComponent-content-loading {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We want to make this on DOM but not visible so that screen reader can read the status message */
|
|
||||||
|
|
||||||
.modelview-loading-component-status-message {
|
|
||||||
top: -1;
|
|
||||||
left: -1px;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
position: absolute;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user