浏览代码

Merge pull request #9104 from telamonian/prettify-sdm-switch

followup #9100: made sdm switch pretty, accessible
Jason Grout 4 年之前
父节点
当前提交
8955e9b2d4
共有 3 个文件被更改,包括 98 次插入26 次删除
  1. 35 16
      packages/application/src/shell.ts
  2. 52 0
      packages/application/style/buttons.css
  3. 11 10
      packages/application/test/shell.spec.ts

+ 35 - 16
packages/application/src/shell.ts

@@ -302,26 +302,45 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell {
     spacer.id = 'jp-top-spacer';
     this.add(spacer, 'top', { rank: 1000 });
 
-    const label = document.createElement('label');
-    label.textContent = 'Single-Document Mode';
-    label.title = 'Single-Document Mode';
-    const checkbox = document.createElement('input');
-    checkbox.type = 'checkbox';
-    checkbox.value = 'single-document';
-    checkbox.title = 'Single-Document Mode';
+    // switch accessibility refs:
+    // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Switch_role
+    // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Accessibility_concerns
+    const sdmSwitch = document.createElement('button');
+    sdmSwitch.className = 'jp-slider jp-slider-sdm';
+    sdmSwitch.setAttribute('role', 'switch');
+    sdmSwitch.value = 'single-document';
+    sdmSwitch.title = 'Single-Document Mode';
     this.modeChanged.connect((_, mode) => {
-      checkbox.checked = mode === 'single-document';
+      sdmSwitch.setAttribute(
+        'aria-checked',
+        mode === 'single-document' ? 'true' : 'false'
+      );
     });
-    checkbox.checked = this.mode === 'single-document';
-    checkbox.addEventListener('change', () => {
-      this.mode = checkbox.checked ? 'single-document' : 'multiple-document';
+    sdmSwitch.setAttribute(
+      'aria-checked',
+      this.mode === 'single-document' ? 'true' : 'false'
+    );
+    sdmSwitch.addEventListener('click', () => {
+      this.mode =
+        sdmSwitch.getAttribute('aria-checked') === 'true'
+          ? 'multiple-document'
+          : 'single-document';
     });
-    label.appendChild(checkbox);
 
-    const checkWidget = new Widget();
-    checkWidget.node.appendChild(label);
-    checkWidget.id = 'jp-single-document-mode';
-    this.add(checkWidget, 'top', { rank: 1010 });
+    const sdmLabel = document.createElement('label');
+    sdmLabel.className = 'jp-slider-label';
+    sdmLabel.textContent = 'Single-Document Mode';
+    sdmLabel.title = 'Single-Document Mode';
+    const sdmTrack = document.createElement('div');
+    sdmTrack.className = 'jp-slider-track';
+    sdmTrack.setAttribute('aria-hidden', 'true');
+    sdmSwitch.appendChild(sdmLabel);
+    sdmSwitch.appendChild(sdmTrack);
+
+    const sdmSwitchWidget = new Widget();
+    sdmSwitchWidget.node.appendChild(sdmSwitch);
+    sdmSwitchWidget.id = 'jp-single-document-mode';
+    this.add(sdmSwitchWidget, 'top', { rank: 1010 });
 
     // Wire up signals to update the title panel of the single document mode to
     // follow the title of this.currentWidget

+ 52 - 0
packages/application/style/buttons.css

@@ -79,3 +79,55 @@ button.jp-mod-styled.jp-mod-warn:active {
   box-shadow: none;
   background-color: rgba(153, 153, 153, 0.2);
 }
+
+.jp-slider {
+  display: flex;
+  align-items: center;
+  font-size: var(--jp-ui-font-size1);
+}
+
+.jp-slider-sdm {
+  border: none;
+  height: 20px;
+}
+
+.jp-slider:hover {
+  background-color: var(--jp-layout-color2);
+}
+
+.jp-slider-label {
+  margin-right: 5px;
+}
+
+.jp-slider-track {
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  background-color: var(--jp-border-color1);
+  -webkit-transition: 0.4s;
+  transition: 0.4s;
+  border-radius: 34px;
+  height: 15px;
+  width: 35px;
+}
+
+.jp-slider-track::before {
+  content: '';
+  height: 10px;
+  width: 10px;
+  margin-left: 5px;
+  margin-right: 50%;
+  background-color: white;
+  -webkit-transition: 0.4s;
+  transition: 0.4s;
+  border-radius: 50%;
+}
+
+.jp-slider[aria-checked='true'] .jp-slider-track {
+  background-color: var(--jp-warn-color0);
+}
+
+.jp-slider[aria-checked='true'] .jp-slider-track::before {
+  margin-left: 50%;
+  margin-right: 5px;
+}

+ 11 - 10
packages/application/test/shell.spec.ts

@@ -84,18 +84,15 @@ describe('LabShell', () => {
 
   describe('#isEmpty()', () => {
     it('should test whether the main area is empty', () => {
-      expect(shell.isEmpty('top')).toBe(true);
+      expect(shell.isEmpty('main')).toBe(true);
       const widget = new Widget();
       widget.id = 'foo';
       shell.add(widget, 'main');
       expect(shell.isEmpty('main')).toBe(false);
     });
 
-    it('should test whether the top area is empty', () => {
-      expect(shell.isEmpty('top')).toBe(true);
-      const widget = new Widget();
-      widget.id = 'foo';
-      shell.add(widget, 'top');
+    it('should test whether the top area is not empty', () => {
+      // account for builtin sdm slider widget
       expect(shell.isEmpty('top')).toBe(false);
     });
 
@@ -152,20 +149,24 @@ describe('LabShell', () => {
       const widget = new Widget();
       widget.id = 'foo';
       shell.add(widget, 'top');
-      expect(shell.isEmpty('top')).toBe(false);
+      // the added widget plus the builtin spacer and sdm slider widgets
+      console.log(toArray(shell.widgets('top')));
+      expect(toArray(shell.widgets('top')).length).toEqual(3);
     });
 
     it('should be a no-op if the widget has no id', () => {
       const widget = new Widget();
       shell.add(widget, 'top');
-      expect(shell.isEmpty('top')).toBe(true);
+      // the builtin spacer and sdm slider widgets alone
+      expect(toArray(shell.widgets('top')).length).toEqual(2);
     });
 
     it('should accept options', () => {
       const widget = new Widget();
       widget.id = 'foo';
       shell.add(widget, 'top', { rank: 10 });
-      expect(shell.isEmpty('top')).toBe(false);
+      // the added widget plus the builtin spacer and sdm slider widgets
+      expect(toArray(shell.widgets('top')).length).toEqual(3);
     });
 
     it('should add widgets according to their ranks', () => {
@@ -175,7 +176,7 @@ describe('LabShell', () => {
       bar.id = 'bar';
       shell.add(foo, 'top', { rank: 20 });
       shell.add(bar, 'top', { rank: 10 });
-      expect(toArray(shell.widgets('top'))).toEqual([bar, foo]);
+      expect(toArray(shell.widgets('top')).slice(0, 2)).toEqual([bar, foo]);
     });
   });