@edux-design/buttons 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -3
- package/README.md +62 -50
- package/dist/chevron-EVM4GL6D.svg +4 -0
- package/dist/chunk-M47FNL4P.mjs +10 -0
- package/dist/elements/Button.js +39 -0
- package/dist/elements/Button.mjs +6 -0
- package/dist/index.d.mts +244 -0
- package/dist/index.d.ts +244 -0
- package/dist/index.js +850 -0
- package/dist/index.mjs +825 -0
- package/package.json +4 -3
- package/src/demo/ButtonCompatibility.stories.jsx +115 -0
- package/src/elements/Button.a11y.test.jsx +60 -0
- package/src/elements/Button.test.jsx +3 -3
- package/src/elements/ButtonCounter.jsx +24 -9
- package/src/elements/ButtonCounter.test.jsx +25 -4
- package/src/elements/ButtonGroup.jsx +20 -5
- package/src/elements/ButtonGroup.test.jsx +61 -0
- package/src/elements/IconButton.jsx +16 -5
- package/src/elements/IconButton.test.jsx +62 -0
- package/src/elements/MenuButton.jsx +45 -21
- package/src/elements/MenuButton.test.jsx +40 -0
- package/src/elements/testA11y.js +14 -0
|
@@ -1,44 +1,68 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { MenuButtonContext } from "../util/MenuButtonContext";
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
3
2
|
import { cx } from "@edux-design/utils";
|
|
4
3
|
import { Chevron } from "@edux-design/icons";
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Props for the MenuButton component.
|
|
8
7
|
*
|
|
9
|
-
* @typedef {
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* @typedef {React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
9
|
+
* children?: React.ReactNode,
|
|
10
|
+
* caret?: React.ReactNode,
|
|
11
|
+
* size?: "small"|"medium",
|
|
12
|
+
* className?: string,
|
|
13
|
+
* }} MenuButtonProps
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* MenuButton component.
|
|
16
18
|
*
|
|
17
|
-
* Provides a styled
|
|
18
|
-
*
|
|
19
|
-
*
|
|
19
|
+
* Provides a styled button intended for menu triggers.
|
|
20
|
+
* Consumers should pass `aria-expanded`, `aria-controls`, and any
|
|
21
|
+
* custom labeling they need. `aria-haspopup="menu"` is applied by default.
|
|
20
22
|
*
|
|
21
23
|
* @component
|
|
22
|
-
* @type {React.
|
|
24
|
+
* @type {React.ForwardRefExoticComponent<MenuButtonProps & React.RefAttributes<HTMLButtonElement>>}
|
|
23
25
|
*
|
|
24
26
|
* @example
|
|
25
27
|
* ```jsx
|
|
26
|
-
* <MenuButton
|
|
28
|
+
* <MenuButton aria-expanded={open} onClick={() => setOpen((prev) => !prev)}>
|
|
29
|
+
* Options
|
|
30
|
+
* </MenuButton>
|
|
27
31
|
* ```
|
|
28
32
|
*/
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
export const MenuButton = forwardRef(function MenuButton(
|
|
34
|
+
{
|
|
35
|
+
children,
|
|
36
|
+
caret,
|
|
37
|
+
size = "medium",
|
|
38
|
+
className,
|
|
39
|
+
type = "button",
|
|
40
|
+
"aria-haspopup": ariaHasPopup = "menu",
|
|
41
|
+
...props
|
|
42
|
+
},
|
|
43
|
+
ref,
|
|
44
|
+
) {
|
|
45
|
+
const styles = cx(
|
|
46
|
+
"inline-flex items-center justify-center gap-8 rounded-lg border-2 border-border-primary-base bg-bg-base px-16 text-fg-primary-emphasis transition-all duration-300 ease-in-out outline-none focus:shadow-focus focus-visible:shadow-focus disabled:cursor-not-allowed disabled:border-border-subtle disabled:bg-bg-inactive disabled:text-fg-inactive",
|
|
32
47
|
{
|
|
33
|
-
"h-
|
|
34
|
-
"h-
|
|
35
|
-
}
|
|
48
|
+
"min-h-control-sm text-sm": size === "small",
|
|
49
|
+
"min-h-control-md text-base": size === "medium",
|
|
50
|
+
},
|
|
51
|
+
className,
|
|
36
52
|
);
|
|
37
53
|
|
|
38
54
|
return (
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
<button
|
|
56
|
+
{...props}
|
|
57
|
+
ref={ref}
|
|
58
|
+
type={type}
|
|
59
|
+
aria-haspopup={ariaHasPopup}
|
|
60
|
+
className={styles}
|
|
61
|
+
>
|
|
62
|
+
{children ? <span>{children}</span> : null}
|
|
63
|
+
<span aria-hidden="true" className="flex items-center text-current">
|
|
64
|
+
{caret ?? <Chevron />}
|
|
65
|
+
</span>
|
|
66
|
+
</button>
|
|
43
67
|
);
|
|
44
|
-
}
|
|
68
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { MenuButton } from "./MenuButton";
|
|
4
|
+
|
|
5
|
+
describe("MenuButton", () => {
|
|
6
|
+
it("renders a semantic button trigger", () => {
|
|
7
|
+
render(<MenuButton>Options</MenuButton>);
|
|
8
|
+
|
|
9
|
+
expect(screen.getByRole("button", { name: "Options" })).toHaveAttribute(
|
|
10
|
+
"aria-haspopup",
|
|
11
|
+
"menu",
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("forwards interactive props to the native button", () => {
|
|
16
|
+
const onClick = vi.fn();
|
|
17
|
+
render(
|
|
18
|
+
<MenuButton aria-expanded={true} onClick={onClick}>
|
|
19
|
+
Options
|
|
20
|
+
</MenuButton>,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const button = screen.getByRole("button", { name: "Options" });
|
|
24
|
+
fireEvent.click(button);
|
|
25
|
+
|
|
26
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(button).toHaveAttribute("aria-expanded", "true");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it.each([
|
|
31
|
+
["small", "min-h-control-sm"],
|
|
32
|
+
["medium", "min-h-control-md"],
|
|
33
|
+
])("maps the %s size to the shared control token", (size, className) => {
|
|
34
|
+
render(<MenuButton size={size}>Options</MenuButton>);
|
|
35
|
+
|
|
36
|
+
expect(screen.getByRole("button", { name: "Options" })).toHaveClass(
|
|
37
|
+
className,
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import axe from "axe-core";
|
|
2
|
+
|
|
3
|
+
export async function expectNoAxeViolations(container) {
|
|
4
|
+
const results = await axe.run(container, {
|
|
5
|
+
rules: {
|
|
6
|
+
"color-contrast": { enabled: false },
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
const message = results.violations
|
|
10
|
+
.map((violation) => `${violation.id}: ${violation.help}`)
|
|
11
|
+
.join("\n");
|
|
12
|
+
|
|
13
|
+
expect(results.violations, message).toHaveLength(0);
|
|
14
|
+
}
|