mirror of
https://github.com/wassname/talk.git
synced 2026-09-12 13:01:11 +08:00
feat: added support for --only flag
Merge pull request #1731 from coralproject/next-watcher-flags Watcher --only Use JSDocs comments (#1727) Merge branch 'next' into prevent-compile-loop-relay Merge pull request #1726 from coralproject/prevent-compile-loop-relay [next] Adapt relay watch config [next] Remove nodemon (#1725) * Remove old nodemon configs * Remove nodemon [next] Jest implementation for React Components (#1733) * Make jest testing work with custom path and css modules * Add first test * feat: added unit tests to ci * fix: updated package-lock.json * Update cssTransform.js * Update cssTransform.js * Fix test in ci Adapt files.exclude (#1736) Permalink ui Adding Copy to clipboard functionality WIP clean request wip progress progress work in progress wip ui functionality Translations :/ wip Merge branch 'permalink' of github.com:coralproject/talk into permalink * 'permalink' of github.com:coralproject/talk: (42 commits) [next] Support server side jest testing (#1747) Update snapshots Add comments Remove precss Move react-responsive to dev deps Remove comment Mobile first approach Support standard css variables, dynamically set spacing-unit Add docs Fully implement Flex and MatchMedia Responsive Components <3 fix: linting fix: adjusted pageInfo Remove obsoloe snapshot Move jsdom to dev deps Mark comments as always returning a value Add comment Fix unit tests Translate, concept for translation and id strings Add aria props ... Adding Attachment and Popover Component merge conflicts progress Any Working Support for refs Ready Merge branch 'permalink' of github.com:coralproject/talk into permalink * 'permalink' of github.com:coralproject/talk: (101 commits) Ready Support for refs Working Any progress merge conflicts Make timeagoFormatter optional More colors Colors Short circuit endless respawn fix: new mongo parser Remove jest from watcher config, as it doesnt run well inside Add jest set Apply suggestions Move react-timeago to dev Upgrade docz Cleanup docz scripts Support watcher sets [next] Support server side jest testing (#1747) Make filter only a pure function ...
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
.root {
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import React, { CSSProperties } from "react";
|
||||
import { Manager, Popper, Reference, RefHandler } from "react-popper";
|
||||
|
||||
interface RenderProps {
|
||||
ref: RefHandler;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
interface InnerProps {
|
||||
body: React.ReactElement<any> | null;
|
||||
children: (props: RenderProps) => React.ReactElement<any>;
|
||||
className?: string;
|
||||
placement?:
|
||||
| "auto-start"
|
||||
| "auto"
|
||||
| "auto-end"
|
||||
| "top-start"
|
||||
| "top"
|
||||
| "top-end"
|
||||
| "right-start"
|
||||
| "right"
|
||||
| "right-end"
|
||||
| "bottom-end"
|
||||
| "bottom"
|
||||
| "bottom-start"
|
||||
| "left-end"
|
||||
| "left"
|
||||
| "left-start";
|
||||
}
|
||||
|
||||
interface Props {
|
||||
ref: any;
|
||||
style: CSSProperties;
|
||||
}
|
||||
|
||||
class Attachment extends React.Component<InnerProps> {
|
||||
public render() {
|
||||
const { children, body, placement = "top" } = this.props;
|
||||
return (
|
||||
<Manager>
|
||||
<Reference>{(props: Props) => children({ ref: props.ref })}</Reference>
|
||||
<Popper
|
||||
placement={placement}
|
||||
modifiers={{ preventOverflow: { enabled: false } }}
|
||||
eventsEnabled
|
||||
positionFixed={false}
|
||||
>
|
||||
{(props: Props) =>
|
||||
body
|
||||
? React.cloneElement(body, {
|
||||
innerRef: props.ref,
|
||||
style: props.style,
|
||||
})
|
||||
: null
|
||||
}
|
||||
</Popper>
|
||||
</Manager>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Attachment;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./Attachment";
|
||||
export { default } from "./Attachment";
|
||||
@@ -19,6 +19,8 @@ interface InnerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
|
||||
/** This is passed by the `withKeyboardFocus` HOC */
|
||||
keyboardFocus: boolean;
|
||||
|
||||
innerRef?: React.RefObject<HTMLDivElement> | any;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,6 +33,7 @@ const BaseButton: StatelessComponent<InnerProps> = ({
|
||||
classes,
|
||||
keyboardFocus,
|
||||
type: typeProp,
|
||||
innerRef,
|
||||
...rest
|
||||
}) => {
|
||||
let Element = "button";
|
||||
@@ -53,7 +56,7 @@ const BaseButton: StatelessComponent<InnerProps> = ({
|
||||
[classes.keyboardFocus]: keyboardFocus,
|
||||
});
|
||||
|
||||
return <Element {...rest} className={rootClassName} />;
|
||||
return <Element {...rest} className={rootClassName} ref={innerRef} />;
|
||||
};
|
||||
|
||||
const enhanced = withStyles(styles)(withKeyboardFocus(BaseButton));
|
||||
|
||||
@@ -1,78 +1,99 @@
|
||||
.root {
|
||||
composes: button from "talk-ui/shared/typography.css";
|
||||
padding: 5px 20px;
|
||||
border-radius: $round-corners;
|
||||
background-color: transparent;
|
||||
/* TODO: hover styles for the default button */
|
||||
|
||||
&:enabled,
|
||||
&:disabled {
|
||||
padding: 5px 20px;
|
||||
border-radius: var(--round-corners);
|
||||
background-color: transparent;
|
||||
/* TODO: hover styles for the default button */
|
||||
}
|
||||
}
|
||||
|
||||
.fullWidth {
|
||||
.root:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.fullWidth:enabled,
|
||||
.fullWidth:disabled {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.primary {
|
||||
background-color: $palette-primary-main;
|
||||
.primary:enabled,
|
||||
.primary:disabled {
|
||||
background-color: var(--palette-primary-main);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.primary:enabled {
|
||||
&:hover {
|
||||
background-color: $palette-primary-light;
|
||||
background-color: var(--palette-primary-light);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $palette-primary-lighter;
|
||||
}
|
||||
|
||||
&.invert {
|
||||
background-color: transparent;
|
||||
border: 1px solid $palette-primary-main;
|
||||
color: $palette-primary-main;
|
||||
|
||||
&:hover {
|
||||
border-color: $palette-primary-light;
|
||||
color: $palette-primary-light;
|
||||
}
|
||||
|
||||
&:active {
|
||||
border-color: $palette-primary-lighter;
|
||||
color: $palette-primary-lighter;
|
||||
}
|
||||
background-color: var(--palette-primary-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background-color: $palette-secondary-main;
|
||||
color: #fff;
|
||||
.primary:enabled.invert,
|
||||
.primary:disabled.invert {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--palette-primary-main);
|
||||
color: var(--palette-primary-main);
|
||||
}
|
||||
|
||||
.primary:enabled.invert {
|
||||
&:hover {
|
||||
background-color: $palette-secondary-light;
|
||||
border-color: var(--palette-primary-light);
|
||||
color: var(--palette-primary-light);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $palette-secondary-lighter;
|
||||
border-color: var(--palette-primary-lighter);
|
||||
color: var(--palette-primary-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
.secondary:enabled,
|
||||
.secondary:disabled {
|
||||
background-color: var(--palette-secondary-main);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.secondary:enabled {
|
||||
&:hover {
|
||||
background-color: var(--palette-secondary-light);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--palette-secondary-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
.secondary:enabled.invert,
|
||||
.secondary:disabled.invert {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--palette-secondary-main);
|
||||
color: var(--palette-secondary-main);
|
||||
}
|
||||
|
||||
.secondary:enabled.invert {
|
||||
&:hover {
|
||||
border-color: var(--palette-secondary-light);
|
||||
color: var(--palette-secondary-light);
|
||||
}
|
||||
|
||||
&.invert {
|
||||
background-color: transparent;
|
||||
border: 1px solid $palette-secondary-main;
|
||||
color: $palette-secondary-main;
|
||||
|
||||
&:hover {
|
||||
border-color: $palette-secondary-light;
|
||||
color: $palette-secondary-light;
|
||||
}
|
||||
|
||||
&:active {
|
||||
border-color: $palette-secondary-lighter;
|
||||
color: $palette-secondary-lighter;
|
||||
}
|
||||
&:active {
|
||||
border-color: var(--palette-secondary-lighter);
|
||||
color: var(--palette-secondary-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This seems to be the best way to target modern touch device browsers.
|
||||
*/
|
||||
@media (-moz-touch-enabled: 1), (pointer:coarse) {
|
||||
@media (-moz-touch-enabled: 1), (pointer: coarse) {
|
||||
/* TODO: Remove hover styles */
|
||||
}
|
||||
|
||||
@@ -10,11 +10,18 @@ import Button from './Button'
|
||||
|
||||
## Basic usage
|
||||
<Playground>
|
||||
<Button style={{marginRight: "10px"}}>Push Me</Button>
|
||||
<Button style={{marginRight: "10px"}} anchor>I'm an Anchor Tag</Button>
|
||||
<Button style={{marginRight: "10px"}} primary>Primary</Button>
|
||||
<Button style={{marginRight: "10px"}} secondary>Secondary</Button>
|
||||
<Button style={{marginTop: "10px"}} primary fullWidth>Full Width</Button>
|
||||
<Button style={{marginTop: "10px"}} primary invert fullWidth>Full Width Invert</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}}>Push Me</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} disabled>Push Me</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} anchor>I'm an Anchor Tag</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} primary>Primary</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} primary disabled>Primary</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} secondary>Secondary</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} secondary disabled>Secondary</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} primary invert>Primary</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} primary invert disabled>Primary</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} secondary invert>Secondary</Button>
|
||||
<Button style={{margin: "0 10px 10px 0"}} secondary invert disabled>Secondary</Button>
|
||||
<Button style={{marginBottom: "10px"}} primary fullWidth>Full Width</Button>
|
||||
<Button style={{marginBottom: "10px"}} primary invert fullWidth>Full Width Invert</Button>
|
||||
</Playground>
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ interface InnerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
|
||||
/** If set renders a button with secondary colors */
|
||||
secondary?: boolean;
|
||||
|
||||
innerRef?: React.RefObject<HTMLDivElement> | any;
|
||||
}
|
||||
|
||||
class Button extends React.Component<InnerProps> {
|
||||
@@ -39,6 +41,7 @@ class Button extends React.Component<InnerProps> {
|
||||
invert,
|
||||
primary,
|
||||
secondary,
|
||||
innerRef,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@@ -53,6 +56,7 @@ class Button extends React.Component<InnerProps> {
|
||||
<BaseButton
|
||||
className={rootClassName}
|
||||
classes={pick(classes, "keyboardFocus")}
|
||||
innerRef={innerRef}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import cn from "classnames";
|
||||
import * as React from "react";
|
||||
import { ReactNode, StatelessComponent } from "react";
|
||||
|
||||
import { withStyles } from "talk-ui/hocs";
|
||||
import { PropTypesOf } from "talk-ui/types";
|
||||
|
||||
import * as styles from "./Center.css";
|
||||
|
||||
interface InnerProps {
|
||||
/**
|
||||
* This prop can be used to add custom classnames.
|
||||
* It is handled by the `withStyles `HOC.
|
||||
*/
|
||||
classes: Partial<typeof styles>;
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const Center: StatelessComponent<InnerProps> = props => {
|
||||
return (
|
||||
<div className={cn(props.className, props.classes.root)}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withStyles(styles)(Center);
|
||||
export type CenterProps = PropTypesOf<typeof enhanced>;
|
||||
export default enhanced;
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from "./Center";
|
||||
export { default } from "./Center";
|
||||
@@ -0,0 +1,99 @@
|
||||
.root {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.halfItemGutter {
|
||||
& > * {
|
||||
margin: 0 calc(0.5 * var(--spacing-unit)) 0 0;
|
||||
}
|
||||
&.directionRowReverse {
|
||||
& > * {
|
||||
margin: 0 0 0 calc(0.5 * var(--spacing-unit));
|
||||
}
|
||||
}
|
||||
&.directionColumn {
|
||||
& > * {
|
||||
margin: 0 0 calc(0.5 * var(--spacing-unit)) 0;
|
||||
}
|
||||
}
|
||||
&.directionColumnReverese {
|
||||
& > * {
|
||||
margin: calc(0.5 * var(--spacing-unit)) 0 0 0;
|
||||
}
|
||||
}
|
||||
& > *:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.itemGutter {
|
||||
& > * {
|
||||
margin: 0 var(--spacing-unit) 0 0;
|
||||
}
|
||||
&.directionRowReverse {
|
||||
& > * {
|
||||
margin: 0 0 0 var(--spacing-unit);
|
||||
}
|
||||
}
|
||||
&.directionColumn {
|
||||
& > * {
|
||||
margin: 0 0 var(--spacing-unit) 0;
|
||||
}
|
||||
}
|
||||
&.directionColumnReverese {
|
||||
& > * {
|
||||
margin: var(--spacing-unit) 0 0 0;
|
||||
}
|
||||
}
|
||||
& > *:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.justifyFlexStart {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.justifyFlexEnd {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.justifyCenter {
|
||||
justify-content: center;
|
||||
}
|
||||
.justifySpaceBetween {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.justifySpaceAround {
|
||||
justify-content: space-around;
|
||||
}
|
||||
.justifySpaceEvenly {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.alignFlexStart {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.alignFlexEnd {
|
||||
align-items: flex-end;
|
||||
}
|
||||
.alignCenter {
|
||||
align-items: center;
|
||||
}
|
||||
.alignBaseline {
|
||||
align-items: baseline;
|
||||
}
|
||||
.alignStretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.directionRow {
|
||||
flex-direction: row;
|
||||
}
|
||||
.directionColumn {
|
||||
flex-direction: column;
|
||||
}
|
||||
.directionRowReverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.directionColumnReverse {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Flex
|
||||
menu: UI Kit
|
||||
---
|
||||
|
||||
import { Playground, PropsTable } from 'docz'
|
||||
import Flex from './Flex'
|
||||
|
||||
# Flex
|
||||
|
||||
`Flex` is a wrapper around `flexbox`.
|
||||
|
||||
## Basic usage
|
||||
<Playground>
|
||||
<Flex justifyContent="center">I'm centered</Flex>
|
||||
</Playground>
|
||||
@@ -0,0 +1,20 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
|
||||
import { PropTypesOf } from "talk-ui/types";
|
||||
|
||||
import Flex from "./Flex";
|
||||
|
||||
it("renders correctly", () => {
|
||||
const props: PropTypesOf<typeof Flex> = {
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
direction: "row",
|
||||
};
|
||||
const wrapper = shallow(
|
||||
<Flex {...props}>
|
||||
<div>Hello World</div>
|
||||
</Flex>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import cn from "classnames";
|
||||
import React from "react";
|
||||
import { StatelessComponent } from "react";
|
||||
|
||||
import { pascalCase } from "talk-common/utils";
|
||||
|
||||
import * as styles from "./Flex.css";
|
||||
|
||||
interface InnerProps {
|
||||
id?: string;
|
||||
role?: string;
|
||||
justifyContent?:
|
||||
| "flex-start"
|
||||
| "flex-end"
|
||||
| "center"
|
||||
| "space-around"
|
||||
| "space-between"
|
||||
| "space-evenly";
|
||||
alignItems?: "flex-start" | "flex-end" | "center" | "baseline" | "stretch";
|
||||
direction?: "row" | "column" | "row-reverse" | "column-reverse";
|
||||
itemGutter?: boolean | "half";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Flex: StatelessComponent<InnerProps> = props => {
|
||||
const {
|
||||
className,
|
||||
justifyContent,
|
||||
alignItems,
|
||||
direction,
|
||||
itemGutter,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const classObject: Record<string, boolean> = {
|
||||
[styles.itemGutter]: itemGutter === true,
|
||||
[styles.halfItemGutter]: itemGutter === "half",
|
||||
};
|
||||
|
||||
if (justifyContent) {
|
||||
classObject[(styles as any)[`justify${pascalCase(justifyContent)}`]] = true;
|
||||
}
|
||||
|
||||
if (alignItems) {
|
||||
classObject[(styles as any)[`align${pascalCase(alignItems)}`]] = true;
|
||||
}
|
||||
|
||||
if (direction) {
|
||||
classObject[(styles as any)[`direction${pascalCase(direction)}`]] = true;
|
||||
}
|
||||
|
||||
const classNames: string = cn(styles.root, className, classObject);
|
||||
|
||||
return <div className={classNames} {...rest} />;
|
||||
};
|
||||
|
||||
export default Flex;
|
||||
@@ -0,0 +1,11 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<div
|
||||
className="Flex-root Flex-justifyCenter Flex-alignCenter Flex-directionRow"
|
||||
>
|
||||
<div>
|
||||
Hello World
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./Flex";
|
||||
export { default } from "./Flex";
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: MatchMedia
|
||||
menu: UI Kit
|
||||
---
|
||||
|
||||
import { Playground, PropsTable } from 'docz'
|
||||
import MatchMedia from './MatchMedia'
|
||||
|
||||
# MatchMedia
|
||||
|
||||
`MatchMedia` uses media queries to provide responsive rendering.
|
||||
|
||||
## Basic usage
|
||||
<Playground>
|
||||
<MatchMedia maxWidth="xs">
|
||||
I'm a very small screen
|
||||
</MatchMedia>
|
||||
<MatchMedia minWidth="xs" maxWidth="sm">
|
||||
I'm a small screen
|
||||
</MatchMedia>
|
||||
<MatchMedia minWidth="sm" maxWidth="md">
|
||||
I'm a medium screen
|
||||
</MatchMedia>
|
||||
<MatchMedia minWidth="md" maxWidth="lg">
|
||||
I'm a large screen
|
||||
</MatchMedia>
|
||||
<MatchMedia minWidth="lg" maxWidth="xl">
|
||||
I'm a very large screen
|
||||
</MatchMedia>
|
||||
<MatchMedia minWidth="xl">
|
||||
I'm a super large screen
|
||||
</MatchMedia>
|
||||
</Playground>
|
||||
|
||||
## Using render props
|
||||
<Playground>
|
||||
<MatchMedia minWidth="lg">
|
||||
{matches => <span>{matches ? "I'm big" : "I'm small"}</span>}
|
||||
</MatchMedia>
|
||||
</Playground>
|
||||
@@ -0,0 +1,27 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
|
||||
import { PropTypesOf } from "talk-ui/types";
|
||||
|
||||
import { MatchMedia } from "./MatchMedia";
|
||||
|
||||
it("renders correctly", () => {
|
||||
const props: PropTypesOf<typeof MatchMedia> = {
|
||||
minWidth: "xs",
|
||||
maxWidth: "sm",
|
||||
component: "div",
|
||||
screen: true,
|
||||
children: <div>Hello World</div>,
|
||||
};
|
||||
const wrapper = shallow(<MatchMedia {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("map new speech prop to older aural prop", () => {
|
||||
const props: PropTypesOf<typeof MatchMedia> = {
|
||||
speech: true,
|
||||
children: <div>Hello World</div>,
|
||||
};
|
||||
const wrapper = shallow(<MatchMedia {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import React from "react";
|
||||
import { ReactNode, StatelessComponent } from "react";
|
||||
import Responsive, { MediaQueryMatchers } from "react-responsive";
|
||||
|
||||
import theme from "../../theme/variables";
|
||||
import UIContext from "../UIContext";
|
||||
|
||||
type Breakpoints = keyof typeof theme.breakpoints;
|
||||
|
||||
interface InnerProps {
|
||||
minWidth?: Breakpoints;
|
||||
maxWidth?: Breakpoints;
|
||||
children: ReactNode | ((matches: boolean) => React.ReactNode);
|
||||
className?: string;
|
||||
component?:
|
||||
| string
|
||||
| React.SFC<any>
|
||||
| React.ClassType<any, any, any>
|
||||
| React.ComponentClass<any>;
|
||||
all?: boolean;
|
||||
print?: boolean;
|
||||
screen?: boolean;
|
||||
speech?: boolean;
|
||||
values?: Partial<MediaQueryMatchers>;
|
||||
}
|
||||
|
||||
export const MatchMedia: StatelessComponent<InnerProps> = props => {
|
||||
const { speech, minWidth, maxWidth, ...rest } = props;
|
||||
const mapped = {
|
||||
// TODO: Temporarily map newer speech to older aural type until
|
||||
// react-responsive supports the speech prop.
|
||||
aural: speech,
|
||||
minWidth: minWidth ? theme.breakpoints[minWidth] + 1 : undefined,
|
||||
maxWidth: maxWidth ? theme.breakpoints[maxWidth] : undefined,
|
||||
};
|
||||
return <Responsive {...rest} {...mapped} />;
|
||||
};
|
||||
|
||||
const MatchMediaWithContext: StatelessComponent<InnerProps> = props => (
|
||||
<UIContext.Consumer>
|
||||
{({ mediaQueryValues }) => (
|
||||
<MatchMedia {...props} values={mediaQueryValues} />
|
||||
)}
|
||||
</UIContext.Consumer>
|
||||
);
|
||||
|
||||
export default MatchMediaWithContext;
|
||||
@@ -0,0 +1,26 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`map new speech prop to older aural prop 1`] = `
|
||||
<MediaQuery
|
||||
aural={true}
|
||||
values={Object {}}
|
||||
>
|
||||
<div>
|
||||
Hello World
|
||||
</div>
|
||||
</MediaQuery>
|
||||
`;
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<MediaQuery
|
||||
component="div"
|
||||
maxWidth={640}
|
||||
minWidth={321}
|
||||
screen={true}
|
||||
values={Object {}}
|
||||
>
|
||||
<div>
|
||||
Hello World
|
||||
</div>
|
||||
</MediaQuery>
|
||||
`;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./MatchMedia";
|
||||
export { default } from "./MatchMedia";
|
||||
@@ -0,0 +1,15 @@
|
||||
.root {
|
||||
padding: 6px 10px;
|
||||
background: #ffffff !important;
|
||||
border: 1px solid #c9cacb;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 1px 0px 4px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 1px;
|
||||
color: #222 !important;
|
||||
display: flex !important;
|
||||
|
||||
&:after,
|
||||
&::before {
|
||||
border-top-color: transparent !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from "react";
|
||||
import { RefHandler } from "react-popper";
|
||||
import Attachment from "../Attachment";
|
||||
import * as styles from "./Popover.css";
|
||||
|
||||
interface RenderProps {
|
||||
toggleShow?: () => void;
|
||||
ref?: RefHandler;
|
||||
}
|
||||
|
||||
interface InnerProps {
|
||||
body: React.ReactElement<any> | null;
|
||||
children: (props: RenderProps) => any;
|
||||
}
|
||||
|
||||
interface State {
|
||||
show: false;
|
||||
}
|
||||
|
||||
class Popover extends React.Component<InnerProps> {
|
||||
public state: State = {
|
||||
show: false,
|
||||
};
|
||||
|
||||
public toggleShow = () => {
|
||||
this.setState((state: State) => ({
|
||||
show: !state.show,
|
||||
}));
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { body, children } = this.props;
|
||||
const { show } = this.state;
|
||||
return (
|
||||
<Attachment body={show ? body : null} className={styles.root}>
|
||||
{({ ref }) => children({ toggleShow: this.toggleShow, ref })}
|
||||
</Attachment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Popover;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./Popover";
|
||||
export { default } from "./Popover";
|
||||
@@ -0,0 +1,4 @@
|
||||
.root {
|
||||
composes: body1 from "talk-ui/shared/typography.css";
|
||||
background-color: transparent;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: RelativeTime
|
||||
menu: UI Kit
|
||||
---
|
||||
|
||||
import { Playground } from 'docz'
|
||||
import RelativeTime from './RelativeTime'
|
||||
|
||||
# RelativeTime
|
||||
|
||||
Renders relative time until given `date`.
|
||||
|
||||
## Basic usage
|
||||
<Playground>
|
||||
<RelativeTime date="2018-07-04T12:14"
|
||||
formatter={(value, unit, suffix, timestamp) => "".concat(value, " ", unit, " ", suffix)} />
|
||||
</Playground>
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from "react";
|
||||
import { create } from "react-test-renderer";
|
||||
|
||||
import { PropTypesOf } from "talk-ui/types";
|
||||
|
||||
import UIContext from "../UIContext";
|
||||
import RelativeTime from "./RelativeTime";
|
||||
|
||||
it("uses default formatter", () => {
|
||||
const props = {
|
||||
date: "2018-12-17T03:24:00.000Z",
|
||||
};
|
||||
const tree = create(<RelativeTime {...props} />).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("uses formatter from context", () => {
|
||||
const context: any = {
|
||||
timeagoFormatter: () => "My Context Formatter",
|
||||
};
|
||||
const props: PropTypesOf<typeof RelativeTime> = {
|
||||
date: "2018-12-17T03:24:00.000Z",
|
||||
};
|
||||
const tree = create(
|
||||
<UIContext.Provider value={context}>
|
||||
<RelativeTime {...props} />
|
||||
</UIContext.Provider>
|
||||
).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("uses formatter from props", () => {
|
||||
const props = {
|
||||
date: "2018-12-17T03:24:00.000Z",
|
||||
formatter: () => "My Props Formatter",
|
||||
};
|
||||
const tree = create(<RelativeTime {...props} />).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import cn from "classnames";
|
||||
import React from "react";
|
||||
import TimeAgo, { Formatter } from "react-timeago";
|
||||
|
||||
import { UIContext } from "talk-ui/components";
|
||||
import { withStyles } from "talk-ui/hocs";
|
||||
import { PropTypesOf } from "talk-ui/types";
|
||||
|
||||
import * as styles from "./RelativeTime.css";
|
||||
|
||||
interface InnerProps {
|
||||
date: string;
|
||||
live?: boolean;
|
||||
classes: typeof styles;
|
||||
className?: string;
|
||||
formatter?: Formatter;
|
||||
}
|
||||
|
||||
const defaultFormatter: Formatter = (value, unit, suffix, timestamp: string) =>
|
||||
new Date(timestamp).toISOString();
|
||||
|
||||
class RelativeTime extends React.Component<InnerProps> {
|
||||
public render() {
|
||||
const { date, classes, live, className, formatter } = this.props;
|
||||
return (
|
||||
<UIContext.Consumer>
|
||||
{({ timeagoFormatter }) => (
|
||||
<TimeAgo
|
||||
date={date}
|
||||
className={cn(className, classes.root)}
|
||||
live={live}
|
||||
formatter={timeagoFormatter || formatter || defaultFormatter}
|
||||
/>
|
||||
)}
|
||||
</UIContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withStyles(styles)(RelativeTime);
|
||||
export type RelativeTimeProps = PropTypesOf<typeof enhanced>;
|
||||
export default enhanced;
|
||||
@@ -0,0 +1,31 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`uses default formatter 1`] = `
|
||||
<time
|
||||
className="RelativeTime-root"
|
||||
dateTime="2018-12-17T03:24:00.000Z"
|
||||
title="2018-12-17T03:24:00.000Z"
|
||||
>
|
||||
2018-12-17T03:24:00.000Z
|
||||
</time>
|
||||
`;
|
||||
|
||||
exports[`uses formatter from context 1`] = `
|
||||
<time
|
||||
className="RelativeTime-root"
|
||||
dateTime="2018-12-17T03:24:00.000Z"
|
||||
title="2018-12-17T03:24:00.000Z"
|
||||
>
|
||||
My Context Formatter
|
||||
</time>
|
||||
`;
|
||||
|
||||
exports[`uses formatter from props 1`] = `
|
||||
<time
|
||||
className="RelativeTime-root"
|
||||
dateTime="2018-12-17T03:24:00.000Z"
|
||||
title="2018-12-17T03:24:00.000Z"
|
||||
>
|
||||
My Context Formatter
|
||||
</time>
|
||||
`;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./RelativeTime";
|
||||
export { default } from "./RelativeTime";
|
||||
@@ -0,0 +1,11 @@
|
||||
.root {
|
||||
font-family: "Source Sans Pro";
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
letter-spacing: calc(0.57em / 16);
|
||||
background: #ffffff;
|
||||
border: 1px solid #979797;
|
||||
box-sizing: border-box;
|
||||
border-radius: 1px;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export const root: string;
|
||||
@@ -0,0 +1,14 @@
|
||||
import cn from "classnames";
|
||||
import React, { InputHTMLAttributes, StatelessComponent } from "react";
|
||||
|
||||
import * as styles from "./TextField.css";
|
||||
|
||||
interface InnerProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
classes?: typeof styles;
|
||||
}
|
||||
|
||||
const Input: StatelessComponent<InnerProps> = ({ className, ...rest }) => {
|
||||
return <input {...rest} className={cn(styles.root, className)} />;
|
||||
};
|
||||
|
||||
export default Input;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./TextField";
|
||||
export { default } from "./TextField";
|
||||
@@ -11,19 +11,19 @@
|
||||
composes: heading2 from "talk-ui/shared/typography.css";
|
||||
}
|
||||
|
||||
.heading3 {
|
||||
.heading3 {
|
||||
composes: heading3 from "talk-ui/shared/typography.css";
|
||||
}
|
||||
|
||||
.heading4 {
|
||||
.heading4 {
|
||||
composes: heading4 from "talk-ui/shared/typography.css";
|
||||
}
|
||||
|
||||
.subtitle1 {
|
||||
.subtitle1 {
|
||||
composes: subtitle1 from "talk-ui/shared/typography.css";
|
||||
}
|
||||
|
||||
.subtitle2 {
|
||||
.subtitle2 {
|
||||
composes: subtitle2 from "talk-ui/shared/typography.css";
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
composes: body1 from "talk-ui/shared/typography.css";
|
||||
}
|
||||
|
||||
.body2 {
|
||||
.body2 {
|
||||
composes: body2 from "talk-ui/shared/typography.css";
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@
|
||||
composes: overline from "talk-ui/shared/typography.css";
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
composes: timestamp from "talk-ui/shared/typography.css";
|
||||
}
|
||||
|
||||
.alignLeft {
|
||||
text-align: left;
|
||||
}
|
||||
@@ -70,7 +74,7 @@
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
margin-bottom: calc(2px * $spacing-unit);
|
||||
margin-bottom: var(--spacing-unit);
|
||||
}
|
||||
|
||||
.colorInherit {
|
||||
@@ -78,17 +82,17 @@
|
||||
}
|
||||
|
||||
.colorPrimary {
|
||||
color: $palette-primary-main;
|
||||
color: var(--palette-primary-main);
|
||||
}
|
||||
|
||||
.colorSecondary {
|
||||
color: $palette-secondary-main;
|
||||
color: var(--palette-secondary-main);
|
||||
}
|
||||
|
||||
.colorTextSecondary {
|
||||
color: $palette-text-secondary;
|
||||
color: var(--palette-text-secondary);
|
||||
}
|
||||
|
||||
.colorError {
|
||||
color: $palette-error-main;
|
||||
color: var(--palette-error-main);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ type Variant =
|
||||
| "subtitle2"
|
||||
| "body1"
|
||||
| "body2"
|
||||
| "button";
|
||||
| "button"
|
||||
| "timestamp";
|
||||
|
||||
// Based on Typography Component of Material UI.
|
||||
// https://github.com/mui-org/material-ui/blob/303199d39b42a321d28347d8440d69166f872f27/packages/material-ui/src/Typography/Typography.js
|
||||
@@ -99,6 +100,8 @@ const Typography: StatelessComponent<InnerProps> = props => {
|
||||
{
|
||||
[classes.colorPrimary]: color === "primary",
|
||||
[classes.colorSecondary]: color === "secondary",
|
||||
[classes.colorError]: color === "error",
|
||||
[classes.colorTextSecondary]: color === "textSecondary",
|
||||
[classes.noWrap]: noWrap,
|
||||
[classes.gutterBottom]: gutterBottom,
|
||||
[classes.paragraph]: paragraph,
|
||||
@@ -129,6 +132,7 @@ Typography.defaultProps = {
|
||||
subtitle2: "h3",
|
||||
body1: "p",
|
||||
body2: "aside",
|
||||
timestamp: "span",
|
||||
},
|
||||
noWrap: false,
|
||||
paragraph: false,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from "react";
|
||||
import { MediaQueryMatchers } from "react-responsive";
|
||||
import { Formatter } from "react-timeago";
|
||||
|
||||
export interface UIContextProps {
|
||||
timeagoFormatter?: Formatter | null;
|
||||
mediaQueryValues?: Partial<MediaQueryMatchers>;
|
||||
}
|
||||
|
||||
const UIContext = React.createContext<UIContextProps>({} as any);
|
||||
|
||||
export default UIContext;
|
||||
@@ -0,0 +1 @@
|
||||
export { default, UIContextProps } from "./UIContext";
|
||||
@@ -1,4 +1,9 @@
|
||||
export { default as BaseButton } from "./BaseButton";
|
||||
export { default as Button } from "./Button";
|
||||
export { default as Center } from "./Center";
|
||||
export { default as Typography } from "./Typography";
|
||||
export { default as Popover } from "./Popover";
|
||||
export { default as TextField } from "./TextField";
|
||||
export { default as RelativeTime } from "./RelativeTime";
|
||||
export { default as UIContext, UIContextProps } from "./UIContext";
|
||||
export { default as Flex } from "./Flex";
|
||||
export { default as MatchMedia } from "./MatchMedia";
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import {
|
||||
InferableComponentEnhancerWithProps,
|
||||
withPropsOnChange,
|
||||
} from "recompose";
|
||||
|
||||
/**
|
||||
* withForwardRef provides a property called `forwardRef` using
|
||||
* the `React.forwardRef` api.
|
||||
*/
|
||||
function withForwardRef<T>(): InferableComponentEnhancerWithProps<
|
||||
{ forwardRef: Ref<any> },
|
||||
{}
|
||||
> {
|
||||
return WrappedComponent => {
|
||||
return React.forwardRef((props, ref) => (
|
||||
<WrappedComponent forwardRef={ref} />
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
export default withForwardRef;
|
||||
@@ -1,59 +1,72 @@
|
||||
.heading1 {
|
||||
font-size: calc(24rem / $rem-base);
|
||||
font-weight: $font-weight-medium;
|
||||
font-size: calc(24rem / var(--rem-base));
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-family: "Manuale";
|
||||
line-height: calc(32em / 24);
|
||||
letter-spacing: calc(0.2em / 16);
|
||||
color: $palette-text-primary;
|
||||
color: var(--palette-text-primary);
|
||||
}
|
||||
|
||||
.heading2 {
|
||||
font-size: calc(20rem / $rem-base);
|
||||
font-weight: $font-weight-medium;
|
||||
font-size: calc(20rem / var(--rem-base));
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-family: "Manuale";
|
||||
line-height: calc(24em / 20);
|
||||
color: $palette-text-primary;
|
||||
color: var(--palette-text-primary);
|
||||
}
|
||||
|
||||
.heading3 {
|
||||
font-size: calc(18rem / $rem-base);
|
||||
font-weight: $font-weight-medium;
|
||||
font-size: calc(18rem / var(--rem-base));
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-family: "Manuale";
|
||||
line-height: calc(20em / 18);
|
||||
letter-spacing: calc(0.2em / 16);
|
||||
color: $palette-text-primary;
|
||||
color: var(--palette-text-primary);
|
||||
}
|
||||
|
||||
.heading4 {
|
||||
font-size: calc(16rem / $rem-base);
|
||||
font-weight: $font-weight-medium;
|
||||
font-size: calc(16rem / var(--rem-base));
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-family: "Manuale";
|
||||
line-height: calc(18em / 16);
|
||||
letter-spacing: calc(0.2em / 16);
|
||||
color: $palette-text-primary;
|
||||
color: var(--palette-text-primary);
|
||||
}
|
||||
|
||||
.subtitle1 {}
|
||||
.subtitle1 {
|
||||
}
|
||||
|
||||
.subtitle2 {}
|
||||
.subtitle2 {
|
||||
}
|
||||
|
||||
.body2 {}
|
||||
.body2 {
|
||||
}
|
||||
|
||||
.body1 {
|
||||
font-size: calc(16rem / $rem-base);
|
||||
font-weight: $font-weight-regular;
|
||||
font-size: calc(16rem / var(--rem-base));
|
||||
font-weight: var(--font-weight-regular);
|
||||
font-family: "Source Sans Pro";
|
||||
line-height: calc(18em / 16);
|
||||
letter-spacing: calc(0.2em / 16);
|
||||
color: $palette-text-primary;
|
||||
color: var(--palette-text-primary);
|
||||
}
|
||||
|
||||
.button {
|
||||
color: $palette-text-secondary;
|
||||
color: var(--palette-text-secondary);
|
||||
font-family: "Source Sans Pro";
|
||||
font-weight: $font-weight-medium;
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-size: 16px;
|
||||
letter-spacing: calc(0.57em / 16);
|
||||
}
|
||||
|
||||
.overline {}
|
||||
.overline {
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: var(--palette-text-secondary);
|
||||
font-family: "Source Sans Pro";
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-size: 14px;
|
||||
line-height: calc(18em / 16);
|
||||
letter-spacing: calc(0.2em / 16);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
Variables defined in `variables.ts` are already available
|
||||
flattened and in kebab case.
|
||||
|
||||
These are additionally variables we define.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--spacing-unit: calc(1px * var(--spacing-unit-small));
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoints-xs) {
|
||||
:root {
|
||||
--spacing-unit: calc(1px * var(--spacing-unit-large));
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ const variables = {
|
||||
palette: {
|
||||
/* Primary colors */
|
||||
primary: {
|
||||
darker: "#0D5B8F",
|
||||
darkest: "#0D5B8F",
|
||||
dark: "#2B7EB5",
|
||||
main: "#3498DB",
|
||||
light: "#67B2E4",
|
||||
@@ -15,7 +15,7 @@ const variables = {
|
||||
},
|
||||
/* Secondary colors */
|
||||
secondary: {
|
||||
darker: "#404345",
|
||||
darkest: "#404345",
|
||||
dark: "#65696B",
|
||||
main: "#787D80",
|
||||
light: "#9A9DA0",
|
||||
@@ -23,7 +23,7 @@ const variables = {
|
||||
},
|
||||
/* Success colors */
|
||||
success: {
|
||||
darker: "#03AB61",
|
||||
darkest: "#03AB61",
|
||||
dark: "#02BD6B",
|
||||
main: "#00CD73",
|
||||
light: "#40D996",
|
||||
@@ -31,7 +31,7 @@ const variables = {
|
||||
},
|
||||
/* Error colors */
|
||||
error: {
|
||||
darker: "#F50F0C",
|
||||
darkest: "#F50F0C",
|
||||
dark: "#FF1F1C",
|
||||
main: "#FA4643",
|
||||
light: "#F26563",
|
||||
@@ -58,7 +58,8 @@ const variables = {
|
||||
},
|
||||
},
|
||||
/* gitter and spacing */
|
||||
spacingUnit: 5,
|
||||
spacingUnitSmall: 5,
|
||||
spacingUnitLarge: 10,
|
||||
/* Borders */
|
||||
roundCorners: "2px",
|
||||
/* Typography */
|
||||
@@ -68,6 +69,14 @@ const variables = {
|
||||
fontWeightLight: 300,
|
||||
fontWeightRegular: 400,
|
||||
fontWeightMedium: 550,
|
||||
/* Breakpoints */
|
||||
breakpoints: {
|
||||
xs: 320,
|
||||
sm: 640,
|
||||
md: 1024,
|
||||
lg: 1400,
|
||||
xl: 1600,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = variables;
|
||||
export default variables;
|
||||
|
||||
@@ -29,4 +29,6 @@ export type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U;
|
||||
*
|
||||
* E.g. type ButtonProps = ReturnPropTypes<Button>;
|
||||
*/
|
||||
export type PropTypesOf<T> = T extends React.ComponentType<infer R> ? R : {};
|
||||
export type PropTypesOf<T> = T extends React.ComponentType<infer R>
|
||||
? R
|
||||
: T extends React.Component<infer S> ? S : {};
|
||||
|
||||
Reference in New Issue
Block a user