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

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

View file

@ -0,0 +1,36 @@
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

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

View file

@ -0,0 +1,14 @@
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

@ -0,0 +1,20 @@
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

@ -0,0 +1,69 @@
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 Style Guide)
* 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;
}
}