Skip to content

Commit

Permalink
feat(tags): Tags dropdown and new features for Menu and Popover
Browse files Browse the repository at this point in the history
Summary:
Focused Content Store should notify observers when focused items change, not just when they're reassigned

Popovers should have a `direction` and optional event onOpened

Menu divider documentation was wrong, menus should support checked items by default

Pressing escape in a popover's input should dismiss the popover

Other changes

Remove specs that make no sense anymore

Small tweak to report build breaking. Shouldn't happen often ;)

Test Plan: Run tests, which will now phone home if they break

Reviewers: evan

Reviewed By: evan

Differential Revision: https://review.inboxapp.com/D1493
  • Loading branch information
bengotow committed May 13, 2015
1 parent 60d9fd4 commit ba00372
Show file tree
Hide file tree
Showing 17 changed files with 349 additions and 141 deletions.
1 change: 0 additions & 1 deletion build/Gruntfile.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,6 @@ module.exports = (grunt) ->
ciTasks.push('mkdmg') if process.platform is 'darwin'
ciTasks.push('create-windows-installer') if process.platform is 'win32'
ciTasks.push('publish-edgehill-build') if process.platform is 'darwin'
# ciTasks.push('publish-build')
grunt.registerTask('ci', ciTasks)

defaultTasks = ['download-atom-shell', 'build', 'set-version']
Expand Down
35 changes: 31 additions & 4 deletions build/tasks/spec-task.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ path = require 'path'

_ = require 'underscore-plus'
async = require 'async'
request = require 'request'

concurrency = 2

Expand Down Expand Up @@ -134,9 +135,35 @@ module.exports = (grunt) ->
grunt.registerTask 'run-edgehill-specs', 'Run the specs', ->
proc = require 'child_process'
done = @async()
testSucceeded = false
testOutput = ""
testProc = proc.spawn("./atom.sh", ["--test"])
testProc.stdout.on 'data', (data) -> console.log(data.toString())
testProc.stderr.on 'data', (data) -> grunt.log.error(data.toString())
testProc.on 'error', (err) -> grunt.log.error("Process error: #{err}")

testProc.stdout.on 'data', (data) ->
str = data.toString()
testOutput += str
console.log(str)
if str.indexOf(' 0 failures') isnt -1
testSucceeded = true

testProc.stderr.on 'data', (data) ->
str = data.toString()
testOutput += str
grunt.log.error(str)

testProc.on 'error', (err) ->
grunt.log.error("Process error: #{err}")

testProc.on 'close', (exitCode, signal) ->
done()
if testSucceeded
return done()
else
testOutput = testOutput.replace(/\x1b\[[^m]+m/g, '')
url = "https://hooks.slack.com/services/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"
request.post
url: url
json:
username: "Edgehill Builds"
text: "Aghhh somebody broke the build. ```#{testOutput}```"
, (err, httpResponse, body) ->
done(false)
67 changes: 3 additions & 64 deletions internal_packages/message-list/lib/message-toolbar-items.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,9 @@ _ = require 'underscore-plus'
React = require 'react'
classNames = require 'classnames'
{Actions, Utils, FocusedContentStore, WorkspaceStore} = require 'inbox-exports'
{RetinaImg} = require 'ui-components'
{RetinaImg, Popover, Menu} = require 'ui-components'

# Note: These always have a thread, but only sometimes get a
# message, depending on where in the UI they are being displayed.

class ReplyButton extends React.Component
@displayName: "ReplyButton"

render: =>
<button className="btn btn-toolbar"
data-tooltip="Reply"
onClick={@_onReply}>
<RetinaImg name="toolbar-reply.png" />
</button>

_onReply: (e) =>
return unless Utils.nodeIsVisible(e.currentTarget)
Actions.composeReply(threadId: FocusedContentStore.focusedId('thread'))
e.stopPropagation()

class ReplyAllButton extends React.Component
@displayName: "ReplyAllButton"

render: =>
<button className="btn btn-toolbar"
data-tooltip="Reply All"
onClick={@_onReplyAll}>
<RetinaImg name="toolbar-reply-all.png" />
</button>

_onReplyAll: (e) =>
return unless Utils.nodeIsVisible(e.currentTarget)
Actions.composeReplyAll(threadId: FocusedContentStore.focusedId('thread'))
e.stopPropagation()

class ForwardButton extends React.Component
@displayName: "ForwardButton"

render: =>
<button className="btn btn-toolbar"
data-tooltip="Forward"
onClick={@_onForward}>
<RetinaImg name="toolbar-forward.png" />
</button>

_onForward: (e) =>
return unless Utils.nodeIsVisible(e.currentTarget)
Actions.composeForward(threadId: FocusedContentStore.focusedId('thread'))
e.stopPropagation()

class ArchiveButton extends React.Component
@displayName: "ArchiveButton"

render: =>
<button className="btn btn-toolbar btn-archive"
data-tooltip="Archive"
onClick={@_onArchive}>
<RetinaImg name="toolbar-archive.png" />
</button>

_onArchive: (e) =>
return unless Utils.nodeIsVisible(e.currentTarget)
Actions.archive()
e.stopPropagation()
ThreadArchiveButton = require './thread-archive-button'

class MessageToolbarItems extends React.Component
@displayName: "MessageToolbarItems"
Expand All @@ -80,7 +19,7 @@ class MessageToolbarItems extends React.Component
"hidden": !@state.threadIsSelected

<div className={classes}>
<ArchiveButton ref="archiveButton" />
<ThreadArchiveButton />
</div>

componentDidMount: =>
Expand Down
22 changes: 22 additions & 0 deletions internal_packages/message-list/lib/thread-archive-button.cjsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
_ = require 'underscore-plus'
React = require 'react'
{Actions, Utils} = require 'inbox-exports'
{RetinaImg} = require 'ui-components'

class ArchiveButton extends React.Component
@displayName: "ArchiveButton"

render: =>
<button className="btn btn-toolbar btn-archive"
data-tooltip="Archive"
onClick={@_onArchive}>
<RetinaImg name="toolbar-archive.png" />
</button>

_onArchive: (e) =>
return unless Utils.nodeIsVisible(e.currentTarget)
Actions.archive()
e.stopPropagation()


module.exports = ArchiveButton
151 changes: 151 additions & 0 deletions internal_packages/message-list/lib/thread-tags-button.cjsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
_ = require 'underscore-plus'
React = require 'react'
Reflux = require 'reflux'
classNames = require 'classnames'
{Actions,
Utils,
AddRemoveTagsTask,
FocusedContentStore,
FocusedContentStore,
DatabaseStore,
Tag,
Thread,
TaskQueue} = require 'inbox-exports'
{RetinaImg,
Popover,
Menu} = require 'ui-components'

TagsStore = Reflux.createStore
init: ->
@_setStoreDefaults()
@_registerListeners()
@_fetch()

items: ->
@_items

_setStoreDefaults: ->
@_items = []

_registerListeners: ->
@listenTo DatabaseStore, @_onDataChanged

_onDataChanged: (change) ->
if change and change.objectClass is Tag.name
@_fetch()

_fetch: ->
DatabaseStore.findAll(Tag).then (tags) =>
@_items = tags
@trigger()


# Note
class ThreadTagsButton extends React.Component
@displayName: 'ThreadTagsButton'

constructor: (@props) ->
@state = @_getStateForSearch('')
@

componentDidMount: ->
@unsubscribers = []
@unsubscribers.push TagsStore.listen @_onStoreChange
@unsubscribers.push FocusedContentStore.listen @_onFocusChange

componentWillUnmount: =>
return unless @unsubscribers
unsubscribe() for unsubscribe in @unsubscribers

render: =>
button = <button className="btn btn-toolbar">
<RetinaImg name="toolbar-tags.png"/>
<RetinaImg name="toolbar-chevron.png"/>
</button>

headerComponents = [
<input type="text"
tabIndex="1"
key="textfield"
className="search"
value={@state.searchValue}
onChange={@_onSearchValueChange}/>
]

<Popover className="tag-picker"
direction="down"
onOpened={@_onShowTags}
buttonComponent={button}>
<Menu ref="menu"
headerComponents={headerComponents}
footerComponents={[]}
items={@state.tags}
itemKey={ (item) -> item.id }
itemContent={@_itemContent}
itemChecked={@_itemChecked}
onSelect={@_onToggleTag}
/>
</Popover>

_itemContent: (tag) =>
if tag.id is 'divider'
<Menu.Item divider={tag.name} />
else
tag.name.charAt(0).toUpperCase() + tag.name.slice(1)

_itemChecked: (tag) =>
return false unless @state.thread
@state.thread.hasTagId(tag.id)

_onShowTags: =>
# Always reset search state when the popover is shown
if @state.searchValue.length > 0
@setState @_getStateForSearch('')

_onToggleTag: (tag) =>
return unless @state.thread

if @state.thread.hasTagId(tag.id)
task = new AddRemoveTagsTask(@state.thread, [], [tag.id])
else
task = new AddRemoveTagsTask(@state.thread, [tag.id], [])

@refs.menu.setSelectedItem(null)

TaskQueue.enqueue(task)

_onFocusChange: (change) =>
if change.impactsCollection('thread')
@_onStoreChange()

_onStoreChange: =>
@setState @_getStateForSearch(@state.searchValue)

_onSearchValueChange: (event) =>
@setState @_getStateForSearch(event.target.value)

_getStateForSearch: (searchValue = '') =>
searchTerm = searchValue.toLowerCase()
thread = FocusedContentStore.focused('thread')
return [] unless thread

tags = _.filter TagsStore.items(), (tag) -> tag.name.toLowerCase().indexOf(searchTerm) is 0

# Some tags are "magic" state and can't be added/removed
tags = _.filter tags, (tag) -> not (tag.id in ['unseen', 'attachment', 'sending', 'drafts', 'sent'])

# Some tags are readonly
tags = _.filter tags, (tag) -> not tag.readonly

# Organize tags into currently applied / not applied
active = []
inactive = []
for tag in tags
if thread.hasTagId(tag.id)
active.push(tag)
else
inactive.push(tag)

{searchValue, thread, tags: [].concat(active, inactive)}

module.exports = ThreadTagsButton

This file was deleted.

9 changes: 9 additions & 0 deletions internal_packages/message-list/stylesheets/message-list.less
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@

@message-max-width: 800px;

.tag-picker {
.menu {
.content-container {
height:250px;
overflow-y:scroll;
}
}
}

.sheet-toolbar {
// This class wraps the items that appear above the message list in the
// toolbar. We want the toolbar items to sit right above the centered
Expand Down
8 changes: 0 additions & 8 deletions internal_packages/search-bar/stylesheets/search-bar.less
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,6 @@
box-shadow: @standard-shadow;
background-color: @menu-color;

.divider {
font-weight:@headings-font-weight;
color: @menu-divider-color;
font-size: @font-size-small;
text-transform: uppercase;
margin-top: 10px;
}

.item {
overflow: hidden;
white-space: nowrap;
Expand Down
2 changes: 1 addition & 1 deletion keymaps/base.cson
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@
'enter': 'menu:enter'

# For Popover Component
'body .popover-container':
'body .popover-container, body .popover-container input':
'escape': 'popover:close'

# Tokenizing Text fields
Expand Down
Loading

0 comments on commit ba00372

Please sign in to comment.