diff --git a/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.fixtures.js b/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.fixtures.js index d89be494217..af2cf6ff005 100644 --- a/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.fixtures.js +++ b/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.fixtures.js @@ -1,7 +1,8 @@ import { noop } from '../../../common/helpers'; export const buttons = [ - { title: 'first', action: { onClick: noop } }, - { title: 'second', action: { href: 'some-url2', 'data-method': 'put' } }, - { title: 'third', action: { onClick: noop } }, + { title: 'first', action: { id: 1, onClick: noop } }, + { title: 'second', action: { id: 2, href: 'some-url2', 'data-method': 'put' } }, + { title: 'third', action: { id: 3, onClick: noop } }, + { title: 'fourth', action: { id: 4, onClick: noop, disabled: true } }, ]; diff --git a/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.js b/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.js index 61ebc5c4e8e..3dceed93cbf 100644 --- a/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.js +++ b/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.js @@ -1,41 +1,95 @@ -import React from 'react'; +import uuidV1 from 'uuid/v1'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import { SplitButton, MenuItem, Button } from 'patternfly-react'; +import { + Button, + Dropdown, + DropdownItem, + DropdownList, + MenuToggle, + MenuToggleAction, +} from '@patternfly/react-core'; +import './actionButtons.scss'; /** * Generate a button or a dropdown of buttons * @param {String} title The title of the button for the title and text inside the button - * @param {Object} action action to preform when the button is click can be href with data-method or Onclick - * @return {Function} button component or splitbutton component + * @param {Object} action action to perform when the button is click can be href with data-method or Onclick + * @return {Function} button component or splitbutton with menu toggle action component */ export const ActionButtons = ({ buttons }) => { + const [isOpen, setIsOpen] = useState(false); + const onToggleClick = () => setIsOpen(!isOpen); + if (!buttons.length) return null; if (buttons.length === 1) return ( - ); - const firstButton = buttons.shift(); + + const [firstButton, ...restButtons] = buttons; + return ( - setIsOpen(openState)} + toggle={toggleRef => ( + + {firstButton.title} + , + ], + }} + aria-label="Menu toggle with action split button" + /> + )} + shouldFocusToggleOnSelect > - {buttons.map(button => ( - - {button.title} - - ))} - + + {restButtons.map(button => ( + + {button.title} + + ))} + + ); }; ActionButtons.propTypes = { buttons: PropTypes.arrayOf( PropTypes.shape({ - action: PropTypes.object, + action: PropTypes.shape({ + id: PropTypes.number, + onClick: PropTypes.func, + href: PropTypes.string, + disabled: PropTypes.bool, + }), title: PropTypes.string, }) ), diff --git a/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.test.js b/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.test.js index 3c97cdd4237..979bf50d818 100644 --- a/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.test.js +++ b/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.test.js @@ -1,12 +1,44 @@ -import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers'; +import React from 'react'; +import { screen, fireEvent, render, act } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; + import { ActionButtons } from './ActionButtons'; import { buttons } from './ActionButtons.fixtures'; const fixtures = { - 'renders ActionButtons with 0 button': { buttons: [] }, - 'renders ActionButtons with 1 button': { buttons: [buttons[0]] }, - 'renders ActionButtons with 3 button': { buttons }, + none: { buttons: [] }, + one: { buttons: [buttons[0]] }, + many: { buttons }, }; -describe('ActionButtons', () => - testComponentSnapshotsWithFixtures(ActionButtons, fixtures)); +describe('ActionButtons', () => { + it('renders with 0 buttons', () => { + render(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + it('renders 1 button', () => { + render(); + expect(screen.getByRole('button')).toHaveTextContent('first'); + }); + + it('renders 2 buttons initially and shows more after clicking toggle', async () => { + render(); + const buttons = screen.getAllByRole('button'); + expect(buttons).toHaveLength(2); + expect(screen.getByRole('button', { name: 'first' })).toBeInTheDocument(); + const toggleButton = screen.getByRole('button', { + name: 'Menu toggle with action split button' + }); + expect(toggleButton).toBeInTheDocument(); + + expect(screen.queryByText('second')).not.toBeInTheDocument(); + expect(screen.queryByText('third')).not.toBeInTheDocument(); + await act(async () => fireEvent.click(toggleButton)); + expect(screen.getByText('second')).toBeInTheDocument(); + expect(screen.getByText('third')).toBeInTheDocument(); + const dropdownItems = screen.getAllByRole('menuitem'); + expect(dropdownItems).toHaveLength(3); + expect(dropdownItems[2]).toBeDisabled(); + }); +}); diff --git a/webpack/assets/javascripts/react_app/components/common/ActionButtons/__snapshots__/ActionButtons.test.js.snap b/webpack/assets/javascripts/react_app/components/common/ActionButtons/__snapshots__/ActionButtons.test.js.snap deleted file mode 100644 index 14ff8be746e..00000000000 --- a/webpack/assets/javascripts/react_app/components/common/ActionButtons/__snapshots__/ActionButtons.test.js.snap +++ /dev/null @@ -1,49 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ActionButtons renders ActionButtons with 0 button 1`] = `""`; - -exports[`ActionButtons renders ActionButtons with 1 button 1`] = ` - -`; - -exports[`ActionButtons renders ActionButtons with 3 button 1`] = ` - - - second - - - third - - -`; diff --git a/webpack/assets/javascripts/react_app/components/common/ActionButtons/actionButtons.scss b/webpack/assets/javascripts/react_app/components/common/ActionButtons/actionButtons.scss new file mode 100644 index 00000000000..c7625555610 --- /dev/null +++ b/webpack/assets/javascripts/react_app/components/common/ActionButtons/actionButtons.scss @@ -0,0 +1,5 @@ +.pf-v5-c-menu__content { + ul[role="menu"].action-buttons { + margin-bottom: 0; + } +}