Skip to content

Commit

Permalink
fix: make virtual keyboard work better on touchscreen devices (#2147)
Browse files Browse the repository at this point in the history
  • Loading branch information
kantord authored Apr 14, 2022
1 parent 867aedb commit 03b3cd7
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 15 deletions.
82 changes: 82 additions & 0 deletions apps/lluis/HorizontalScroller.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<script>
import { onMount } from "svelte"
import Icon from "./Icon.svelte"

let scroller
let scrollPosition = 0
let scrollWidth = 200
let containerWidth = 100
let needsScroll = scrollWidth > containerWidth

const updateScrollPosition = () => {
scrollPosition = scroller.scrollLeft
containerWidth = scroller.offsetWidth
scrollWidth = scroller.scrollWidth - containerWidth
}

onMount(() => {
window.addEventListener("resize", updateScrollPosition)
updateScrollPosition()

return () => {
window.removeEventListener("resize", updateScrollPosition)
}
})
</script>

<div class="wrapper">
<div class="content" bind:this={scroller} on:scroll={updateScrollPosition}>
<slot />
</div>

{#if scrollPosition > 0}
<div class="shadow-left"><Icon icon="circle-arrow-left" /></div>
{/if}

{#if needsScroll && scrollPosition < scrollWidth}
<div class="shadow-right"><Icon icon="circle-arrow-right" /></div>
{/if}
</div>

<style type="text/scss">
.wrapper {
width: 100%;
height: 100%;
position: relative;
}

.content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow-x: auto;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}

.shadow-right,
.shadow-left {
display: flex;
position: absolute;
width: 24px;
height: 48px;
top: 0;
bottom: 0;
align-items: center;
bottom: calc(100vh - 68px);
}

.shadow-left {
left: 0;
background: linear-gradient(270deg, transparent, white, white);
}

.shadow-right {
right: 0;
background: linear-gradient(90deg, transparent, white, white);
}
</style>
14 changes: 10 additions & 4 deletions apps/web/cypress/integration/common/virtual_keyboard.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import { Then } from "cypress-cucumber-preprocessor/steps"

Then("I see a virtual keyboard with {int} keys", (n) => {
cy.get(".virtual-keyboard").find(">*").should("have.length", n)
cy.get(".virtual-keyboard .keys").find(">*").should("have.length", n)
})

Then("the keys on the virtual keyboard have proper labels", () => {
cy.get(".virtual-keyboard").find(">*").contains("á").should("be.visible")
cy.get(".virtual-keyboard .keys")
.find(">*")
.contains("á")
.should("be.visible")
})

Then("clicking on a key types into the input field", () => {
cy.get(".virtual-keyboard").find(">*").contains("á").click()
cy.get(".virtual-keyboard .keys").find(">*").contains("á").click()
cy.get("input").should("have.value", "á")
})

Then("the virtual keyboard is inactive", () => {
cy.get(".virtual-keyboard").find(">*").contains("á").should("be.disabled")
cy.get(".virtual-keyboard .keys")
.find(">*")
.contains("á")
.should("be.disabled")
})
4 changes: 4 additions & 0 deletions apps/web/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
faEnvelope,
faHeart,
faSpinner,
faCircleArrowRight,
faCircleArrowLeft,
} from "@fortawesome/free-solid-svg-icons"
import { faTwitter } from "@fortawesome/free-brands-svg-icons"

Expand All @@ -25,6 +27,8 @@ library.add(faLock)
library.add(faEnvelope)
library.add(faHeart)
library.add(faSpinner)
library.add(faCircleArrowRight)
library.add(faCircleArrowLeft)
dom.watch()

window.startMsw = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,62 @@
<script lang="typescript">
// Manual test: http://localhost:3000/course/test/skill/short-input-test-3?testChallenge=86665e4f61fa
import Button from "../../../../lluis/Button.svelte"
import HorizontalScroller from "../../../../lluis/HorizontalScroller.svelte"

export let characters: Array<string>
export let disabled: boolean
export let handleVirtualKey
</script>

<div class="virtual-keyboard">
{#each characters as character}
<Button
style="key"
tabIndex={-1}
size="small"
disabled="{disabled}"
on:click="{handleVirtualKey(character)}"
>
{character}
</Button>
{/each}
<HorizontalScroller>
<slot>
<div class="keys">
{#each characters as character}
<Button
style="key"
tabIndex={-1}
size="small"
{disabled}
on:click={handleVirtualKey(character)}
>
{character}
</Button>
{/each}
</div>
</slot>
</HorizontalScroller>
</div>

<style type="text/scss">
.virtual-keyboard {
display: flex;
flex-wrap: wrap;
margin-top: 2em;
height: 100%;
}

.virtual-keyboard .keys {
display: flex;
flex-wrap: wrap;
}

@media only screen and (pointer: coarse) {
.virtual-keyboard .keys {
display: flex;
flex-wrap: nowrap;
padding-left: 12px;
padding-right: 12px;
}

.virtual-keyboard {
height: 48px;
position: fixed;
bottom: 68px;
left: 0;
right: 0;
background-color: white;
z-index: 100;
}
}
</style>

0 comments on commit 03b3cd7

Please sign in to comment.