Skip to content

Commit

Permalink
feat(a11y): Redesign searchbox to include label
Browse files Browse the repository at this point in the history
  • Loading branch information
sdrozdsap committed Sep 10, 2024
1 parent 7f8e2bd commit 6c5b71e
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ export interface FeatureTogglesInterface {
*/
a11yUnitsListKeyboardControls?: boolean;

/**
* Adds label to the `SearchBoxComponent` search input
*/
a11ySearchboxLabel?: boolean;

/**
* When set to `true`, product titles in `CartItemComponent`, `QuickOrderItemComponent`, `WishListItemComponent`
* adopt a more link-like style, appearing blue with an underline. This enhances visual cues for clickable elements,
Expand Down Expand Up @@ -544,6 +549,7 @@ export const defaultFeatureToggles: Required<FeatureTogglesInterface> = {
a11ySearchBoxMobileFocus: false,
a11yFacetKeyboardNavigation: false,
a11yUnitsListKeyboardControls: false,
a11ySearchboxLabel: false,
a11yCartItemsLinksStyles: false,
a11yHideSelectBtnForSelectedAddrOrPayment: false,
a11yFocusableCarouselControls: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ if (environment.cpq) {
a11ySearchBoxMobileFocus: true,
a11yFacetKeyboardNavigation: true,
a11yUnitsListKeyboardControls: true,
a11ySearchboxLabel: true,
a11yCartItemsLinksStyles: true,
a11yHideSelectBtnForSelectedAddrOrPayment: true,
a11yFocusableCarouselControls: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<div [attr.aria-label]="'searchBox.productSearch' | cxTranslate" role="search">
<div
*cxFeature="'!a11ySearchboxLabel'"
[attr.aria-label]="'searchBox.productSearch' | cxTranslate"
role="search"
>
<label class="searchbox" [class.dirty]="!!searchInput.value">
<!-- TODO: (CXSPA-6929) - Remove feature flag next major release -->
<input
Expand Down Expand Up @@ -57,6 +61,74 @@
</button>
</label>
</div>
<div
*cxFeature="'a11ySearchboxLabel'"
[attr.aria-label]="'searchBox.productSearch' | cxTranslate"
role="search"
class="cx-searchbox-container"
>
<label
class="searchbox cxFeat_a11ySearchboxLabel"
[class.dirty]="!!searchInput.value"
>
<span class="cx-input-label">{{ 'common.search' | cxTranslate }}</span>
<div class="cx-label-inner-container">
<input
#searchInput
[placeholder]="'searchBox.placeholder' | cxTranslate"
autocomplete="off"
aria-describedby="initialDescription"
aria-controls="results"
[attr.tabindex]="
a11ySearchBoxMobileFocusEnabled ? getTabIndex(isMobile | async) : null
"
[attr.aria-label]="'searchBox.placeholder' | cxTranslate"
(focus)="a11ySearchBoxMobileFocusEnabled ? null : open()"
(click)="open()"
(input)="search(searchInput.value)"
(blur)="close($any($event))"
(keydown.tab)="
a11ySearchBoxMobileFocusEnabled ? close($any($event)) : null
"
(keydown.escape)="close($any($event))"
(keydown.enter)="
close($any($event), true);
launchSearchResult($any($event), searchInput.value);
updateChosenWord(searchInput.value)
"
(keydown.arrowup)="focusPreviousChild($any($event))"
(keydown.arrowdown)="focusNextChild($any($event))"
value="{{ chosenWord }}"
/>

<button
[attr.aria-label]="'common.reset' | cxTranslate"
[title]="'common.reset' | cxTranslate"
(click)="clear(searchInput)"
class="reset"
>
<cx-icon [type]="iconTypes.RESET"></cx-icon>
</button>

<div
role="presentation"
class="search-icon"
[title]="'common.search' | cxTranslate"
>
<cx-icon [type]="iconTypes.SEARCH"></cx-icon>
</div>
</div>
<button
#searchButton
[attr.aria-label]="'common.search' | cxTranslate"
[title]="'common.search' | cxTranslate"
class="search"
(click)="open()"
>
<cx-icon [type]="iconTypes.SEARCH"></cx-icon>
</button>
</label>
</div>

<div
*ngIf="results$ | async as result"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
PageType,
RoutingService,
WindowRef,
useFeatureStyles,
} from '@spartacus/core';
import { Observable, Subscription, of } from 'rxjs';
import { filter, map, switchMap, tap } from 'rxjs/operators';
Expand Down Expand Up @@ -114,7 +115,9 @@ export class SearchBoxComponent implements OnInit, OnDestroy {
protected componentData: CmsComponentData<CmsSearchBoxComponent>,
protected winRef: WindowRef,
protected routingService: RoutingService
) {}
) {
useFeatureStyles('a11ySearchboxLabel');
}

/**
* Returns the SearchBox configuration. The configuration is driven by multiple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,35 @@
@include media-breakpoint-down(sm) {
// hide the input on mobile when there's no interaction with searchbox
cx-searchbox {
input {
// we cannot use display:none, visible:hidden or opacity: 0
// as this will no longer emit a focus event to the controller logic
width: 0;
padding: 0;
// cxFeat_a11ySearchboxLabel class is only applied if a11ySearchboxLabel flag is true
// Needed to add this class manually since:
// 1. %cx-searchbox__body can't be styled with `@include forFeature('...')`
// 2. We can't apply changes for when feature flag is NOT enabled and it would be
// complicated to achieve desired behaviour without this possibility.
// TODO: When removing feature flag `a11ySearchboxLabel` next major release remove also cxFeat_a11ySearchboxLabe class
// and all styles for label:not(.cxFeat_a11ySearchboxLabel)
label:not(.cxFeat_a11ySearchboxLabel) {
input {
// we cannot use display:none, visible:hidden or opacity: 0
// as this will no longer emit a focus event to the controller logic
width: 0;
padding: 0;
}
}

.cxFeat_a11ySearchboxLabel {
.cx-label-inner-container,
input {
width: 0;
padding: 0;
border: none;
}

.cx-input-label {
display: none;
}
}

button.reset {
display: none;
}
Expand All @@ -67,6 +90,7 @@
}

%cx-searchbox {
--cx-mobile-header-height: 60px;
@include media-breakpoint-up(md) {
// we position the parent relative to ensure the result panel
// is aligned to the left of searchbox
Expand All @@ -85,14 +109,36 @@
}
}

@include forFeature('a11ySearchboxLabel') {
> .cx-searchbox-container {
@include media-breakpoint-up(md) {
background-color: unset;
position: unset;
}
}
}

a,
.message {
padding: 6px 16px;
color: currentColor;
user-select: none;
}

label {
@include forFeature('a11ySearchboxLabel') {
.cx-input-label {
color: var(--cx-color-text);

@include media-breakpoint-down(sm) {
position: absolute;
top: var(--cx-mobile-header-height);
left: 10px;
z-index: 30;
}
}
}

label:not(.cxFeat_a11ySearchboxLabel) {
display: flex;
align-content: stretch;
margin: 0;
Expand Down Expand Up @@ -181,6 +227,121 @@
}
}

label.cxFeat_a11ySearchboxLabel {
display: flex;
align-content: stretch;
align-items: center;
margin: 0;
padding-top: 6px;
padding-inline-end: 6px;
padding-bottom: 6px;
padding-inline-start: 10px;
gap: 15px;

@include media-breakpoint-up(md) {
// hide search icon when the input is dirty
&.dirty div.search-icon {
display: none;
}
}

&:not(.dirty) button.reset {
display: none;
}

.cx-label-inner-container {
display: flex;
align-content: stretch;
align-items: center;
padding-top: 6px;
padding-inline-end: 6px;
padding-bottom: 6px;
padding-inline-start: 10px;

@include media-breakpoint-up(md) {
border: 1px solid var(--cx-color-medium);
width: 27vw;
min-width: 300px;
max-width: 550px;
background-color: var(--cx-color-inverse);
}

@include media-breakpoint-down(sm) {
position: absolute;
left: 0;
top: var(--cx-mobile-header-height);
width: 100%;
background-color: var(--cx-color-inverse);
z-index: 20;
padding-top: 25px;
}
}

input {
background: none;
border: none;
outline: none;
display: block;

@include media-breakpoint-down(sm) {
width: 100%;
padding: 6px 16px;
height: 48px;
border: 1px solid var(--cx-color-medium);
border-radius: 4px;
}

flex-basis: 100%;
height: 35px;
color: var(--cx-color-text);
z-index: 20;

@include placeholder {
color: currentColor;

@include forFeature('a11yImproveContrast') {
color: var(--cx-color-dark);
}
}
}

button,
div.search-icon {
flex-basis: 48px;
text-align: center;
background: none;
border: none;
padding: 6px;
color: var(--cx-color-medium);

@include forFeature('a11yImproveContrast') {
color: var(--cx-color-secondary);
}

@include media-breakpoint-down(sm) {
color: var(--cx-color-primary);
font-size: var(--cx-font-size, 1.563rem);

&.reset {
display: none;
}
}

&.reset cx-icon {
&:before {
font-size: 1.4rem;
}
@include media-breakpoint-down(sm) {
position: relative;
left: 74px;
z-index: 20;
top: 52px;
margin-top: 0;
}
}
}
}

.results {
// hide the result by default
display: none;
Expand All @@ -197,6 +358,10 @@

@include media-breakpoint-down(sm) {
top: 105px;

@include forFeature('a11ySearchboxLabel') {
top: calc(var(--cx-mobile-header-height) + 79px);
}
z-index: 10;
}

Expand Down

0 comments on commit 6c5b71e

Please sign in to comment.