Skip to content

Commit

Permalink
feat: add optional className props (#912)
Browse files Browse the repository at this point in the history
* fix: optional className not being passed

to the Flex.tsx & FlexItem.tsx components.

This change explicity combines classNames via the Emotion's cx
utility. classNames were incorrectly passed into Emotion's css util,
which resulted in the classNames disappearing.

* feat: add optional className props

to Actions and Graphic Elements components.

This allows the developer user to add custom styling.

* feat: add optional header.className prop to Card

Allows the developer user to add custom styling.

* test: update snapshot tests

* chore: format comments for consistency
  • Loading branch information
davidtaing authored Jan 10, 2023
1 parent 666ba73 commit c47eb90
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 27 deletions.
13 changes: 11 additions & 2 deletions packages/avatar/components/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@ export interface AvatarProps {
* Which icon size to use for the width and height
*/
size?: IconSize;
/**
* Allows custom styling
*/
className?: string;
}

const Avatar = ({ label, src, size = DEFAULT_AVATAR_SIZE }: AvatarProps) => (
const Avatar = ({
label,
src,
className,
size = DEFAULT_AVATAR_SIZE
}: AvatarProps) => (
<div
className={cx(avatarContainer, avatarSize(iconSizes[size]))}
className={cx(avatarContainer, avatarSize(iconSizes[size]), className)}
role="img"
aria-label={label}
data-cy="avatar"
Expand Down
7 changes: 6 additions & 1 deletion packages/badge/components/ColorCodedBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ export interface ColorCodedBadgeProps {
color?: React.CSSProperties["color"];
iconShape?: SystemIcons;
children?: JSX.Element | string;
/**
* Allows custom styling
*/
className?: string;
}

const ICON_SIZE = "xxs";

const ColorCodedBadge = ({
children,
color,
className,
iconShape
}: ColorCodedBadgeProps) => {
const iconSize = parseInt(iconSizes[ICON_SIZE], 10);

return (
<Badge appearance="outline">
<Badge appearance="outline" className={className}>
{iconShape ? (
<Icon shape={iconShape} size={ICON_SIZE} color={color} />
) : (
Expand Down
13 changes: 11 additions & 2 deletions packages/badge/components/badge.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from "react";
import { cx } from "@emotion/css";
import { badge } from "../style";

export type BadgeAppearance =
Expand All @@ -12,11 +13,19 @@ export type BadgeAppearance =
export interface BadgeProps {
appearance?: BadgeAppearance;
children: React.ReactNode | string;
/**
* Allows custom styling
*/
className?: string;
}

export const Badge = ({ appearance = "default", children }: BadgeProps) => {
export const Badge = ({
appearance = "default",
children,
className
}: BadgeProps) => {
return (
<span className={badge(appearance)} data-cy="badge">
<span className={cx(badge(appearance), className)} data-cy="badge">
{children}
</span>
);
Expand Down
11 changes: 8 additions & 3 deletions packages/badge/components/badgeButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { css } from "@emotion/css";
import { css, cx } from "@emotion/css";
import * as React from "react";
import Clickable from "../../clickable/components/clickable";
import { badge as badgeButton } from "../style";
Expand All @@ -11,6 +11,10 @@ export interface BadgeButtonProps {
| "warning"
| "danger"
| "outline";
/**
* Allows custom styling
*/
className?: string;
onClick: (event?: React.SyntheticEvent<HTMLElement>) => void;
/**
* Tab index indicates if an element can be focused for more information see
Expand All @@ -25,19 +29,20 @@ export interface BadgeButtonProps {
const BadgeButton = ({
appearance = "default",
children,
className,
onClick,
tabIndex = -1,
"data-cy": dataCy = "badgeButton"
}: BadgeButtonProps) => {
const className = css`
const defaultClassName = css`
outline: none;
cursor: pointer;
${badgeButton(appearance)};
`;

return (
<Clickable action={onClick} tabIndex={tabIndex} data-cy={dataCy}>
<span className={className}>{children}</span>
<span className={cx(defaultClassName, className)}>{children}</span>
</Clickable>
);
};
Expand Down
5 changes: 4 additions & 1 deletion packages/button/components/ButtonBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export interface ButtonProps extends LinkProps {
*/
ariaLabel?: string;
children?: React.ReactNode | string;
/**
* Allows custom styling
*/
className?: string;
/**
* whether or not the button is enabled
*/
Expand Down Expand Up @@ -88,7 +92,6 @@ export interface ButtonProps extends LinkProps {

export interface ButtonBaseProps extends ButtonProps {
appearance: ButtonAppearances;
className?: string;
}

const ButtonContent = ({ iconStart, iconEnd, isProcessing, children }) => {
Expand Down
23 changes: 16 additions & 7 deletions packages/card/components/ButtonCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export interface ButtonCardProps extends CardProps {
* Whether the component's child input has focus
*/
hasFocus?: boolean;
/**
* Allows custom styling
*/
className?: string;
}

const ButtonCard = ({
Expand All @@ -35,6 +39,7 @@ const ButtonCard = ({
disabled,
hasFocus,
onClick,
className,
onKeyPress,
...other
}: ButtonCardProps) => {
Expand Down Expand Up @@ -64,13 +69,17 @@ const ButtonCard = ({

return (
<Card
className={cx(buttonCard, {
[buttonCardActive]: isActive,
[buttonCardDisabled]: disabled,
[buttonCardDisabledActive]: disabled && isActive,
[buttonCardFocused]: hasFocus,
[buttonCardFocusedActive]: hasFocus && isActive
})}
className={cx(
buttonCard,
{
[buttonCardActive]: isActive,
[buttonCardDisabled]: disabled,
[buttonCardDisabledActive]: disabled && isActive,
[buttonCardFocused]: hasFocus,
[buttonCardFocusedActive]: hasFocus && isActive
},
className
)}
{...buttonProps}
data-cy="buttonCard"
{...other}
Expand Down
16 changes: 14 additions & 2 deletions packages/card/components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,14 @@ export interface CardProps extends React.HTMLProps<HTMLDivElement> {
* Size can be set to "s", "m", or "l" to increase the header height. If not specified, size will default to medium.
*/
size?: HeaderSizes;
/**
* Allows custom header styling
*/
className?: string;
};

/**
* Allows custom styling
*/
className?: string;
}

Expand All @@ -62,7 +68,13 @@ const Card = ({
{...other}
>
{header?.headerImg && (
<div className={cx(cardHeaderImage, headerHeight[headerSize])}>
<div
className={cx(
cardHeaderImage,
headerHeight[headerSize],
header.className
)}
>
<img src={header.headerImg} alt={header.headerImgAltText ?? ""} />
</div>
)}
Expand Down
33 changes: 25 additions & 8 deletions packages/icon/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,33 @@ const DEFAULT_ICON_SIZE: IconSize = "s";

export type IconShapes = SystemIcons | ProductIcons;
export interface IconProps {
/** If an icon is more than decorative and requires further context include a description for screen readers */
/**
* If an icon is more than decorative and requires further context include a description for screen readers
*/
ariaLabel?: string;
/** The fill color of the icon */
/**
* Allows custom styling
*/
className?: string;
/**
* The fill color of the icon
*/
color?: string;
/** The id of the SVG symbol we're rendering from a generated sprite */
/**
* The id of the SVG symbol we're rendering from a generated sprite
*/
shape: SystemIcons | ProductIcons;
/** Which icon size to use for the width and height of the icon */
/**
* Which icon size to use for the width and height of the icon
*/
size?: IconSize;
/** human-readable selector used for writing tests */
/**
* human-readable selector used for writing tests
*/
["data-cy"]?: string;
/** Sets display to block if true */
/**
* Sets display to block if true
*/
block?: boolean;
}

Expand All @@ -31,7 +47,8 @@ const Icon = ({
shape,
ariaLabel,
"data-cy": dataCy,
block
block,
className
}: IconProps) => {
const svgColor = color || "currentColor";
const iconSize = iconSizes[size];
Expand All @@ -44,7 +61,7 @@ const Icon = ({
viewBox={`0 0 ${parseInt(iconSize, 10)} ${parseInt(iconSize, 10)}`}
role="img"
aria-label={ariaLabel || `${shape} icon`}
className={cx(icon, tintSVG(svgColor), block ? blockIcon : "")}
className={cx(icon, tintSVG(svgColor), block ? blockIcon : "", className)}
data-cy={["icon", dataCy].filter(Boolean).join(" ")}
>
<use xlinkHref={`#${shape}`} />
Expand Down
10 changes: 9 additions & 1 deletion packages/inlineBorderedItems/components/InlineBorderedItems.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import * as React from "react";
import { cx } from "@emotion/css";
import { SpaceSize } from "../../shared/styles/styleUtils/modifiers/modifierUtils";
import { inlineBorderedItems } from "../style";

export interface InlineBorderedItemsProps {
children:
| Array<React.ReactElement<HTMLElement>>
| React.ReactElement<HTMLElement>;
/**
* Allows custom styling
*/
className?: string;
gutterSize?: SpaceSize;
}

const InlineBorderedItems = ({
children,
className,
gutterSize = "s"
}: InlineBorderedItemsProps) => (
<div className={inlineBorderedItems(gutterSize!)}>{children}</div>
<div className={cx(inlineBorderedItems(gutterSize!), className)}>
{children}
</div>
);

export default InlineBorderedItems;

0 comments on commit c47eb90

Please sign in to comment.