From 242fef400de0bbbe0ade01c8642793d1b9f15799 Mon Sep 17 00:00:00 2001 From: Mikko Ahlroth Date: Thu, 4 Jul 2019 09:23:10 +0300 Subject: [PATCH] Improve budget joining view and add screen reader compatibility --- frontend/src/components/budget-join.ts | 43 ++++++++++++++++----- frontend/src/components/budget-qr-reader.ts | 29 ++++++++++++-- frontend/src/components/icon.ts | 13 +------ frontend/src/components/styles.ts | 15 +++++++ 4 files changed, 77 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/budget-join.ts b/frontend/src/components/budget-join.ts index b15f8a5..2739a94 100644 --- a/frontend/src/components/budget-join.ts +++ b/frontend/src/components/budget-join.ts @@ -1,12 +1,15 @@ import { LitElement, html } from 'lit-element' -import { InputField } from './input'; -import { QRReadEvent } from './budget-qr-reader'; +import { InputField } from './input' +import './budget-qr-reader' +import { QRReadEvent } from './budget-qr-reader' +import { ERROR_TIMER } from '../config' export class JoinBudgetEvent extends CustomEvent<{ uuid: string }> { } class BudgetJoinComponent extends LitElement { hasError: boolean = false showQRReader: boolean = false + errorTimer: number = null static get properties() { return { @@ -15,9 +18,30 @@ class BudgetJoinComponent extends LitElement { } } + firstUpdated() { + const secretInputEl = this.shadowRoot.getElementById('uuid') + secretInputEl.focusInput() + } + + updated(changedProperties: Map) { + // If error was turned on, turn it off after a moment + if (changedProperties.has('hasError')) { + if (this.errorTimer != null) { + window.clearTimeout(this.errorTimer) + } + + this.errorTimer = window.setTimeout(() => { + this.hasError = false + this.errorTimer = null + }, ERROR_TIMER * 1000) + } + + return super.updated(changedProperties) + } + onJoin(e: Event) { e.preventDefault() - const inputEl = this.shadowRoot.getElementById('join-budget-uuid') + const inputEl = this.shadowRoot.getElementById('uuid') this.dispatchEvent(new JoinBudgetEvent( 'joinBudget', @@ -42,12 +66,10 @@ class BudgetJoinComponent extends LitElement { render() { return html` -

Add below the budget secret that you can get from the person who created the budget.

- +
${this.showQRReader ? html`` : null}
- ${this.hasError ? html`

Could not add given budget, please check it is correctly typed.

` : null} + ${this.hasError ? html`

Could not add given budget, please check it is correctly typed.

` : null} ` } } diff --git a/frontend/src/components/budget-qr-reader.ts b/frontend/src/components/budget-qr-reader.ts index 150ea62..7c49858 100644 --- a/frontend/src/components/budget-qr-reader.ts +++ b/frontend/src/components/budget-qr-reader.ts @@ -1,5 +1,6 @@ -import { LitElement, html } from 'lit-element' +import { LitElement, html, css } from 'lit-element' import { BrowserQRCodeReader, VideoInputDevice } from '../vendor/zxing-typescript/src/browser/BrowserQRCodeReader' +import { SR_ONLY } from './styles' export class QRReadEvent extends CustomEvent<{ text: string }> { } @@ -9,16 +10,26 @@ class BudgetQRReaderComponent extends LitElement { reader: BrowserQRCodeReader inputs: VideoInputDevice[] currentInput: string + currentInputLabel: string test: string + scanActive: boolean static get properties() { return { inputs: { type: Array }, currentInput: { type: String }, - test: { type: String } + currentInputLabel: { type: String }, + test: { type: String }, + scanActive: { type: Boolean } } } + static get styles() { + return css` + ${SR_ONLY} + ` + } + constructor() { super() @@ -31,6 +42,7 @@ class BudgetQRReaderComponent extends LitElement { this.inputs = await this.reader.getVideoInputDevices() if (this.inputs.length > 0) { this.currentInput = this.inputs[0].deviceId + this.currentInputLabel = this.inputs[0].label this.startRead() } } @@ -54,6 +66,7 @@ class BudgetQRReaderComponent extends LitElement { let found = false do { + this.scanActive = true const result = await this.reader.decodeFromInputVideoDevice(this.currentInput, videoEl) const text = result.getText() if (this._isUUID(text)) { @@ -67,13 +80,23 @@ class BudgetQRReaderComponent extends LitElement { } stopRead() { + this.scanActive = false this.reader.reset() } + inputChanged() { + const inputEl = this.shadowRoot.getElementById('input') + this.currentInput = inputEl.value + this.currentInputLabel = inputEl.options[inputEl.selectedIndex].label + this.stopRead() + this.startRead() + } + render() { return html` + ${this.scanActive ? html`` : null} - ${this.inputs.map(input => this.renderVideoInput(input))} ` diff --git a/frontend/src/components/icon.ts b/frontend/src/components/icon.ts index cf92984..21391f9 100644 --- a/frontend/src/components/icon.ts +++ b/frontend/src/components/icon.ts @@ -1,4 +1,5 @@ import { LitElement, html, css, unsafeCSS } from 'lit-element' +import { SR_ONLY } from './styles' /** * An accessible Font Awesome icon. Normally FA icons are not read by screen readers, leading to bad @@ -54,17 +55,7 @@ function icon2css(name: string, code: string) { } const FA_CSS = css` - /* From https://webaim.org/techniques/css/invisiblecontent/ */ - .sr-only { - clip: rect(1px, 1px, 1px, 1px); - clip-path: inset(50%); - height: 1px; - width: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - } + ${SR_ONLY} .fa, .fas, diff --git a/frontend/src/components/styles.ts b/frontend/src/components/styles.ts index 5342000..6e3f866 100644 --- a/frontend/src/components/styles.ts +++ b/frontend/src/components/styles.ts @@ -4,3 +4,18 @@ import { css } from 'lit-element' * Breakpoint media query to use for mobile layouts. */ export const MOB_BP = css`@media (max-width: 768px)` + +/** Show something only for screen readers. */ +export const SR_ONLY = css` + /* From https://webaim.org/techniques/css/invisiblecontent/ */ + .sr-only { + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(50%); + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + } +`