Skip to content

Commit

Permalink
[A11y][TableListView] Refactor to use context to return focus (#188774)
Browse files Browse the repository at this point in the history
## Summary

Closes elastic/observability-dev#3345

Refactoring the tags portion of the table list view to return focus
after closing the tags popover

---------

Co-authored-by: Sébastien Loix <[email protected]>
  • Loading branch information
rshen91 and sebelga authored Jul 23, 2024
1 parent ac9165d commit c11d534
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import type {
} from '../table_list_view_table';
import type { TableItemsRowActions } from '../types';
import { TableSortSelect } from './table_sort_select';
import { TagFilterPanel } from './tag_filter_panel';
import { TagFilterPanel, TagFilterContextProvider } from './tag_filter_panel';
import { useTagFilterPanel } from './use_tag_filter_panel';
import type { Params as UseTagFilterPanelParams } from './use_tag_filter_panel';
import type { SortColumnField } from './table_sort_select';
Expand Down Expand Up @@ -188,32 +188,9 @@ export function Table<T extends UserContentCommonSchema>({

return {
type: 'custom_component',
component: () => {
return (
<TagFilterPanel
isPopoverOpen={isPopoverOpen}
isInUse={isInUse}
closePopover={closePopover}
options={options}
totalActiveFilters={totalActiveFilters}
onFilterButtonClick={onFilterButtonClick}
onSelectChange={onSelectChange}
clearTagSelection={clearTagSelection}
/>
);
},
component: TagFilterPanel,
};
}, [
isPopoverOpen,
isInUse,
isTaggingEnabled,
closePopover,
options,
totalActiveFilters,
onFilterButtonClick,
onSelectChange,
clearTagSelection,
]);
}, [isTaggingEnabled]);

const userFilterPanel = useMemo<SearchFilterConfig | null>(() => {
return createdByEnabled
Expand Down Expand Up @@ -295,22 +272,33 @@ export function Table<T extends UserContentCommonSchema>({
selectedUsers={tableFilter.createdBy}
showNoUserOption={showNoUserOption}
>
<EuiInMemoryTable<T>
itemId="id"
items={visibleItems}
columns={tableColumns}
pagination={pagination}
loading={isFetchingItems}
message={noItemsMessage}
selection={selection}
search={search}
executeQueryOptions={{ enabled: false }}
sorting={sorting}
onChange={onTableChange}
data-test-subj="itemsInMemTable"
rowHeader="attributes.title"
tableCaption={tableCaption}
/>
<TagFilterContextProvider
isPopoverOpen={isPopoverOpen}
isInUse={isInUse}
closePopover={closePopover}
onFilterButtonClick={onFilterButtonClick}
onSelectChange={onSelectChange}
options={options}
totalActiveFilters={totalActiveFilters}
clearTagSelection={clearTagSelection}
>
<EuiInMemoryTable<T>
itemId="id"
items={visibleItems}
columns={tableColumns}
pagination={pagination}
loading={isFetchingItems}
message={noItemsMessage}
selection={selection}
search={search}
executeQueryOptions={{ enabled: false }}
sorting={sorting}
onChange={onTableChange}
data-test-subj="itemsInMemTable"
rowHeader="attributes.title"
tableCaption={tableCaption}
/>
</TagFilterContextProvider>
</UserFilterContextProvider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const saveBtnWrapperCSS = css`
width: 100%;
`;

interface Props {
interface Context {
clearTagSelection: () => void;
closePopover: () => void;
isPopoverOpen: boolean;
Expand All @@ -54,18 +54,28 @@ interface Props {
onSelectChange: (updatedOptions: TagOptionItem[]) => void;
}

export const TagFilterPanel: FC<Props> = ({
isPopoverOpen,
isInUse,
options,
totalActiveFilters,
onFilterButtonClick,
onSelectChange,
closePopover,
clearTagSelection,
}) => {
const TagFilterContext = React.createContext<Context | null>(null);

export const TagFilterContextProvider: FC<Context> = ({ children, ...props }) => {
return <TagFilterContext.Provider value={props}>{children}</TagFilterContext.Provider>;
};

export const TagFilterPanel: FC<{}> = ({}) => {
const { euiTheme } = useEuiTheme();
const { navigateToUrl, currentAppId$, getTagManagementUrl } = useServices();
const componentContext = React.useContext(TagFilterContext);
if (!componentContext)
throw new Error('TagFilterPanel must be used within a TagFilterContextProvider');
const {
isPopoverOpen,
isInUse,
options,
totalActiveFilters,
onFilterButtonClick,
onSelectChange,
closePopover,
clearTagSelection,
} = componentContext;
const isSearchVisible = options.length > 10;

const searchBoxCSS = css`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { useEffect, useState, useCallback } from 'react';
import React, { useEffect, useState, useCallback, useRef } from 'react';
import type { MouseEvent } from 'react';
import { Query, EuiFlexGroup, EuiFlexItem, EuiText, EuiHealth, EuiBadge } from '@elastic/eui';
import type { FieldValueOptionType } from '@elastic/eui';
Expand Down Expand Up @@ -54,21 +54,27 @@ export const useTagFilterPanel = ({
const [options, setOptions] = useState<TagOptionItem[]>([]);
const [tagSelection, setTagSelection] = useState<TagSelection>({});
const totalActiveFilters = Object.keys(tagSelection).length;
const onClickTime = useRef<number>(0);

const onSelectChange = useCallback(
(updatedOptions: TagOptionItem[]) => {
// Note: see data flow comment in useEffect() below
// onSelectChange() handler is to support keyboard navigation. When user clicks on a tag
// we call the onOptionClick() imperatively below and we don't need to do anything here.
const timeSinceOptionClick = Date.now() - onClickTime.current;
if (timeSinceOptionClick < 100) return;

const diff = updatedOptions.find((item, index) => item.checked !== options[index].checked);
if (diff) {
addOrRemoveIncludeTagFilter(diff.tag);
}
},
[options, addOrRemoveIncludeTagFilter]
[addOrRemoveIncludeTagFilter, options]
);

const onOptionClick = useCallback(
(tag: Tag) => (e: MouseEvent) => {
const withModifierKey = (isMac && e.metaKey) || (!isMac && e.ctrlKey);
onClickTime.current = Date.now();

if (withModifierKey) {
addOrRemoveExcludeTagFilter(tag);
Expand Down

0 comments on commit c11d534

Please sign in to comment.