Skip to content

Commit

Permalink
Merge pull request #399 from dcos-labs/mp/feat/DCOS-57983-add-toggle-…
Browse files Browse the repository at this point in the history
…button

DCOS-57983: adds ToggleBox and related component
  • Loading branch information
brandonc authored Sep 4, 2019
2 parents 7f33c51 + d83b533 commit fe5c211
Show file tree
Hide file tree
Showing 29 changed files with 2,560 additions and 43 deletions.
41 changes: 11 additions & 30 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/card/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ Cards are used to visually group related content. Cards can be used to make it e

## Aspect ratio
Sometimes we want a card's size to obey a specific aspect ratio. When an aspect ratio is defined, the card's width and height will always follow that proportion unless the content is too high: in which case the card's height will increase.

## ButtonCard
A `ButtonCard` component is used to render a card that a user can interact with. It has all of the same properties as a `Card` component, but can also appear in an "on", "off", or "disabled" state.
62 changes: 62 additions & 0 deletions packages/card/components/ButtonCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Card, { CardProps } from "./Card";
import {
buttonCard,
buttonCardActive,
buttonCardDisabled,
buttonCardDisabledActive,
buttonCardFocused,
buttonCardFocusedActive
} from "../style";
import { cx } from "emotion";

export interface ButtonCardProps extends CardProps {
/**
* Whether the component should look and act like a disabled element
*/
disabled?: boolean;
/**
* Whether the component is in the "on" state
*/
isActive?: boolean;
/**
* Whether the component is controlled by a checkbox or radio input
*/
isInput?: boolean;
/**
* Whether the component's child input has focus
*/
hasFocus?: boolean;
}

class ButtonCard extends Card<ButtonCardProps, {}> {
public render() {
const { isActive, isInput, disabled, hasFocus, ...other } = this.props;
const tabIndex = disabled ? -1 : 0;
const buttonProps = !isInput
? {
tabIndex,
role: "button",
"aria-disabled": disabled,
"aria-pressed": isActive
}
: {};
const buttonCardProps = {
...{ "data-cy": "buttonCard" },
...buttonProps,
...other
};

return this.getCardElement(
buttonCardProps,
cx(buttonCard, {
[buttonCardActive]: isActive,
[buttonCardDisabled]: disabled,
[buttonCardDisabledActive]: disabled && isActive,
[buttonCardFocused]: hasFocus,
[buttonCardFocusedActive]: hasFocus && isActive
})
);
}
}

export default ButtonCard;
21 changes: 16 additions & 5 deletions packages/card/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as React from "react";
import { style } from "../style";
import { cardBase } from "../style";
import { cx } from "emotion";
import { preserveAspectRatio, padding } from "../../shared/styles/styleUtils";
import { SpaceSize } from "../../shared/styles/styleUtils/modifiers/modifierUtils";

export interface CardProps {
export interface CardProps extends React.HTMLProps<HTMLDivElement> {
/**
* `[width, height]` Keeps the card's width and height at a specific proportion. e.g.: 2:1 would be [2, 1]
*/
Expand All @@ -16,19 +16,30 @@ export interface CardProps {
children?: React.ReactNode | string;
}

class Card extends React.PureComponent<CardProps, {}> {
class Card<P extends CardProps, S extends {}> extends React.PureComponent<
P,
S
> {
public static defaultProps: Partial<CardProps> = {
paddingSize: "m"
};

public render() {
const { children, aspectRatio, paddingSize } = this.props;
return this.getCardElement(this.props);
}

protected getCardElement(cardProps: CardProps, additionalClasses?: string) {
const { children, aspectRatio, paddingSize, ...other } = cardProps;
const aspectRatioStyle = aspectRatio
? preserveAspectRatio(aspectRatio[0], aspectRatio[1])
: null;

return (
<div className={cx(style, aspectRatioStyle)} data-cy="card">
<div
className={cx(cardBase, aspectRatioStyle, additionalClasses)}
data-cy="card"
{...other}
>
<div className={padding("all", paddingSize)}>{children}</div>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions packages/card/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as Card } from "./components/Card";
export { default as ButtonCard } from "./components/ButtonCard";
26 changes: 26 additions & 0 deletions packages/card/stories/ButtonCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as React from "react";
import { storiesOf } from "@storybook/react";
import { withReadme } from "storybook-readme";
import { action } from "@storybook/addon-actions";
import ButtonCard from "../components/ButtonCard";
import { SpacingBox } from "../../styleUtils/modifiers";

const readme = require("../README.md");

storiesOf("Card/ButtonCard", module)
.addDecorator(withReadme([readme]))
.add("default", () => <ButtonCard>default</ButtonCard>)
.add("active", () => <ButtonCard isActive={true}>isActive</ButtonCard>)
.add("disabled", () => (
<React.Fragment>
<SpacingBox side="bottom">
<ButtonCard disabled={true}>disabled</ButtonCard>
</SpacingBox>
<ButtonCard disabled={true} isActive={true}>
disabled + isActive
</ButtonCard>
</React.Fragment>
))
.add("with onClick", () => (
<ButtonCard onClick={action("button clicked")}>default</ButtonCard>
));
79 changes: 75 additions & 4 deletions packages/card/style.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,84 @@
import { css } from "emotion";
import { border, borderRadius } from "../shared/styles/styleUtils/index";
import { themeBgPrimary } from "../design-tokens/build/js/designTokens";
import { borderRadius } from "../shared/styles/styleUtils/index";
import {
themeBgPrimary,
themeBorder,
themeBrandPrimary,
themeBgDisabled,
themeTextColorDisabled,
themeBgHover
} from "../design-tokens/build/js/designTokens";

export const style = css`
export const cardBase = css`
background-color: ${themeBgPrimary};
${border("all")};
box-shadow: 0 0 0 1px ${themeBorder};
${borderRadius("default")};
> div {
height: 100%;
}
`;

const buttonCardFocusStyles = `
box-shadow: 0 0 0 1px ${themeBrandPrimary};
outline: 0;
`;

const buttonCardActiveStyles = `
box-shadow: 0 0 0 2px ${themeBrandPrimary};
`;

const buttonCardFocusedActiveStyles = `background-color: ${themeBgHover};`;

export const buttonCard = css`
cursor: pointer;
&:hover,
&:focus {
${buttonCardFocusStyles};
}
&:active {
${buttonCardActiveStyles};
}
`;

export const buttonCardActive = css`
&,
&:hover,
&:focus {
${buttonCardActiveStyles};
}
&:focus {
${buttonCardFocusedActiveStyles};
}
`;

export const buttonCardFocused = css`
${buttonCardFocusStyles};
`;

export const buttonCardDisabled = css`
background-color: ${themeBgDisabled};
color: ${themeTextColorDisabled};
cursor: auto;
&,
&:hover,
&:focus {
box-shadow: 0 0 0 0 transparent;
}
`;

export const buttonCardDisabledActive = css`
&,
&:hover,
&:focus {
box-shadow: 0 0 0 2px ${themeBorder};
}
`;

export const buttonCardFocusedActive = css`
${buttonCardFocusedActiveStyles};
`;
Loading

0 comments on commit fe5c211

Please sign in to comment.