diff --git a/components/button/src/split-button/split-button.js b/components/button/src/split-button/split-button.js index 95290477be..fadbc21a92 100644 --- a/components/button/src/split-button/split-button.js +++ b/components/button/src/split-button/split-button.js @@ -7,6 +7,7 @@ import PropTypes from 'prop-types' import React, { Component } from 'react' import css from 'styled-jsx/css' import { Button } from '../index.js' +import i18n from '../locales/index.js' const rightButton = css.resolve` button { @@ -18,8 +19,25 @@ class SplitButton extends Component { state = { open: false, } + anchorRef = React.createRef() + componentDidMount() { + document.addEventListener('keydown', this.handleKeyDown) + } + + componentWillUnmount() { + document.removeEventListener('keydown', this.handleKeyDown) + } + handleKeyDown = (event) => { + event.preventDefault() + if (event.key === 'Escape' && this.state.open) { + event.stopPropagation() + this.setState({ open: false }) + this.anchorRef.current && this.anchorRef.current.focus() + } + } + onClick = (payload, event) => { if (this.props.onClick) { this.props.onClick( @@ -33,7 +51,9 @@ class SplitButton extends Component { } } - onToggle = () => this.setState({ open: !this.state.open }) + onToggle = () => { + this.setState((prevState) => ({ open: !prevState.open })) + } render() { const { open } = this.state @@ -94,6 +114,8 @@ class SplitButton extends Component { tabIndex={tabIndex} className={cx(className, rightButton.className)} dataTest={`${dataTest}-toggle`} + title={i18n.t('Toggle dropdown')} + aria-label={i18n.t('Toggle dropdown')} > {arrow} diff --git a/components/button/src/split-button/split-button.test.js b/components/button/src/split-button/split-button.test.js new file mode 100644 index 0000000000..d7c8ef2027 --- /dev/null +++ b/components/button/src/split-button/split-button.test.js @@ -0,0 +1,85 @@ +import { render, fireEvent, cleanup, waitFor } from '@testing-library/react' +import React from 'react' +import '@testing-library/jest-dom/extend-expect' +import { SplitButton } from './split-button.js' + +describe('SplitButton', () => { + afterEach(cleanup) + + it('renders button with children', () => { + const { getByText } = render(Click me) + expect(getByText('Click me')).toBeInTheDocument() + }) + + it('toggles dropdown when left button is clicked', () => { + const { getByTestId, queryByTestId } = render() + const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle') + + fireEvent.click(toggleButton) + expect( + queryByTestId('dhis2-uicore-splitbutton-menu') + ).toBeInTheDocument() + + fireEvent.click(toggleButton) + expect( + queryByTestId('dhis2-uicore-splitbutton-menu') + ).not.toBeInTheDocument() + }) + + it('renders dropdown content when open is true', () => { + const { getByTestId } = render( + Dropdown Content} /> + ) + + const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle') + fireEvent.click(toggleButton) + + expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument() + }) + + it("does not close dropdown 'Enter' key is pressed", async () => { + const { getByTestId } = render( + Dropdown Content} /> + ) + + const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle') + fireEvent.click(toggleButton) + expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument() + + fireEvent.keyDown(document, { key: 'Enter' }) + + // Use waitFor to wait for the DOM to update + await waitFor(() => { + expect( + getByTestId('dhis2-uicore-splitbutton-menu') + ).toBeInTheDocument() + }) + }) + + it('closes dropdown when escape key is pressed', async () => { + const { getByTestId, queryByTestId } = render( + Dropdown Content} /> + ) + + const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle') + fireEvent.click(toggleButton) + expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument() + + fireEvent.keyDown(document, { key: 'Escape' }) + + // Use waitFor to wait for the DOM to update + await waitFor(() => { + expect( + queryByTestId('dhis2-uicore-splitbutton-menu') + ).not.toBeInTheDocument() + }) + }) + + it('adds title and aria-label attributes to the right button', () => { + const { getByTestId } = render() + const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle') + + expect(toggleButton).toHaveAttribute('title', 'Toggle dropdown') + expect(toggleButton).toHaveAttribute('aria-label', 'Toggle dropdown') + }) +})