Merge branch 'ui-components' of github.com:coralproject/talk into ui-components

This commit is contained in:
Belen Curcio
2018-08-03 09:25:11 -03:00
159 changed files with 6379 additions and 1540 deletions
@@ -3,7 +3,14 @@ import React from "react";
import simulant from "simulant";
import sinon from "sinon";
import ClickOutside from "./ClickOutside";
import UIContext from "../UIContext";
import {
ClickFarAwayCallback,
ClickFarAwayRegister,
ClickOutside,
default as ClickOutsideWithContext,
} from "./ClickOutside";
let container: HTMLElement;
@@ -60,3 +67,57 @@ it("should ignore click inside", () => {
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();
});
@@ -1,13 +1,30 @@
import React from "react";
import React, { StatelessComponent } 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;
interface Props {
onClickOutside: () => void;
/**
* A way to listen for clicks that are e.g. outside of the
* current frame for `ClickOutside`
*/
registerClickFarAway?: ClickFarAwayRegister;
children: React.ReactNode;
}
class ClickOutside extends React.Component<Props> {
export class ClickOutside extends React.Component<Props> {
public domNode: Element | null = null;
private unlisten?: ClickFarAwayUnlistenCallback;
public handleClick = (e: MouseEvent) => {
const { onClickOutside } = this.props;
@@ -17,17 +34,43 @@ class ClickOutside extends React.Component<Props> {
}
};
public handleClickFarAway = () => {
const { onClickOutside } = this.props;
// tslint:disable-next-line:no-unused-expression
onClickOutside && onClickOutside();
};
public componentDidMount() {
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;
}
}
export default ClickOutside;
const ClickOutsideWithContext: StatelessComponent<Props> = props => (
<UIContext.Consumer>
{({ registerClickFarAway }) => (
<ClickOutside {...props} registerClickFarAway={registerClickFarAway} />
)}
</UIContext.Consumer>
);
export default ClickOutsideWithContext;
@@ -0,0 +1 @@
export { default as ClickOutside, ClickFarAwayRegister } from "./ClickOutside";
@@ -1,9 +1,11 @@
import { shallow } from "enzyme";
import { mount, shallow } from "enzyme";
import React from "react";
import { MediaQueryMatchers } from "react-responsive";
import { PropTypesOf } from "talk-ui/types";
import { MatchMedia } from "./MatchMedia";
import UIContext from "../UIContext";
import { default as MatchMediaWithContext, MatchMedia } from "./MatchMedia";
it("renders correctly", () => {
const props: PropTypesOf<typeof MatchMedia> = {
@@ -25,3 +27,20 @@ it("map new speech prop to older aural prop", () => {
const wrapper = shallow(<MatchMedia {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("should get mediaQueryValues from context", () => {
const mediaQueryValues: Partial<MediaQueryMatchers> = {
width: 100,
};
const context: any = {
mediaQueryValues,
};
const wrapper = mount(
<UIContext.Provider value={context}>
<MatchMediaWithContext maxWidth="xs">
<span>Hello World</span>
</MatchMediaWithContext>
</UIContext.Provider>
);
expect(wrapper.find(MatchMedia).prop("values")).toEqual(mediaQueryValues);
});
@@ -2,9 +2,18 @@ import React from "react";
import { MediaQueryMatchers } from "react-responsive";
import { Formatter } from "react-timeago";
import { ClickFarAwayRegister } from "../ClickOutside";
export interface UIContextProps {
/** Allows to integrate translated strings into `RelativeTime` Component */
timeagoFormatter?: Formatter | null;
/** Allows testing `MatchMedia` by setting media query values */
mediaQueryValues?: Partial<MediaQueryMatchers>;
/**
* A way to listen for clicks that are e.g. outside of the
* current frame for `ClickOutside`
*/
registerClickFarAway?: ClickFarAwayRegister;
}
const UIContext = React.createContext<UIContextProps>({} as any);