diff --git a/entry_types/scrolled/package/spec/frontend/Tooltip-spec.js b/entry_types/scrolled/package/spec/frontend/Tooltip-spec.js index 0824bb26b..d850d20e4 100644 --- a/entry_types/scrolled/package/spec/frontend/Tooltip-spec.js +++ b/entry_types/scrolled/package/spec/frontend/Tooltip-spec.js @@ -1,6 +1,7 @@ import React from 'react' -import {render, fireEvent, waitFor} from '@testing-library/react' +import {render, waitFor} from '@testing-library/react' import '@testing-library/jest-dom/extend-expect' +import userEvent from '@testing-library/user-event'; import {Tooltip} from 'frontend/Tooltip'; @@ -8,7 +9,7 @@ describe('Tooltip', () => { it('renders trigger', () => { const {getByTestId} = render( - {(buttonProps) => } + {(triggerProps) => } ); expect(getByTestId('trigger')).toBeDefined(); @@ -23,143 +24,164 @@ describe('Tooltip', () => { expect(getByTestId('content')).toBeDefined(); }); - it('opens tooltip when button is clicked', () => { + it('opens tooltip when button is clicked', async () => { const {getByTestId} = render( content}> - {(buttonProps) => } + {(triggerProps) => } ); + const user = userEvent.setup(); const button = getByTestId('trigger'); expect(button.getAttribute('aria-expanded')).toBe('false'); - fireEvent.click(button); + await user.click(button); expect(button.getAttribute('aria-expanded')).toBe('true'); }); - it('closes tooltip when button is clicked again', () => { + it('closes tooltip when button is clicked again', async () => { const {getByTestId} = render( content}> - {(buttonProps) => } + {(triggerProps) => } ); + const user = userEvent.setup(); const button = getByTestId('trigger'); - fireEvent.click(button); + await user.click(button); expect(button.getAttribute('aria-expanded')).toBe('true'); - fireEvent.click(button); + await user.click(button); expect(button.getAttribute('aria-expanded')).toBe('false'); }); - it('closes tooltip when ESC key is pressed', () => { + it('closes tooltip when ESC key is pressed', async () => { const {getByTestId} = render( content}> - {(buttonProps) => } + {(triggerProps) => } ); + const user = userEvent.setup(); const button = getByTestId('trigger'); - fireEvent.click(button); + await user.click(button); expect(button.getAttribute('aria-expanded')).toBe('true'); - fireEvent.keyDown(button, {key: 'Escape'}); + await user.keyboard('{Escape}'); expect(button.getAttribute('aria-expanded')).toBe('false'); }); it('returns focus to button when ESC key is pressed', async () => { const {getByTestId} = render( content button}> - {(buttonProps) => } + {(triggerProps) => } ); + const user = userEvent.setup(); const button = getByTestId('trigger'); const contentButton = getByTestId('content-button'); - fireEvent.click(button); + await user.click(button); contentButton.focus(); expect(document.activeElement).toBe(contentButton); - fireEvent.keyDown(button, {key: 'Escape'}); + await user.keyboard('{Escape}'); await waitFor(() => { expect(document.activeElement).toBe(button); }); }); - it('sets aria-expanded and aria-controls attributes', () => { + it('sets aria-expanded and aria-controls attributes', async () => { const {getByTestId} = render( content}> - {(buttonProps) => } + {(triggerProps) => } ); + const user = userEvent.setup(); const button = getByTestId('trigger'); expect(button.getAttribute('aria-expanded')).toBe('false'); expect(button.getAttribute('aria-controls')).toBe('tooltip-test'); - fireEvent.click(button); + await user.click(button); expect(button.getAttribute('aria-expanded')).toBe('true'); expect(button.getAttribute('aria-controls')).toBe('tooltip-test'); }); - it('does not toggle on click when openOnHover is true', () => { + it('does not toggle on click when openOnHover is true', async () => { const {getByTestId} = render( content}> - {(buttonProps) => } + {(triggerProps) => } ); + const user = userEvent.setup(); const button = getByTestId('trigger'); expect(button.getAttribute('aria-expanded')).toBeNull(); - fireEvent.click(button); + await user.click(button); expect(button.getAttribute('aria-expanded')).toBeNull(); }); - it('does not toggle on click when fixed is true', () => { + it('sets aria-describedby when openOnHover is true', () => { + const {getByTestId} = render( + content}> + {(triggerProps) => } + + ); + + const button = getByTestId('trigger'); + + expect(button.getAttribute('aria-describedby')).toBe('tooltip-test'); + }); + + it('does not toggle on click when fixed is true', async () => { const {getByTestId} = render( content}> - {(buttonProps) => } + {(triggerProps) => } ); + const user = userEvent.setup(); const button = getByTestId('trigger'); expect(button.getAttribute('aria-expanded')).toBeNull(); - fireEvent.click(button); + await user.click(button); expect(button.getAttribute('aria-expanded')).toBeNull(); }); - it('closes tooltip when focus leaves the container', () => { + it('closes tooltip when focus leaves the container', async () => { const {getByTestId} = render( <> - content}> - {(buttonProps) => } + content button}> + {(triggerProps) => } ); + const user = userEvent.setup(); const button = getByTestId('trigger'); - const outsideButton = getByTestId('outside'); + const contentButton = getByTestId('content-button'); - fireEvent.click(button); + await user.click(button); expect(button.getAttribute('aria-expanded')).toBe('true'); - fireEvent.blur(button, {relatedTarget: outsideButton}); + contentButton.focus(); + await user.keyboard('{Tab}'); expect(button.getAttribute('aria-expanded')).toBe('false'); }); diff --git a/entry_types/scrolled/package/spec/widgets/defaultNavigation/DefaultNavigation/chapters-spec.js b/entry_types/scrolled/package/spec/widgets/defaultNavigation/DefaultNavigation/chapters-spec.js index 452cb8063..4fc1f47e6 100644 --- a/entry_types/scrolled/package/spec/widgets/defaultNavigation/DefaultNavigation/chapters-spec.js +++ b/entry_types/scrolled/package/spec/widgets/defaultNavigation/DefaultNavigation/chapters-spec.js @@ -27,4 +27,28 @@ describe('DefaultNavigation - Chapters', () => { expect(queryByRole('link', {name: 'Second chapter'})).not.toBeNull(); expect(queryByRole('link', {name: 'Hidden chapter'})).toBeNull(); }); + + it('sets aria-current on current chapter link', () => { + const {getByRole} = renderInEntry( + , + { + seed: { + chapters: [ + {id: 1, configuration: {title: 'First chapter'}}, + {id: 2, configuration: {title: 'Second chapter'}} + ], + sections: [ + {chapterId: 1}, + {chapterId: 2} + ] + } + } + ); + + const firstChapterLink = getByRole('link', {name: 'First chapter'}); + const secondChapterLink = getByRole('link', {name: 'Second chapter'}); + + expect(firstChapterLink.getAttribute('aria-current')).toBe('location'); + expect(secondChapterLink.getAttribute('aria-current')).toBeNull(); + }); }); \ No newline at end of file diff --git a/entry_types/scrolled/package/src/frontend/Tooltip.js b/entry_types/scrolled/package/src/frontend/Tooltip.js index 773d5574d..cb67991c1 100644 --- a/entry_types/scrolled/package/src/frontend/Tooltip.js +++ b/entry_types/scrolled/package/src/frontend/Tooltip.js @@ -1,4 +1,4 @@ -import React, {useState, useRef} from 'react' +import React, {useState, useRef, useEffect} from 'react' import classNames from 'classnames'; import styles from './Tooltip.module.css' @@ -33,18 +33,40 @@ export function Tooltip({ }; const handleBlur = (event) => { - if (isOpen && !containerRef.current?.contains(event.relatedTarget)) { + if (isOpen && + event.relatedTarget && + !containerRef.current?.contains(event.relatedTarget)) { setIsOpen(false); } }; const isControlled = !openOnHover && !fixed; - const buttonProps = isControlled ? { + useEffect(() => { + if (!isControlled || !isOpen) { + return; + } + + const handleDocumentClick = (event) => { + if (!containerRef.current?.contains(event.target)) { + setIsOpen(false); + } + }; + + document.addEventListener('click', handleDocumentClick); + + return () => { + document.removeEventListener('click', handleDocumentClick); + }; + }, [isControlled, isOpen]); + + const triggerProps = isControlled ? { onClick: handleClick, ref: buttonRef, 'aria-expanded': isOpen, 'aria-controls': tooltipId + } : openOnHover ? { + 'aria-describedby': tooltipId } : {}; return ( @@ -55,7 +77,7 @@ export function Tooltip({ })} onKeyDown={isControlled ? handleKeyDown : undefined} onBlur={isControlled ? handleBlur : undefined}> - {typeof children === 'function' ? children(buttonProps) : children} + {typeof children === 'function' ? children(triggerProps) : children} (
- props.handleMenuClick(props.chapterLinkId)}> + onClick={() => props.handleMenuClick(props.chapterLinkId)} + aria-current={props.active ? 'location' : undefined}> {presence(props.title) || t('pageflow_scrolled.public.navigation.chapter', {number: props.chapterIndex})} {!isBlank(props.summary) &&

- {item} + {renderLink} ) } diff --git a/entry_types/scrolled/package/src/widgets/defaultNavigation/DefaultNavigation.js b/entry_types/scrolled/package/src/widgets/defaultNavigation/DefaultNavigation.js index e712b26eb..adaae9406 100644 --- a/entry_types/scrolled/package/src/widgets/defaultNavigation/DefaultNavigation.js +++ b/entry_types/scrolled/package/src/widgets/defaultNavigation/DefaultNavigation.js @@ -157,11 +157,12 @@ export function DefaultNavigation({ onFocus={() => setNavExpanded(true)}>

+ + {(hasChapters || hasMenu) && } - {renderNav()} diff --git a/entry_types/scrolled/package/src/widgets/defaultNavigation/LegalInfoMenu.js b/entry_types/scrolled/package/src/widgets/defaultNavigation/LegalInfoMenu.js index ee2c6c2b1..5ee0eba36 100644 --- a/entry_types/scrolled/package/src/widgets/defaultNavigation/LegalInfoMenu.js +++ b/entry_types/scrolled/package/src/widgets/defaultNavigation/LegalInfoMenu.js @@ -50,8 +50,8 @@ export function LegalInfoMenu(props) { horizontalOffset={props.tooltipOffset - 30} arrowPos={120 - props.tooltipOffset} content={content}> - {(buttonProps) => ( -