CEC-4499: add bulk update configs support (#357)

* add taskRunner util

* add bulk update config flow
This commit is contained in:
Tristan Timblin
2023-06-14 13:53:32 -04:00
committed by GitHub
parent a68c00b4ad
commit de1a5dcd2d
11 changed files with 621 additions and 7 deletions

View File

@@ -0,0 +1,103 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DownloadFileLink Render 1`] = `
<div>
<div
aria-label="choose action"
class="MuiButtonGroup-root MuiButtonGroup-contained"
role="group"
>
<button
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-containedPrimary"
tabindex="0"
type="button"
>
<span
class="MuiButton-label"
>
Action One
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
<button
aria-haspopup="menu"
aria-label="select action"
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-containedPrimary MuiButton-containedSizeSmall MuiButton-sizeSmall"
tabindex="0"
type="button"
>
<span
class="MuiButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root"
data-testid="ArrowDropDownIcon"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="m7 10 5 5 5-5z"
/>
</svg>
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div>
</div>
`;
exports[`DropDownButton Render 1`] = `
<div>
<div
aria-label="choose action"
class="MuiButtonGroup-root MuiButtonGroup-contained"
role="group"
>
<button
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-containedPrimary"
tabindex="0"
type="button"
>
<span
class="MuiButton-label"
>
Action One
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
<button
aria-haspopup="menu"
aria-label="select action"
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-containedPrimary MuiButton-containedSizeSmall MuiButton-sizeSmall"
tabindex="0"
type="button"
>
<span
class="MuiButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root"
data-testid="ArrowDropDownIcon"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="m7 10 5 5 5-5z"
/>
</svg>
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div>
</div>
`;

View File

@@ -0,0 +1,109 @@
import { useRef, useState } from "react";
import {
Button,
ButtonGroup,
ClickAwayListener,
Grow,
MenuItem,
MenuList,
Paper,
Popper,
} from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
const DropDownButton = ({ actions = [], payload = [] }) => {
const [open, setOpen] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const anchorRef = useRef(null);
const handleClick = () => {
actions[selectedIndex].trigger(...payload);
};
const handleMenuItemClick = (event, index) => {
setSelectedIndex(index);
setOpen(false);
}
const handleToggle = () => {
setOpen(open => !open);
};
const handleClose = (event) => {
if (
anchorRef.current &&
anchorRef.current.contains(event.target)
) {
return;
}
setOpen(false);
};
return (
<>
<ButtonGroup
color="primary"
variant="contained"
ref={anchorRef}
aria-label="choose action"
>
<Button
onClick={handleClick}
disabled={actions[selectedIndex].disabled}
>
{actions[selectedIndex].name}
</Button>
<Button
size="small"
aria-controls={open ? 'split-button-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-label="select action"
aria-haspopup="menu"
onClick={handleToggle}
>
<ArrowDropDownIcon />
</Button>
</ButtonGroup>
<Popper
sx={{
zIndex: 100,
}}
open={open}
anchorEl={anchorRef.current}
role={undefined}
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id="split-button-menu" autoFocusItem>
{actions.map((action, index) => (
<MenuItem
key={action.name}
disabled={actions[index].disabled}
selected={index === selectedIndex}
onClick={(event) => handleMenuItemClick(event, index)}
>
{action.name}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</>
)
}
export default DropDownButton;

View File

@@ -0,0 +1,80 @@
import React from "react";
import { fireEvent, render, waitFor, screen } from "@testing-library/react";
import DropDownButton from ".";
import addSnapshotSerializer from "../../../utils/snapshot";
describe("DropDownButton", () => {
beforeAll(() => {
addSnapshotSerializer(expect);
});
it("Render", async () => {
const actions = [
{
name: "Action One",
disabled: false,
trigger: (paramOne, paramTwo) => {}
},
{
name: "Action Two",
disabled: false,
trigger: (paramOne, paramTwo) => {}
},
];
const { container } = render(
<DropDownButton
actions={actions}
payload={["somePayload", "someOtherPayload"]}
/>
);
await waitFor(() => {
/* render */
});
expect(container).toMatchSnapshot();
});
it("properly disables an action", async () => {
const actions = [
{
name: "Disabled Action",
disabled: true,
trigger: () => {}
},
];
const { getByText } = render(
<DropDownButton
actions={actions}
payload={[]}
/>
);
await waitFor(() => {
/* render */
});
const buttonEl = getByText("Disabled Action").parentElement;
expect(buttonEl).toHaveProperty("disabled", true);
});
it("properly passes payload to callback", async () => {
const actions = [
{
name: "Action One",
disabled: false,
trigger: jest.fn(),
},
];
render(
<DropDownButton
actions={actions}
payload={["somePayload", "somePayload2"]}
/>
);
const buttonEl = screen.getByText("Action One");
fireEvent.click(buttonEl);
expect(actions[0].trigger).toHaveBeenCalledWith("somePayload", "somePayload2");
});
});