From 9ff1b33302b1c9feff7b502aef1d175c43fb45ae Mon Sep 17 00:00:00 2001
From: Christoph Reiter <reiter.christoph@gmail.com>
Date: Tue, 13 Aug 2019 11:45:56 +0200
Subject: [PATCH] Make the language switcher a toggle button

This duplicates the language switcher on the tugraz.at site.

Compared to the previous implementation we now show the next language
and use the translations for the next language, so that users only knowing
english can understand it.

In case of more languages we just cycle through them, which isn't ideal but
good enough for now.
---
 .../src/i18n/de/translation.json              |   4 +-
 .../src/i18n/en/translation.json              |   4 +-
 .../language-select/src/language-select.js    | 114 ++++++++++--------
 packages/language-select/test/unit.js         |  17 +++
 4 files changed, 87 insertions(+), 52 deletions(-)

diff --git a/packages/language-select/src/i18n/de/translation.json b/packages/language-select/src/i18n/de/translation.json
index 5ee4f041..62536512 100644
--- a/packages/language-select/src/i18n/de/translation.json
+++ b/packages/language-select/src/i18n/de/translation.json
@@ -1,4 +1,6 @@
 {
   "de": "Deutsch",
-  "en": "Englisch"
+  "en": "Englisch",
+  "de-action": "Auf Deutsch anzeigen",
+  "en-action": "Auf Englisch anzeigen"
 }
diff --git a/packages/language-select/src/i18n/en/translation.json b/packages/language-select/src/i18n/en/translation.json
index f8615781..1177302e 100644
--- a/packages/language-select/src/i18n/en/translation.json
+++ b/packages/language-select/src/i18n/en/translation.json
@@ -1,4 +1,6 @@
 {
   "de": "German",
-  "en": "English"
+  "en": "English",
+  "de-action": "Switch to German",
+  "en-action": "Switch to English"
 }
diff --git a/packages/language-select/src/language-select.js b/packages/language-select/src/language-select.js
index 53d05ed7..21d87172 100644
--- a/packages/language-select/src/language-select.js
+++ b/packages/language-select/src/language-select.js
@@ -9,53 +9,83 @@ class LanguageSelect extends LitElement {
 
     constructor() {
         super();
-        this.lang = 'de';
+        this._lang = 'de';
         this.languages = ['de', 'en'];
 
+        this.onExternalChange = this.onExternalChange.bind(this);
+
+        // for the i18next scanner
         i18n.t('de');
+        i18n.t('de-action');
         i18n.t('en');
+        i18n.t('en-action');
+    }
 
-        this.onExternalChange = this.onExternalChange.bind(this);
+    _getNextLanguage() {
+        var index = this.languages.indexOf(this.lang);
+        var next = this.languages[index + 1];
+        if (typeof next === 'undefined')
+            next = this.languages[0];
+        return next;
     }
 
     static get properties() {
         return {
             lang: {type: String},
+            next: {type: String},
             languages: {type: Array},
         };
     }
 
+    set lang(value) {
+        const oldValue = this.lang;
+        const oldNext = this.next;
+        this._lang = value;
+        this.requestUpdate('lang', oldValue);
+        this.requestUpdate('next', oldNext);
+
+        if (oldValue !== value) {
+            const event = new CustomEvent("vpu-language-changed", {
+                bubbles: true,
+                detail: {'lang': value}
+            });
+            this.dispatchEvent(event);
+
+            // Unlike other cases we use the next language for the translations so that
+            // users not knowing the current language can understand it.
+            // In case of more than two this doesn't make that much sense, but for now..
+            i18n.changeLanguage(this.next);
+        }
+    }
+
+    get lang() {
+        return this._lang;
+    }
+
+    set next(value) {
+    }
+
+    get next() {
+        return this._getNextLanguage();
+    }
+
     static get styles() {
         return css`
-            select {
-                background:
-                    linear-gradient(45deg, transparent 50%, black 50%),
-                    linear-gradient(135deg, black 50%, transparent 50%),
-                    linear-gradient(to right, white, white);
-                background-position:
-                    calc(100% - 21px) calc(1em + 2px),
-                    calc(100% - 16px) calc(1em + 2px),
-                    100% 0;
-                background-size:
-                    5px 5px,
-                    5px 5px,
-                    2.5em 2.5em;
-                background-repeat: no-repeat;
-
-                border: 1px solid #000;
-                line-height: 1.5em;
-                padding: 0.5em 3.5em 0.5em 0.5em;
-
-                border-radius: 0;
-                margin: 0;
-                -webkit-box-sizing: border-box;
-                -moz-box-sizing: border-box;
-                box-sizing: border-box;
-                -webkit-appearance:none;
-                -moz-appearance:none;
+            a:hover {
+                background-color: #000;
+                color: #fff;
+            }
+
+            a {
+                padding: 5px;
+                display: inline-block;
+                text-decoration: none;
+                color: #000;
+                transition: background-color 0.15s, color 0.15s;
+                font-weight: 300;
             }
         `;
-    } 
+    }
 
     onExternalChange(e) {
         this.lang = e.detail.lang
@@ -71,31 +101,15 @@ class LanguageSelect extends LitElement {
         super.disconnectedCallback();
     }
 
-    update(changedProperties) {
-        changedProperties.forEach((oldValue, propName) => {
-            if (propName === 'lang') {
-                const event = new CustomEvent("vpu-language-changed", {
-                    bubbles: true,
-                    detail: {'lang': this.lang},
-                });
-                this.dispatchEvent(event);
-
-                i18n.changeLanguage(this.lang);
-            }
-        });
-
-        super.update(changedProperties);
-    }
-
-    onSelectChange(e) {
-        this.lang = e.target.value;
+    onClick(e) {
+        e.preventDefault();
+        this.lang = this.next;
     }
 
     render() {
+        var linkTitle = i18n.t(this.next + '-action');
         return html`
-            <select @change=${this.onSelectChange}>
-                ${this.languages.map(i => html`<option value="${i}" ?selected=${this.lang === i}>${i18n.t(i)}</option>`)}
-            </select> 
+            <a href="#" title="${linkTitle}" @click=${this.onClick}>${this.next.toUpperCase()}</a>
         `;
     }
 }
diff --git a/packages/language-select/test/unit.js b/packages/language-select/test/unit.js
index ea01a63e..3511aa8a 100644
--- a/packages/language-select/test/unit.js
+++ b/packages/language-select/test/unit.js
@@ -3,8 +3,15 @@ import '../src/demo.js';
 
 describe('vpu-language-select basics', () => {
   let node;
+  let events = [];
+
+  function handler(e) {
+    events.push(e);
+  }
 
   beforeEach(async () => {
+    events.length = 0;
+    window.addEventListener('vpu-language-changed', handler);
     node = document.createElement('vpu-language-select');
     document.body.appendChild(node);
     await node.updateComplete;
@@ -12,11 +19,21 @@ describe('vpu-language-select basics', () => {
 
   afterEach(() => {
     node.remove();
+    window.removeEventListener('vpu-language-changed', handler);
   });
 
   it('should render', () => {
       expect(node).to.have.property('shadowRoot');
   });
+
+  it('change language events', () => {
+    node.lang = 'en';
+    expect(node.next).to.equal('de');
+    expect(events.length).to.equal(1);
+    node.lang = 'de';
+    expect(node.next).to.equal('en');
+    expect(events.length).to.equal(2);
+  });
 });
 
 describe('vpu-language-select demo', () => {
-- 
GitLab