mirror of
https://github.com/wassname/talk.git
synced 2026-08-07 11:29:44 +08:00
Merge branch 'ui-components' of github.com:coralproject/talk into ui-components
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user