mirror of
https://github.com/flarum/core.git
synced 2025-07-17 14:51:19 +02:00
[A11Y] Accessibility improvements for the Search component (#3017)
* Remove deprecated code * Accessibility improvements for Search component
This commit is contained in:
@@ -103,14 +103,18 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||||||
|
|
||||||
const searchLabel = extractText(app.translator.trans('core.forum.header.search_placeholder'));
|
const searchLabel = extractText(app.translator.trans('core.forum.header.search_placeholder'));
|
||||||
|
|
||||||
|
const isActive = !!currentSearch;
|
||||||
|
const shouldShowResults = !!(!this.loadingSources && this.state.getValue() && this.hasFocus);
|
||||||
|
const shouldShowClearButton = !!(!this.loadingSources && this.state.getValue());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
role="search"
|
role="search"
|
||||||
className={classList({
|
aria-label={app.translator.trans('core.forum.header.search_role_label')}
|
||||||
Search: true,
|
className={classList('Search', {
|
||||||
open: this.state.getValue() && this.hasFocus,
|
open: this.state.getValue() && this.hasFocus,
|
||||||
focused: this.hasFocus,
|
focused: this.hasFocus,
|
||||||
active: !!currentSearch,
|
active: isActive,
|
||||||
loading: !!this.loadingSources,
|
loading: !!this.loadingSources,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
@@ -125,18 +129,23 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||||||
onfocus={() => (this.hasFocus = true)}
|
onfocus={() => (this.hasFocus = true)}
|
||||||
onblur={() => (this.hasFocus = false)}
|
onblur={() => (this.hasFocus = false)}
|
||||||
/>
|
/>
|
||||||
{this.loadingSources ? (
|
{!!this.loadingSources && <LoadingIndicator size="small" display="inline" containerClassName="Button Button--icon Button--link" />}
|
||||||
<LoadingIndicator size="small" display="inline" containerClassName="Button Button--icon Button--link" />
|
{shouldShowClearButton && (
|
||||||
) : currentSearch ? (
|
<button
|
||||||
<button className="Search-clear Button Button--icon Button--link" onclick={this.clear.bind(this)}>
|
className="Search-clear Button Button--icon Button--link"
|
||||||
|
onclick={this.clear.bind(this)}
|
||||||
|
aria-label={app.translator.trans('core.forum.header.search_clear_button_accessible_label')}
|
||||||
|
>
|
||||||
{icon('fas fa-times-circle')}
|
{icon('fas fa-times-circle')}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ul className="Dropdown-menu Search-results">
|
<ul
|
||||||
{this.state.getValue() && this.hasFocus ? this.sources.map((source) => source.view(this.state.getValue())) : ''}
|
className="Dropdown-menu Search-results"
|
||||||
|
aria-hidden={!shouldShowResults || undefined}
|
||||||
|
aria-live={shouldShowResults ? 'polite' : undefined}
|
||||||
|
>
|
||||||
|
{shouldShowResults && this.sources.map((source) => source.view(this.state.getValue()))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -174,7 +183,7 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||||||
|
|
||||||
this.$('.Search-results')
|
this.$('.Search-results')
|
||||||
.on('mousedown', (e) => e.preventDefault())
|
.on('mousedown', (e) => e.preventDefault())
|
||||||
.on('click', () => this.$('input').blur())
|
.on('click', () => this.$('input').trigger('blur'))
|
||||||
|
|
||||||
// Whenever the mouse is hovered over a search result, highlight it.
|
// Whenever the mouse is hovered over a search result, highlight it.
|
||||||
.on('mouseenter', '> li:not(.Dropdown-header)', function () {
|
.on('mouseenter', '> li:not(.Dropdown-header)', function () {
|
||||||
@@ -223,7 +232,7 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
|||||||
.on('focus', function () {
|
.on('focus', function () {
|
||||||
$(this)
|
$(this)
|
||||||
.one('mouseup', (e) => e.preventDefault())
|
.one('mouseup', (e) => e.preventDefault())
|
||||||
.select();
|
.trigger('select');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.updateMaxHeightHandler = this.updateMaxHeight.bind(this);
|
this.updateMaxHeightHandler = this.updateMaxHeight.bind(this);
|
||||||
|
@@ -1,5 +1,16 @@
|
|||||||
.Search {
|
.Search {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
&-clear {
|
||||||
|
// It looks very weird due to the padding given to the button..
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...so we display the ring around the icon inside the button, with an offset
|
||||||
|
.add-keyboard-focus-ring-nearby("> *");
|
||||||
|
.add-keyboard-focus-ring-nearby-offset("> *", 4px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@media @tablet-up {
|
@media @tablet-up {
|
||||||
.Search {
|
.Search {
|
||||||
|
@@ -130,3 +130,39 @@
|
|||||||
.offset();
|
.offset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This mixin allows support for a custom element nearby the focused one
|
||||||
|
* to have a focus style applied to it
|
||||||
|
*
|
||||||
|
* For example...
|
||||||
|
*
|
||||||
|
*? button { .add-keyboard-focus-ring-nearby("+ .myOtherElement") }
|
||||||
|
* becomes
|
||||||
|
*? button:-moz-focusring + .myOtherElement { <styles> }
|
||||||
|
*? button:focus-within + .myOtherElement { <styles> }
|
||||||
|
*/
|
||||||
|
.add-keyboard-focus-ring-nearby-offset(@nearbySelector, @offset) {
|
||||||
|
@realNearbySelector: ~"@{nearbySelector}";
|
||||||
|
|
||||||
|
.offset() {
|
||||||
|
outline-offset: @offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to declare these separately, otherwise
|
||||||
|
// browsers will ignore `:focus-visible` as they
|
||||||
|
// don't understand `:-moz-focusring`
|
||||||
|
|
||||||
|
// These are the keyboard-only versions of :focus
|
||||||
|
&:-moz-focusring {
|
||||||
|
@{realNearbySelector} {
|
||||||
|
.offset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@{realNearbySelector} {
|
||||||
|
.offset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -348,7 +348,9 @@ core:
|
|||||||
log_in_link: => core.ref.log_in
|
log_in_link: => core.ref.log_in
|
||||||
log_out_button: => core.ref.log_out
|
log_out_button: => core.ref.log_out
|
||||||
profile_button: Profile
|
profile_button: Profile
|
||||||
|
search_clear_button_accessible_label: Clear search query
|
||||||
search_placeholder: Search Forum
|
search_placeholder: Search Forum
|
||||||
|
search_role_label: Search Forum
|
||||||
session_dropdown_accessible_label: Toggle session options dropdown menu
|
session_dropdown_accessible_label: Toggle session options dropdown menu
|
||||||
settings_button: => core.ref.settings
|
settings_button: => core.ref.settings
|
||||||
sign_up_link: => core.ref.sign_up
|
sign_up_link: => core.ref.sign_up
|
||||||
|
Reference in New Issue
Block a user