Skip to content

Commit

Permalink
[ally] Add a11y controls (ymatsakova) (#368)
Browse files Browse the repository at this point in the history
  • Loading branch information
ymatsakova committed Nov 4, 2023
1 parent 568d33b commit 3d4799d
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 6 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "4.5.7",
"version": "4.5.8",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down
2 changes: 2 additions & 0 deletions src/components/body/EmojiCategory.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
display: flex;
align-items: center;
font-weight: bold;
font-size: 16px;
margin: 0;
text-transform: capitalize;
backdrop-filter: blur(3px);
padding: var(--epr-category-label-padding);
Expand Down
2 changes: 1 addition & 1 deletion src/components/body/EmojiCategory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function EmojiCategory({
data-name={category}
aria-label={categoryName}
>
<div className={ClassNames.label}>{categoryName}</div>
<h2 className={ClassNames.label}>{categoryName}</h2>
<div className={ClassNames.categoryContent}>{children}</div>
</li>
);
Expand Down
10 changes: 10 additions & 0 deletions src/components/header/Search.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
min-width: 0;
}

.EmojiPickerReact .epr-search-container .epr-status-visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}

.EmojiPickerReact .epr-search-container input.epr-search {
outline: none;
transition: all 0.2s ease-in-out;
Expand Down
14 changes: 13 additions & 1 deletion src/components/header/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function Search() {
const clearSearch = useClearSearch();
const placeholder = useSearchPlaceHolderConfig();
const autoFocus = useAutoFocusSearchConfig();
const { onChange } = useFilter();
const { statusSearchResults, searchTerm, onChange } = useFilter();

const input = SearchInputRef?.current;
const value = input?.value;
Expand All @@ -62,6 +62,7 @@ export function Search() {
onFocus={closeAllOpenToggles}
className="epr-search"
type="text"
aria-controls='epr-search-id'
placeholder={placeholder}
onChange={event => {
setInc(inc + 1);
Expand All @@ -71,6 +72,17 @@ export function Search() {
}}
ref={SearchInputRef}
/>
{searchTerm ? (
<div
role='status'
className={clsx('epr-status-search-results', 'epr-status-visually-hidden')}
aria-live='polite'
id='epr-search-id'
aria-atomic='true'
>
{statusSearchResults}
</div>
) : null}
<div className="epr-icn-search" />
<Button
className={clsx('epr-btn-clear-search', 'epr-visible-on-search-only')}
Expand Down
16 changes: 13 additions & 3 deletions src/components/navigation/CategoryNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,36 @@ export function CategoryNavigation() {
const hideCustomCategory = useShouldHideCustomEmojis();

return (
<div className="epr-category-nav" ref={CategoryNavigationRef}>
<div
className='epr-category-nav'
role='tablist'
aria-label='Category navigation'
id='epr-category-nav-id'
ref={CategoryNavigationRef}
>
{categoriesConfig.map(categoryConfig => {
const category = categoryFromCategoryConfig(categoryConfig);
const isActiveCategory = category === activeCategory;

if (isCustomCategory(categoryConfig) && hideCustomCategory) {
return null;
}

return (
<Button
tabIndex={isSearchMode ? -1 : 0}
tabIndex={(isSearchMode || isActiveCategory) ? -1 : 0}
className={clsx('epr-cat-btn', `epr-icn-${category}`, {
[ClassNames.active]: category === activeCategory
[ClassNames.active]: isActiveCategory
})}
key={category}
onClick={() => {
setActiveCategory(category);
scrollCategoryIntoView(category);
}}
aria-label={categoryNameFromCategoryConfig(categoryConfig)}
aria-selected={isActiveCategory}
role='tab'
aria-controls='epr-category-nav-id'
/>
);
})}
Expand Down
7 changes: 7 additions & 0 deletions src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ import { CustomEmoji } from './customEmojiConfig';
const KNOWN_FAILING_EMOJIS = ['2640-fe0f', '2642-fe0f', '2695-fe0f'];

export const DEFAULT_SEARCH_PLACEHOLDER = 'Search';
export const SEARCH_RESULTS_NO_RESULTS_FOUND = 'No results found';
export const SEARCH_RESULTS_SUFFIX =
' found. Use up and down arrow keys to navigate.';
export const SEARCH_RESULTS_ONE_RESULT_FOUND =
'1 result' + SEARCH_RESULTS_SUFFIX;
export const SEARCH_RESULTS_MULTIPLE_RESULTS_FOUND =
'%n results' + SEARCH_RESULTS_SUFFIX;

export function mergeConfig(
userConfig: PickerConfig = {}
Expand Down
16 changes: 16 additions & 0 deletions src/config/useConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import {
import { CategoriesConfig } from './categoryConfig';
import {
DEFAULT_SEARCH_PLACEHOLDER,
SEARCH_RESULTS_NO_RESULTS_FOUND,
SEARCH_RESULTS_ONE_RESULT_FOUND,
SEARCH_RESULTS_MULTIPLE_RESULTS_FOUND,
PickerDimensions,
PreviewConfig
} from './config';
Expand Down Expand Up @@ -126,3 +129,16 @@ function getDimension(dimensionConfig: PickerDimensions): PickerDimensions {
? `${dimensionConfig}px`
: dimensionConfig;
}

export function useSearchResultsConfig(searchResultsCount: number): string {
const hasResults = searchResultsCount > 0;
const isPlural = searchResultsCount > 1;

if (hasResults) {
return isPlural ?
SEARCH_RESULTS_MULTIPLE_RESULTS_FOUND.replace('%n', searchResultsCount.toString())
: SEARCH_RESULTS_ONE_RESULT_FOUND;
}

return SEARCH_RESULTS_NO_RESULTS_FOUND;
}
10 changes: 10 additions & 0 deletions src/hooks/useFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DataEmoji } from '../dataUtils/DataTypes';
import { emojiNames } from '../dataUtils/emojiSelectors';

import { useFocusSearchInput } from './useFocus';
import { useSearchResultsConfig } from '../config/useConfig';

function useSetFilterRef() {
const filterRef = useFilterRef();
Expand Down Expand Up @@ -63,11 +64,13 @@ export function useFilter() {
const applySearch = useApplySearch();

const [searchTerm] = useSearchTermState();
const statusSearchResults = getStatusSearchResults(filterRef.current, searchTerm);

return {
onChange,
searchTerm,
SearchInputRef,
statusSearchResults,
};

function onChange(inputValue: string) {
Expand Down Expand Up @@ -183,3 +186,10 @@ export function getNormalizedSearchTerm(str: string): string {

return str.trim().toLowerCase();
}

function getStatusSearchResults(filterState: FilterState, searchTerm: string): string {
if (!filterState?.[searchTerm]) return '';

const searchResultsCount = Object.entries(filterState?.[searchTerm])?.length || 0;
return useSearchResultsConfig(searchResultsCount);
}

0 comments on commit 3d4799d

Please sign in to comment.