GAR: Fix Voice Over for maps and devices in web UI

Added aria-label for dropzone as "Device map"
Added aria-label for dragzone as positional info
Added aria-label for cube-sprite as "Device"
Modified cursor CSS attribute

Video: go/netsim-video-cl2490776
Bug: 272784395
Change-Id: If4a9d98c3210186f02efa58c8e784b7f1d96e877
diff --git a/ui/dist/js/cube-sprite.js b/ui/dist/js/cube-sprite.js
index c6c741f..d422ca4 100644
--- a/ui/dist/js/cube-sprite.js
+++ b/ui/dist/js/cube-sprite.js
@@ -12,7 +12,7 @@
           outline: ${this.highlighted&&this.controls?e`dashed`:e``};
         }
       </style>
-      <div class="cube">
+      <div class="cube" role="button" tabindex="0" aria-label="Device">
         <div></div>
         <div></div>
         <div></div>
@@ -35,6 +35,7 @@
       transform-origin: center;
       transform-style: preserve-3d;
       transform: translateZ(calc(var(--posZ) * 1px));
+      cursor: move;
     }
 
     .cube {
diff --git a/ui/dist/js/device-dragzone.js b/ui/dist/js/device-dragzone.js
index 163932c..03eff1e 100644
--- a/ui/dist/js/device-dragzone.js
+++ b/ui/dist/js/device-dragzone.js
@@ -1,5 +1 @@
-import{__decorate as t}from"../node_modules/tslib/tslib.es6.js";import{css as e,LitElement as r,html as s}from"https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js";import{property as a,customElement as i}from"https://cdn.skypack.dev/pin/lit@v2.5.0-jYRq0AKQogjUdUh7SCAE/mode=imports/optimized/lit/decorators.js";import{simulationState as d}from"./device-observer.js";var o;let n=o=class extends r{constructor(){super(),this.action="move",this.addEventListener("dragstart",this.handleDragStart),this.addEventListener("dragend",this.handleDragEnd),this.addEventListener("click",this.handleSelect)}connectedCallback(){this.draggable=!0}handleDragStart(t){this.style.opacity="0.4",t.dataTransfer&&t.target&&(o.dragged=t.target,t.dataTransfer.effectAllowed="move"===this.action?"move":"copy")}handleDragEnd(){this.style.opacity="1",o.dragged=null}handleSelect(t){this.style.opacity="1",t.target&&d.patchSelected(t.target.id)}render(){return s` <slot></slot> `}};n.styles=e`
-    :host {
-      cursor: move;
-    }
-  `,t([a({type:String,attribute:"action"})],n.prototype,"action",void 0),n=o=t([i("ns-device-dragzone")],n);export{n as DeviceDragZone};
+import{__decorate as t}from"../node_modules/tslib/tslib.es6.js";import{LitElement as e,html as r}from"https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js";import{property as a,customElement as i}from"https://cdn.skypack.dev/pin/lit@v2.5.0-jYRq0AKQogjUdUh7SCAE/mode=imports/optimized/lit/decorators.js";import{simulationState as d}from"./device-observer.js";var s;let o=s=class extends e{constructor(){super(),this.action="move",this.addEventListener("dragstart",this.handleDragStart),this.addEventListener("dragend",this.handleDragEnd),this.addEventListener("click",this.handleSelect)}connectedCallback(){this.draggable=!0}handleDragStart(t){this.style.opacity="0.4",t.dataTransfer&&t.target&&(s.dragged=t.target,t.dataTransfer.effectAllowed="move"===this.action?"move":"copy")}handleDragEnd(){this.style.opacity="1",s.dragged=null}handleSelect(t){this.style.opacity="1",t.target&&d.patchSelected(t.target.id)}render(){return r` <slot></slot> `}};t([a({type:String,attribute:"action"})],o.prototype,"action",void 0),o=s=t([i("ns-device-dragzone")],o);export{o as DeviceDragZone};
diff --git a/ui/dist/js/device-dropzone.js b/ui/dist/js/device-dropzone.js
index bf0b7fa..b42f428 100644
--- a/ui/dist/js/device-dropzone.js
+++ b/ui/dist/js/device-dropzone.js
@@ -1,5 +1 @@
-import{__decorate as e}from"../node_modules/tslib/tslib.es6.js";import{css as t,LitElement as r,html as o}from"https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js";import{property as i,customElement as s}from"https://cdn.skypack.dev/pin/lit@v2.5.0-jYRq0AKQogjUdUh7SCAE/mode=imports/optimized/lit/decorators.js";import{DeviceDragZone as n}from"./device-dragzone.js";import{simulationState as d}from"./device-observer.js";var l;let a=l=class extends r{constructor(){super(),this.serial="",this.type="",this.addEventListener("drop",this.handleDrop),this.addEventListener("drag",this.handleDragOver),this.addEventListener("dragenter",l.handleDragEnter),this.addEventListener("dragleave",l.handleDragLeave),this.addEventListener("dragover",this.handleDragOver)}static handleDragEnter(e){e.preventDefault()}static handleDragLeave(e){e.preventDefault()}slottedDropZone(){var e;const t=null===(e=this.shadowRoot)||void 0===e?void 0:e.querySelector("slot");return null==t?void 0:t.assignedElements({flatten:!0})[0]}handleDrop(e){var t,r,o,i;e.preventDefault();const s=this.slottedDropZone();if(s){const l=n.dragged;"move"===(null===(t=e.dataTransfer)||void 0===t?void 0:t.effectAllowed)?(null===(r=l.parentNode)||void 0===r||r.removeChild(l),l.style.opacity="",s.appendChild(l)):s.appendChild(l.cloneNode(!0));const a=s.lastChild;if(a){const t=s.getBoundingClientRect();a.setAttribute("action","move"),a.style.position="absolute",a.style.left=e.clientX-t.left+"px",a.style.top=e.clientY-t.top+"px",a.style.opacity="1.0";let r=null===(o=a.getElementsByTagName("ns-cube-sprite").item(0))||void 0===o?void 0:o.getAttribute("id");void 0===r&&(r=null===(i=a.getElementsByTagName("ns-pyramid-sprite").item(0))||void 0===i?void 0:i.getAttribute("id")),null==r&&(r=""),d.handleDrop(r,(e.clientX-t.left)/100,(e.clientY-t.top)/100)}}}handleDragOver(e){e.preventDefault(),this.slottedDropZone()}render(){return o`<slot></slot>`}};a.styles=t`
-    :host {
-      cursor: move;
-    }
-  `,e([i({type:String,attribute:"serial"})],a.prototype,"serial",void 0),e([i({type:String,attribute:"type"})],a.prototype,"type",void 0),a=l=e([s("ns-device-dropzone")],a);export{a as DeviceDropZone};
+import{__decorate as e}from"../node_modules/tslib/tslib.es6.js";import{LitElement as t,html as i}from"https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js";import{property as r,customElement as o}from"https://cdn.skypack.dev/pin/lit@v2.5.0-jYRq0AKQogjUdUh7SCAE/mode=imports/optimized/lit/decorators.js";import{DeviceDragZone as n}from"./device-dragzone.js";import{simulationState as d}from"./device-observer.js";var s;let l=s=class extends t{constructor(){super(),this.serial="",this.type="",this.addEventListener("drop",this.handleDrop),this.addEventListener("drag",this.handleDragOver),this.addEventListener("dragenter",s.handleDragEnter),this.addEventListener("dragleave",s.handleDragLeave),this.addEventListener("dragover",this.handleDragOver)}static handleDragEnter(e){e.preventDefault()}static handleDragLeave(e){e.preventDefault()}slottedDropZone(){var e;const t=null===(e=this.shadowRoot)||void 0===e?void 0:e.querySelector("slot");return null==t?void 0:t.assignedElements({flatten:!0})[0]}handleDrop(e){var t,i,r,o;e.preventDefault();const s=this.slottedDropZone();if(s){const l=n.dragged;"move"===(null===(t=e.dataTransfer)||void 0===t?void 0:t.effectAllowed)?(null===(i=l.parentNode)||void 0===i||i.removeChild(l),l.style.opacity="",s.appendChild(l)):s.appendChild(l.cloneNode(!0));const a=s.lastChild;if(a){const t=s.getBoundingClientRect();a.setAttribute("action","move"),a.style.position="absolute",a.style.left=e.clientX-t.left+"px",a.style.top=e.clientY-t.top+"px",a.style.opacity="1.0";let i=null===(r=a.getElementsByTagName("ns-cube-sprite").item(0))||void 0===r?void 0:r.getAttribute("id");void 0===i&&(i=null===(o=a.getElementsByTagName("ns-pyramid-sprite").item(0))||void 0===o?void 0:o.getAttribute("id")),null==i&&(i=""),d.handleDrop(i,(e.clientX-t.left)/100,(e.clientY-t.top)/100)}}}handleDragOver(e){e.preventDefault(),this.slottedDropZone()}render(){return i`<slot></slot>`}};e([r({type:String,attribute:"serial"})],l.prototype,"serial",void 0),e([r({type:String,attribute:"type"})],l.prototype,"type",void 0),l=s=e([o("ns-device-dropzone")],l);export{l as DeviceDropZone};
diff --git a/ui/dist/js/device-map.js b/ui/dist/js/device-map.js
index af9d96a..b12b2ec 100644
--- a/ui/dist/js/device-map.js
+++ b/ui/dist/js/device-map.js
@@ -1,11 +1,15 @@
-import{__decorate as e}from"../node_modules/tslib/tslib.es6.js";import{css as t,LitElement as i,html as o}from"https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js";import{property as s,customElement as r}from"https://cdn.skypack.dev/pin/lit@v2.5.0-jYRq0AKQogjUdUh7SCAE/mode=imports/optimized/lit/decorators.js";import{styleMap as n}from"https://cdn.jsdelivr.net/gh/lit/dist@2/all/lit-all.min.js";import{simulationState as a}from"./device-observer.js";let d=class extends i{constructor(){super(...arguments),this.deviceData=[],this.imageIdx=0,this.numImages=3,this.isometric=!1,this.onChangeMap=()=>{this.imageIdx=(this.imageIdx+1)%this.numImages},this.handleIsometricView=()=>{this.isometric=!this.isometric}}connectedCallback(){super.connectedCallback(),a.registerObserver(this),window.addEventListener("map-button-clicked",this.onChangeMap),window.addEventListener("isometric-button-clicked",this.handleIsometricView)}disconnectedCallback(){window.removeEventListener("isometric-button-clicked",this.handleIsometricView),window.removeEventListener("map-button-clicked",this.onChangeMap),a.removeObserver(this),super.disconnectedCallback()}onNotify(e){this.deviceData=e.devices,this.requestUpdate()}render(){const e=["red","orange","yellow","green","blue","indigo","purple"],t=this.isometric?"perspective(200rem) rotateX(60deg) rotateY(0deg) rotateZ(0deg) scale3d(0.8,0.8,0.8); top: 250px":"none; top: 0px;";return o`
-      <ns-device-dropzone>
+import{__decorate as e}from"../node_modules/tslib/tslib.es6.js";import{css as t,LitElement as i,html as o}from"https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js";import{property as r,customElement as s}from"https://cdn.skypack.dev/pin/lit@v2.5.0-jYRq0AKQogjUdUh7SCAE/mode=imports/optimized/lit/decorators.js";import{styleMap as n}from"https://cdn.jsdelivr.net/gh/lit/dist@2/all/lit-all.min.js";import{simulationState as a}from"./device-observer.js";let d=class extends i{constructor(){super(...arguments),this.deviceData=[],this.imageIdx=0,this.numImages=3,this.isometric=!1,this.onChangeMap=()=>{this.imageIdx=(this.imageIdx+1)%this.numImages},this.handleIsometricView=()=>{this.isometric=!this.isometric}}connectedCallback(){super.connectedCallback(),a.registerObserver(this),window.addEventListener("map-button-clicked",this.onChangeMap),window.addEventListener("isometric-button-clicked",this.handleIsometricView)}disconnectedCallback(){window.removeEventListener("isometric-button-clicked",this.handleIsometricView),window.removeEventListener("map-button-clicked",this.onChangeMap),a.removeObserver(this),super.disconnectedCallback()}onNotify(e){this.deviceData=e.devices,this.requestUpdate()}render(){const e=["red","orange","yellow","green","blue","indigo","purple"],t=this.isometric?"perspective(200rem) rotateX(60deg) rotateY(0deg) rotateZ(0deg) scale3d(0.8,0.8,0.8); top: 250px":"none; top: 0px;";return o`
+      <ns-device-dropzone role="region" tabindex="0" aria-label="Device map">
         <div id="dropzone" class="box pattern${this.imageIdx}">
           ${this.deviceData.map(((t,i)=>o`
               ${!0===t.visible?o`
                     <ns-device-dragzone
                       .action=${"move"}
                       style=${n({position:"absolute",left:100*t.position.x+"px",top:100*t.position.y+"px"})}
+                      role="region"
+                      tabindex="1"
+                      aria-label="Position: ${Math.round(100*t.position.x)}, ${Math.round(100*t.position.y)}, ${Math.round(100*t.position.z)}"
+                      aria-live="assertive"
                     >
                       <ns-cube-sprite
                         id=${t.name}
@@ -75,4 +79,4 @@
     ns-device-dragzone {
       transform-style: inherit;
     }
-  `,e([s()],d.prototype,"deviceData",void 0),e([s()],d.prototype,"imageIdx",void 0),e([s()],d.prototype,"numImages",void 0),e([s({type:Boolean,reflect:!0})],d.prototype,"isometric",void 0),d=e([r("ns-device-map")],d);export{d as DeviceMap};
+  `,e([r()],d.prototype,"deviceData",void 0),e([r()],d.prototype,"imageIdx",void 0),e([r()],d.prototype,"numImages",void 0),e([r({type:Boolean,reflect:!0})],d.prototype,"isometric",void 0),d=e([s("ns-device-map")],d);export{d as DeviceMap};
diff --git a/ui/ts/cube-sprite.ts b/ui/ts/cube-sprite.ts
index 0497136..cf19f79 100644
--- a/ui/ts/cube-sprite.ts
+++ b/ui/ts/cube-sprite.ts
@@ -85,6 +85,7 @@
       transform-origin: center;
       transform-style: preserve-3d;
       transform: translateZ(calc(var(--posZ) * 1px));
+      cursor: move;
     }
 
     .cube {
@@ -183,7 +184,7 @@
           outline: ${this.highlighted && this.controls ? css`dashed` : css``};
         }
       </style>
-      <div class="cube">
+      <div class="cube" role="button" tabindex="0" aria-label="Device">
         <div></div>
         <div></div>
         <div></div>
diff --git a/ui/ts/device-dragzone.ts b/ui/ts/device-dragzone.ts
index 8dd3a12..2dea480 100644
--- a/ui/ts/device-dragzone.ts
+++ b/ui/ts/device-dragzone.ts
@@ -1,4 +1,4 @@
-import {css, html, LitElement} from 'lit';
+import {html, LitElement} from 'lit';
 import {customElement, property} from 'lit/decorators.js';
 
 import {simulationState} from './device-observer.js';
@@ -9,12 +9,6 @@
 
   @property({type: String, attribute: 'action'}) action = 'move';
 
-  static styles = css`
-    :host {
-      cursor: move;
-    }
-  `;
-
   constructor() {
     super();
     this.addEventListener('dragstart', this.handleDragStart);
diff --git a/ui/ts/device-dropzone.ts b/ui/ts/device-dropzone.ts
index 71fe375..20daec5 100644
--- a/ui/ts/device-dropzone.ts
+++ b/ui/ts/device-dropzone.ts
@@ -1,4 +1,4 @@
-import {css, html, LitElement} from 'lit';
+import {html, LitElement} from 'lit';
 import {customElement, property} from 'lit/decorators.js';
 
 import {DeviceDragZone} from './device-dragzone.js';
@@ -10,12 +10,6 @@
 
   @property({type: String, attribute: 'type'}) type: string = '';
 
-  static styles = css`
-    :host {
-      cursor: move;
-    }
-  `;
-
   constructor() {
     super();
     this.addEventListener('drop', this.handleDrop);
diff --git a/ui/ts/device-map.ts b/ui/ts/device-map.ts
index 7ab4fe4..85c2f3d 100644
--- a/ui/ts/device-map.ts
+++ b/ui/ts/device-map.ts
@@ -117,20 +117,28 @@
         'none; top: 0px;';
 
     return html`
-      <ns-device-dropzone>
+      <ns-device-dropzone role="region" tabindex="0" aria-label="Device map">
         <div id="dropzone" class="box pattern${this.imageIdx}">
           ${
         this.deviceData.map(
             (device, idx) => html`
               ${
-                device.visible === true ? html`
+                device.visible === true ?
+                    html`
                     <ns-device-dragzone
                       .action=${'move'}
                       style=${styleMap({
-                  position: 'absolute',
-                  left: `${device.position.x * 100}px`,
-                  top: `${device.position.y * 100}px`,
-                })}
+                      position: 'absolute',
+                      left: `${device.position.x * 100}px`,
+                      top: `${device.position.y * 100}px`,
+                    })}
+                      role="region"
+                      tabindex="1"
+                      aria-label="Position: ${
+                        Math.round(device.position.x * 100)}, ${
+                        Math.round(device.position.y * 100)}, ${
+                        Math.round(device.position.z * 100)}"
+                      aria-live="assertive"
                     >
                       <ns-cube-sprite
                         id=${device.name}
@@ -144,7 +152,7 @@
                       ></ns-cube-sprite>
                     </ns-device-dragzone>
                   ` :
-                                          html``}
+                    html``}
             `)}
         </div>
         <style>