Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ModalTopActions): top actions component for internal use #2415

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.actions {
position: absolute;
right: var(--spacing-large);
top: var(--spacing-large);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";
import styles from "./ModalTopActions.module.scss";
import { ModalTopActionsButtonColor, ModalTopActionsColor, ModalTopActionsProps } from "./ModalTopActions.types";
import Flex from "../../Flex/Flex";
import IconButton from "../../IconButton/IconButton";
import { CloseSmall } from "../../Icon/Icons";
import { ButtonColor } from "../../Button/ButtonConstants";

const colorToButtonColor: Record<ModalTopActionsColor, ModalTopActionsButtonColor> = {
dark: ButtonColor.ON_INVERTED_BACKGROUND,
light: ButtonColor.ON_PRIMARY_COLOR
};

const ModalTopActions = ({ renderAction, color, closeButtonAriaLabel, onClose }: ModalTopActionsProps) => {
const buttonColor = colorToButtonColor[color] || ButtonColor.PRIMARY;

return (
<Flex className={styles.actions}>
{typeof renderAction === "function" ? renderAction(buttonColor) : renderAction}
<IconButton
icon={CloseSmall}
onClick={onClose}
size={IconButton.sizes.SMALL}
kind={IconButton.kinds.TERTIARY}
color={buttonColor}
ariaLabel={closeButtonAriaLabel}
YossiSaadi marked this conversation as resolved.
Show resolved Hide resolved
/>
</Flex>
);
};

export default ModalTopActions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";
import MenuButton from "../../MenuButton/MenuButton";
import IconButton from "../../IconButton/IconButton";
import { ButtonColor } from "../../Button/ButtonConstants";

export type ModalTopActionsColor = "light" | "dark";
export type ModalTopActionsButtonColor =
| ButtonColor.PRIMARY
| ButtonColor.ON_PRIMARY_COLOR
| ButtonColor.ON_INVERTED_BACKGROUND;

export interface ModalTopActionsProps {
/**
* action can be passed either as a function or direct
* it allows passing back to consumer the color he chose, so he won't have to define it twice
*/
renderAction?:
| React.ReactElement<typeof MenuButton | typeof IconButton>
| ((color?: ModalTopActionsButtonColor) => React.ReactElement<typeof MenuButton | typeof IconButton>);
color?: ModalTopActionsColor;
closeButtonAriaLabel?: string;
onClose?: React.MouseEventHandler<HTMLDivElement>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from "react";
import { render, fireEvent, within } from "@testing-library/react";
import ModalTopActions from "../ModalTopActions";
import IconButton from "../../../IconButton/IconButton";
import { Feedback as FeedbackIcon } from "../../../Icon/Icons";
import { ButtonColor } from "../../../Button/ButtonConstants";
import { camelCase } from "lodash-es";

describe("ModalTopActions", () => {
const closeButtonAriaLabel = "Close modal";

it("renders the close button with the correct aria-label", () => {
const { getByLabelText } = render(<ModalTopActions closeButtonAriaLabel={closeButtonAriaLabel} />);

expect(getByLabelText(closeButtonAriaLabel)).toBeInTheDocument();
});

it("calls onClose when the close button is clicked", () => {
const mockOnClose = jest.fn();

const { getByLabelText } = render(
<ModalTopActions onClose={mockOnClose} closeButtonAriaLabel={closeButtonAriaLabel} />
);

fireEvent.click(getByLabelText(closeButtonAriaLabel));
expect(mockOnClose).toHaveBeenCalled();
});

it("does not fail when onClose is not provided", () => {
const { getByLabelText } = render(<ModalTopActions closeButtonAriaLabel={closeButtonAriaLabel} />);
fireEvent.click(getByLabelText(closeButtonAriaLabel));
expect(() => getByLabelText(closeButtonAriaLabel)).not.toThrow();
});

it("renders the action button using the renderAction prop as a function", () => {
const renderAction = jest.fn(color => <IconButton data-testid="extra-action" icon={FeedbackIcon} color={color} />);
const { getByTestId } = render(<ModalTopActions renderAction={renderAction} />);

expect(within(getByTestId("extra-action")).getByTestId("icon")).toBeInTheDocument();
});

it("calls renderAction with correct color argument", () => {
const renderAction = jest.fn(color => <IconButton data-testid="extra-action" icon={FeedbackIcon} color={color} />);
render(<ModalTopActions color="dark" renderAction={renderAction} />);

expect(renderAction).toHaveBeenCalledWith(ButtonColor.ON_INVERTED_BACKGROUND);
});

it("renders the action button using the renderAction prop directly", () => {
const renderAction = (
<IconButton data-testid="extra-action" icon={FeedbackIcon} color={IconButton.colors.ON_PRIMARY_COLOR} />
);
const { getByTestId } = render(<ModalTopActions renderAction={renderAction} />);

expect(within(getByTestId("extra-action")).getByTestId("icon")).toBeInTheDocument();
});

it("applies the correct color when 'dark' is passed", () => {
const { getByLabelText } = render(<ModalTopActions color="dark" closeButtonAriaLabel={closeButtonAriaLabel} />);
expect(getByLabelText(closeButtonAriaLabel)).toHaveClass(camelCase("color-" + ButtonColor.ON_INVERTED_BACKGROUND));
});

it("applies the correct color when 'light' is passed", () => {
const { getByLabelText } = render(<ModalTopActions color="light" closeButtonAriaLabel={closeButtonAriaLabel} />);
expect(getByLabelText(closeButtonAriaLabel)).toHaveClass(camelCase("color-" + ButtonColor.ON_PRIMARY_COLOR));
});

it("applies the default color when no color is passed", () => {
const { getByLabelText } = render(<ModalTopActions closeButtonAriaLabel={closeButtonAriaLabel} />);
expect(getByLabelText(closeButtonAriaLabel)).toHaveClass(camelCase("color-" + ButtonColor.PRIMARY));
});
});