Improve budget joining view and add screen reader compatibility

This commit is contained in:
Mikko Ahlroth 2019-07-04 09:23:10 +03:00
parent 1d10c9d9f7
commit 242fef400d
4 changed files with 77 additions and 23 deletions

View file

@ -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 = <InputField>this.shadowRoot.getElementById('uuid')
secretInputEl.focusInput()
}
updated(changedProperties: Map<string | number | symbol, any>) {
// 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 = <InputField>this.shadowRoot.getElementById('join-budget-uuid')
const inputEl = <InputField>this.shadowRoot.getElementById('uuid')
this.dispatchEvent(new JoinBudgetEvent(
'joinBudget',
@ -42,12 +66,10 @@ class BudgetJoinComponent extends LitElement {
render() {
return html`
<p>Add below the budget secret that you can get from the person who created the budget.</p>
<input-field
id="join-budget-uuid"
id="uuid"
type="text"
label="Budget secret"
placeholder="aaaaaaaa-bbbb-4ccc-9ddd-ffffffffffff"
label="Budget secret (ask someone to share theirs)"
maxlength="36"
pattern="[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}"
required
@ -56,14 +78,17 @@ class BudgetJoinComponent extends LitElement {
<fa-icon name="plus" alt="Join budget"></fa-icon>
</fa-button>
<fa-button type="button" @click=${this.toggleShowQRReader}>
<fa-icon name="qrcode" alt="Read QR using camera"></fa-icon>
<fa-icon
name="qrcode"
alt="${(!this.showQRReader) ? 'Read QR using camera' : 'Stop reading QR'}"
></fa-icon>
</fa-button>
<div>
${this.showQRReader ? html`<budget-qr-reader @qrRead=${this.onQRRead}></budget-qr-reader>` : null}
</div>
${this.hasError ? html`<p>Could not add given budget, please check it is correctly typed.</p>` : null}
${this.hasError ? html`<p role="alert">Could not add given budget, please check it is correctly typed.</p>` : null}
`
}
}

View file

@ -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 = <HTMLSelectElement>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`<p role="alert" class="sr-only">Now scanning for QR codes with device ${this.currentInputLabel}.</p>` : null}
<video id="video" width="300" height="200" style="border: 1px solid gray"></video>
<select id="qr-reader-input">
<select id="input" @change="${this.inputChanged}">
${this.inputs.map(input => this.renderVideoInput(input))}
</select>
`

View file

@ -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,

View file

@ -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;
}
`