mirror of
https://github.com/wassname/talk.git
synced 2026-09-11 12:51:33 +08:00
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:
@@ -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
Reference in New Issue
Block a user