Big refactor: ts/ directory for all typescript, including react

Split out test-specific and general utility react components too.

And moved our test/legacy* files for the Style Guide into a styleguide/
subdirectory of test/.

I think we'll be able to live in this directory structure for a while.
This commit is contained in:
Scott Nonnenberg 2018-04-05 15:30:40 -07:00
parent 50d4dbaae7
commit 23537546fe
No known key found for this signature in database
GPG key ID: 5F82280C35134661
18 changed files with 54 additions and 37 deletions

View file

@ -1,6 +0,0 @@
```jsx
<util.ConversationContext theme={util.theme}>
<Message />
</util.ConversationContext>
```

View file

@ -1,36 +0,0 @@
import React from 'react';
/**
* A placeholder Message component, giving the structure of a plain message with none of
* the dynamic functionality. We can build off of this going forward.
*/
export class Message extends React.Component<{}, {}> {
public render() {
return (
<li className="entry outgoing sent delivered">
<span className="avatar" />
<div className="bubble">
<div className="sender" dir="auto" />
<div className="attachments" />
<p className="content" dir="auto">
<span className="body">
Hi there. How are you doing? Feeling pretty good? Awesome.
</span>
</p>
<div className="meta">
<span
className="timestamp"
data-timestamp="1522800995425"
title="Tue, Apr 3, 2018 5:16 PM"
>
1 minute ago
</span>
<span className="status hide" />
<span className="timer" />
</div>
</div>
</li>
);
}
}

View file

@ -1,2 +0,0 @@
This is Reply.md.

View file

@ -1,14 +0,0 @@
import React from 'react';
interface Props { name: string; }
interface State { count: number; }
export class Reply extends React.Component<Props, State> {
public render() {
return (
<div>Placeholder</div>
);
}
}

View file

@ -1,20 +0,0 @@
Rendering a real `Whisper.MessageView` using `<util.ConversationContext />` and
`<util.BackboneWrapper />`.
```jsx
const model = new Whisper.Message({
type: 'outgoing',
body: 'text',
sent_at: Date.now() - 5000,
})
const View = Whisper.MessageView;
const options = {
model,
};
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper
View={View}
options={options}
/>
</util.ConversationContext>
```

View file

@ -1,69 +0,0 @@
import React from 'react';
interface Props {
/** The View class, which will be instantiated then treated like a Backbone View */
readonly View: BackboneViewConstructor;
/** Options to be passed along to the view when constructed */
readonly options: object;
}
interface BackboneView {
remove: () => void;
render: () => void;
el: HTMLElement;
}
interface BackboneViewConstructor {
new (options: object): BackboneView;
}
/**
* Allows Backbone Views to be rendered inside of React (primarily for the styleguide)
* while we slowly replace the internals of a given Backbone view with React.
*/
export class BackboneWrapper extends React.Component<Props, {}> {
protected el: Element | null = null;
protected view: BackboneView | null = null;
public componentWillUnmount() {
this.teardown();
}
public shouldComponentUpdate() {
// we're handling all updates manually
return false;
}
public render() {
return <div ref={this.setEl} />;
}
protected setEl = (element: HTMLDivElement | null) => {
this.el = element;
this.setup();
}
protected setup = () => {
const { el } = this;
const { View, options } = this.props;
if (!el) {
return;
}
this.view = new View(options);
this.view.render();
// It's important to let the view create its own root DOM element. This ensures that
// its tagName property actually takes effect.
el.appendChild(this.view.el);
}
protected teardown() {
if (!this.view) {
return;
}
this.view.remove();
this.view = null;
}
}

View file

@ -1,8 +0,0 @@
The simplest example of using the `<ConversationContext />` component:
```jsx
<util.ConversationContext theme={util.theme}>
<div>Just a plain bit of text</div>
</util.ConversationContext>
```

View file

@ -1,31 +0,0 @@
import React from 'react';
interface Props {
/**
* Corresponds to the theme setting in the app, and the class added to the root element.
*/
theme: 'ios' | 'android' | 'android-dark';
}
/**
* Provides the parent elements necessary to allow the main Signal Desktop stylesheet to
* apply (with no changes) to messages in this context.
*/
export class ConversationContext extends React.Component<Props, {}> {
public render() {
const { theme } = this.props;
return (
<div className={theme}>
<div className="conversation">
<div className="discussion-container" style={{padding: '0.5em'}}>
<ul className="message-list">
{this.props.children}
</ul>
</div>
</div>
</div>
);
}
}

View file

@ -1,83 +0,0 @@
import moment from 'moment';
import qs from 'qs';
import React from 'react';
import ReactDOM from 'react-dom';
// Helper components used in the styleguide, exposed at 'util' in the global scope via the
// context option in react-styleguidist.
export { ConversationContext } from './ConversationContext';
export { BackboneWrapper } from './BackboneWrapper';
// Here we can make things inside Webpack available to Backbone views like preload.js.
import { Message } from '../conversation/Message';
import { Reply } from '../conversation/Reply';
// TypeScript wants two things when you import:
// 1) a normal typescript file
// 2) a javascript file with type definitions
// Anything else will raise an error, that it can't find the module. And so, we ignore...
// @ts-ignore
import gif from '../../../fixtures/giphy-GVNvOUpeYmI7e.gif';
// @ts-ignore
import mp3 from '../../../fixtures/incompetech-com-Agnus-Dei-X.mp3';
// @ts-ignore
import txt from '../../../fixtures/lorem-ipsum.txt';
// @ts-ignore
import mp4 from '../../../fixtures/pixabay-Soap-Bubble-7141.mp4';
export {
mp3,
gif,
mp4,
txt,
};
// Required, or TypeScript complains about adding keys to window
const parent = window as any;
const query = window.location.search.replace(/^\?/, '');
const urlOptions = qs.parse(query);
const theme = urlOptions.theme || 'android';
const locale = urlOptions.locale || 'en';
// @ts-ignore
import localeMessages from '../../../_locales/en/messages.json';
// @ts-ignore
import { setup } from '../../modules/i18n';
const i18n = setup(locale, localeMessages);
export {
theme,
locale,
i18n,
};
parent.i18n = i18n;
parent.moment = moment;
parent.moment.updateLocale(locale, {
relativeTime: {
h: parent.i18n('timestamp_h'),
m: parent.i18n('timestamp_m'),
s: parent.i18n('timestamp_s'),
},
});
parent.moment.locale(locale);
parent.React = React;
parent.ReactDOM = ReactDOM;
const SignalReact = parent.Signal.React = parent.Signal.React || {};
SignalReact.Message = Message;
SignalReact.Reply = Reply;