Fix search typeahead accessibility

pull/6449/head
Chocobozzz 2024-06-10 16:20:50 +02:00
parent 9c215124d1
commit 44eedafed4
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
6 changed files with 49 additions and 69 deletions

View File

@ -2,7 +2,7 @@
<input
type="search" id="search-video" name="search-video" #searchVideo i18n-placeholder placeholder="Search videos, playlists, channels…"
[(ngModel)]="search" (ngModelChange)="onSearchChange()" (keydown)="handleKey($event)"
autocomplete="off"
autocomplete="off" aria-describedby="typeahead-input-help"
>
<button class="border-0 search-button" title="Search" i18n-title (click)="doSearch()">
@ -17,40 +17,38 @@
role="option" aria-selected="true" tabindex="0"
(mouseenter)="onSuggestionHover(i)" (click)="onSuggestionClicked(result)" (keyup.enter)="onSuggestionClicked(result)"
>
<my-suggestion [result]="result" [highlight]="search"></my-suggestion>
<my-suggestion
[result]="result" [highlight]="search"
[describedby]="showSearchGlobalHelp() ? 'typeahead-help' : undefined"
></my-suggestion>
</li>
</ul>
<!-- suggestion help, not shown until one of the suggestions is selected and specific to that suggestion -->
<div *ngIf="showSearchGlobalHelp()" id="typeahead-help" class="overflow-hidden">
<div *ngIf="showSearchGlobalHelp()" id="typeahead-suggestion-help" class="overflow-hidden">
<div class="d-flex justify-content-between">
<div class="small-title" i18n>GLOBAL SEARCH</div>
<div class="advanced-search-status muted">
<span *ngIf="serverConfig" class="me-1" i18n>using {{ serverConfig.search.searchIndex.url }}</span>
</div>
<div class="small-title mb-2" i18n>GLOBAL SEARCH</div>
<span *ngIf="serverConfig" class="muted" i18n>using {{ serverConfig.search.searchIndex.url }}</span>
</div>
<div class="muted" i18n>Results will be augmented with those of a third-party index. Only data necessary to make the query will be sent.</div>
</div>
<!-- search instructions, when search input is empty -->
<div *ngIf="areInstructionsDisplayed()" id="typeahead-instructions" class="overflow-hidden">
<div [hidden]="this.search" id="typeahead-input-help" class="overflow-hidden">
<span class="muted" i18n>Your query will be matched against video names or descriptions, channel names.</span>
<div class="d-flex justify-content-between mt-3">
<div class="small-title" i18n>ADVANCED SEARCH</div>
<div class="advanced-search-status c-help">
<span [ngClass]="canSearchAnyURI ? 'text-success' : 'muted'" i18n-title title="Determines whether you can resolve any distant content, or if this instance only allows doing so for instances it follows.">
<span *ngIf="canSearchAnyURI()" class="me-1" i18n>any instance</span>
<span *ngIf="!canSearchAnyURI()" class="me-1" i18n>only followed instances</span>
</span>
</div>
</div>
<div class="mt-3 mb-2 small-title" i18n>ADVANCED SEARCH</div>
<ul>
<li>
<em>&#64;channel_id&#64;domain</em> <span class="flex-auto muted" i18n>will list the matching channel</span>
</li>
<li>
<em>URL</em> <span class="muted" i18n>will list the matching channel</span>
</li>
<li>
<em>UUID</em> <span class="muted" i18n>will list the matching video</span>
</li>

View File

@ -37,27 +37,25 @@
width: 100%;
}
#typeahead-help,
#typeahead-instructions,
li.suggestion {
#typeahead-suggestion-help,
#typeahead-input-help,
.suggestion {
border: 1px solid pvar(--mainBackgroundColor);
background: pvar(--mainBackgroundColor);
transition: .3s ease;
transition-property: box-shadow;
cursor: pointer;
// soft border-radius for the last suggestion and the link inside
&:last-of-type {
&,
::ng-deep a {
border-bottom-right-radius: 3px;
border-bottom-left-radius: 3px;
}
}
}
#typeahead-help,
#typeahead-instructions {
#typeahead-suggestion-help,
#typeahead-input-help,
.suggestion:last-of-type {
border-bottom-right-radius: 3px;
border-bottom-left-radius: 3px;
}
#typeahead-suggestion-help,
#typeahead-input-help {
margin-top: 9px;
width: 100%;
padding: .5rem 1rem;
@ -110,21 +108,20 @@ li.suggestion {
display: none;
}
&:focus,
::ng-deep &:focus-within {
&:focus-within {
> div:last-child {
@media screen and (min-width: $mobile-view) {
display: initial !important;
}
#typeahead-help,
#typeahead-instructions,
li.suggestion {
#typeahead-suggestion-help,
#typeahead-input-help,
.suggestion {
box-shadow: rgba(0, 0, 0, 0.2) 0 10px 20px -5px;
}
}
::ng-deep input {
input {
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 20px 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
@ -136,21 +133,10 @@ li.suggestion {
}
}
.advanced-search-status {
height: max-content;
cursor: default;
&.c-help {
cursor: help;
}
}
.small-title {
@include in-content-small-title;
margin-bottom: .5rem;
}
::ng-deep my-suggestion {
my-suggestion {
width: 100%;
}

View File

@ -70,10 +70,6 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewChecked, OnDes
if (this.keyboardEventsManager) this.keyboardEventsManager.change.unsubscribe()
}
areInstructionsDisplayed () {
return !this.search
}
showSearchGlobalHelp () {
return this.search && this.areSuggestionsOpened && this.keyboardEventsManager?.activeItem?.result?.type === 'search-index'
}

View File

@ -1,18 +1,18 @@
<a tabindex="-1" class="d-flex flex-auto flex-items-center p-2" [class.focus-visible]="active">
<div class="flex-shrink-0 me-2 text-center">
<my-global-icon iconName="search"></my-global-icon>
</div>
<img class="avatar me-2 flex-shrink-0 d-none" alt="" aria-label="Team" src="" width="28" height="28">
<a
tabindex="-1" class="d-flex flex-auto align-center p-2" [class.focus-visible]="active"
[title]="getTitle()"
[attr.aria-describedby]="describedby"
>
<my-global-icon class="me-2" iconName="search"></my-global-icon>
<div
class="flex-auto overflow-hidden text-start no-wrap css-truncate css-truncate-target"
class="flex-auto overflow-hidden no-wrap d-flex align-center"
[attr.aria-label]="result.text"
>
{{ result.text }}
</div>
<div class="border rounded flex-shrink-0 px-1 bg-gray text-gray-light ms-1 f6">
<div class="result-type border rounded flex-shrink-0 px-1 ms-1 f6">
<span *ngIf="result.type === 'search-instance'" i18n>In this instance's network</span>
<span *ngIf="result.type === 'search-index'" i18n>In the vidiverse</span>
</div>

View File

@ -17,11 +17,8 @@ a {
}
}
.bg-gray {
.result-type {
background-color: pvar(--mainBackgroundColor);
}
.text-gray-light {
color: pvar(--mainForegroundColor);
}
@ -30,6 +27,5 @@ my-global-icon {
width: 17px;
position: relative;
top: -2px;
margin: 5px;
top: -1px;
}

View File

@ -23,12 +23,16 @@ export type SuggestionPayloadType = 'search-instance' | 'search-index'
export class SuggestionComponent implements OnInit, ListKeyManagerOption {
@Input() result: SuggestionPayload
@Input() highlight: string
@Input() describedby: string
disabled = false
active = false
getLabel () {
return this.result.text
getTitle () {
if (this.result.type === 'search-instance') return $localize`Search "${this.result.text}" in this instance's network`
if (this.result.type === 'search-index') return $localize`Search "${this.result.text}" in the vidiverse`
return undefined
}
ngOnInit () {