new admin branding (#2692)

* add comment revisions

* add strings

* update fixtures

* fix fixtures

* [CORL-629] Update admin moderation containers to use new branding tokens (#2600)

* Convert the moderation card components to use new styles

CORL-629

* Style the details area in the moderate card using new font and color styles

CORL-629

* Update FlagDetails on Moderate Card to match new branding styles

CORL-629

* Remove typography from ModerateCard Username

CORL-629

* Remove unnecessary new typography tokens created while experimenting

CORL-629

* Update background colors of username buttons on moderate card

CORL-629

* Update approve reject buttons to use new colors

CORL-629

* Fix alignment of username and "Reply to" in InReplyTo component

CORL-629

* Update moderated by on moderate card to match new branding

CORL-629

* Create distinct admin marker and counter components with new branding styles

Update moderate card components to use these distinct newly branded styles.

CORL-629

* Create independent Tabs control with new styling for admin area

Use it on the moderate card details area.

CORL-629

* Remove letter spacing from moderate card styles

CORL-629

* Update "DETAILS" tab on moderate card to be named "INFO"

CORL-629

* Update some missing v2 css variables

CORL-629

* Tweak the spacing of the COMMENT link and title on the moderate card

CORL-629

* Update details font color on moderate card

CORL-629

* Update spacing on newly styled Admin Tab component

CORL-629

* Update spacing of reply to username on the moderate card

CORL-629

* Update moderate card comment text area minimum width and height

CORL-629

* Update vertical divider on moderate card to updated branding

CORL-629

* Introduce new bold open sans font face

Downgrades all existing bold's to semi-bold as they were equivalent before.

CORL-629

* Update moderate card details tabs to use new bold open sans font

CORL-629

* Create an admin specific timestamp control that implements new branding

CORL-629

* Migrate shared moderate card styles into child component files

CORL-629

* [CORL-631] Update admin side main navigation to new branding styles (#2625)

* Convert the moderation card components to use new styles

CORL-629

* Style the details area in the moderate card using new font and color styles

CORL-629

* Update FlagDetails on Moderate Card to match new branding styles

CORL-629

* Remove typography from ModerateCard Username

CORL-629

* Remove unnecessary new typography tokens created while experimenting

CORL-629

* Update background colors of username buttons on moderate card

CORL-629

* Update approve reject buttons to use new colors

CORL-629

* Fix alignment of username and "Reply to" in InReplyTo component

CORL-629

* Update moderated by on moderate card to match new branding

CORL-629

* Create distinct admin marker and counter components with new branding styles

Update moderate card components to use these distinct newly branded styles.

CORL-629

* Create independent Tabs control with new styling for admin area

Use it on the moderate card details area.

CORL-629

* Remove letter spacing from moderate card styles

CORL-629

* Update "DETAILS" tab on moderate card to be named "INFO"

CORL-629

* Update some missing v2 css variables

CORL-629

* Tweak the spacing of the COMMENT link and title on the moderate card

CORL-629

* Update details font color on moderate card

CORL-629

* Update spacing on newly styled Admin Tab component

CORL-629

* Update spacing of reply to username on the moderate card

CORL-629

* Update moderate card comment text area minimum width and height

CORL-629

* Update vertical divider on moderate card to updated branding

CORL-629

* Introduce new bold open sans font face

Downgrades all existing bold's to semi-bold as they were equivalent before.

CORL-629

* Update moderate card details tabs to use new bold open sans font

CORL-629

* Create an admin specific timestamp control that implements new branding

CORL-629

* Migrate shared moderate card styles into child component files

CORL-629

* Update main navigation admin side to use new branding styles

CORL-631

* Bring in some preliminary color and font styles for the search bar

This helps it match navigation, but does not update all the sub-styles of
the search bar's many child components.

CORL-631

* [CORL-694] use new styles for decision history (#2644)

* use new styles for decision history

* remove reference to nonexistent variable

* update snaps

* [CORL 695] admin branding - user menu (#2650)

* update user menu, popover, and dropdown to new design tokens

* fix feature button colours

* use v2 spacing vars

* admin branding: tables (#2651)

* update user menu, popover, and dropdown to new design tokens

* fix feature button colours

* use v2 spacing vars

* style stories and users table

* update snap

* update user status font weight

* remove duplicate declaration

* fix line height references

* update design tokens for text inputs and selects (#2658)

* use box shadow instead of outline for focus states

* Update snapshots to adjust for resolved merge conflicts

* Bring new branding styles to button components (#2667)

* Create preliminary filled and outline buttons that match branding

* Add adornment button variant to newly branded button

* Add the ghost variant to the newly branded button component

* Add underlined variant to the newly branded button component

* Remove external Icon composes property

* Convert camel-case button variables to kebab-case

* [CORL-672] Configuration pages new branding (#2673)

* style config side nav

* general configuration tab branding

* organization tab branding

* moderation configuration branding

* banned/suspect words config branding

* email config branding

* advanced settings branding

* auth config branding

* move helpertext and fieldset components

* add textarea component

* add back gutters on table search

* update snaps

* fix header component to accept labels

* use new spacing form components

* use spacing components for advanced config

* fix tests

* fix line height of radio buttons

* update validation message branding

* update snaps

* fix paths casing

* try lowercase then

* add docs for new form components

* move description component

* update snaps

* move components to /ui/v2

* [CORL-673] Update the moderate area search menu to use new branding styles (#2672)

* Create preliminary filled and outline buttons that match branding

* Add adornment button variant to newly branded button

* Add the ghost variant to the newly branded button component

* Add underlined variant to the newly branded button component

* Remove external Icon composes property

* Convert camel-case button variables to kebab-case

* Preliminarily migrate raw components into v2 to prep for branding updates

CORL-673

* Preliminarily update the search results bar to use new branding tokens

CORL-673

* Update styles to new branding on migrated v2 client-ui components

CORL-673

* Update see all and moderate all search option icons

CORL-673

* style config side nav

* general configuration tab branding

* organization tab branding

* moderation configuration branding

* banned/suspect words config branding

* email config branding

* advanced settings branding

* auth config branding

* move helpertext and fieldset components

* add textarea component

* add back gutters on table search

* update snaps

* fix header component to accept labels

* use new spacing form components

* use spacing components for advanced config

* fix tests

* fix line height of radio buttons

* update validation message branding

* update snaps

* fix paths casing

* try lowercase then

* add docs for new form components

* move description component

* update snaps

* move components to /ui/v2

* Set size="md" on the Icon component and not the Localized component

CORL-673

* tag component

* [CORL-669] user drawer + misc branding (#2686)

* tag component

* top of user drawer branding

* recent history in drawer

* add tooltips component

* use correct spacing prop

* history tabs

* user history and rejected comments

* move callout component'

* moderator notes components

* account history table

* table user row

* swap in tabs components

* remove overflow:hidden on buttons

* update snaps

* replace overflow scroll with auto

* clean up from rebasing vs master

* fix ts

* sort exports

* fix confusing linting errors

* update snaps

* clean up nav bar design

* adjust nav hover colors

* comment content component

* update nav hover styles

* fix search bar border

* clean up moderate card components

* more user drawer cleanup

* user role switcher

* update textfield vars and variants

* update components in community page

* move clickoutside component

* clean up stories table

* moderate route components

* single moderate view

* queue components

* delay component

* relative time component

* decision history components

* user drawer components cleanup

* configure components

* clean up buttons

* update snaps

* add all exports

* [CORL-674] admin branding: modals (#2695)

* update hotkeys modal components

* hotkeys modal

* user status change modals

* user status change modals

* invite users modal

* fix tests

* fix incorrect i18n string

* resolve merge conflicts

* move fonts to devdeps

* Fix username props interface

* fix dom nesting in components

* update formatting in strings

* rewrite delay component as functional component

* remove tslint comments

* change configure header component props
This commit is contained in:
Tessa Thornton
2019-11-26 11:26:53 -05:00
committed by GitHub
parent 0e15bc46d2
commit 4ee8cf7c96
625 changed files with 20631 additions and 7766 deletions
@@ -3,7 +3,7 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-2);
padding: var(--v2-spacing-2);
width: 100%;
text-align: left;
line-height: 1;
@@ -34,13 +34,13 @@
justify-content: baseline;
align-items: center;
color: var(--palette-grey-main);
margin-right: var(--spacing-2);
margin-right: var(--v2-spacing-2);
}
.iconAfter {
display: flex;
align-items: center;
justify-content: baseline;
margin-left: var(--spacing-2);
margin-left: var(--v2-spacing-2);
text-decoration: unset;
}
.iconOpenInNew {
@@ -49,5 +49,5 @@
}
.blankAdornment {
padding-right: var(--spacing-6);
padding-right: var(--v2-spacing-6);
}
@@ -1,5 +1,6 @@
.root {
composes: markerText from "coral-ui/shared/typography.css";
color: var(--palette-error-main);
border: 1px solid currentColor;
border-radius: 20px;
@@ -144,4 +144,10 @@
.inputLabel {
composes: inputLabel from "coral-ui/shared/typography.css";
}
}
/* V2 Typography */
.bodyCommentV2 {
composes: bodyCommentV2 from "coral-ui/shared/typography.css";
}
@@ -27,7 +27,9 @@ type Variant =
| "fieldDescription"
| "inputLabel"
| "detail"
| "timestamp";
| "timestamp"
// V2
| "bodyCommentV2";
// Based on Typography Component of Material UI.
// https://github.com/mui-org/material-ui/blob/303199d39b42a321d28347d8440d69166f872f27/packages/material-ui/src/Typography/Typography.js
@@ -62,7 +64,9 @@ interface Props extends PropTypesOf<typeof Box> {
| "error"
| "errorDark"
| "textDark"
| "success";
| "success"
// V2
| "textPrimaryV2";
/**
* The container used for the root node.
* Either a string to use a DOM element, a component, or an element.
@@ -171,6 +175,8 @@ Typography.defaultProps = {
timestamp: "span",
inputLabel: "label",
detail: "p",
// V2
bodyCommentV2: "p",
},
noWrap: false,
paragraph: false,
@@ -0,0 +1,18 @@
.root {
background-color: var(--v2-palette-main-nav-background-default);
border-bottom: 1px solid var(--v2-palette-divider);
}
.container {
max-width: 1280px;
margin: 0 auto;
height: calc(8 * var(--mini-unit));
box-sizing: border-box;
}
.gutterBegin {
padding-left: calc(2 * var(--mini-unit));
}
.gutterEnd {
padding-right: calc(2 * var(--mini-unit));
}
@@ -0,0 +1,37 @@
---
name: AppBar
menu: UI Kit
---
import { Playground, PropsTable } from "docz";
import { AppBar, Begin, End, Navigation, NavigationItem, Divider } from "./";
import { Logo } from "../Brand";
import Flex from "../Flex";
import Icon from "../Icon";
import BaseButton from "../BaseButton";
# AppBar
## Basic usage
<Playground>
<AppBar gutterBegin gutterEnd>
<Begin itemGutter="double">
<Logo />
<Navigation>
<NavigationItem active>Home</NavigationItem>
<NavigationItem>Team</NavigationItem>
</Navigation>
</Begin>
<End itemGutter>
<BaseButton>
<Icon size="lg">history</Icon>
</BaseButton>
<Divider />
<BaseButton>
<Icon size="lg">account_box</Icon>
</BaseButton>
</End>
</AppBar>
</Playground>
@@ -0,0 +1,18 @@
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import { PropTypesOf } from "coral-framework/types";
import AppBar from "./AppBar";
it("renders correctly", () => {
const props: PropTypesOf<typeof AppBar> = {
children: "child",
gutterBegin: true,
gutterEnd: true,
className: "custom",
};
const renderer = createRenderer();
renderer.render(<AppBar {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,38 @@
import cn from "classnames";
import React, { FunctionComponent } from "react";
import { Flex } from "coral-ui/components";
import { withStyles } from "coral-ui/hocs";
import styles from "./AppBar.css";
interface Props {
children?: React.ReactNode;
className?: string;
gutterBegin?: boolean;
gutterEnd?: boolean;
classes: typeof styles;
}
const AppBar: FunctionComponent<Props> = ({
gutterBegin,
gutterEnd,
className,
children,
classes,
...rest
}) => {
return (
<div
{...rest}
className={cn(classes.root, className, {
[classes.gutterBegin]: gutterBegin,
[classes.gutterEnd]: gutterEnd,
})}
>
<Flex className={classes.container}>{children}</Flex>
</div>
);
};
export default withStyles(styles)(AppBar);
@@ -0,0 +1,3 @@
.root {
flex-grow: 1;
}
@@ -0,0 +1,17 @@
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import { PropTypesOf } from "coral-framework/types";
import End from "./End";
it("renders correctly", () => {
const props: PropTypesOf<typeof End> = {
children: "children",
className: "custom",
itemGutter: true,
};
const renderer = createRenderer();
renderer.render(<End {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,27 @@
import cn from "classnames";
import React, { FunctionComponent } from "react";
import { Flex } from "coral-ui/components";
import { withStyles } from "coral-ui/hocs";
import { PropTypesOf } from "coral-ui/types";
import styles from "./Begin.css";
interface Props extends PropTypesOf<typeof Flex> {
children?: React.ReactNode;
className?: string;
classes: typeof styles;
}
const Begin: FunctionComponent<Props> = ({
className,
children,
classes,
...rest
}) => (
<Flex className={cn(className, classes.root)} alignItems="center" {...rest}>
{children}
</Flex>
);
export default withStyles(styles)(Begin);
@@ -0,0 +1,4 @@
.root {
height: 32px;
border-right: 2px solid var(--v2-palette-main-nav-divider);
}
@@ -0,0 +1,10 @@
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import Divider from "./Divider";
it("renders correctly", () => {
const renderer = createRenderer();
renderer.render(<Divider />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,17 @@
import cn from "classnames";
import React, { FunctionComponent } from "react";
import { withStyles } from "coral-ui/hocs";
import styles from "./Divider.css";
interface Props {
className?: string;
classes: typeof styles;
}
const Divider: FunctionComponent<Props> = ({ className, classes, ...rest }) => (
<div {...rest} className={cn(classes.root, className)} />
);
export default withStyles(styles)(Divider);
@@ -0,0 +1,4 @@
.root {
flex-grow: 0;
flex-shrink: 0;
}
@@ -0,0 +1,17 @@
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import { PropTypesOf } from "coral-framework/types";
import End from "./End";
it("renders correctly", () => {
const props: PropTypesOf<typeof End> = {
children: "children",
className: "custom",
itemGutter: true,
};
const renderer = createRenderer();
renderer.render(<End {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,27 @@
import cn from "classnames";
import React, { FunctionComponent } from "react";
import { Flex } from "coral-ui/components";
import { withStyles } from "coral-ui/hocs";
import { PropTypesOf } from "coral-ui/types";
import styles from "./End.css";
interface Props extends PropTypesOf<typeof Flex> {
children?: React.ReactNode;
className?: string;
classes: typeof styles;
}
const End: FunctionComponent<Props> = ({
classes,
className,
children,
...rest
}) => (
<Flex className={cn(className, classes.root)} alignItems="center" {...rest}>
{children}
</Flex>
);
export default withStyles(styles)(End);
@@ -0,0 +1,11 @@
.root {
height: 100%;
}
.ul {
list-style: none;
padding: 0;
display: flex;
height: 100%;
margin: 0;
}
@@ -0,0 +1,15 @@
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import { PropTypesOf } from "coral-framework/types";
import Navigation from "./Navigation";
it("renders correctly", () => {
const props: PropTypesOf<typeof Navigation> = {
children: "children",
};
const renderer = createRenderer();
renderer.render(<Navigation {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,24 @@
import cn from "classnames";
import React, { FunctionComponent } from "react";
import { withStyles } from "coral-ui/hocs";
import styles from "./Navigation.css";
interface Props {
className?: string;
classes: typeof styles;
children?: React.ReactNode;
}
const Navigation: FunctionComponent<Props> = ({
children,
className,
classes,
}) => (
<nav className={cn(classes.root, className)}>
<ul className={classes.ul}>{children}</ul>
</nav>
);
export default withStyles(styles)(Navigation);
@@ -0,0 +1,45 @@
$main-nav-text: var(--v2-colors-mono-100);
$main-nav-background: var(--v2-colors-pure-white);
$main-nav-hover-background: var(--v2-colors-coral-200);
$main-nav-active-background: var(--v2-colors-coral-500);
$main-nav-active-text: var(--v2-colors-pure-white);
$main-nav-hover-text: var(--v2-colors-mono-500);
.root {
}
.anchor {
font-family: var(--v2-font-family-primary);
font-weight: var(--v2-font-weight-primary-semi-bold);
font-size: var(--v2-font-size-3);
text-transform: uppercase;
color: $main-nav-text;
height: 100%;
display: inline-flex;
align-items: center;
padding: 0 var(--mini-unit);
text-decoration: none;
&:hover {
cursor: pointer;
color: $main-nav-hover-text;
background-color: $main-nav-hover-background;
}
}
.active {
font-family: var(--v2-font-family-primary);
font-weight: var(--v2-font-weight-primary-bold);
font-size: var(--v2-font-size-3);
text-transform: uppercase;
background-color: $main-nav-active-background;
text-decoration: none;
color: $main-nav-active-text;
&:hover {
color: $main-nav-active-text;
background-color: $main-nav-active-background;
}
}
@@ -0,0 +1,20 @@
import { noop } from "lodash";
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import { PropTypesOf } from "coral-framework/types";
import NavigationItem from "./NavigationItem";
it("renders correctly", () => {
const props: PropTypesOf<typeof NavigationItem> = {
href: "/moderate",
children: "link",
onClick: noop,
active: true,
className: "custom",
};
const renderer = createRenderer();
renderer.render(<NavigationItem {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,41 @@
import cn from "classnames";
import React, { FunctionComponent } from "react";
import { withStyles } from "coral-ui/hocs";
import styles from "./NavigationItem.css";
interface Props {
children: React.ReactNode;
href: string;
className?: string;
active?: boolean;
onClick?: React.EventHandler<React.MouseEvent>;
classes: typeof styles;
}
const NavigationItem: FunctionComponent<Props> = ({
className,
active,
href,
onClick,
children,
classes,
...rest
}) => {
return (
<li className={cn(className, classes.root)} {...rest}>
<a
className={cn(classes.anchor, {
[classes.active]: active,
})}
href={href}
onClick={onClick}
>
{children}
</a>
</li>
);
};
export default withStyles(styles)(NavigationItem);
@@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<AppBar
className="custom"
classes={
Object {
"container": "AppBar-container",
"gutterBegin": "AppBar-gutterBegin",
"gutterEnd": "AppBar-gutterEnd",
"root": "AppBar-root",
}
}
gutterBegin={true}
gutterEnd={true}
>
child
</AppBar>
`;
@@ -0,0 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<End
className="custom"
classes={
Object {
"root": "End-root",
}
}
itemGutter={true}
>
children
</End>
`;
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<Divider
classes={
Object {
"root": "Divider-root",
}
}
/>
`;
@@ -0,0 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<End
className="custom"
classes={
Object {
"root": "End-root",
}
}
itemGutter={true}
>
children
</End>
`;
@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<Navigation
classes={
Object {
"root": "Navigation-root",
"ul": "Navigation-ul",
}
}
>
children
</Navigation>
`;
@@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<NavigationItem
active={true}
className="custom"
classes={
Object {
"active": "NavigationItem-active",
"anchor": "NavigationItem-anchor",
"root": "NavigationItem-root",
}
}
href="/moderate"
onClick={[Function]}
>
link
</NavigationItem>
`;
@@ -0,0 +1,6 @@
export { default as AppBar } from "./AppBar";
export { default as Begin } from "./Begin";
export { default as End } from "./End";
export { default as Navigation } from "./Navigation";
export { default as NavigationItem } from "./NavigationItem";
export { default as Divider } from "./Divider";
@@ -0,0 +1,10 @@
.root {
position: absolute !important;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
@@ -0,0 +1,18 @@
---
name: AriaInfo
menu: UI Kit
---
import { Playground } from "docz";
import AriaInfo from "./AriaInfo";
# AriaInfo
Renders Text that is only seen by screen readers.
## Basic usage
<Playground>
<AriaInfo>This can only be seen by screen readers</AriaInfo>
<div>Above this line is a hidden text that you can't see</div>
</Playground>
@@ -0,0 +1,14 @@
import React from "react";
import TestRenderer from "react-test-renderer";
import { PropTypesOf } from "coral-framework/types";
import AriaInfo from "./AriaInfo";
it("renders correctly", () => {
const props: PropTypesOf<typeof AriaInfo> = {
children: "This text is only for screen readers",
};
const renderer = TestRenderer.create(<AriaInfo {...props} />);
expect(renderer.toJSON()).toMatchSnapshot();
});
@@ -0,0 +1,36 @@
import cn from "classnames";
import React, { AllHTMLAttributes, FunctionComponent, Ref } from "react";
import { withForwardRef, withStyles } from "coral-ui/hocs";
import { PropTypesOf } from "coral-ui/types";
import styles from "./AriaInfo.css";
interface Props extends AllHTMLAttributes<HTMLElement> {
/**
* This prop can be used to add custom classnames.
* It is handled by the `withStyles `HOC.
*/
classes: typeof styles;
component?: string;
children: React.ReactNode;
/** Internal: Forwarded Ref */
forwardRef?: Ref<HTMLElement>;
}
const AriaInfo: FunctionComponent<Props> = props => {
const { component, className, classes, forwardRef: ref, ...rest } = props;
const Component: React.ComponentType<
React.HTMLAttributes<HTMLElement> & React.ClassAttributes<HTMLElement>
> = component! as any;
const rootClassName = cn(classes.root, className);
return <Component className={rootClassName} {...rest} ref={ref} />;
};
AriaInfo.defaultProps = {
component: "div",
};
const enhanced = withForwardRef(withStyles(styles)(AriaInfo));
export type AriaInfoProps = PropTypesOf<typeof enhanced>;
export default enhanced;
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<div
className="AriaInfo-root"
>
This text is only for screen readers
</div>
`;
@@ -0,0 +1 @@
export { default, AriaInfoProps } from "./AriaInfo";
@@ -0,0 +1,15 @@
.root {
display: none;
background: black;
position: fixed;
/* fix to bottom solves some mobile scrolling issues. */
bottom: 0;
left: 0;
width: 100%;
height: 100%;
}
.active {
display: block;
opacity: 0.5;
}
@@ -0,0 +1,32 @@
---
name: Backdrop
menu: UI Kit
---
import { Playground, PropsTable } from "docz";
import Container from "react-with-state-props";
import Button from "../Button";
import Backdrop from "./Backdrop";
# Backdrop
## Basic usage
<Playground>
<Container
state={{ value: false }}
render={props => (
<div>
<Backdrop active={props.value} />
<Button
variant="filled"
color="primary"
onClick={() => props.setValue(!props.value)}
>
Toggle
</Button>
</div>
)}
/>
</Playground>
@@ -0,0 +1,16 @@
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import Backdrop from "./Backdrop";
it("renders correctly", () => {
const renderer = createRenderer();
renderer.render(<Backdrop />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
it("renders correctly when active", () => {
const renderer = createRenderer();
renderer.render(<Backdrop active />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,35 @@
import cn from "classnames";
import React, { FunctionComponent, HTMLAttributes } from "react";
import { withStyles } from "coral-ui/hocs";
import styles from "./Backdrop.css";
interface Props extends HTMLAttributes<HTMLDivElement> {
className?: string;
/**
* Override or extend the styles applied to the component.
*/
classes: typeof styles;
active?: boolean;
}
const Backdrop: FunctionComponent<Props> = ({
classes,
active,
className,
children,
...rest
}) => {
const rootClassName = cn(
classes.root,
{
[classes.active]: Boolean(active),
},
className
);
return <div className={rootClassName} {...rest} />;
};
const enhanced = withStyles(styles)(Backdrop);
export default enhanced;
@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<Backdrop
classes={
Object {
"active": "Backdrop-active",
"root": "Backdrop-root",
}
}
/>
`;
exports[`renders correctly when active 1`] = `
<Backdrop
active={true}
classes={
Object {
"active": "Backdrop-active",
"root": "Backdrop-root",
}
}
/>
`;
@@ -0,0 +1 @@
export { default } from "./Backdrop";
@@ -0,0 +1,43 @@
.root {
/* reset button */
user-select: none;
font-family: inherit;
outline: none;
border: none;
touch-action: manipulation;
padding: 0;
margin: 0;
/* Unify anchor and button. */
cursor: pointer;
display: inline-block;
box-sizing: border-box;
text-align: center;
text-decoration: none;
align-items: flex-start;
vertical-align: middle;
white-space: nowrap;
background: transparent;
font-size: inherit;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
-webkit-appearance: none;
&:disabled {
cursor: default;
}
&::-moz-focus-inner {
border: 0;
}
&:-moz-focusring {
textshadow: 0 0 0 #000;
}
}
.keyboardFocus {
@mixin outline;
}
.mouseHover {
}
@@ -0,0 +1,28 @@
---
name: BaseButton
menu: UI Kit
---
import { Playground, PropsTable } from 'docz'
import BaseButton from './BaseButton'
# BaseButton
`BaseButton` strips away browser specific styling and unifies the look of the `button` and the `a` tag.
It detects a focus that came from the keyboard rather than mouse or touch and styles the button using
the `className` provided in the `classes.keyboardFocus` property.
When used as a `button` tag the default `type` is set to `button` instead of the standard `submit` in order
to avoid unintended form submissions.
## Basic usage
<Playground>
<BaseButton>Push Me</BaseButton>
</Playground>
Instead of an `button` tag, we can render an `a` tag instead:
<Playground>
<BaseButton anchor>Push Me</BaseButton>
</Playground>
@@ -0,0 +1,24 @@
import React from "react";
import TestRenderer from "react-test-renderer";
import { PropTypesOf } from "coral-framework/types";
import BaseButton from "./BaseButton";
it("renders correctly", () => {
const props: PropTypesOf<typeof BaseButton> = {
className: "my-class",
children: "Push Me",
};
const renderer = TestRenderer.create(<BaseButton {...props} />);
expect(renderer.toJSON()).toMatchSnapshot();
});
it("renders as anchor", () => {
const props: PropTypesOf<typeof BaseButton> = {
anchor: true,
children: "Push Me",
};
const renderer = TestRenderer.create(<BaseButton {...props} />);
expect(renderer.toJSON()).toMatchSnapshot();
});
@@ -0,0 +1,99 @@
import cn from "classnames";
import React, {
ButtonHTMLAttributes,
EventHandler,
FocusEvent,
FunctionComponent,
MouseEvent,
Ref,
TouchEvent,
} from "react";
import {
withForwardRef,
withKeyboardFocus,
withMouseHover,
withStyles,
} from "coral-ui/hocs";
import { PropTypesOf } from "coral-ui/types";
import styles from "./BaseButton.css";
interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
/** If set renders an anchor tag instead */
anchor?: boolean;
href?: string;
target?: string;
rel?: string;
/**
* This prop can be used to add custom classnames.
* It is handled by the `withStyles `HOC.
*/
classes: typeof styles;
/** Internal: Forwarded Ref */
forwardRef?: Ref<HTMLButtonElement | HTMLAnchorElement>;
type?: "submit" | "reset" | "button";
// These handlers are passed down by the `withMouseHover` HOC.
mouseHover: boolean;
onMouseOver: React.EventHandler<MouseEvent<HTMLElement>>;
onMouseOut: React.EventHandler<MouseEvent<HTMLElement>>;
onTouchEnd: React.EventHandler<TouchEvent<HTMLElement>>;
// These handlers are passed down by the `withKeyboardFocus` HOC.
onFocus: EventHandler<FocusEvent<HTMLElement>>;
onBlur: EventHandler<FocusEvent<HTMLElement>>;
keyboardFocus: boolean;
}
/**
* A button whose styling is stripped off to a minimum and supports
* keyboard focus. It is the base for our other buttons.
*/
const BaseButton: FunctionComponent<Props> = ({
anchor,
className,
classes,
keyboardFocus,
mouseHover,
forwardRef,
type,
...rest
}) => {
let Element: React.ComponentType<
React.ButtonHTMLAttributes<HTMLButtonElement | HTMLAnchorElement> &
React.ClassAttributes<HTMLButtonElement | HTMLAnchorElement>
> = "button" as any;
if (anchor) {
Element = "a" as any;
}
if (anchor && type) {
// eslint-disable-next-line no-console
console.warn(
"BaseButton used as anchor does not support the `type` property"
);
} else if (type === undefined) {
// Default to button
type = "button";
}
const rootClassName = cn(classes.root, className, {
[classes.keyboardFocus]: keyboardFocus,
[classes.mouseHover]: mouseHover,
});
return (
<Element {...rest} className={rootClassName} ref={forwardRef} type={type} />
);
};
const enhanced = withForwardRef(
withStyles(styles)(withMouseHover(withKeyboardFocus(BaseButton)))
);
export type BaseButtonProps = PropTypesOf<typeof enhanced>;
export default enhanced;
@@ -0,0 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders as anchor 1`] = `
<a
className="BaseButton-root"
onBlur={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Push Me
</a>
`;
exports[`renders correctly 1`] = `
<button
className="BaseButton-root my-class"
onBlur={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Push Me
</button>
`;
@@ -0,0 +1 @@
export { default, BaseButtonProps } from "./BaseButton";
@@ -0,0 +1,240 @@
.root {
}
/* Margin */
.ml-1 {
margin-left: var(--v2-spacing-1) !important;
}
.mr-1 {
margin-right: var(--v2-spacing-1) !important;
}
.mt-1 {
margin-top: var(--v2-spacing-1) !important;
}
.mb-1 {
margin-bottom: var(--v2-spacing-1) !important;
}
.ml-2 {
margin-left: var(--v2-spacing-2) !important;
}
.mr-2 {
margin-right: var(--v2-spacing-2) !important;
}
.mt-2 {
margin-top: var(--v2-spacing-2) !important;
}
.mb-2 {
margin-bottom: var(--v2-spacing-2) !important;
}
.ml-3 {
margin-left: var(--v2-spacing-3) !important;
}
.mr-3 {
margin-right: var(--v2-spacing-3) !important;
}
.mt-3 {
margin-top: var(--v2-spacing-3) !important;
}
.mb-3 {
margin-bottom: var(--v2-spacing-3) !important;
}
.ml-4 {
margin-left: var(--v2-spacing-4) !important;
}
.mr-4 {
margin-right: var(--v2-spacing-4) !important;
}
.mt-4 {
margin-top: var(--v2-spacing-4) !important;
}
.mb-4 {
margin-bottom: var(--v2-spacing-4) !important;
}
.ml-5 {
margin-left: var(--v2-spacing-5) !important;
}
.mr-5 {
margin-right: var(--v2-spacing-5) !important;
}
.mt-5 {
margin-top: var(--v2-spacing-5) !important;
}
.mb-5 {
margin-bottom: var(--v2-spacing-5) !important;
}
.ml-6 {
margin-left: var(--v2-spacing-6) !important;
}
.mr-6 {
margin-right: var(--v2-spacing-6) !important;
}
.mt-6 {
margin-top: var(--v2-spacing-6) !important;
}
.mb-6 {
margin-bottom: var(--v2-spacing-6) !important;
}
.ml-7 {
margin-left: var(--v2-spacing-7) !important;
}
.mr-7 {
margin-right: var(--v2-spacing-7) !important;
}
.mt-7 {
margin-top: var(--v2-spacing-7) !important;
}
.mb-7 {
margin-bottom: var(--v2-spacing-7) !important;
}
.ml-8 {
margin-left: var(--v2-spacing-8) !important;
}
.mr-8 {
margin-right: var(--v2-spacing-8) !important;
}
.mt-8 {
margin-top: var(--v2-spacing-8) !important;
}
.mb-8 {
margin-bottom: var(--v2-spacing-8) !important;
}
.ml-9 {
margin-left: var(--v2-spacing-9) !important;
}
.mr-9 {
margin-right: var(--v2-spacing-9) !important;
}
.mt-9 {
margin-top: var(--v2-spacing-9) !important;
}
.mb-9 {
margin-bottom: var(--v2-spacing-9) !important;
}
/* Padding */
.pl-1 {
padding-left: var(--v2-spacing-1) !important;
}
.pr-1 {
padding-right: var(--v2-spacing-1) !important;
}
.pt-1 {
padding-top: var(--v2-spacing-1) !important;
}
.pb-1 {
padding-bottom: var(--v2-spacing-1) !important;
}
.pl-2 {
padding-left: var(--v2-spacing-2) !important;
}
.pr-2 {
padding-right: var(--v2-spacing-2) !important;
}
.pt-2 {
padding-top: var(--v2-spacing-2) !important;
}
.pb-2 {
padding-bottom: var(--v2-spacing-2) !important;
}
.pl-3 {
padding-left: var(--v2-spacing-3) !important;
}
.pr-3 {
padding-right: var(--v2-spacing-3) !important;
}
.pt-3 {
padding-top: var(--v2-spacing-3) !important;
}
.pb-3 {
padding-bottom: var(--v2-spacing-3) !important;
}
.pl-4 {
padding-left: var(--v2-spacing-4) !important;
}
.pr-4 {
padding-right: var(--v2-spacing-4) !important;
}
.pt-4 {
padding-top: var(--v2-spacing-4) !important;
}
.pb-4 {
padding-bottom: var(--v2-spacing-4) !important;
}
.pl-5 {
padding-left: var(--v2-spacing-5) !important;
}
.pr-5 {
padding-right: var(--v2-spacing-5) !important;
}
.pt-5 {
padding-top: var(--v2-spacing-5) !important;
}
.pb-5 {
padding-bottom: var(--v2-spacing-5) !important;
}
.pl-6 {
padding-left: var(--v2-spacing-6) !important;
}
.pr-6 {
padding-right: var(--v2-spacing-6) !important;
}
.pt-6 {
padding-top: var(--v2-spacing-6) !important;
}
.pb-6 {
padding-bottom: var(--v2-spacing-6) !important;
}
.pl-7 {
padding-left: var(--v2-spacing-7) !important;
}
.pr-7 {
padding-right: var(--v2-spacing-7) !important;
}
.pt-7 {
padding-top: var(--v2-spacing-7) !important;
}
.pb-7 {
padding-bottom: var(--v2-spacing-7) !important;
}
.pl-8 {
padding-left: var(--v2-spacing-8) !important;
}
.pr-8 {
padding-right: var(--v2-spacing-8) !important;
}
.pt-8 {
padding-top: var(--v2-spacing-8) !important;
}
.pb-8 {
padding-bottom: var(--v2-spacing-8) !important;
}
.pl-9 {
padding-left: var(--v2-spacing-9) !important;
}
.pr-9 {
padding-right: var(--v2-spacing-9) !important;
}
.pt-9 {
padding-top: var(--v2-spacing-9) !important;
}
.pb-9 {
padding-bottom: var(--v2-spacing-9) !important;
}
@@ -0,0 +1,30 @@
---
menu: UI Kit
name: Box
---
import { Playground } from "docz";
import Box from "./Box";
# Box
## Usage
<Playground>
<Box padding={2}>First box</Box>
<Box padding={2} marginTop={2}>
Second box
</Box>
</Playground>
## Short Aliases
<Playground>
<Box p={2}>First box</Box>
<Box p={2} mt={2}>
Second box
</Box>
<Box p={2} mt={2} clone>
<div>Cloned div</div>
</Box>
</Playground>
@@ -0,0 +1,164 @@
import cn from "classnames";
import React, {
FunctionComponent,
HTMLAttributes,
ReactElement,
Ref,
} from "react";
import { withForwardRef, withStyles } from "coral-ui/hocs";
import { Spacing } from "coral-ui/theme/variables";
import styles from "./Box.css";
interface Props extends HTMLAttributes<any> {
/**
* This prop can be used to add custom classnames.
* It is handled by the `withStyles `HOC.
*/
classes: typeof styles;
/* Margin */
m?: Spacing;
mx?: Spacing;
my?: Spacing;
margin?: Spacing;
marginStart?: Spacing;
marginEnd?: Spacing;
marginLeft?: Spacing;
marginRight?: Spacing;
marginTop?: Spacing;
marginBottom?: Spacing;
ms?: Spacing;
me?: Spacing;
ml?: Spacing;
mr?: Spacing;
mt?: Spacing;
mb?: Spacing;
/* Padding */
p?: Spacing;
px?: Spacing;
py?: Spacing;
padding?: Spacing;
paddingStart?: Spacing;
paddingEnd?: Spacing;
paddingLeft?: Spacing;
paddingRight?: Spacing;
paddingTop?: Spacing;
paddingBottom?: Spacing;
ps?: Spacing;
pe?: Spacing;
pl?: Spacing;
pr?: Spacing;
pt?: Spacing;
pb?: Spacing;
/** If set, will perform React.cloneElement instead */
clone?: boolean;
/** The name of the Box to render */
children?: React.ReactNode;
/** Internal: Forwarded Ref */
forwardRef?: Ref<HTMLElement>;
/**
* The container used for the root node.
* Either a string to use a DOM element, a component, or an element.
*/
container?: React.ReactElement<any> | React.ComponentType<any> | string;
}
const Box: FunctionComponent<Props> = props => {
const {
clone,
classes,
className,
forwardRef,
container,
children,
m,
mx,
my,
margin,
marginStart,
marginEnd,
marginLeft,
marginRight,
marginTop,
marginBottom,
ml,
mr,
ms,
me,
mt,
mb,
p,
px,
py,
padding,
paddingStart,
paddingEnd,
paddingLeft,
paddingRight,
paddingTop,
paddingBottom,
pl,
pr,
ps,
pe,
pt,
pb,
...rest
} = props;
const final = {
ml: ml || marginLeft || ms || marginStart || mx || m || margin,
mr: mr || marginRight || me || marginEnd || mx || m || margin,
mt: mt || marginTop || my || m || margin,
mb: mb || marginBottom || my || m || margin,
pl: pl || paddingLeft || ps || paddingStart || px || p || padding,
pr: pr || paddingRight || pe || paddingEnd || px || p || padding,
pt: pt || paddingTop || py || p || padding,
pb: pb || paddingBottom || py || p || padding,
};
const rootClassName = cn(
classes.root,
className,
/* Margin */
final.ml && (classes as any)[`ml-${final.ml}`],
final.mr && (classes as any)[`mr-${final.mr}`],
final.mt && (classes as any)[`mt-${final.mt}`],
final.mb && (classes as any)[`mb-${final.mb}`],
/* Padding */
final.pl && (classes as any)[`pl-${final.pl}`],
final.pr && (classes as any)[`pr-${final.pr}`],
final.pt && (classes as any)[`pt-${final.pt}`],
final.pb && (classes as any)[`pb-${final.pb}`]
);
const innerProps = {
className: rootClassName,
ref: forwardRef,
...rest,
};
const Container = container!;
if (clone) {
const child = React.Children.only(children) as ReactElement;
return React.cloneElement(child, innerProps);
}
if (React.isValidElement<any>(Container)) {
return React.cloneElement(Container, { ...innerProps, children });
} else {
return <Container {...innerProps}>{children}</Container>;
}
};
Box.defaultProps = {
container: "div",
} as Partial<Props>;
const enhanced = withForwardRef(withStyles(styles)(Box));
export default enhanced;
@@ -0,0 +1 @@
export { default } from "./Box";
@@ -0,0 +1,508 @@
/* Design tokens */
$button-default-outline-background: var(--v2-colors-pure-white);
$button-default-outline-foreground: var(--v2-colors-teal-600);
$button-default-outline-active: var(--v2-colors-teal-500);
$button-default-filled-background: var(--v2-colors-teal-600);
$button-default-filled-foreground: var(--v2-colors-pure-white);
$button-default-filled-hover: var(--v2-colors-teal-500);
$button-default-filled-active: var(--v2-colors-teal-400);
$button-alert-outline-background: var(--v2-colors-pure-white);
$button-alert-outline-foreground: var(--v2-colors-red-500);
$button-alert-outline-active: var(--v2-colors-red-400);
$button-alert-filled-background: var(--v2-colors-red-500);
$button-alert-filled-foreground: var(--v2-colors-pure-white);
$button-alert-filled-hover: var(--v2-colors-red-400);
$button-alert-filled-active: var(--v2-colors-red-300);
$button-emphasis-outline-background: var(--v2-colors-pure-white);
$button-emphasis-outline-foreground: var(--v2-colors-blue-500);
$button-emphasis-outline-active: var(--v2-colors-blue-400);
$button-emphasis-filled-background: var(--v2-colors-blue-500);
$button-emphasis-filled-foreground: var(--v2-colors-pure-white);
$button-emphasis-filled-hover: var(--v2-colors-blue-400);
$button-emphasis-filled-active: var(--v2-colors-blue-300);
$button-mono-outline-background: var(--v2-colors-pure-white);
$button-mono-outline-foreground: var(--v2-colors-grey-600);
$button-mono-outline-active: var(--v2-colors-grey-500);
$button-mono-filled-background: var(--v2-colors-grey-600);
$button-mono-filled-foreground: var(--v2-colors-pure-white);
$button-mono-filled-hover: var(--v2-colors-grey-500);
$button-mono-filled-active: var(--v2-colors-grey-400);
$button-adornment-foreground: var(--v2-colors-pure-white);
$button-adornment-default-background: var(--v2-colors-teal-600);
$button-adornment-alert-background: var(--v2-colors-red-500);
$button-adornment-emphasis-background: var(--v2-colors-blue-500);
$button-adornment-mono-background: var(--v2-colors-grey-600);
$button-adornment-default-hover: var(--v2-colors-teal-500);
$button-adornment-alert-hover: var(--v2-colors-red-400);
$button-adornment-emphasis-hover: var(--v2-colors-blue-400);
$button-adornment-mono-hover: var(--v2-colors-grey-400);
$button-adornment-default-active: var(--v2-colors-teal-400);
$button-adornment-alert-active: var(--v2-colors-red-300);
$button-adornment-emphasis-active: var(--v2-colors-blue-300);
$button-adornment-mono-active: var(--v2-colors-grey-300);
$button-ghost-default-foreground: var(--v2-colors-teal-600);
$button-ghost-alert-foreground: var(--v2-colors-red-500);
$button-ghost-emphasis-foreground: var(--v2-colors-blue-500);
$button-ghost-mono-foreground: var(--v2-colors-grey-600);
$button-ghost-active-foreground: var(--v2-colors-pure-white);
$button-underlined-default-foreground: var(--v2-colors-teal-600);
$button-underlined-alert-foreground: var(--v2-colors-red-500);
$button-underlined-emphasis-foreground: var(--v2-colors-blue-500);
$button-underlined-mono-foreground: var(--v2-colors-grey-600);
$button-underlined-default-hover: var(--v2-colors-teal-500);
$button-underlined-alert-hover: var(--v2-colors-red-400);
$button-underlined-emphasis-hover: var(--v2-colors-blue-400);
$button-underlined-mono-hover: var(--v2-colors-grey-500);
$button-underlined-default-active: var(--v2-colors-teal-400);
$button-underlined-alert-active: var(--v2-colors-red-300);
$button-underlined-emphasis-active: var(--v2-colors-blue-300);
$button-underlined-mono-active: var(--v2-colors-grey-400);
/* Class Definitions */
.root {
border-radius: var(--v2-round-corners);
background-color: transparent;
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
border: 0px solid transparent;
& > * {
margin: 0 var(--v2-spacing-1) 0 0;
/* convince safari */
align-self: center;
}
& > *:last-child {
margin: 0;
}
}
.uppercase {
text-transform: uppercase;
}
.root.disabled {
opacity: 0.4;
cursor: default;
}
.fullWidth {
display: flex;
width: 100%;
box-sizing: border-box;
}
.sizeSmall {
color: $button-default-outline-foreground;
font-family: var(--v2-font-family-primary);
font-weight: var(--v2-font-weight-primary-bold);
font-size: var(--v2-font-size-2);
line-height: 1;
padding: var(--v2-spacing-1);
}
.sizeMedium {
color: $button-default-outline-foreground;
font-family: var(--v2-font-family-primary);
font-weight: var(--v2-font-weight-primary-semi-bold);
font-size: var(--v2-font-size-2);
line-height: 1.28;
padding: var(--v2-spacing-3) var(--v2-spacing-5);
}
.sizeLarge {
color: $button-default-outline-foreground;
font-family: var(--v2-font-family-primary);
font-weight: var(--v2-font-weight-primary-semi-bold);
font-size: var(--v2-line-height-reset);
line-height: 1.25;
padding: var(--v2-spacing-4) var(--v2-spacing-6);
}
.variantDefault,
.variantPlain {
&.colorDefault {
color: $button-default-outline-foreground;
}
&.colorAlert {
color: $button-alert-outline-foreground;
}
&.colorEmphasis {
color: $button-emphasis-outline-foreground;
}
&.colorMono {
color: $button-mono-outline-foreground;
}
}
.variantDefault {
border-width: 1px;
&.colorDefault {
border-color: $button-default-outline-foreground;
background-color: $button-default-outline-background;
}
&.colorAlert {
border-color: $button-alert-outline-foreground;
background-color: $button-alert-outline-background;
}
&.colorEmphasis {
border-color: $button-emphasis-outline-foreground;
background-color: $button-emphasis-outline-background;
}
&.colorMono {
border-color: $button-mono-outline-foreground;
background-color: $button-mono-outline-background;
}
&:not(.disabled) {
&.colorDefault {
&.mouseHover {
background-color: $button-default-outline-foreground;
color: $button-default-outline-background;
}
&:active,
&.active {
color: $button-default-outline-background;
border-color: $button-default-outline-active;
background-color: $button-default-outline-active;
}
}
&.colorAlert {
&.mouseHover {
color: $button-alert-outline-background;
background-color: $button-alert-outline-foreground;
}
&:active,
&.active {
color: $button-alert-outline-background;
border-color: $button-alert-outline-active;
background-color: $button-alert-outline-active;
}
}
&.colorEmphasis {
&.mouseHover {
color: $button-emphasis-outline-background;
background-color: $button-emphasis-outline-foreground;
}
&:active,
&.active {
border-color: $button-emphasis-outline-active;
background-color: $button-emphasis-outline-active;
color: $button-emphasis-outline-background;
}
}
&.colorMono {
&.mouseHover {
background-color: $button-mono-outline-foreground;
color: $button-mono-outline-background;
}
&:active,
&.active {
border-color: $button-mono-outline-active;
background-color: $button-mono-outline-active;
color: $button-mono-outline-background;
}
}
}
}
.variantPlain {
padding: 0;
}
.variantFilled {
border-width: 1px;
&.colorDefault {
border-color: $button-default-filled-background;
color: $button-default-filled-foreground;
background-color: $button-default-filled-background;
}
&.colorAlert {
border-color: $button-alert-filled-background;
color: $button-alert-filled-foreground;
background-color: $button-alert-filled-background;
}
&.colorEmphasis {
border-color: $button-emphasis-filled-background;
color: $button-emphasis-filled-foreground;
background-color: $button-emphasis-filled-background;
}
&.colorMono {
border-color: $button-mono-filled-background;
color: $button-mono-filled-foreground;
background-color: $button-mono-filled-background;
}
&:not(.disabled) {
&.colorDefault {
&.mouseHover {
border-color: $button-default-filled-hover;
color: $button-default-filled-foreground;
background-color: $button-default-filled-hover;
}
&:active,
&.active {
border-color: $button-default-filled-active;
color: $button-default-filled-foreground;
background-color: $button-default-filled-active;
}
}
&.colorAlert {
&.mouseHover {
border-color: $button-alert-filled-hover;
color: $button-alert-filled-foreground;
background-color: $button-alert-filled-hover;
}
&:active,
&.active {
border-color: $button-alert-filled-active;
color: $button-alert-filled-foreground;
background-color: $button-alert-filled-active;
}
}
&.colorEmphasis {
&.mouseHover {
border-color: $button-emphasis-filled-hover;
color: $button-emphasis-filled-foreground;
background-color: $button-emphasis-filled-hover;
}
&:active,
&.active {
border-color: $button-emphasis-filled-active;
color: $button-emphasis-filled-foreground;
background-color: $button-emphasis-filled-active;
}
}
&.colorMono {
&.mouseHover {
border-color: $button-mono-filled-hover;
color: $button-mono-filled-foreground;
background-color: $button-mono-filled-hover;
}
&:active,
&.active {
border-color: $button-mono-filled-active;
color: $button-mono-filled-foreground;
background-color: $button-mono-filled-active;
}
}
}
}
.variantAdornment {
color: $button-adornment-foreground;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
&.colorDefault {
background-color: $button-adornment-default-background;
border-color: $button-adornment-default-background;
}
&.colorAlert {
background-color: $button-adornment-alert-background;
border-color: $button-adornment-alert-background;
}
&.colorEmphasis {
background-color: $button-adornment-emphasis-background;
border-color: $button-adornment-emphasis-background;
}
&.colorMono {
background-color: $button-adornment-mono-background;
border-color: $button-adornment-mono-background;
}
&:not(.disabled) {
&.colorDefault {
&.mouseHover {
background-color: $button-adornment-default-hover;
border-color: $button-adornment-default-hover;
}
&:active,
&.active {
background-color: $button-adornment-default-active;
border-color: $button-adornment-default-active;
}
}
&.colorAlert {
&.mouseHover {
background-color: $button-adornment-alert-hover;
border-color: $button-adornment-alert-hover;
}
&:active,
&.active {
background-color: $button-adornment-alert-active;
border-color: $button-adornment-alert-active;
}
}
&.colorEmphasis {
&.mouseHover {
background-color: $button-adornment-emphasis-hover;
border-color: $button-adornment-emphasis-hover;
}
&:active,
&.active {
background-color: $button-adornment-emphasis-active;
border-color: $button-adornment-emphasis-active;
}
}
&.colorMono {
&.mouseHover {
background-color: $button-adornment-mono-hover;
border-color: $button-adornment-mono-hover;
}
&:active,
&.active {
background-color: $button-adornment-mono-active;
border-color: $button-adornment-mono-active;
}
}
}
}
.variantGhost {
border: 1px solid transparent;
&.colorDefault {
color: $button-ghost-default-foreground;
}
&.colorAlert {
color: $button-ghost-alert-foreground;
}
&.colorEmphasis {
color: $button-ghost-emphasis-foreground;
}
&.colorMono {
color: $button-ghost-mono-foreground;
}
&:not(.disabled) {
&.colorDefault {
&.mouseHover {
border: 1px solid currentColor;
}
&:active,
&.active {
border: 1px solid currentColor;
color: $button-ghost-active-foreground;
background-color: $button-ghost-default-foreground;
}
}
&.colorAlert {
&.mouseHover {
border: 1px solid currentColor;
}
&:active,
&.active {
border: 1px solid currentColor;
color: $button-ghost-active-foreground;
background-color: $button-ghost-alert-foreground;
}
}
&.colorEmphasis {
&.mouseHover {
border: 1px solid currentColor;
}
&:active,
&.active {
border: 1px solid currentColor;
color: $button-ghost-active-foreground;
background-color: $button-ghost-emphasis-foreground;
}
}
&.colorMono {
&.mouseHover {
border: 1px solid currentColor;
}
&:active,
&.active {
border: 1px solid currentColor;
color: $button-ghost-active-foreground;
background-color: $button-ghost-mono-foreground;
}
}
}
}
.variantUnderlined {
padding-right: 0px;
padding-left: 0px;
border-radius: 0;
border-bottom: 1px solid currentColor;
padding: 0;
&.colorDefault {
color: $button-underlined-default-foreground;
}
&.colorAlert {
color: $button-underlined-alert-foreground;
}
&.colorEmphasis {
color: $button-underlined-emphasis-foreground;
}
&.colorMono {
color: $button-underlined-mono-foreground;
}
&:not(.disabled) {
&.colorDefault {
&.mouseHover {
color: $button-underlined-default-hover;
}
&:active,
&.active {
color: $button-underlined-default-active;
}
}
&.colorAlert {
&.mouseHover {
color: $button-underlined-alert-hover;
}
&:active,
&.active {
color: $button-underlined-alert-active;
}
}
&.colorEmphasis {
&.mouseHover {
color: $button-underlined-emphasis-hover;
}
&:active,
&.active {
color: $button-underlined-emphasis-active;
}
}
&.colorMono {
&.mouseHover {
color: $button-underlined-mono-hover;
}
&:active,
&.active {
color: $button-underlined-mono-active;
}
}
}
}
@@ -0,0 +1,206 @@
---
name: Button
menu: UI Kit
---
import { Playground } from "docz";
import Button from "./Button";
import ButtonIcon from "./ButtonIcon";
import Icon from "../Icon";
import TextField from "../TextField";
import Flex from "../Flex";
# Button
## Regular Button
<Playground>
<Flex itemGutter wrap>
<Button>Push Me</Button>
<Button size="small">Push Me</Button>
<Button size="large">Push Me</Button>
<Button color="primary">Push Me</Button>
<Button color="error">Push Me</Button>
<Button color="success">Push Me</Button>
<Button color="dark">Push Me</Button>
<Button disabled>Push Me</Button>
<Button active>Push Me</Button>
<Button>
<ButtonIcon>face</ButtonIcon>
<span>Push Me</span>
</Button>
<Button fullWidth>Push Me</Button>
</Flex>
</Playground>
## Filled Button
<Playground>
<Flex itemGutter wrap>
<Button variant="filled">Push Me</Button>
<Button variant="filled" size="small">
Push Me
</Button>
<Button variant="filled" size="large">
Push Me
</Button>
<Button variant="filled" color="primary">
Push Me
</Button>
<Button variant="filled" color="error">
Push Me
</Button>
<Button variant="filled" color="success">
Push Me
</Button>
<Button variant="filled" color="dark">
Push Me
</Button>
<Button variant="filled" disabled>
Push Me
</Button>
<Button variant="filled" active>
Push Me
</Button>
<Button variant="filled">
<ButtonIcon>face</ButtonIcon>
<span>Push Me</span>
</Button>
<Button variant="filled" fullWidth>
Push Me
</Button>
</Flex>
</Playground>
## Outlined Button
<Playground>
<Flex itemGutter wrap>
<Button variant="outlined">Push Me</Button>
<Button variant="outlined" size="small">
Push Me
</Button>
<Button variant="outlined" size="large">
Push Me
</Button>
<Button variant="outlined" color="primary">
Push Me
</Button>
<Button variant="outlined" color="error">
Push Me
</Button>
<Button variant="outlined" color="success">
Push Me
</Button>
<Button variant="outlined" color="dark">
Push Me
</Button>
<Button variant="outlined" disabled>
Push Me
</Button>
<Button variant="outlined" active>
Push Me
</Button>
<Button variant="outlined">
<ButtonIcon>face</ButtonIcon>
<span>Push Me</span>
</Button>
<Button variant="outlined" fullWidth>
Push Me
</Button>
</Flex>
</Playground>
## Ghost Button
<Playground>
<Flex itemGutter wrap>
<Button variant="ghost">Push Me</Button>
<Button variant="ghost" size="small">
Push Me
</Button>
<Button variant="ghost" size="large">
Push Me
</Button>
<Button variant="ghost" color="primary">
Push Me
</Button>
<Button variant="ghost" color="error">
Push Me
</Button>
<Button variant="ghost" color="success">
Push Me
</Button>
<Button variant="ghost" color="dark">
Push Me
</Button>
<Button variant="ghost" disabled>
Push Me
</Button>
<Button variant="ghost" active>
Push Me
</Button>
<Button variant="ghost">
<ButtonIcon>face</ButtonIcon>
<span>Push Me</span>
</Button>
<Button variant="ghost" fullWidth>
Push Me
</Button>
</Flex>
</Playground>
## Underlined Button
<Playground>
<Flex itemGutter wrap>
<Button variant="underlined">Push Me</Button>
<Button variant="underlined" size="small">
Push Me
</Button>
<Button variant="underlined" size="large">
Push Me
</Button>
<Button variant="underlined" color="primary">
Push Me
</Button>
<Button variant="underlined" color="error">
Push Me
</Button>
<Button variant="underlined" color="success">
Push Me
</Button>
<Button variant="underlined" color="dark">
Push Me
</Button>
<Button variant="underlined" disabled>
Push Me
</Button>
<Button variant="underlined" active>
Push Me
</Button>
<Button variant="underlined">
<Icon>face</Icon>
<span>Push Me</span>
</Button>
<Button variant="underlined" fullWidth>
Push Me
</Button>
</Flex>
</Playground>
## TextField Adornment
<Playground>
<Flex itemGutter wrap>
<TextField
placeholder="This is a placeholder"
variant="seamlessAdornment"
adornment={
<Button variant="adornment" color="dark">
<Icon size="md">search</Icon>
</Button>
}
/>
</Flex>
</Playground>
@@ -0,0 +1,62 @@
import React from "react";
import ShallowRenderer from "react-test-renderer/shallow";
import { PropTypesOf } from "coral-framework/types";
import { Button } from "./Button";
it("renders correctly", () => {
const props: PropTypesOf<typeof Button> = {
classes: {
mouseHover: "mouseHover",
keyboardFocus: "keyboardFocus",
} as any,
children: "Push me",
};
const renderer = ShallowRenderer.createRenderer();
renderer.render(<Button {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
it("forwards ref", () => {
const props: PropTypesOf<typeof Button> = {
// eslint-disable-next-line:no-empty
forwardRef: () => {},
classes: {} as any,
children: "Push me",
};
const renderer = ShallowRenderer.createRenderer();
renderer.render(<Button {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
it("renders a medium sized, alert colored filled button with fullWidth", () => {
const props: PropTypesOf<typeof Button> = {
color: "alert",
variant: "filled",
fullWidth: true,
classes: {
colorAlert: "colorAlert",
variantFilled: "variantFilled",
sizeMedium: "sizeMedium",
fullWidth: "fullWidth",
} as any,
children: "Push me",
};
const renderer = ShallowRenderer.createRenderer();
renderer.render(<Button {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
it("renders active state", () => {
const props: PropTypesOf<typeof Button> = {
active: true,
classes: {
active: "active",
} as any,
children: "Push me",
};
const renderer = ShallowRenderer.createRenderer();
renderer.render(<Button {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,118 @@
import cn from "classnames";
import { pick } from "lodash";
import React, { Ref } from "react";
import { withForwardRef, withStyles } from "coral-ui/hocs";
import { Omit, PropTypesOf } from "coral-ui/types";
import BaseButton, { BaseButtonProps } from "../BaseButton";
import styles from "./Button.css";
interface Props extends Omit<BaseButtonProps, "ref"> {
/** If set renders an anchor tag instead */
anchor?: boolean;
href?: string;
target?: string;
/**
* This prop can be used to add custom classnames.
* It is handled by the `withStyles `HOC.
*/
classes: typeof styles & BaseButtonProps["classes"];
/** Size of the button */
size?: "small" | "medium" | "large";
/** Color of the button */
color?: "default" | "alert" | "emphasis" | "mono";
/** Variant of the button */
variant?:
| "default"
| "filled"
| "adornment"
| "ghost"
| "underlined"
| "plain";
/** If set renders a full width button */
fullWidth?: boolean;
/** if set to false, button text will not be uppercase */
uppercase?: boolean;
/** If set renders active state e.g. to implement toggle buttons */
active?: boolean;
type?: "submit" | "reset" | "button";
/** Internal: Forwarded Ref */
forwardRef?: Ref<HTMLButtonElement>;
}
export class Button extends React.Component<Props> {
public static defaultProps: Partial<Props> = {
size: "medium",
variant: "default",
color: "default",
uppercase: true,
};
public render() {
const {
active,
classes,
color,
className,
size,
fullWidth,
disabled,
forwardRef,
variant,
type,
uppercase,
...rest
} = this.props;
const rootClassName = cn(
classes.root,
{
[classes.sizeMedium]: size === "medium",
[classes.sizeSmall]: size === "small",
[classes.sizeLarge]: size === "large",
[classes.colorDefault]: color === "default",
[classes.colorAlert]: color === "alert",
[classes.colorEmphasis]: color === "emphasis",
[classes.colorMono]: color === "mono",
[classes.variantDefault]: variant === "default",
[classes.variantFilled]: variant === "filled",
[classes.variantAdornment]: variant === "adornment",
[classes.variantGhost]: variant === "ghost",
[classes.variantUnderlined]: variant === "underlined",
[classes.variantPlain]: variant === "plain",
[classes.uppercase]: uppercase,
[classes.fullWidth]: fullWidth,
[classes.active]: active,
[classes.disabled]: disabled,
},
className
);
return (
<BaseButton
className={rootClassName}
classes={pick(classes, "keyboardFocus", "mouseHover")}
disabled={disabled}
ref={forwardRef}
type={type}
data-variant={variant}
data-color={color}
data-active={active}
{...rest}
/>
);
}
}
const enhanced = withForwardRef(withStyles(styles)(Button));
export type ButtonProps = PropTypesOf<typeof enhanced>;
export default enhanced;
@@ -0,0 +1,8 @@
.root {
&:first-child {
margin-left: -4px;
}
&:last-child {
margin-right: -4px;
}
}
@@ -0,0 +1,30 @@
import React from "react";
import ShallowRenderer from "react-test-renderer/shallow";
import { PropTypesOf } from "coral-framework/types";
import { ButtonIcon } from "./ButtonIcon";
it("renders correctly", () => {
const props: PropTypesOf<typeof ButtonIcon> = {
classes: {
root: "root",
} as any,
children: "Push me",
};
const renderer = ShallowRenderer.createRenderer();
renderer.render(<ButtonIcon {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
it("forwards ref", () => {
const props: PropTypesOf<typeof ButtonIcon> = {
// eslint-disable-next-line:no-empty
forwardRef: () => {},
classes: {} as any,
children: "Push me",
};
const renderer = ShallowRenderer.createRenderer();
renderer.render(<ButtonIcon {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,38 @@
import cn from "classnames";
import React, { FunctionComponent, HTMLAttributes, Ref } from "react";
import Icon, { IconProps } from "coral-ui/components/v2/Icon";
import { withForwardRef, withStyles } from "coral-ui/hocs";
import { Omit, PropTypesOf } from "coral-ui/types";
import styles from "./ButtonIcon.css";
interface Props extends Omit<HTMLAttributes<HTMLSpanElement>, "color"> {
/**
* This prop can be used to add custom classnames.
* It is handled by the `withStyles `HOC.
*/
classes: typeof styles & IconProps["classes"];
size?: IconProps["size"];
/** The name of the icon to render */
children: string;
/** Internal: Forwarded Ref */
forwardRef?: Ref<HTMLSpanElement>;
}
export const ButtonIcon: FunctionComponent<Props> = props => {
const { classes, className, forwardRef, ...rest } = props;
const rootClassName = cn(classes.root, className);
return <Icon className={rootClassName} {...rest} ref={forwardRef} />;
};
ButtonIcon.defaultProps = {
size: "sm",
} as Partial<Props>;
const enhanced = withForwardRef(withStyles(styles)(ButtonIcon));
export type ButtonIconProps = PropTypesOf<typeof enhanced>;
export default enhanced;
@@ -0,0 +1,51 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`forwards ref 1`] = `
<ForwardRef(forwardRef)
className=""
classes={Object {}}
data-color="default"
data-variant="default"
>
Push me
</ForwardRef(forwardRef)>
`;
exports[`renders a medium sized, alert colored filled button with fullWidth 1`] = `
<ForwardRef(forwardRef)
className="sizeMedium colorAlert variantFilled fullWidth"
classes={Object {}}
data-color="alert"
data-variant="filled"
>
Push me
</ForwardRef(forwardRef)>
`;
exports[`renders active state 1`] = `
<ForwardRef(forwardRef)
className="active"
classes={Object {}}
data-active={true}
data-color="default"
data-variant="default"
>
Push me
</ForwardRef(forwardRef)>
`;
exports[`renders correctly 1`] = `
<ForwardRef(forwardRef)
className=""
classes={
Object {
"keyboardFocus": "keyboardFocus",
"mouseHover": "mouseHover",
}
}
data-color="default"
data-variant="default"
>
Push me
</ForwardRef(forwardRef)>
`;
@@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`forwards ref 1`] = `
<ForwardRef(forwardRef)
className=""
size="sm"
>
Push me
</ForwardRef(forwardRef)>
`;
exports[`renders correctly 1`] = `
<ForwardRef(forwardRef)
className="root"
size="sm"
>
Push me
</ForwardRef(forwardRef)>
`;
@@ -0,0 +1,2 @@
export { default, default as Button, ButtonProps } from "./Button";
export { default as ButtonIcon, ButtonIconProps } from "./ButtonIcon";
@@ -0,0 +1,51 @@
$callout-regular-bg: var(--v2-colors-grey-100);
$callout-regular-text: var(--v2-colors-mono-500);
$callout-regular-border: var(--v2-colors-grey-300);
.root {
font-family: var(--v2-font-family-primary);
font-weight: var(--v2-font-weight-primary-regular);
font-size: var(--v2-font-size-2);
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
padding: var(--v2-spacing-4);
box-sizing: border-box;
border-width: 1px;
border-style: solid;
word-break: break-word;
}
.borderless {
border-style: none;
}
.colorRegular {
background-color: $callout-regular-bg;
border-color: $callout-regular-border;
color: $callout-regular-text;
}
.colorPrimary {
background-color: var(--palette-primary-lightest);
border-color: var(--palette-primary-light);
color: var(--palette-text-primary);
}
.colorError {
background-color: var(--palette-error-lightest);
border-color: var(--palette-error-light);
color: var(--palette-text-primary);
}
.colorSuccess {
background-color: var(--palette-success-lightest);
border-color: var(--palette-success-light);
color: var(--palette-text-primary);
}
.fullWidth {
display: flex;
width: 100%;
}
@@ -0,0 +1,23 @@
---
name: CallOut
menu: UI Kit
---
import { Playground, PropsTable } from 'docz'
import CallOut from './CallOut'
import HorizontalGutter from '../HorizontalGutter'
# CallOut
## Basic Use
<Playground>
<HorizontalGutter>
<CallOut>This is a component for a callout. Any text that you are wanting to draw attention to such as community guidelines, announcements, etc. can be placed in something like a callout box. The color of the callout box can be customized according your own needs.</CallOut>
<CallOut color="primary">This is a component for a callout. Any text that you are wanting to draw attention to such as community guidelines, announcements, etc. can be placed in something like a callout box. The color of the callout box can be customized according your own needs.</CallOut>
<CallOut color="error">This is a component for a callout. Any text that you are wanting to draw attention to such as community guidelines, announcements, etc. can be placed in something like a callout box. The color of the callout box can be customized according your own needs.</CallOut>
<CallOut color="error">The email address or password you entered is incorrect. Try again</CallOut>
<CallOut color="error" fullWidth>The email address or password you entered is incorrect. Try again</CallOut>
</HorizontalGutter>
</Playground>
@@ -0,0 +1,16 @@
import React from "react";
import TestRenderer from "react-test-renderer";
import { PropTypesOf } from "coral-ui/types";
import CallOut from "./CallOut";
it("renders correctly", () => {
const props: PropTypesOf<typeof CallOut> = {
className: "custom",
color: "error",
children: "Hello World",
};
const renderer = TestRenderer.create(<CallOut {...props} />);
expect(renderer.toJSON()).toMatchSnapshot();
});
@@ -0,0 +1,73 @@
import cn from "classnames";
import React, { FunctionComponent, ReactNode } from "react";
import { withStyles } from "coral-ui/hocs";
import styles from "./CallOut.css";
export interface CallOutProps {
/**
* The content of the component.
*/
children: ReactNode;
/**
* Convenient prop to override the root styling.
*/
className?: string;
/**
* Override or extend the styles applied to the component.
*/
classes: typeof styles;
/**
* Color of the CallOut
*/
color?: "regular" | "primary" | "error" | "success";
/*
* If set renders a full width CallOut
*/
fullWidth?: boolean;
/*
* If set removes border
*/
borderless?: boolean;
}
const CallOut: FunctionComponent<CallOutProps> = props => {
const {
borderless,
className,
classes,
color,
fullWidth,
children,
...rest
} = props;
const rootClassName = cn(
classes.root,
{
[classes.colorRegular]: color === "regular",
[classes.colorError]: color === "error",
[classes.colorPrimary]: color === "primary",
[classes.colorSuccess]: color === "success",
[classes.borderless]: borderless,
[classes.fullWidth]: fullWidth,
},
className
);
return (
<div className={rootClassName} {...rest}>
<div>{children}</div>
</div>
);
};
CallOut.defaultProps = {
color: "regular",
fullWidth: false,
} as Partial<CallOutProps>;
const enhanced = withStyles(styles)(CallOut);
export default enhanced;
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<div
className="CallOut-root CallOut-colorError custom"
>
<div>
Hello World
</div>
</div>
`;
@@ -0,0 +1 @@
export { default } from "./CallOut";
@@ -0,0 +1,18 @@
.root {
position: relative;
padding: var(--v2-spacing-4);
box-sizing: border-box;
border: 1px solid rgba(0, 0, 0, 0.25);
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.25);
border-radius: var(--round-corners);
background-color: var(--palette-common-white);
/*
Fallback begin
Unfortunately Firefox / IE does not support
`word-break: break-word` yet.
*/
word-break: break-all;
/* Fallback end */
word-break: break-word;
}
@@ -0,0 +1,37 @@
---
name: Card
menu: UI Kit
---
import { Playground, PropsTable } from "docz";
import Card from "./Card";
import CloseButton from "./CloseButton";
import HorizontalGutter from "../HorizontalGutter";
# Card
## Basic Use
<Playground>
<HorizontalGutter size="double">
<Card>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</Card>
<Card>
<CloseButton />
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt
in culpa qui officia deserunt mollit anim id est laborum.
</Card>
</HorizontalGutter>
</Playground>
@@ -0,0 +1,15 @@
import React from "react";
import TestRenderer from "react-test-renderer";
import { PropTypesOf } from "coral-ui/types";
import Card from "./Card";
it("renders correctly", () => {
const props: PropTypesOf<typeof Card> = {
className: "custom",
children: "Hello World",
};
const renderer = TestRenderer.create(<Card {...props} />);
expect(renderer.toJSON()).toMatchSnapshot();
});
@@ -0,0 +1,39 @@
import cn from "classnames";
import React, { FunctionComponent, ReactNode, Ref } from "react";
import { withForwardRef, withStyles } from "coral-ui/hocs";
import styles from "./Card.css";
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* The content of the component.
*/
children: ReactNode;
/**
* Convenient prop to override the root styling.
*/
className?: string;
/**
* Override or extend the styles applied to the component.
*/
classes: typeof styles;
tabIndex?: number;
forwardRef?: Ref<HTMLDivElement>;
}
const Card: FunctionComponent<CardProps> = props => {
const { className, classes, children, forwardRef, ...rest } = props;
const rootClassName = cn(classes.root, className);
return (
<div className={rootClassName} {...rest} ref={forwardRef}>
{children}
</div>
);
};
const enhanced = withForwardRef(withStyles(styles)(Card));
export default enhanced;
@@ -0,0 +1,8 @@
.root {
min-height: 0px;
margin-right: -5px;
}
.icon {
display: block;
}
@@ -0,0 +1,41 @@
import React, { FunctionComponent, Ref } from "react";
import { withForwardRef, withStyles } from "coral-ui/hocs";
import { Omit, PropTypesOf } from "coral-ui/types";
import BaseButton from "../BaseButton";
import Flex from "../Flex";
import Icon from "../Icon";
import styles from "./CloseButton.css";
export interface CloseButtonProps
extends Omit<PropTypesOf<typeof BaseButton>, "ref"> {
/**
* Override or extend the styles applied to the component.
*/
classes: typeof styles & PropTypesOf<typeof BaseButton>["classes"];
/** Internal: Forwarded Ref */
forwardRef?: Ref<HTMLButtonElement>;
}
const CloseButton: FunctionComponent<CloseButtonProps> = props => {
const {
classes: { icon: iconClassName, ...restClasses },
forwardRef,
...rest
} = props;
return (
<Flex justifyContent="flex-end">
<BaseButton classes={restClasses} ref={forwardRef} {...rest}>
<Icon className={iconClassName} size="md">
close
</Icon>
</BaseButton>
</Flex>
);
};
const enhanced = withForwardRef(withStyles(styles)(CloseButton));
export default enhanced;
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<div
className="Card-root custom"
>
Hello World
</div>
`;
@@ -0,0 +1,2 @@
export { default } from "./Card";
export { default as CardCloseButton } from "./CloseButton";
@@ -0,0 +1,91 @@
.root {
}
.input {
cursor: pointer;
position: absolute; /* take it out of document flow */
opacity: 0; /* hide it */
}
.label {
position: relative;
display: inline-block;
cursor: pointer;
user-select: none;
padding-left: calc(var(--v2-spacing-3) + 14px);
color: var(--v2-palette-input-label);
font-size: var(--v2-font-size-3);
font-weight: var(--v2-font-weight-primary-regular);
font-family: var(--v2-font-family-primary);
line-height: var(--v2-line-height-reset);
}
.labelSpan {
padding-bottom: 1px;
}
.labelLight {
color: var(--palette-text-light);
}
/* Box. */
.input + .label:before {
content: "";
position: absolute;
left: 0px;
top: 50%;
transform: translateY(-50%);
display: inline-block;
width: 14px;
height: 14px;
background: var(--v2-palette-input-background);
border: 1px solid var(--v2-palette-input-label);
box-sizing: border-box;
}
/* Box focus */
.label.focus:before {
@mixin outline;
}
/* Box checked */
.input:checked + .label:before {
background: var(--v2-palette-primary-main);
border: 1px solid var(--v2-palette-primary-main);
}
/* Disabled state label. */
.input:disabled + .label {
cursor: auto;
opacity: 0.6;
}
/* Disabled box. */
.input:disabled + .label:before {
box-shadow: none;
border: 1px solid var(--v2-palette-input-disabled-foreground);
background: var(--v2-palette-input-disabled-background);
}
/* Checkmark. Could be replaced with an image */
.input:checked + .label:after {
content: "";
position: absolute;
left: 2px;
top: 50%;
margin-top: -1px;
transform: translateY(-50%);
color: var(--v2-palette-input-background);
background: currentColor;
width: 2px;
height: 2px;
box-shadow: 2px 0 0 currentColor, 4px 0 0 currentColor,
4px -2px 0 currentColor, 4px -4px 0 currentColor, 4px -6px 0 currentColor,
4px -8px 0 currentColor;
transform: rotate(45deg);
box-sizing: border-box;
}
.input:checked:disabled + .label:after {
color: var(--v2-palette-input-disabled-foreground);
}
@@ -0,0 +1,19 @@
---
name: CheckBox
menu: UI Kit
---
import { Playground, PropsTable } from 'docz'
import CheckBox from './CheckBox.tsx'
import HorizontalGutter from '../HorizontalGutter'
# CheckBox
## Basic Use
<Playground>
<HorizontalGutter>
<CheckBox value="true">Yes I agree</CheckBox>
<CheckBox value="false" disabled>No I don't agree</CheckBox>
<CheckBox value="undefined" disabled checked>I don't know</CheckBox>
</HorizontalGutter>
</Playground>
@@ -0,0 +1,17 @@
import React from "react";
import TestRenderer from "react-test-renderer";
import { PropTypesOf } from "coral-ui/types";
import CheckBox from "./CheckBox";
it("renders correctly", () => {
const props: PropTypesOf<typeof CheckBox> = {
className: "custom",
id: "checkboxID",
checked: true,
children: "Yes I agree",
};
const renderer = TestRenderer.create(<CheckBox {...props} />);
expect(renderer.toJSON()).toMatchSnapshot();
});
@@ -0,0 +1,90 @@
import cn from "classnames";
import React, { ChangeEvent, Component, EventHandler, FocusEvent } from "react";
import uuid from "uuid/v4";
import { withKeyboardFocus, withStyles } from "coral-ui/hocs";
import styles from "./CheckBox.css";
export interface CheckBoxProps {
id?: string;
/**
* checked or not.
*/
checked?: boolean;
/**
* Convenient prop to override the root styling.
*/
className?: string;
/**
* Override or extend the styles applied to the component.
*/
classes: typeof styles;
/**
* Mark as readonly
*/
readOnly?: boolean;
/**
* Name
*/
name?: string;
/**
* onChange
*/
onChange?: EventHandler<ChangeEvent<HTMLInputElement>>;
disabled?: boolean;
children: React.ReactNode;
/**
* Display a light text.
*/
light?: boolean;
// These handlers are passed down by the `withKeyboardFocus` HOC.
onFocus: EventHandler<FocusEvent<HTMLElement>>;
onBlur: EventHandler<FocusEvent<HTMLElement>>;
keyboardFocus: boolean;
}
class CheckBox extends Component<CheckBoxProps> {
public state = {
randomID: uuid(),
};
public render() {
const {
className,
classes,
id,
light,
children,
keyboardFocus,
...rest
} = this.props;
const rootClassName = cn(classes.root, className);
const finalID = id || this.state.randomID;
return (
<div className={rootClassName}>
<input
className={classes.input}
type="checkbox"
id={finalID}
{...rest}
/>
<label
className={cn(classes.label, {
[classes.labelLight]: light,
[classes.focus]: keyboardFocus,
})}
htmlFor={finalID}
>
<span className={classes.labelSpan}>{children}</span>
</label>
</div>
);
}
}
const enhanced = withStyles(styles)(withKeyboardFocus(CheckBox));
export default enhanced;
@@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<div
className="CheckBox-root custom"
>
<input
checked={true}
className="CheckBox-input"
id="checkboxID"
onBlur={[Function]}
onFocus={[Function]}
type="checkbox"
/>
<label
className="CheckBox-label"
htmlFor="checkboxID"
>
<span
className="CheckBox-labelSpan"
>
Yes I agree
</span>
</label>
</div>
`;
@@ -0,0 +1,2 @@
export * from "./CheckBox";
export { default } from "./CheckBox";
@@ -0,0 +1,30 @@
---
name: ClickOutside
menu: UI Kit
---
import { Playground, PropsTable } from 'docz'
import ClickOutside from './ClickOutside'
import Button from '../Button/Button'
# ClickOutside
A Component to handle click events outside the children components.
## Basic usage
Wrap a Component with `<ClickOutside>` and pass a function to `onClickOutside`. This function will trigger when the user
clicks outside the component.
### Test
Click the blue background. It should trigger an alert. Nothing should happen if you click the button.
<Playground>
<div id="outside" style={{background: 'blue', padding: '10px'}}>
<ClickOutside onClickOutside={e => {
if (e.srcElement.id === "outside") {
alert('You clicked outside!');
}}}>
<Button variant="filled">Push Me</Button>
</ClickOutside>
</div>
</Playground>
@@ -0,0 +1,122 @@
import { mount } from "enzyme";
import React from "react";
import simulant from "simulant";
import sinon from "sinon";
import UIContext from "../UIContext";
import {
ClickFarAwayCallback,
ClickFarAwayRegister,
ClickOutside,
default as ClickOutsideWithContext,
} from "./ClickOutside";
let container: HTMLElement;
beforeAll(() => {
container = document.createElement("div");
document.body.appendChild(container);
});
afterAll(() => {
document.body.removeChild(container);
});
it("should render correctly", () => {
const noop = () => null;
const wrapper = mount(
<ClickOutside onClickOutside={noop}>
<span>Hello World!</span>
</ClickOutside>
);
expect(wrapper.html()).toMatchSnapshot();
wrapper.unmount();
});
it("should detect click outside", () => {
const onClickOutside = sinon.spy();
const wrapper = mount(
<ClickOutside onClickOutside={onClickOutside}>
<span>Hello World!</span>
</ClickOutside>,
{
attachTo: container,
}
);
simulant.fire(container, "click");
expect(onClickOutside.calledOnce).toEqual(true);
wrapper.unmount();
});
it("should ignore click inside", () => {
const onClickOutside = sinon.spy();
const wrapper = mount(
<ClickOutside onClickOutside={onClickOutside}>
<button id="click-outside-test-button">Push Me</button>
</ClickOutside>,
{
attachTo: container,
}
);
const target = document.getElementById("click-outside-test-button")!;
simulant.fire(target, "click");
expect(onClickOutside.calledOnce).toEqual(false);
wrapper.unmount();
});
it("should detect click far away", () => {
let emitFarAwayClick: ClickFarAwayCallback = Function;
const unlisten = sinon.spy();
const registerClickFarAway: ClickFarAwayRegister = cb => {
emitFarAwayClick = cb;
return unlisten;
};
const onClickOutside = sinon.spy();
const wrapper = mount(
<ClickOutside
onClickOutside={onClickOutside}
registerClickFarAway={registerClickFarAway}
>
<button id="click-outside-test-button">Push Me</button>
</ClickOutside>,
{
attachTo: container,
}
);
expect(onClickOutside.calledOnce).toEqual(false);
emitFarAwayClick();
expect(onClickOutside.calledOnce).toEqual(true);
expect(unlisten.calledOnce).toEqual(false);
wrapper.unmount();
expect(unlisten.calledOnce).toEqual(true);
});
it("should get registerClickFarAway from context", () => {
const registerClickFarAway: ClickFarAwayRegister = sinon.spy();
const onClickOutside = sinon.spy();
const context: any = {
registerClickFarAway,
};
const wrapper = mount(
<UIContext.Provider value={context}>
<ClickOutsideWithContext
onClickOutside={onClickOutside}
registerClickFarAway={registerClickFarAway}
>
<button id="click-outside-test-button">Push Me</button>
</ClickOutsideWithContext>
</UIContext.Provider>,
{
attachTo: container,
}
);
expect(wrapper.find(ClickOutside).prop("registerClickFarAway")).toEqual(
registerClickFarAway
);
wrapper.unmount();
});
@@ -0,0 +1,78 @@
import React, { FunctionComponent } from "react";
import { findDOMNode } from "react-dom";
import UIContext from "../UIContext";
export type ClickFarAwayCallback = () => void;
export type ClickFarAwayUnlistenCallback = () => void;
export type ClickFarAwayRegister = (
callback: ClickFarAwayCallback
) => ClickFarAwayUnlistenCallback;
export interface ClickOutsideProps {
onClickOutside: (e?: MouseEvent) => void;
/**
* A way to listen for clicks that are e.g. outside of the
* current frame for `ClickOutside`
*/
registerClickFarAway?: ClickFarAwayRegister;
children: React.ReactNode;
}
export class ClickOutside extends React.Component<ClickOutsideProps> {
public domNode: Element | null = null;
private unlisten?: ClickFarAwayUnlistenCallback;
public handleClick = (e: MouseEvent) => {
const { onClickOutside } = this.props;
if (!e || !this.domNode!.contains(e.target as HTMLInputElement)) {
// eslint-disable-next-line no-unused-expressions
onClickOutside && onClickOutside(e);
}
};
public handleClickFarAway = () => {
const { onClickOutside } = this.props;
// eslint-disable-next-line no-unused-expressions
onClickOutside && onClickOutside();
};
public componentDidMount() {
// TODO: find another solution to `findDOMNode`.
// eslint-disable-next-line react/no-find-dom-node
this.domNode = findDOMNode(this) as Element;
document.addEventListener("click", this.handleClick, true);
// Listen to far away clicks.
if (this.props.registerClickFarAway) {
this.unlisten = this.props.registerClickFarAway(this.handleClickFarAway);
}
}
public componentWillUnmount() {
document.removeEventListener("click", this.handleClick, true);
// Unlisten to far away clicks.
if (this.unlisten) {
this.unlisten();
this.unlisten = undefined;
}
}
public render() {
return this.props.children;
}
}
const ClickOutsideWithContext: FunctionComponent<ClickOutsideProps> = props => (
<UIContext.Consumer>
{({ registerClickFarAway }) => (
<ClickOutside {...props} registerClickFarAway={registerClickFarAway} />
)}
</UIContext.Consumer>
);
export default ClickOutsideWithContext;
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render correctly 1`] = `"<span>Hello World!</span>"`;
@@ -0,0 +1 @@
export { default, ClickFarAwayRegister } from "./ClickOutside";
@@ -0,0 +1,56 @@
.root {
font-size: var(--v2-font-size-2);
font-weight: var(--v2-font-weight-primary-bold);
font-family: var(--v2-font-family-primary);
line-height: var(--v2-line-height-reset);
letter-spacing: calc(0.2em / 14);
border-radius: var(--round-corners);
padding: 1px 3px 1px 3px;
color: inherit;
}
.sizeSmall {
font-size: var(--v2-font-size-1);
padding: 2px 3px;
line-height: var(--v2-line-height-reset);
}
.text {
color: var(--v2-palette-counter-badge-foreground);
}
.colorInherit {
background-color: currentColor;
}
.colorPrimary {
background-color: var(--palette-primary-main);
}
.colorGrey {
background-color: var(--palette-grey-lightest);
box-shadow: inset 0px 0px 0px 1px var(--palette-grey-dark);
& .text {
color: var(--palette-grey-dark);
}
}
.colorDark {
background-color: var(--palette-grey-dark);
}
.colorError {
background-color: var(--palette-error-darkest);
}
.colorEmphasis {
background-color: var(--v2-palette-counter-badge-background-emphasis);
}
.colorDefault {
background-color: var(--v2-palette-counter-badge-background-default);
}
.colorAlert {
background-color: var(--v2-palette-counter-badge-background-alert);
}
@@ -0,0 +1,34 @@
---
name: Counter
menu: UI Kit
---
import { Playground, PropsTable } from "docz";
import Flex from "../Flex";
import Counter from "./Counter";
# Counter
## Basic usage
<Playground>
<Flex alignItems="center">
<span>Comments</span> <Counter>23</Counter>
</Flex>
</Playground>
<Playground>
<Flex itemGutter>
<Counter size="sm">23</Counter>
<Counter size="sm" color="primary">
15
</Counter>
<Counter size="sm" color="grey">
10
</Counter>
<Counter size="sm" color="dark">
20
</Counter>
</Flex>
</Playground>
@@ -0,0 +1,10 @@
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import Counter from "./Counter";
it("renders correctly", () => {
const renderer = createRenderer();
renderer.render(<Counter>20</Counter>);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,65 @@
import cn from "classnames";
import React, { FunctionComponent } from "react";
import { withStyles } from "coral-ui/hocs";
import styles from "./Counter.css";
interface Props {
className?: string;
/**
* Override or extend the styles applied to the component.
*/
classes: typeof styles;
/**
* The color of the component. It supports those theme colors that make sense for this component.
*/
color?:
| "inherit"
| "dark"
| "grey"
| "primary"
| "error"
| "default"
| "emphasis"
| "alert";
size?: "sm" | "md";
}
const Counter: FunctionComponent<Props> = ({
classes,
color,
className,
children,
size,
...rest
}) => {
const rootClassName = cn(
classes.root,
{
[classes.colorPrimary]: color === "primary",
[classes.colorDark]: color === "dark",
[classes.colorGrey]: color === "grey",
[classes.colorInherit]: color === "inherit",
[classes.colorError]: color === "error",
[classes.colorDefault]: color === "default",
[classes.colorEmphasis]: color === "emphasis",
[classes.colorAlert]: color === "alert",
[classes.sizeSmall]: size === "sm",
},
className
);
return (
<span className={rootClassName} {...rest}>
<span className={classes.text}>{children}</span>
</span>
);
};
Counter.defaultProps = {
color: "inherit",
};
const enhanced = withStyles(styles)(Counter);
export default enhanced;
@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<Counter
classes={
Object {
"colorAlert": "Counter-colorAlert",
"colorDark": "Counter-colorDark",
"colorDefault": "Counter-colorDefault",
"colorEmphasis": "Counter-colorEmphasis",
"colorError": "Counter-colorError",
"colorGrey": "Counter-colorGrey",
"colorInherit": "Counter-colorInherit",
"colorPrimary": "Counter-colorPrimary",
"root": "Counter-root",
"sizeSmall": "Counter-sizeSmall",
"text": "Counter-text",
}
}
color="inherit"
>
20
</Counter>
`;
@@ -0,0 +1 @@
export { default } from "./Counter";
@@ -0,0 +1,22 @@
---
name: Delay
menu: UI Kit
---
import { Playground, PropsTable } from "docz";
import Delay from "./Delay.tsx";
import HorizontalGutter from "../HorizontalGutter";
import Spinner from "../Spinner";
# Delay
## Basic Use
<Playground>
<HorizontalGutter size="double">
<div>Spinner will show up in 3 seconds</div>
<Delay>
<Spinner />
</Delay>
</HorizontalGutter>
</Playground>
@@ -0,0 +1,22 @@
import React from "react";
import TestRenderer from "react-test-renderer";
import { PropTypesOf } from "coral-ui/types";
import Delay from "./Delay";
it("renders correctly", () => {
jest.useFakeTimers();
try {
const props: PropTypesOf<typeof Delay> = {
children: "custom",
ms: 3000,
};
const renderer = TestRenderer.create(<Delay {...props}>Hello</Delay>);
expect(renderer.toJSON()).toMatchSnapshot();
jest.advanceTimersByTime(3000);
expect(renderer.toJSON()).toMatchSnapshot();
} finally {
jest.useRealTimers();
}
});
@@ -0,0 +1,28 @@
import React, { FunctionComponent, useEffect, useState } from "react";
interface Props {
ms?: number;
children: React.ReactNode;
}
const Delay: FunctionComponent<Props> = ({ ms, children }) => {
const [render, setRender] = useState<boolean>(false);
useEffect(() => {
const timeout = setTimeout(() => {
setRender(true);
}, ms);
return () => {
clearTimeout(timeout);
};
}, [ms]);
if (!render) {
return null;
}
return <>{children}</>;
};
Delay.defaultProps = {
ms: 500,
};
export default Delay;
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `null`;
exports[`renders correctly 2`] = `"Hello"`;
@@ -0,0 +1 @@
export { default } from "./Delay";
@@ -0,0 +1,23 @@
$divider-border-color: var(--v2-colors-grey-300);
.root {
border-top: 0;
border-bottom: 1px solid $divider-border-color;
padding: 0;
}
.spacing1 {
margin: var(--v2-spacing-1) 0;
}
.spacing2 {
margin: var(--v2-spacing-2) 0;
}
.spacing3 {
margin: var(--v2-spacing-3) 0;
}
.spacing4 {
margin: var(--v2-spacing-4) 0;
}
@@ -0,0 +1,23 @@
import cn from "classnames";
import React, { FunctionComponent } from "react";
import styles from "./Divider.css";
interface Props {
spacing?: 1 | 2 | 3 | 4;
}
const Divider: FunctionComponent<Props> = ({ spacing = 3 }) => {
return (
<hr
className={cn(styles.root, {
[styles.spacing1]: spacing && spacing === 1,
[styles.spacing2]: spacing && spacing === 2,
[styles.spacing3]: spacing && spacing === 3,
[styles.spacing4]: spacing && spacing === 4,
})}
/>
);
};
export default Divider;

Some files were not shown because too many files have changed in this diff Show More