mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-10-06 11:16:10 +08:00
fix(template): Rewrite in ES6, add missing windowTypes
This commit is contained in:
parent
fce1673aac
commit
9e3dbd3873
11 changed files with 213 additions and 184 deletions
|
@ -232,7 +232,10 @@ PackagesStore = Reflux.createStore
|
||||||
type: 'git'
|
type: 'git'
|
||||||
url: ''
|
url: ''
|
||||||
engines:
|
engines:
|
||||||
nylas: ">=#{NylasEnv.getVersion()}"
|
nylas: ">=#{NylasEnv.getVersion().split('-')[0]}"
|
||||||
|
windowTypes:
|
||||||
|
default: true
|
||||||
|
composer: true
|
||||||
description: "Enter a description of your package!"
|
description: "Enter a description of your package!"
|
||||||
dependencies: {}
|
dependencies: {}
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
{ComponentRegistry} = require 'nylas-exports'
|
|
||||||
|
|
||||||
MyComposerButton = require './my-composer-button'
|
|
||||||
MyMessageSidebar = require './my-message-sidebar'
|
|
||||||
|
|
||||||
module.exports =
|
|
||||||
# Activate is called when the package is loaded. If your package previously
|
|
||||||
# saved state using `serialize` it is provided.
|
|
||||||
#
|
|
||||||
activate: (@state) ->
|
|
||||||
ComponentRegistry.register MyComposerButton,
|
|
||||||
role: 'Composer:ActionButton'
|
|
||||||
|
|
||||||
ComponentRegistry.register MyMessageSidebar,
|
|
||||||
role: 'MessageListSidebar:ContactCard'
|
|
||||||
|
|
||||||
# Serialize is called when your package is about to be unmounted.
|
|
||||||
# You can return a state object that will be passed back to your package
|
|
||||||
# when it is re-activated.
|
|
||||||
#
|
|
||||||
serialize: ->
|
|
||||||
|
|
||||||
# This **optional** method is called when the window is shutting down,
|
|
||||||
# or when your package is being updated or disabled. If your package is
|
|
||||||
# watching any files, holding external resources, providing commands or
|
|
||||||
# subscribing to events, release them here.
|
|
||||||
#
|
|
||||||
deactivate: ->
|
|
||||||
ComponentRegistry.unregister(MyComposerButton)
|
|
||||||
ComponentRegistry.unregister(MyMessageSidebar)
|
|
33
static/package-template/lib/main.es6
Normal file
33
static/package-template/lib/main.es6
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import {ComponentRegistry} from 'nylas-exports';
|
||||||
|
|
||||||
|
import MyComposerButton from './my-composer-button';
|
||||||
|
import MyMessageSidebar from './my-message-sidebar';
|
||||||
|
|
||||||
|
// Activate is called when the package is loaded. If your package previously
|
||||||
|
// saved state using `serialize` it is provided.
|
||||||
|
//
|
||||||
|
export function activate() {
|
||||||
|
ComponentRegistry.register(MyComposerButton, {
|
||||||
|
role: 'Composer:ActionButton',
|
||||||
|
});
|
||||||
|
ComponentRegistry.register(MyMessageSidebar, {
|
||||||
|
role: 'MessageListSidebar:ContactCard',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize is called when your package is about to be unmounted.
|
||||||
|
// You can return a state object that will be passed back to your package
|
||||||
|
// when it is re-activated.
|
||||||
|
//
|
||||||
|
export function serialize() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// This **optional** method is called when the window is shutting down,
|
||||||
|
// or when your package is being updated or disabled. If your package is
|
||||||
|
// watching any files, holding external resources, providing commands or
|
||||||
|
// subscribing to events, release them here.
|
||||||
|
//
|
||||||
|
export function deactivate() {
|
||||||
|
ComponentRegistry.unregister(MyComposerButton);
|
||||||
|
ComponentRegistry.unregister(MyMessageSidebar);
|
||||||
|
}
|
|
@ -1,43 +0,0 @@
|
||||||
{Utils, DraftStore, React} = require 'nylas-exports'
|
|
||||||
{RetinaImg} = require 'nylas-component-kit'
|
|
||||||
|
|
||||||
class MyComposerButton extends React.Component
|
|
||||||
|
|
||||||
# Note: You should assign a new displayName to avoid naming
|
|
||||||
# conflicts when injecting your item
|
|
||||||
@displayName: 'MyComposerButton'
|
|
||||||
|
|
||||||
# When you register as a composer button, you receive a
|
|
||||||
# reference to the draft, and you can look it up to perform
|
|
||||||
# actions and retrieve data.
|
|
||||||
@propTypes:
|
|
||||||
draftClientId: React.PropTypes.string.isRequired
|
|
||||||
|
|
||||||
render: =>
|
|
||||||
<div className="my-package">
|
|
||||||
<button className="btn btn-toolbar" onClick={ => @_onClick()} ref="button">
|
|
||||||
Hello World
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
_onClick: =>
|
|
||||||
# To retrieve information about the draft, we fetch the current editing
|
|
||||||
# session from the draft store. We can access attributes of the draft
|
|
||||||
# and add changes to the session which will be appear immediately.
|
|
||||||
DraftStore.sessionForClientId(@props.draftClientId).then (session) =>
|
|
||||||
newSubject = "#{session.draft().subject} - It Worked!"
|
|
||||||
|
|
||||||
dialog = @_getDialog()
|
|
||||||
dialog.showMessageBox
|
|
||||||
title: 'Here we go...'
|
|
||||||
detail: "Adjusting the subject line To `#{newSubject}`"
|
|
||||||
buttons: ['OK']
|
|
||||||
type: 'info'
|
|
||||||
|
|
||||||
session.changes.add({subject: newSubject})
|
|
||||||
|
|
||||||
_getDialog: =>
|
|
||||||
require('remote').require('dialog')
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = MyComposerButton
|
|
48
static/package-template/lib/my-composer-button.jsx
Normal file
48
static/package-template/lib/my-composer-button.jsx
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import {DraftStore, React} from 'nylas-exports';
|
||||||
|
|
||||||
|
export default class MyComposerButton extends React.Component {
|
||||||
|
|
||||||
|
// Note: You should assign a new displayName to avoid naming
|
||||||
|
// conflicts when injecting your item
|
||||||
|
static displayName = 'MyComposerButton';
|
||||||
|
|
||||||
|
// When you register as a composer button, you receive a
|
||||||
|
// reference to the draft, and you can look it up to perform
|
||||||
|
// actions and retrieve data.
|
||||||
|
static propTypes = {
|
||||||
|
draftClientId: React.PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
_onClick = () => {
|
||||||
|
// To retrieve information about the draft, we fetch the current editing
|
||||||
|
// session from the draft store. We can access attributes of the draft
|
||||||
|
// and add changes to the session which will be appear immediately.
|
||||||
|
DraftStore.sessionForClientId(this.props.draftClientId).then((session) => {
|
||||||
|
const newSubject = `${session.draft().subject} - It Worked!`;
|
||||||
|
|
||||||
|
const dialog = this._getDialog();
|
||||||
|
dialog.showMessageBox({
|
||||||
|
title: 'Here we go...',
|
||||||
|
detail: "Adjusting the subject line To `#{newSubject}`",
|
||||||
|
buttons: ['OK'],
|
||||||
|
type: 'info',
|
||||||
|
});
|
||||||
|
|
||||||
|
session.changes.add({subject: newSubject});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_getDialog() {
|
||||||
|
return require('remote').require('dialog');
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="my-package">
|
||||||
|
<button className="btn btn-toolbar" onClick={() => this._onClick()} ref="button">
|
||||||
|
Hello World
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,66 +0,0 @@
|
||||||
{Utils,
|
|
||||||
React,
|
|
||||||
FocusedContactsStore} = require 'nylas-exports'
|
|
||||||
{RetinaImg} = require 'nylas-component-kit'
|
|
||||||
|
|
||||||
class MyMessageSidebar extends React.Component
|
|
||||||
@displayName: 'MyMessageSidebar'
|
|
||||||
|
|
||||||
# Providing container styles tells the app how to constrain
|
|
||||||
# the column your component is being rendered in. The min and
|
|
||||||
# max size of the column are chosen automatically based on
|
|
||||||
# these values.
|
|
||||||
@containerStyles:
|
|
||||||
order: 1
|
|
||||||
flexShrink: 0
|
|
||||||
|
|
||||||
# This sidebar component listens to the FocusedContactStore,
|
|
||||||
# which gives us access to the Contact object of the currently
|
|
||||||
# selected person in the conversation. If you wanted to take
|
|
||||||
# the contact and fetch your own data, you'd want to create
|
|
||||||
# your own store, so the flow of data would be:
|
|
||||||
#
|
|
||||||
# FocusedContactStore => Your Store => Your Component
|
|
||||||
#
|
|
||||||
constructor: (@props) ->
|
|
||||||
@state = @_getStateFromStores()
|
|
||||||
|
|
||||||
componentDidMount: =>
|
|
||||||
@unsubscribe = FocusedContactsStore.listen(@_onChange)
|
|
||||||
|
|
||||||
componentWillUnmount: =>
|
|
||||||
@unsubscribe()
|
|
||||||
|
|
||||||
render: =>
|
|
||||||
if @state.contact
|
|
||||||
content = @_renderContent()
|
|
||||||
else
|
|
||||||
content = @_renderPlaceholder()
|
|
||||||
|
|
||||||
<div className="my-message-sidebar">
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
_renderContent: =>
|
|
||||||
# Want to include images or other static assets in your components?
|
|
||||||
# Reference them using the nylas:// URL scheme:
|
|
||||||
#
|
|
||||||
# <RetinaImg
|
|
||||||
# url="nylas://<<package.name>>/assets/checkmark_template@2x.png"
|
|
||||||
# mode={RetinaImg.Mode.ContentIsMask}/>
|
|
||||||
#
|
|
||||||
<div className="header">
|
|
||||||
<h1>{@state.contact.displayName()} is the focused contact.</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
_renderPlaceholder: =>
|
|
||||||
<div> No Data Available </div>
|
|
||||||
|
|
||||||
_onChange: =>
|
|
||||||
@setState(@_getStateFromStores())
|
|
||||||
|
|
||||||
_getStateFromStores: =>
|
|
||||||
contact: FocusedContactsStore.focusedContact()
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = MyMessageSidebar
|
|
78
static/package-template/lib/my-message-sidebar.jsx
Normal file
78
static/package-template/lib/my-message-sidebar.jsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import {
|
||||||
|
React,
|
||||||
|
FocusedContactsStore,
|
||||||
|
} from 'nylas-exports';
|
||||||
|
|
||||||
|
export default class MyMessageSidebar extends React.Component {
|
||||||
|
static displayName = 'MyMessageSidebar';
|
||||||
|
|
||||||
|
// This sidebar component listens to the FocusedContactStore,
|
||||||
|
// which gives us access to the Contact object of the currently
|
||||||
|
// selected person in the conversation. If you wanted to take
|
||||||
|
// the contact and fetch your own data, you'd want to create
|
||||||
|
// your own store, so the flow of data would be:
|
||||||
|
|
||||||
|
// FocusedContactStore => Your Store => Your Component
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = this._getStateFromStores();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.unsubscribe = FocusedContactsStore.listen(this._onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onChange = () => {
|
||||||
|
this.setState(this._getStateFromStores());
|
||||||
|
}
|
||||||
|
|
||||||
|
_getStateFromStores = () => {
|
||||||
|
return {
|
||||||
|
contact: FocusedContactsStore.focusedContact(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderContent() {
|
||||||
|
// Want to include images or other static assets in your components?
|
||||||
|
// Reference them using the nylas:// URL scheme:
|
||||||
|
//
|
||||||
|
// <RetinaImg
|
||||||
|
// url="nylas://<<package.name>>/assets/checkmark_templatethis.2x.png"
|
||||||
|
// mode={RetinaImg.Mode.ContentIsMask}/>
|
||||||
|
//
|
||||||
|
return (
|
||||||
|
<div className="header">
|
||||||
|
<h1>{this.state.contact.displayName()} is the focused contact.</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderPlaceholder() {
|
||||||
|
return (
|
||||||
|
<div> No Data Available </div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const content = (this.state.contact) ? this._renderContent() : this._renderPlaceholder();
|
||||||
|
return (
|
||||||
|
<div className="my-message-sidebar">
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Providing container styles tells the app how to constrain
|
||||||
|
// the column your component is being rendered in. The min and
|
||||||
|
// max size of the column are chosen automatically based on
|
||||||
|
// these values.
|
||||||
|
MyMessageSidebar.containerStyles = {
|
||||||
|
order: 1,
|
||||||
|
flexShrink: 0,
|
||||||
|
};
|
|
@ -1,19 +0,0 @@
|
||||||
{ComponentRegistry} = require 'nylas-exports'
|
|
||||||
{activate, deactivate} = require '../lib/main'
|
|
||||||
|
|
||||||
MyMessageSidebar = require '../lib/my-message-sidebar'
|
|
||||||
MyComposerButton = require '../lib/my-composer-button'
|
|
||||||
|
|
||||||
describe "activate", ->
|
|
||||||
it "should register the composer button and sidebar", ->
|
|
||||||
spyOn(ComponentRegistry, 'register')
|
|
||||||
activate()
|
|
||||||
expect(ComponentRegistry.register).toHaveBeenCalledWith(MyComposerButton, {role: 'Composer:ActionButton'})
|
|
||||||
expect(ComponentRegistry.register).toHaveBeenCalledWith(MyMessageSidebar, {role: 'MessageListSidebar:ContactCard'})
|
|
||||||
|
|
||||||
describe "deactivate", ->
|
|
||||||
it "should unregister the composer button and sidebar", ->
|
|
||||||
spyOn(ComponentRegistry, 'unregister')
|
|
||||||
deactivate()
|
|
||||||
expect(ComponentRegistry.unregister).toHaveBeenCalledWith(MyComposerButton)
|
|
||||||
expect(ComponentRegistry.unregister).toHaveBeenCalledWith(MyMessageSidebar)
|
|
23
static/package-template/spec/main-spec.es6
Normal file
23
static/package-template/spec/main-spec.es6
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import {ComponentRegistry} from 'nylas-exports';
|
||||||
|
import {activate, deactivate} from '../lib/main';
|
||||||
|
|
||||||
|
import MyMessageSidebar from '../lib/my-message-sidebar';
|
||||||
|
import MyComposerButton from '../lib/my-composer-button';
|
||||||
|
|
||||||
|
describe("activate", () => {
|
||||||
|
it("should register the composer button and sidebar", () => {
|
||||||
|
spyOn(ComponentRegistry, 'register');
|
||||||
|
activate();
|
||||||
|
expect(ComponentRegistry.register).toHaveBeenCalledWith(MyComposerButton, {role: 'Composer:ActionButton'});
|
||||||
|
expect(ComponentRegistry.register).toHaveBeenCalledWith(MyMessageSidebar, {role: 'MessageListSidebar:ContactCard'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("deactivate", () => {
|
||||||
|
it("should unregister the composer button and sidebar", () => {
|
||||||
|
spyOn(ComponentRegistry, 'unregister');
|
||||||
|
deactivate();
|
||||||
|
expect(ComponentRegistry.unregister).toHaveBeenCalledWith(MyComposerButton);
|
||||||
|
expect(ComponentRegistry.unregister).toHaveBeenCalledWith(MyMessageSidebar);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,25 +0,0 @@
|
||||||
{React} = require 'nylas-exports'
|
|
||||||
ReactTestUtils = React.addons.TestUtils
|
|
||||||
|
|
||||||
MyComposerButton = require '../lib/my-composer-button'
|
|
||||||
|
|
||||||
dialogStub =
|
|
||||||
showMessageBox: jasmine.createSpy('showMessageBox')
|
|
||||||
|
|
||||||
describe "MyComposerButton", ->
|
|
||||||
beforeEach ->
|
|
||||||
@component = ReactTestUtils.renderIntoDocument(
|
|
||||||
<MyComposerButton draftClientId="test" />
|
|
||||||
)
|
|
||||||
|
|
||||||
it "should render into the page", ->
|
|
||||||
expect(@component).toBeDefined()
|
|
||||||
|
|
||||||
it "should have a displayName", ->
|
|
||||||
expect(MyComposerButton.displayName).toBe('MyComposerButton')
|
|
||||||
|
|
||||||
it "should show a dialog box when clicked", ->
|
|
||||||
spyOn(@component, '_onClick')
|
|
||||||
buttonNode = React.findDOMNode(@component.refs.button)
|
|
||||||
ReactTestUtils.Simulate.click(buttonNode)
|
|
||||||
expect(@component._onClick).toHaveBeenCalled()
|
|
27
static/package-template/spec/my-composer-button-spec.jsx
Normal file
27
static/package-template/spec/my-composer-button-spec.jsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import {React} from 'nylas-exports';
|
||||||
|
const ReactTestUtils = React.addons.TestUtils
|
||||||
|
|
||||||
|
import MyComposerButton from '../lib/my-composer-button';
|
||||||
|
|
||||||
|
describe("MyComposerButton", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
this.component = ReactTestUtils.renderIntoDocument(
|
||||||
|
<MyComposerButton draftClientId="test" />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render into the page", () => {
|
||||||
|
expect(this.component).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have a displayName", () => {
|
||||||
|
expect(MyComposerButton.displayName).toBe('MyComposerButton');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show a dialog box when clicked", () => {
|
||||||
|
spyOn(this.component, '_onClick');
|
||||||
|
const buttonNode = React.findDOMNode(this.component.refs.button);
|
||||||
|
ReactTestUtils.Simulate.click(buttonNode);
|
||||||
|
expect(this.component._onClick).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
Loading…
Add table
Reference in a new issue