From 6a9ea9cde0d0b3bb0fc9d03492a3e434e4e90e82 Mon Sep 17 00:00:00 2001 From: Kevin Liu Date: Wed, 4 Dec 2024 11:41:31 -0800 Subject: [PATCH] T80804 KebabButton (#1509) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Changes Please provide a brief summary of the changes made and why they were made. Include any notes, screenshots, or videos that may be helpful for developers reviewing this pull request. ## ✅ Checklist Easy UI has certain UX standards that must be met. In general, non-trivial changes should meet the following criteria: - [x] Visuals match Design Specs in Figma - [x] Stories accompany any component changes - [x] Code is in accordance with our style guide - [x] Design tokens are utilized - [x] Unit tests accompany any component changes - [x] TSDoc is written for any API surface area - [ ] Specs are up-to-date - [x] Console is free from warnings - [x] No accessibility violations are reported - [x] Cross-browser check is performed (Chrome, Safari, Firefox) - [x] Changeset is added ~Strikethrough~ any items that are not applicable to this pull request. --------- Co-authored-by: Kevin Liu --- .changeset/quiet-ways-laugh.md | 5 +++ .../DataGrid/ActionsCellContent.module.scss | 10 ----- .../src/DataGrid/ActionsCellContent.tsx | 14 +++---- .../src/DataGrid/ExpandCellContent.tsx | 2 +- easy-ui-react/src/KebabButton/KebabButton.mdx | 20 ++++++++++ .../src/KebabButton/KebabButton.module.scss | 11 ++++++ .../src/KebabButton/KebabButton.stories.tsx | 38 ++++++++++++++++++ .../src/KebabButton/KebabButton.test.tsx | 11 ++++++ easy-ui-react/src/KebabButton/KebabButton.tsx | 39 +++++++++++++++++++ easy-ui-react/src/KebabButton/index.ts | 1 + .../UnstyledPressButton.module.scss | 0 .../UnstyledPressButton.tsx | 0 12 files changed, 132 insertions(+), 19 deletions(-) create mode 100644 .changeset/quiet-ways-laugh.md create mode 100644 easy-ui-react/src/KebabButton/KebabButton.mdx create mode 100644 easy-ui-react/src/KebabButton/KebabButton.module.scss create mode 100644 easy-ui-react/src/KebabButton/KebabButton.stories.tsx create mode 100644 easy-ui-react/src/KebabButton/KebabButton.test.tsx create mode 100644 easy-ui-react/src/KebabButton/KebabButton.tsx create mode 100644 easy-ui-react/src/KebabButton/index.ts rename easy-ui-react/src/{DataGrid => UnstyledButton}/UnstyledPressButton.module.scss (100%) rename easy-ui-react/src/{DataGrid => UnstyledButton}/UnstyledPressButton.tsx (100%) diff --git a/.changeset/quiet-ways-laugh.md b/.changeset/quiet-ways-laugh.md new file mode 100644 index 000000000..db98d8aee --- /dev/null +++ b/.changeset/quiet-ways-laugh.md @@ -0,0 +1,5 @@ +--- +"@easypost/easy-ui": minor +--- + +feat: KebabButton diff --git a/easy-ui-react/src/DataGrid/ActionsCellContent.module.scss b/easy-ui-react/src/DataGrid/ActionsCellContent.module.scss index 8a0f303e3..d95b0de94 100644 --- a/easy-ui-react/src/DataGrid/ActionsCellContent.module.scss +++ b/easy-ui-react/src/DataGrid/ActionsCellContent.module.scss @@ -4,13 +4,3 @@ display: inline-flex; gap: design-token("space.1.5"); } - -.MenuButton { - display: inline-flex; - border-radius: design-token("shape.border_radius.sm"); -} - -.open { - background: design-token("color.primary.700"); - color: design-token("color.neutral.000"); -} diff --git a/easy-ui-react/src/DataGrid/ActionsCellContent.tsx b/easy-ui-react/src/DataGrid/ActionsCellContent.tsx index 5706a13bd..5c10fd033 100644 --- a/easy-ui-react/src/DataGrid/ActionsCellContent.tsx +++ b/easy-ui-react/src/DataGrid/ActionsCellContent.tsx @@ -1,10 +1,9 @@ -import MoreVertIcon from "@easypost/easy-ui-icons/MoreVert"; import React, { useCallback, useState } from "react"; import { Icon } from "../Icon"; import { Menu } from "../Menu"; import { Text } from "../Text"; -import { classNames } from "../utilities/css"; -import { UnstyledPressButton } from "./UnstyledPressButton"; +import { KebabButton } from "../KebabButton"; +import { UnstyledPressButton } from "../UnstyledButton/UnstyledPressButton"; import { useDataGridRow } from "./context"; import { ActionRowAction as ActionRowActionType, @@ -45,14 +44,13 @@ function MenuRowAction({ rowAction }: { rowAction: MenuRowActionType }) { setIsOpen(isOpen); }, []); - const className = classNames(styles.MenuButton, isOpen && styles.open); return ( - - {accessibilityLabel} - - + {rowAction.renderMenuOverlay()} diff --git a/easy-ui-react/src/DataGrid/ExpandCellContent.tsx b/easy-ui-react/src/DataGrid/ExpandCellContent.tsx index a7b2c187c..ff54c1eda 100644 --- a/easy-ui-react/src/DataGrid/ExpandCellContent.tsx +++ b/easy-ui-react/src/DataGrid/ExpandCellContent.tsx @@ -2,7 +2,7 @@ import ArrowForwardIos from "@easypost/easy-ui-icons/ArrowForwardIos"; import React, { useCallback } from "react"; import { Icon } from "../Icon"; import { classNames } from "../utilities/css"; -import { UnstyledPressButton } from "./UnstyledPressButton"; +import { UnstyledPressButton } from "../UnstyledButton/UnstyledPressButton"; import { Text } from "../Text"; import styles from "./ExpandCellContent.module.scss"; diff --git a/easy-ui-react/src/KebabButton/KebabButton.mdx b/easy-ui-react/src/KebabButton/KebabButton.mdx new file mode 100644 index 000000000..59140d5b0 --- /dev/null +++ b/easy-ui-react/src/KebabButton/KebabButton.mdx @@ -0,0 +1,20 @@ +import React from "react"; +import { Canvas, Meta, ArgTypes } from "@storybook/blocks"; +import { KebabButton } from "./KebabButton"; +import * as KebabButtonStories from "./KebabButton.stories"; + + + +# KebabButton + +A `` is a kebab menu icon button. The icon represents an overflow menu that reveals a list of options or actions related to the current context when clicked. + +## Use with Menu + +A `` is designed to used as trigger for ``. + + + +## Properties + + diff --git a/easy-ui-react/src/KebabButton/KebabButton.module.scss b/easy-ui-react/src/KebabButton/KebabButton.module.scss new file mode 100644 index 000000000..fc3496025 --- /dev/null +++ b/easy-ui-react/src/KebabButton/KebabButton.module.scss @@ -0,0 +1,11 @@ +@use "../styles/common" as *; + +.KebabButton { + display: inline-flex; + border-radius: design-token("shape.border_radius.md"); +} + +.KebabButton[aria-expanded="true"] { + background-color: design-token("color.primary.700"); + color: design-token("color.neutral.000"); +} diff --git a/easy-ui-react/src/KebabButton/KebabButton.stories.tsx b/easy-ui-react/src/KebabButton/KebabButton.stories.tsx new file mode 100644 index 000000000..d8833465c --- /dev/null +++ b/easy-ui-react/src/KebabButton/KebabButton.stories.tsx @@ -0,0 +1,38 @@ +import { action } from "@storybook/addon-actions"; +import { Meta, StoryObj } from "@storybook/react"; +import React from "react"; +import { OverlayLayoutDecorator } from "../utilities/storybook"; +import { Menu } from "../Menu"; +import { KebabButton } from "./KebabButton"; + +type Story = StoryObj; + +const meta: Meta = { + title: "Components/Button/KebabButton", + component: KebabButton, + argTypes: { + accessibilityLabel: { + control: "text", + description: "Optional custom accessibility label describing the action.", + }, + }, + args: { accessibilityLabel: "Actions" }, +}; + +export default meta; + +export const WithMenu: Story = { + render: () => ( + + + + + + Copy + Cut + Paste + + + ), + decorators: [OverlayLayoutDecorator], +}; diff --git a/easy-ui-react/src/KebabButton/KebabButton.test.tsx b/easy-ui-react/src/KebabButton/KebabButton.test.tsx new file mode 100644 index 000000000..96c0b3e77 --- /dev/null +++ b/easy-ui-react/src/KebabButton/KebabButton.test.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { KebabButton } from "./KebabButton"; + +describe("", () => { + it("should render a button with icon", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + expect(screen.getByRole("img", { hidden: true })).toBeInTheDocument(); + }); +}); diff --git a/easy-ui-react/src/KebabButton/KebabButton.tsx b/easy-ui-react/src/KebabButton/KebabButton.tsx new file mode 100644 index 000000000..6e0c52ec0 --- /dev/null +++ b/easy-ui-react/src/KebabButton/KebabButton.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import MoreVertIcon from "@easypost/easy-ui-icons/MoreVert"; +import { AriaButtonProps } from "react-aria"; +import { UnstyledPressButton } from "../UnstyledButton/UnstyledPressButton"; +import { Text } from "../Text"; +import { Icon } from "../Icon"; +import styles from "./KebabButton.module.scss"; + +export type KebabButtonProps = AriaButtonProps & { + /** Optional custom accessibility label describing the action. */ + accessibilityLabel?: string; +}; + +/** + * Typically used as a trigger to display a set of options + * for the user to choose from. + * + * @remarks + * Can be used alongside Easy UI's `` as the trigger + * element. + */ +export const KebabButton = React.forwardRef( + (props, inRef) => { + const { accessibilityLabel = "Actions", ...restProps } = props; + + return ( + + {accessibilityLabel} + + + ); + }, +); + +KebabButton.displayName = "KebabButton"; diff --git a/easy-ui-react/src/KebabButton/index.ts b/easy-ui-react/src/KebabButton/index.ts new file mode 100644 index 000000000..388964dc2 --- /dev/null +++ b/easy-ui-react/src/KebabButton/index.ts @@ -0,0 +1 @@ +export * from "./KebabButton"; diff --git a/easy-ui-react/src/DataGrid/UnstyledPressButton.module.scss b/easy-ui-react/src/UnstyledButton/UnstyledPressButton.module.scss similarity index 100% rename from easy-ui-react/src/DataGrid/UnstyledPressButton.module.scss rename to easy-ui-react/src/UnstyledButton/UnstyledPressButton.module.scss diff --git a/easy-ui-react/src/DataGrid/UnstyledPressButton.tsx b/easy-ui-react/src/UnstyledButton/UnstyledPressButton.tsx similarity index 100% rename from easy-ui-react/src/DataGrid/UnstyledPressButton.tsx rename to easy-ui-react/src/UnstyledButton/UnstyledPressButton.tsx