Skip to content

Commit

Permalink
feat(taglist): add skip to search
Browse files Browse the repository at this point in the history
This is to provide a skip link to quickly navigate to the search via keyboard.

Cf. https://webaim.org/techniques/skipnav/#hidden

Closes #435
  • Loading branch information
Niklas Kiefer committed Oct 12, 2023
1 parent c69357d commit 4e2ee2c
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 0 deletions.
20 changes: 20 additions & 0 deletions packages/form-js-viewer/assets/form-js-base.css
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,26 @@
background-color: var(--color-background);
}

.fjs-container .fjs-taglist-skip-link {
outline: none;
border: none;
background-color: transparent;
color: transparent;
height: 0px;
width: 0px;
position: absolute;
left: -100px;
}

.fjs-container .fjs-taglist-skip-link:focus {
position: relative;
height: auto;
width: auto;
border: solid 1px var(--color-accent);
color: var(--color-accent);
left: 0;
}

.fjs-container .fjs-taglist .fjs-taglist-tag {
display: flex;
overflow: hidden;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import DropdownList from './parts/DropdownList';
import Description from '../Description';
import Errors from '../Errors';
import Label from '../Label';
import SkipLink from './parts/SkipLink';

import { sanitizeMultiSelectValue } from '../util/sanitizerUtil';

Expand Down Expand Up @@ -138,6 +139,10 @@ export default function Taglist(props) {
}
};

const onSkipToSearch = () => {
searchbarRef.current.focus();
};

const shouldDisplayDropdown = useMemo(() => !disabled && loadState === LOAD_STATES.LOADED && isDropdownExpanded && !isEscapeClosed, [ disabled, isDropdownExpanded, isEscapeClosed, loadState ]);

return <div
Expand All @@ -155,6 +160,7 @@ export default function Taglist(props) {
label={ label }
required={ required }
id={ prefixId(`${id}-search`, formId) } />
{ (!disabled && !readonly && !!values.length) && <SkipLink className="fjs-taglist-skip-link" label="Skip to search" onSkip={ onSkipToSearch } /> }
<div class={ classNames('fjs-taglist', { 'fjs-disabled': disabled, 'fjs-readonly': readonly }) }>
{ loadState === LOAD_STATES.LOADED &&
<div class="fjs-taglist-tags">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import classNames from 'classnames';

import { useCallback } from 'preact/hooks';

export default function SkipLink(props) {

const {
className,
label,
onSkip
} = props;

const onKeyDown = useCallback(event => {
if (event.key === 'Enter') {
event.preventDefault();
event.stopPropagation();
onSkip();
}
}, [ onSkip ]);


return (
<a
href="#"
class={ classNames('fjs-skip-link', className) }
onKeyDown={ onKeyDown }
>{ label }</a>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,23 @@ describe('Taglist', function() {
});


it('should render skip to search link', function() {

// when
const { container } = createTaglist({
field: {
...defaultField
},
value: [ 'tag1', 'tag2', 'tag3' ]
});

// then
const skipLink = container.querySelector('.fjs-taglist-skip-link');

expect(skipLink).to.exist;
});


describe('interaction', function() {

describe('tag deletion', function() {
Expand Down

0 comments on commit 4e2ee2c

Please sign in to comment.