Improve accessibility of Channel View (#2984)

* Improve channel info bar

* Reduce width of channel search bar

* fix sizing

* improve channel view accessibility

Co-Authored-By: Jason <84899178+jasonhenriquez@users.noreply.github.com>

* Update src/renderer/components/ft-channel-bubble/ft-channel-bubble.js

Co-authored-by: PikachuEXE <pikachuexe@gmail.com>

* Stop space from clicking channel-bubble (links)

Co-authored-by: vallode <18506096+vallode@users.noreply.github.com>
Co-authored-by: Jason <84899178+jasonhenriquez@users.noreply.github.com>
Co-authored-by: PikachuEXE <pikachuexe@gmail.com>
This commit is contained in:
ChunkyProgrammer 2023-01-04 22:54:20 -05:00 committed by GitHub
parent 156176aca8
commit bff8dc4326
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 165 additions and 55 deletions

View File

@ -1,4 +1,5 @@
import Vue from 'vue'
import { sanitizeForHtmlId } from '../../helpers/accessibility'
export default Vue.extend({
name: 'FtChannelBubble',
@ -21,8 +22,20 @@ export default Vue.extend({
selected: false
}
},
computed: {
sanitizedId: function() {
return 'channelBubble' + sanitizeForHtmlId(this.channelName)
}
},
methods: {
handleClick: function () {
handleClick: function (event) {
if (event instanceof KeyboardEvent) {
if (event.target.getAttribute('role') === 'link' && event.key !== 'Enter') {
return
}
event.preventDefault()
}
if (this.showSelected) {
this.selected = !this.selected
}

View File

@ -1,7 +1,10 @@
<template>
<div
class="bubblePadding"
tabindex="0"
:aria-labelledby="sanitizedId"
@click="handleClick"
@keydown.space.enter.prevent="handleClick($event)"
>
<img
class="bubble"
@ -16,7 +19,10 @@
class="icon"
/>
</div>
<div class="channelName">
<div
:id="sanitizedId"
class="channelName"
>
{{ channelName }}
</div>
</div>

View File

@ -6,7 +6,7 @@
}
.channelDetails {
padding: 0 0 16px;
padding: 0;
}
.channelBannerContainer {
@ -75,38 +75,51 @@
.channelSearch {
width: 220px;
margin-left: auto;
align-self: flex-end;
flex: 1 1 0%;
}
.sortSelect {
align-self: flex-end;
margin-left: auto;
}
.channelInfoTabs {
position: relative;
width: 100%;
margin-top: -16px;
margin-bottom: -13px;
height: auto;
justify-content: unset;
gap: 32px;
padding: .3em 0;
}
.tabs {
display: flex;
flex: 0 1 33%;
}
.tab {
padding: 15px;
font-size: 15px;
cursor: pointer;
flex: 1 1 0%;
align-self: flex-end;
text-align: center;
color: var(--tertiary-text-color);
}
.selectedTab {
color: var(--primary-text-color);
border-bottom: 3px solid var(--primary-color);
margin-bottom: -3px;
font-weight: bold;
box-sizing: border-box;
border-bottom: 3px solid transparent;
}
.tab:hover {
font-weight: bold;
border-bottom: 3px solid var(--tertiary-text-color);
}
.selectedTab,
.selectedTab:hover {
color: var(--primary-text-color);
border-bottom: 3px solid var(--primary-color);
font-weight: bold;
box-sizing: border-box;
}
.aboutTab {
@ -125,10 +138,7 @@
.channelSearch {
margin-top: 10px;
}
.elementList {
margin-top: 15px;
max-width: 250px;
}
.elementListLoading {
@ -164,3 +174,15 @@
.channelLineContainer p {
margin: 0;
}
@media only screen and (max-width: 800px) {
.channelInfoTabs {
height: auto;
flex-flow: column-reverse;
}
.channelSearch {
width: 100%;
max-width: none;
}
}

View File

@ -68,6 +68,11 @@ export default Vue.extend({
playlistSelectValues: [
'last',
'newest'
],
tabInfoValues: [
'videos',
'playlists',
'about'
]
}
},
@ -652,8 +657,34 @@ export default Vue.extend({
}
},
changeTab: function (tab) {
changeTab: function (tab, event) {
if (event instanceof KeyboardEvent) {
// use arrowkeys to navigate
event.preventDefault()
if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
const index = this.tabInfoValues.indexOf(tab)
// focus left or right tab with wrap around
tab = (event.key === 'ArrowLeft')
? this.tabInfoValues[(index > 0 ? index : this.tabInfoValues.length) - 1]
: this.tabInfoValues[(index + 1) % this.tabInfoValues.length]
const tabNode = document.getElementById(`${tab}Tab`)
event.target.setAttribute('tabindex', '-1')
tabNode.setAttribute('tabindex', 0)
tabNode.focus({ focusVisible: true })
return
}
}
const currentTabNode = document.querySelector('.tabs > .tab[aria-selected="true"]')
const newTabNode = document.getElementById(`${tab}Tab`)
document.querySelector('.tabs > .tab[tabindex="0"]').setAttribute('tabindex', '-1')
newTabNode.setAttribute('tabindex', '0')
currentTabNode.setAttribute('aria-selected', 'false')
newTabNode.setAttribute('aria-selected', 'true')
this.currentTab = tab
newTabNode.focus({ focusVisible: true })
},
newSearch: function (query) {

View File

@ -30,6 +30,7 @@
<img
class="channelThumbnail"
:src="thumbnailUrl"
alt=""
>
<div
class="channelLineContainer"
@ -74,50 +75,57 @@
class="channelInfoTabs"
>
<div
class="tab"
:class="(currentTab==='videos')?'selectedTab':''"
@click="changeTab('videos')"
class="tabs"
role="tablist"
:aria-label="$t('Channel.Channel Tabs')"
>
{{ $t("Channel.Videos.Videos").toUpperCase() }}
</div>
<div
class="tab"
:class="(currentTab==='playlists')?'selectedTab':''"
@click="changeTab('playlists')"
>
{{ $t("Channel.Playlists.Playlists").toUpperCase() }}
</div>
<div
class="tab"
:class="(currentTab==='about')?'selectedTab':''"
@click="changeTab('about')"
>
{{ $t("Channel.About.About").toUpperCase() }}
<div
id="videosTab"
class="tab"
:class="(currentTab==='videos')?'selectedTab':''"
role="tab"
aria-selected="true"
aria-controls="videoPanel"
tabindex="0"
@click="changeTab('videos')"
@keydown.left.right.enter.space="changeTab('videos', $event)"
>
{{ $t("Channel.Videos.Videos").toUpperCase() }}
</div>
<div
id="playlistsTab"
class="tab"
role="tab"
aria-selected="false"
aria-controls="playlistPanel"
tabindex="-1"
:class="(currentTab==='playlists')?'selectedTab':''"
@click="changeTab('playlists')"
@keydown.left.right.enter.space="changeTab('playlists', $event)"
>
{{ $t("Channel.Playlists.Playlists").toUpperCase() }}
</div>
<div
id="aboutTab"
class="tab"
role="tab"
aria-selected="false"
aria-controls="aboutPanel"
tabindex="-1"
:class="(currentTab==='about')?'selectedTab':''"
@click="changeTab('about')"
@keydown.left.right.enter.space="changeTab('about', $event)"
>
{{ $t("Channel.About.About").toUpperCase() }}
</div>
</div>
<ft-input
:placeholder="$t('Channel.Search Channel')"
:show-clear-text-button="true"
class="channelSearch"
@click="newSearch"
/>
<ft-select
v-show="currentTab === 'videos'"
class="sortSelect"
:value="videoSelectValues[0]"
:select-names="videoSelectNames"
:select-values="videoSelectValues"
:placeholder="$t('Search Filters.Sort By.Sort By')"
@change="videoSortBy = $event"
/>
<ft-select
v-show="currentTab === 'playlists'"
class="sortSelect"
:value="playlistSelectValues[0]"
:select-names="playlistSelectNames"
:select-values="playlistSelectValues"
:placeholder="$t('Search Filters.Sort By.Sort By')"
@change="playlistSortBy = $event"
/>
</ft-flex-box>
</div>
</ft-card>
@ -127,6 +135,7 @@
>
<div
v-if="currentTab === 'about'"
id="aboutPanel"
class="aboutTab"
>
<h2>
@ -151,10 +160,29 @@
:channel-name="channel.author || channel.channelName"
:channel-id="channel.channelId"
:channel-thumbnail="channel.authorThumbnails[channel.authorThumbnails.length - 1].url"
role="link"
@click="goToChannel(channel.channelId)"
/>
</ft-flex-box>
</div>
<ft-select
v-show="currentTab === 'videos'"
class="sortSelect"
:value="videoSelectValues[0]"
:select-names="videoSelectNames"
:select-values="videoSelectValues"
:placeholder="$t('Search Filters.Sort By.Sort By')"
@change="videoSortBy = $event"
/>
<ft-select
v-show="currentTab === 'playlists'"
class="sortSelect"
:value="playlistSelectValues[0]"
:select-names="playlistSelectNames"
:select-values="playlistSelectValues"
:placeholder="$t('Search Filters.Sort By.Sort By')"
@change="playlistSortBy = $event"
/>
<ft-loader
v-if="isElementListLoading"
/>
@ -164,7 +192,10 @@
>
<ft-element-list
v-show="currentTab === 'videos'"
id="videoPanel"
:data="latestVideos"
role="tabpanel"
aria-labelledby="videosTab"
/>
<ft-flex-box
v-if="currentTab === 'videos' && latestVideos.length === 0"
@ -175,7 +206,10 @@
</ft-flex-box>
<ft-element-list
v-show="currentTab === 'playlists'"
id="playlistPanel"
:data="latestPlaylists"
role="tabpanel"
aria-labelledby="playlistsTab"
/>
<ft-flex-box
v-if="currentTab === 'playlists' && latestPlaylists.length === 0"
@ -198,7 +232,11 @@
<div
v-if="showFetchMoreButton"
class="getNextPage"
role="button"
tabindex="0"
@click="handleFetchMore"
@keydown.space.prevent="handleFetchMore"
@keydown.enter.prevent="handleFetchMore"
>
<font-awesome-icon :icon="['fas', 'search']" /> {{ $t("Search Filters.Fetch more results") }}
</div>