fix: use radiogroup for instance switcher (#1634)

* fix: use radiogroup for instance switcher

progress on #1633

* fixup

* add unique id
This commit is contained in:
Nolan Lawson 2019-11-17 20:51:28 -05:00 committed by GitHub
parent 53b6c5f6a1
commit 92d77c34be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 25 deletions

View File

@ -0,0 +1,43 @@
<!-- Modeled after https://www.w3.org/TR/2016/WD-wai-aria-practices-1.1-20160317/examples/radio/radio.html -->
<div class="radio-group focus-fix {className}"
role="radiogroup"
aria-label={label}
aria-owns={ariaOwns}
on:keydown="onKeyDown(event)"
ref:radiogroup
>
<slot></slot>
</div>
<script>
import { times } from '../../_utils/lodash-lite'
export default {
methods: {
onKeyDown (e) {
// ArrowUp and ArrowDown should change the focused/checked radio button per
// https://www.w3.org/TR/2016/WD-wai-aria-practices-1.1-20160317/examples/radio/radio.html
const { key, target } = e
if (!['ArrowUp', 'ArrowDown'].includes(key)) {
return
}
if (!target.classList.contains('radio-group-button')) {
return
}
const buttons = Array.from(this.refs.radiogroup.getElementsByClassName('radio-group-button'))
const len = buttons.length
const index = Math.max(0, buttons.findIndex(button => button.getAttribute('aria-checked') === 'true'))
const newIndex = (len + (index + (key === 'ArrowUp' ? -1 : 1))) % len // increment/decrement and wrap around
buttons[newIndex].focus()
buttons[newIndex].click()
}
},
data: () => ({
className: ''
}),
computed: {
ariaOwns: ({ length, id }) => (
times(length, index => `radio-group-button-${id}-${index}`).join(' ')
)
}
}
</script>

View File

@ -0,0 +1,17 @@
<button id="radio-group-button-{id}-{index}"
class="radio-group-button {className}"
role="radio"
aria-label={label}
title={label}
aria-checked={checked}
on:click
>
<slot></slot>
</button>
<script>
export default {
data: () => ({
className: ''
})
}
</script>

View File

@ -3,26 +3,30 @@
{#if $isUserLoggedIn}
<p>Instances you've logged in to:</p>
<SettingsList label="Instances">
{#each instanceStates as instance}
<SettingsListRow>
<SettingsListButton className="instance-switcher-instance-name"
href="/settings/instances/{instance.name}"
label={instance.name}
ariaLabel={instance.label} />
<div class="instance-switcher-button-wrapper">
<button class="instance-switcher-button"
aria-label={instance.switchLabel}
title={instance.switchLabel}
aria-pressed={instance.current}
on:click="onSwitchToThisInstance(event, instance.name)">
<SvgIcon className="instance-switcher-button-svg"
href={instance.current ? '#fa-star' : '#fa-star-o'} />
</button>
</div>
</SettingsListRow>
{/each}
</SettingsList>
<RadioGroup id="instance-switch" label="Switch to instance" length={numInstances}>
<SettingsList label="Instances">
{#each instanceStates as instance}
<SettingsListRow>
<SettingsListButton className="instance-switcher-instance-name"
href="/settings/instances/{instance.name}"
label={instance.name}
ariaLabel={instance.label} />
<div class="instance-switcher-button-wrapper">
<RadioGroupButton
id="instance-switch"
className="instance-switcher-button"
label={instance.switchLabel}
checked={instance.current}
index={instance.index}
on:click="onSwitchToThisInstance(event, instance.name)">
<SvgIcon className="instance-switcher-button-svg"
href={instance.current ? '#fa-star' : '#fa-star-o'} />
</RadioGroupButton>
</div>
</SettingsListRow>
{/each}
</SettingsList>
</RadioGroup>
<p><a rel="prefetch" href="/settings/instances/add">Add another instance</a></p>
{:else}
<p>You're not logged in to any instances.</p>
@ -40,7 +44,7 @@
border: 1px solid var(--settings-list-item-border);
min-width: 44px;
}
.instance-switcher-button {
:global(.instance-switcher-button) {
display: flex;
position: absolute;
top: 0;
@ -69,6 +73,8 @@
import SettingsListRow from '../../../_components/settings/SettingsListRow.html'
import SettingsListButton from '../../../_components/settings/SettingsListButton.html'
import SvgIcon from '../../../_components/SvgIcon.html'
import RadioGroup from '../../../_components/radio/RadioGroup.html'
import RadioGroupButton from '../../../_components/radio/RadioGroupButton.html'
export default {
components: {
@ -76,7 +82,9 @@
SettingsList,
SettingsListRow,
SettingsListButton,
SvgIcon
SvgIcon,
RadioGroup,
RadioGroupButton
},
methods: {
onSwitchToThisInstance (e, instanceName) {
@ -88,13 +96,15 @@
store: () => store,
computed: {
instanceStates: ({ $loggedInInstancesAsList }) => (
$loggedInInstancesAsList.map(({ name, current }) => ({
$loggedInInstancesAsList.map(({ name, current }, index) => ({
index,
name,
current,
label: `${name} ${current ? '(current instance)' : ''}`,
switchLabel: current ? `${name} is the current instance` : `Switch to ${name}`
switchLabel: `Switch to ${name}`
}))
)
),
numInstances: ({ $loggedInInstancesAsList }) => $loggedInInstancesAsList.length
}
}
</script>

View File

@ -36,3 +36,11 @@ export function sum (list) {
}
return total
}
export function times (n, func) {
const res = []
for (let i = 0; i < n; i++) {
res.push(func(i))
}
return res
}