From 2988e00406cc457b3af6752d857c562aae6b4729 Mon Sep 17 00:00:00 2001 From: Eve Essex Date: Fri, 11 Aug 2017 15:56:54 -0400 Subject: [PATCH 1/9] use reaction text component --- .../content2/section_list/index.coffee | 15 ++-- .../content2/section_list/index.styl | 5 -- .../content2/sections/text/index.coffee | 79 +++++++++-------- .../content2/sections/text/index.styl | 87 +------------------ package.json | 2 +- 5 files changed, 52 insertions(+), 136 deletions(-) diff --git a/client/apps/edit/components/content2/section_list/index.coffee b/client/apps/edit/components/content2/section_list/index.coffee index e7cd85847..eb9c2d473 100644 --- a/client/apps/edit/components/content2/section_list/index.coffee +++ b/client/apps/edit/components/content2/section_list/index.coffee @@ -7,7 +7,9 @@ React = require 'react' SectionContainer = React.createFactory require '../section_container/index.coffee' SectionTool = React.createFactory require '../section_tool/index.coffee' DragContainer = React.createFactory require '../../../../../components/drag_drop/index.coffee' -RichTextParagraph = React.createFactory require '../../../../../components/rich_text/components/input_paragraph.coffee' +RichTextParagraph = React.createFactory require '../../../../../components/rich_text2/components/input_paragraph.coffee' +components = require('@artsy/reaction-force/dist/components/publishing/index').default +Text = React.createFactory components.Text { div } = React.DOM module.exports = React.createClass @@ -78,8 +80,9 @@ module.exports = React.createClass className: 'edit-sections__postscript' 'data-layout': 'column_width' }, - RichTextParagraph { - text: @props.article.get('postscript') or '' - onChange: @setPostscript - placeholder: 'Postscript (optional)' - } + Text { layout: 'postscript'}, + RichTextParagraph { + text: @props.article.get('postscript') or '' + onChange: @setPostscript + placeholder: 'Postscript (optional)' + } diff --git a/client/apps/edit/components/content2/section_list/index.styl b/client/apps/edit/components/content2/section_list/index.styl index 2c2994c4a..2dbfaffc8 100644 --- a/client/apps/edit/components/content2/section_list/index.styl +++ b/client/apps/edit/components/content2/section_list/index.styl @@ -7,11 +7,6 @@ max-width standard-body-w-margin margin 0 auto padding 60px 20px 20px - font-style italic - Garamond s23 - p - margin-top 1em - margin-bottom 1em .public-DraftEditorPlaceholder-root position absolute color gray-dark-color diff --git a/client/apps/edit/components/content2/sections/text/index.coffee b/client/apps/edit/components/content2/sections/text/index.coffee index 1e726d596..583b6c7ec 100644 --- a/client/apps/edit/components/content2/sections/text/index.coffee +++ b/client/apps/edit/components/content2/sections/text/index.coffee @@ -30,6 +30,8 @@ editor = (props) -> React.createElement Editor, props { div } = React.DOM EditNav = React.createFactory require '../../../../../../components/rich_text2/components/edit_nav.coffee' InputUrl = React.createFactory require '../../../../../../components/rich_text2/components/input_url.coffee' +components = require('@artsy/reaction-force/dist/components/publishing/index').default +Text = React.createFactory components.Text module.exports = React.createClass displayName: 'SectionText' @@ -292,42 +294,43 @@ module.exports = React.createClass className: 'edit-section--text' + isEditing onClick: @focus }, - if @state.showMenu - EditNav { - hasFeatures: @state.hasFeatures - blocks: blockTypes @props.article.get('layout'), @state.hasFeatures - toggleBlock: @toggleBlockType - styles: inlineStyles @props.article.get('layout'), @state.hasFeatures - toggleStyle: @toggleInlineStyle - promptForLink: @promptForLink - makePlainText: @makePlainText - position: @state.selectionTarget - } - div { - className: 'edit-section--text__input' - onMouseUp: @checkSelection - onKeyUp: @checkSelection - }, - editor { - ref: 'editor' - editorState: @state.editorState - spellCheck: true - onChange: @onChange - decorators: decorators - handleKeyCommand: @handleKeyCommand - keyBindingFn: keyBindingFnFull - handlePastedText: @onPaste - blockRenderMap: blockRenderMap @props.article.get('layout'), @state.hasFeatures - handleReturn: @handleReturn - onTab: @handleTab - onUpArrow: @handleChangeSection - onDownArrow: @handleChangeSection - } - if @props.editing and @state.showUrlInput - InputUrl { - removeLink: @removeLink - confirmLink: @confirmLink - selectionTarget: @state.selectionTarget - pluginType: @state.pluginType - urlValue: @state.urlValue + Text { layout: @props.article.get 'layout' }, + if @state.showMenu + EditNav { + hasFeatures: @state.hasFeatures + blocks: blockTypes @props.article.get('layout'), @state.hasFeatures + toggleBlock: @toggleBlockType + styles: inlineStyles @props.article.get('layout'), @state.hasFeatures + toggleStyle: @toggleInlineStyle + promptForLink: @promptForLink + makePlainText: @makePlainText + position: @state.selectionTarget } + div { + className: 'edit-section--text__input' + onMouseUp: @checkSelection + onKeyUp: @checkSelection + }, + editor { + ref: 'editor' + editorState: @state.editorState + spellCheck: true + onChange: @onChange + decorators: decorators + handleKeyCommand: @handleKeyCommand + keyBindingFn: keyBindingFnFull + handlePastedText: @onPaste + blockRenderMap: blockRenderMap @props.article.get('layout'), @state.hasFeatures + handleReturn: @handleReturn + onTab: @handleTab + onUpArrow: @handleChangeSection + onDownArrow: @handleChangeSection + } + if @props.editing and @state.showUrlInput + InputUrl { + removeLink: @removeLink + confirmLink: @confirmLink + selectionTarget: @state.selectionTarget + pluginType: @state.pluginType + urlValue: @state.urlValue + } diff --git a/client/apps/edit/components/content2/sections/text/index.styl b/client/apps/edit/components/content2/sections/text/index.styl index 7d7419ae0..3d6de940d 100644 --- a/client/apps/edit/components/content2/sections/text/index.styl +++ b/client/apps/edit/components/content2/sections/text/index.styl @@ -17,8 +17,6 @@ ul li .public-DraftStyleDefault-ltr, li.public-DraftStyleDefault-unorderedListItem .public-DraftStyleDefault-ltr list-style disc - li - padding .5em 0 nav &.has-plugins button[name='remove-formatting'], @@ -29,89 +27,6 @@ .artist-follow background-image none !important &:before - content url(/icons/circle_plus.svg) - padding-left 10px - vertical-align -8.5px - &:after - content 'Follow' - garamond('s-body') - text-transform none - letter-spacing 0px + content url(/icons/circle_plus.svg) !important -.classic .edit-section--text - garamond l-body - p - padding-top .75em - padding-bottom .75em - h2 - garamond l-header - font-size 28px - line-height 1.2em - padding-top .5em - h3 - avant-garde body - line-height 27px - span - font-style normal !important - blockquote - garamond l-headline - text-align center - -.standard .edit-section--text, -.feature .edit-section--text - Garamond s23 - p - padding-top 1em - padding-bottom 1em - &:first-child - padding-top 0 - &:last-child - padding-bottom 0 - h2 - Garamond s40 - h3 - Unica s19 - padding-top 23px - blockquote - padding-top 46px - padding-bottom 46px - .content-end - background-color black - height 15px - width @height - border-radius 50% - display inline-block - margin-left 15px - -.standard .edit-section--text - blockquote - Garamond s50 - -.feature .edit-section--text - blockquote - Unica s69 - h1 - Unica s40 - text-align center - padding-bottom 46px - padding-top 107px - position relative - &:before - position absolute - top 69px - right calc(50% \- 7.5px) - content '.' - height 15px - width @height - background black - border-radius 50% - color rgba(0,0,0,0) - .content-start - Unica s69, medium - float left - line-height .5em - margin-right 10px - margin-top .298em - text-transform uppercase - diff --git a/package.json b/package.json index 0276dfffb..cab576f88 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "deploy": "sh scripts/deploy.sh" }, "dependencies": { - "@artsy/reaction-force": "^0.1.49", + "@artsy/reaction-force": "^0.3.1", "airtable": "^0.4.5", "artsy-backbone-mixins": "git://github.com/artsy/artsy-backbone-mixins.git", "artsy-ezel-components": "git://github.com/artsy/artsy-ezel-components.git", From 2a65f6f8523d3aaa8c96e28a33f96c5d5fa58e87 Mon Sep 17 00:00:00 2001 From: Eve Essex Date: Fri, 11 Aug 2017 17:30:41 -0400 Subject: [PATCH 2/9] dont import by function --- .../content2/sections/text/index.coffee | 96 ++++++++----------- .../content2/sections/text/test/index.coffee | 4 +- 2 files changed, 43 insertions(+), 57 deletions(-) diff --git a/client/apps/edit/components/content2/sections/text/index.coffee b/client/apps/edit/components/content2/sections/text/index.coffee index 583b6c7ec..154d7aec5 100644 --- a/client/apps/edit/components/content2/sections/text/index.coffee +++ b/client/apps/edit/components/content2/sections/text/index.coffee @@ -3,41 +3,27 @@ ReactDOM = require 'react-dom' _s = require 'underscore.string' sd = require('sharify').data window.process = {env: {NODE_ENV: sd.NODE_ENV}} +components = require('@artsy/reaction-force/dist/components/publishing/index').default +Config = require './draft_config.coffee' +EditNav = React.createFactory require '../../../../../../components/rich_text2/components/edit_nav.coffee' +InputUrl = React.createFactory require '../../../../../../components/rich_text2/components/input_url.coffee' +Text = React.createFactory components.Text +Utils = require '../../../../../../components/rich_text2/utils/index.coffee' { CompositeDecorator, ContentState, Editor, EditorState, RichUtils, Modifier } = require 'draft-js' -{ blockTypes, - blockRenderMap, - decorators, - inlineStyles } = require './draft_config.coffee' -{ convertFromRichHtml, - convertToRichHtml, - getExistingLinkData, - getSelectionDetails, - getSelectionLocation, - keyBindingFnFull, - moveSelection, - setContentStartEnd, - setSelectionToStart, - standardizeSpacing, - stickyControlsBox, - stripCharacterStyles, - stripGoogleStyles } = require '../../../../../../components/rich_text2/utils/index.coffee' editor = (props) -> React.createElement Editor, props { div } = React.DOM -EditNav = React.createFactory require '../../../../../../components/rich_text2/components/edit_nav.coffee' -InputUrl = React.createFactory require '../../../../../../components/rich_text2/components/input_url.coffee' -components = require('@artsy/reaction-force/dist/components/publishing/index').default -Text = React.createFactory components.Text + module.exports = React.createClass displayName: 'SectionText' getInitialState: -> - editorState: EditorState.createEmpty(new CompositeDecorator(decorators(@props.article.get('layout')))) + editorState: EditorState.createEmpty(new CompositeDecorator(Config.decorators(@props.article.get('layout')))) focus: false html: null selectionTarget: null @@ -49,12 +35,12 @@ module.exports = React.createClass componentDidMount: -> if @props.section.get('body')?.length - html = standardizeSpacing @props.section.get('body') + html = Utils.standardizeSpacing @props.section.get('body') unless @props.article.get('layout') is 'classic' - html = setContentStartEnd(html, @props.article.get('layout'), @props.isStartText, @props.isEndText) - blocksFromHTML = convertFromRichHtml html - editorState = EditorState.createWithContent(blocksFromHTML, new CompositeDecorator(decorators(@props.article.get('layout')))) - editorState = setSelectionToStart(editorState) if @props.editing + html = Utils.setContentStartEnd(html, @props.article.get('layout'), @props.isStartText, @props.isEndText) + blocksFromHTML = Utils.convertFromRichHtml html + editorState = EditorState.createWithContent(blocksFromHTML, new CompositeDecorator(Config.decorators(@props.article.get('layout')))) + editorState = Utils.setSelectionToStart(editorState) if @props.editing @setState html: html editorState: editorState @@ -64,7 +50,7 @@ module.exports = React.createClass componentDidUpdate: (prevProps) -> if @props.isEndText isnt prevProps.isEndText or @props.isStartText isnt prevProps.isStartText unless @props.article.get('layout') is 'classic' - html = setContentStartEnd(@props.section.get('body'), @props.article.get('layout'), @props.isStartText, @props.isEndText) + html = Utils.setContentStartEnd(@props.section.get('body'), @props.article.get('layout'), @props.isStartText, @props.isEndText) @props.section.set('body', html) if @props.editing and @props.editing isnt prevProps.editing @focus() @@ -72,7 +58,7 @@ module.exports = React.createClass @refs.editor.blur() onChange: (editorState) -> - html = convertToRichHtml editorState, @props.article.get('layout') + html = Utils.convertToRichHtml editorState, @props.article.get('layout') @setState editorState: editorState, html: html @props.section.set('body', html) @@ -86,7 +72,7 @@ module.exports = React.createClass @refs.editor.focus() handleReturn: (e) -> - selection = getSelectionDetails(@state.editorState) + selection = Utils.getSelectionDetails(@state.editorState) # dont split from the first block, to avoid creating empty blocks # dont split from the middle of a paragraph if selection.isFirstBlock or selection.anchorOffset @@ -104,16 +90,16 @@ module.exports = React.createClass @props.onSetEditing index handleBackspace: (e) -> - selection = getSelectionDetails(@state.editorState) + selection = Utils.getSelectionDetails(@state.editorState) # only merge a section if cursor is in first character of first block if selection.isFirstBlock and selection.anchorOffset is 0 and @props.sections.models[@props.index - 1].get('type') is 'text' mergeIntoHTML = @props.sections.models[@props.index - 1].get('body') @props.sections.models[@props.index - 1].destroy() newHTML = mergeIntoHTML + @state.html - blocksFromHTML = convertFromRichHtml newHTML + blocksFromHTML = Utils.convertFromRichHtml newHTML newSectionState = EditorState.push(@state.editorState, blocksFromHTML, null) - newSectionState = setSelectionToStart newSectionState + newSectionState = Utils.setSelectionToStart newSectionState @onChange newSectionState @props.onSetEditing @props.index - 1 @@ -121,7 +107,7 @@ module.exports = React.createClass direction = 0 direction = -1 if e.key in ['ArrowUp', 'ArrowLeft'] direction = 1 if e.key in ['ArrowDown', 'ArrowRight'] - selection = getSelectionDetails @state.editorState + selection = Utils.getSelectionDetails @state.editorState # if cursor is arrowing forward from last charachter of last block, # or cursor is arrowing back from first character of first block, # jump to adjacent section @@ -131,7 +117,7 @@ module.exports = React.createClass else if e.key in ['ArrowLeft', 'ArrowRight'] # manually move cursor to make up for draft's missing l/r arrow fallbacks shift = if e.shiftKey then true else false - newEditorState = moveSelection @state.editorState, selection, direction, shift + newEditorState = Utils.moveSelection @state.editorState, selection, direction, shift @onChange(newEditorState) else return true @@ -148,7 +134,7 @@ module.exports = React.createClass currentState = EditorState.push(editorState, currentContent, 'remove-range') newSectionContent = ContentState.createFromBlockArray newBlockArray newSectionState = EditorState.push(editorState, newSectionContent, null) - newSectionHtml = convertToRichHtml(newSectionState) + newSectionHtml = Utils.convertToRichHtml(newSectionState) @onChange currentState @props.sections.add {type: 'text', body: newSectionHtml}, {at: @props.index + 1} return 'handled' @@ -157,10 +143,10 @@ module.exports = React.createClass { editorState } = @state unless html html = '
' + text + '
' - html = stripGoogleStyles(html) - blocksFromHTML = convertFromRichHtml html + html = Utils.stripGoogleStyles(html) + blocksFromHTML = Utils.convertFromRichHtml html convertedHtml = blocksFromHTML.getBlocksAsArray().map (contentBlock) => - unstyled = stripCharacterStyles contentBlock, true + unstyled = Utils.stripCharacterStyles contentBlock, true unless unstyled.getType() in @availableBlocks() or unstyled.getType() is 'LINK' unstyled = unstyled.set 'type', 'unstyled' return unstyled @@ -175,13 +161,13 @@ module.exports = React.createClass noLinks = RichUtils.toggleLink editorState, selection, null noBlocks = RichUtils.toggleBlockType noLinks, 'unstyled' noStyles = noBlocks.getCurrentContent().getBlocksAsArray().map (contentBlock) => - stripCharacterStyles contentBlock + Utils.stripCharacterStyles contentBlock newState = ContentState.createFromBlockArray noStyles if !selection.isCollapsed() @onChange EditorState.push(editorState, newState, null) availableBlocks: -> - blockMap = blockRenderMap(@props.article.get('layout'), @state.hasFeatures) + blockMap = Config.blockRenderMap(@props.article.get('layout'), @state.hasFeatures) available = Object.keys(blockMap.toObject()) return Array.from(available) @@ -196,14 +182,14 @@ module.exports = React.createClass @handleBackspace e else if e in ['italic', 'bold'] if @props.article.get('layout') is 'classic' and - getSelectionDetails(@state.editorState).anchorType is 'header-three' + Utils.getSelectionDetails(@state.editorState).anchorType is 'header-three' return 'handled' newState = RichUtils.handleKeyCommand @state.editorState, e @onChange newState if newState else if e is 'strikethrough' @toggleInlineStyle 'STRIKETHROUGH' else if e is 'link-prompt' - className = getExistingLinkData(@state.editorState).className + className = Utils.getExistingLinkData(@state.editorState).className return @promptForLink() unless className?.includes 'is-follow-link' @promptForLink 'artist' @@ -231,19 +217,19 @@ module.exports = React.createClass return 'handled' toggleInlineStyle: (inlineStyle) -> - selection = getSelectionDetails(@state.editorState) + selection = Utils.getSelectionDetails(@state.editorState) if selection.anchorType is 'header-three' and @props.article.get('layout') is 'classic' block = @state.editorState.getCurrentContent().getBlockForKey(selection.anchorKey) - stripCharacterStyles block + Utils.stripCharacterStyles block else @onChange RichUtils.toggleInlineStyle(@state.editorState, inlineStyle) promptForLink: (pluginType) -> selectionTarget = null unless window.getSelection().isCollapsed - location = getSelectionLocation $(ReactDOM.findDOMNode(@refs.editor)).offset() - selectionTarget = stickyControlsBox(location, 25, 200) - url = getExistingLinkData(@state.editorState).url + location = Utils.getSelectionLocation $(ReactDOM.findDOMNode(@refs.editor)).offset() + selectionTarget = Utils.stickyControlsBox(location, 25, 200) + url = Utils.getExistingLinkData(@state.editorState).url @setState showUrlInput: true showMenu: false @@ -281,9 +267,9 @@ module.exports = React.createClass checkSelection: -> if !window.getSelection().isCollapsed - location = getSelectionLocation $(ReactDOM.findDOMNode(@refs.editor)).offset() + location = Utils.getSelectionLocation $(ReactDOM.findDOMNode(@refs.editor)).offset() selectionTargetL = if @state.hasFeatures then 125 else 100 - @setState showMenu: true, selectionTarget: stickyControlsBox(location, -93, selectionTargetL) + @setState showMenu: true, selectionTarget: Utils.stickyControlsBox(location, -93, selectionTargetL) else @setState showMenu: false @@ -298,9 +284,9 @@ module.exports = React.createClass if @state.showMenu EditNav { hasFeatures: @state.hasFeatures - blocks: blockTypes @props.article.get('layout'), @state.hasFeatures + blocks: Config.blockTypes @props.article.get('layout'), @state.hasFeatures toggleBlock: @toggleBlockType - styles: inlineStyles @props.article.get('layout'), @state.hasFeatures + styles: Config.inlineStyles @props.article.get('layout'), @state.hasFeatures toggleStyle: @toggleInlineStyle promptForLink: @promptForLink makePlainText: @makePlainText @@ -316,11 +302,11 @@ module.exports = React.createClass editorState: @state.editorState spellCheck: true onChange: @onChange - decorators: decorators + decorators: Config.decorators handleKeyCommand: @handleKeyCommand - keyBindingFn: keyBindingFnFull + keyBindingFn: Utils.keyBindingFnFull handlePastedText: @onPaste - blockRenderMap: blockRenderMap @props.article.get('layout'), @state.hasFeatures + blockRenderMap: Config.blockRenderMap @props.article.get('layout'), @state.hasFeatures handleReturn: @handleReturn onTab: @handleTab onUpArrow: @handleChangeSection diff --git a/client/apps/edit/components/content2/sections/text/test/index.coffee b/client/apps/edit/components/content2/sections/text/test/index.coffee index 3bc998da4..0dd2dbe05 100644 --- a/client/apps/edit/components/content2/sections/text/test/index.coffee +++ b/client/apps/edit/components/content2/sections/text/test/index.coffee @@ -43,8 +43,8 @@ describe 'Section Text', -> ) @SectionText.__set__ 'InputUrl', React.createFactory InputUrl @SectionText.__set__ 'EditNav', React.createFactory EditNav - @SectionText.__set__ 'stickyControlsBox', sinon.stub().returns {top: 20, left: 40} - @SectionText.__set__ 'getSelectionLocation', sinon.stub().returns({top: 20, left: 40}) + @SectionText.__set__ 'Utils.stickyControlsBox', sinon.stub().returns {top: 20, left: 40} + @SectionText.__set__ 'Utils.getSelectionLocation', sinon.stub().returns({top: 20, left: 40}) @sections = new Backbone.Collection [ { body: '

01 here is a link.

In 2016, K mounted a solo show at prescient Berlin gallery KOW, restaging his installation It’s Spring and the Weather is Great so let’s close all object matters (2012), for which he created seven step ladders with microphones and instruments attached for a performance initially meant to take place at Speakers’ Corner in London’s Hyde Park that was eventually mounted in 2010 at the Serpentine Galleries.



' From 44efde993cbf4b1c3ef41bbac17ce9925b0e1786 Mon Sep 17 00:00:00 2001 From: Eve Essex Date: Fri, 11 Aug 2017 17:57:04 -0400 Subject: [PATCH 3/9] begin paragraph refactor --- .../content2/section_list/index.coffee | 17 +- .../rich_text2/components/paragraph.coffee | 201 ++++++ .../components/rich_text2/utils/config.coffee | 25 + yarn.lock | 637 +++++++++++++----- 4 files changed, 690 insertions(+), 190 deletions(-) create mode 100644 client/components/rich_text2/components/paragraph.coffee create mode 100644 client/components/rich_text2/utils/config.coffee diff --git a/client/apps/edit/components/content2/section_list/index.coffee b/client/apps/edit/components/content2/section_list/index.coffee index eb9c2d473..ff6e7cf3c 100644 --- a/client/apps/edit/components/content2/section_list/index.coffee +++ b/client/apps/edit/components/content2/section_list/index.coffee @@ -7,7 +7,7 @@ React = require 'react' SectionContainer = React.createFactory require '../section_container/index.coffee' SectionTool = React.createFactory require '../section_tool/index.coffee' DragContainer = React.createFactory require '../../../../../components/drag_drop/index.coffee' -RichTextParagraph = React.createFactory require '../../../../../components/rich_text2/components/input_paragraph.coffee' +Paragraph = React.createFactory require '../../../../../components/rich_text2/components/paragraph.coffee' components = require('@artsy/reaction-force/dist/components/publishing/index').default Text = React.createFactory components.Text { div } = React.DOM @@ -42,6 +42,7 @@ module.exports = React.createClass setPostscript: (html) -> html = null if html is '

' + debugger @props.article.set('postscript', html) @props.saveArticle() @@ -80,9 +81,11 @@ module.exports = React.createClass className: 'edit-sections__postscript' 'data-layout': 'column_width' }, - Text { layout: 'postscript'}, - RichTextParagraph { - text: @props.article.get('postscript') or '' - onChange: @setPostscript - placeholder: 'Postscript (optional)' - } + Paragraph { + html: @props.article.get('postscript') or '' + onChange: @setPostscript + placeholder: 'Postscript (optional)' + postscript: true + linked: true + layout: @props.article.get('layout') + } diff --git a/client/components/rich_text2/components/paragraph.coffee b/client/components/rich_text2/components/paragraph.coffee new file mode 100644 index 000000000..5fe6b3f9e --- /dev/null +++ b/client/components/rich_text2/components/paragraph.coffee @@ -0,0 +1,201 @@ +# A basic paragraph component supports bold and italic styles, +# optionally links, and linebreaks, and can format in +# types of postscript, caption or lead paragraph + +# RichTextParagraph { +# html *required +# onChange *required +# placeholder +# layout: article.layout +# postscript: default false +# linked: default false +# linebreaks: default true +# } + +React = require 'react' +ReactDOM = require 'react-dom' +sd = require('sharify').data +window.process = {env: {NODE_ENV: sd.NODE_ENV}} +Config = require '../utils/config.coffee' +Utils = require '../utils/index.coffee' +{ ContentState, + CompositeDecorator, + Editor, + EditorState, + RichUtils, + Modifier } = require 'draft-js' +{ convertToHTML, convertFromHTML } = require 'draft-convert' +{ div, a } = React.DOM +editor = (props) -> React.createElement Editor, props +components = require('@artsy/reaction-force/dist/components/publishing/index').default +InputUrl = React.createFactory require './input_url.coffee' +Text = React.createFactory components.Text + +module.exports = React.createClass + displayName: 'RichTextParagraph' + + getInitialState: -> + editorState: EditorState.createEmpty( + new CompositeDecorator Config.decorators(@hasLinks()) + ) + focus: false + showUrlInput: false + showMenu: false + urlValue: '' + selectionTarget: null + + componentDidMount: -> + if $(@props.html).text().length + blocksFromHTML = @convertFromHTML(@props.html) + @setState + html: @props.html + editorState: EditorState.createWithContent( + blocksFromHTML, + new CompositeDecorator Config.decorators(@hasLinks()) + ) + + hasLinks: -> + return true if @props.linked or @props.type in ['caption', 'postscript'] + + onChange: (editorState) -> + html = @convertToHtml editorState + @setState editorState: editorState, html: html + @props.onChange(html) + + focus: -> + @setState focus: true + @refs.editor.focus() + + onBlur: -> + @setState focus: false + + convertFromHTML: (html) -> + blocksFromHTML = convertFromHTML({ + htmlToEntity: (nodeName, node) -> + if nodeName is 'a' + debugger + data = {url: node.href} + return Entity.create( + 'LINK', + 'MUTABLE', + data + ) + })(html) + return blocksFromHTML + + convertToHtml: (editorState) -> + html = convertToHTML({ + entityToHTML: (entity, originalText) -> + if entity.type is 'LINK' + debugger + return a { href: entity.data.url} + return originalText + })(editorState.getCurrentContent()) + html = html + .replace /(\r\n|\n|\r)/gm, '' + .replace /<\/p>

/g, ' ' + .replace(/ /g, '  ') + html = if html is '

' then '' else html + return html + + handleKeyCommand: (e) -> + return 'handled' if @props.linebreaks is false and e is 'split-block' + if e is 'link-prompt' + @promptForLink() if @hasLinks() + return 'handled' + if e in ['bold', 'italic'] + return 'handled' if @props.postscript and e is 'italic' + return 'handled' if @props.caption and e is 'bold' + newState = RichUtils.handleKeyCommand @state.editorState, e + @onChange newState if newState + return 'not-handled' + + promptForLink: (e) -> + { editorState } = @state + e.preventDefault() if e + selection = editorState.getSelection() + selectionTarget = {top: 0, left: 0} + url = '' + if !selection.isCollapsed() + location = Utils.getSelectionLocation $(ReactDOM.findDOMNode(@refs.editor)).offset() + selectionTarget = Utils.stickyControlsBox(location, 25, 200) + contentState = editorState.getCurrentContent() + startKey = selection.getStartKey() + startOffset = selection.getStartOffset() + blockWithLinkAtBeginning = contentState.getBlockForKey(startKey) + linkKey = blockWithLinkAtBeginning.getEntityAt(startOffset) + if linkKey + linkInstance = contentState.getEntity(linkKey) + url = linkInstance.getData().url + @setState + showUrlInput: true + showMenu: false + urlValue: url + selectionTarget: selectionTarget + + confirmLink: (url) -> + debugger + { editorState } = @state + contentState = editorState.getCurrentContent() + contentStateWithEntity = contentState.createEntity( + 'LINK' + 'MUTABLE' + {url: url} + ) + entityKey = contentStateWithEntity.getLastCreatedEntityKey() + newEditorState = EditorState.set editorState, { currentContent: contentStateWithEntity } + @setState({ + showUrlInput: false + showMenu: false + urlValue: '' + selectionTarget: null + }) + @onChange RichUtils.toggleLink( + newEditorState + newEditorState.getSelection() + entityKey + ) + + removeLink: (e) -> + e.preventDefault() + { editorState } = @state + selection = editorState.getSelection() + if !selection.isCollapsed() + @setState({ + showUrlInput: false + urlValue: '' + editorState: RichUtils.toggleLink(editorState, selection, null) + }) + + printUrlInput: -> + if @state.showUrlInput + InputUrl { + selectionTarget: @state.selectionTarget + removeLink: @removeLink + confirmLink: @confirmLink + urlValue: @state.urlValue + } + + render: -> + Text { + layout: @props.layout + postscript: @props.postscript + }, + div { + className: 'rich-text--paragraph' + onClick: @focus + onBlur: @onBlur + }, + editor { + ref: 'editor' + placeholder: @props.placeholder + editorState: @state.editorState + spellCheck: true + onChange: @onChange + blockRenderMap: Config.blockRenderMap() + handleReturn: @handleReturn + handleKeyCommand: @handleKeyCommand + keyBindingFn: Utils.keyBindingFnCaption + # handlePastedText: @onPaste + } + @printUrlInput() \ No newline at end of file diff --git a/client/components/rich_text2/utils/config.coffee b/client/components/rich_text2/utils/config.coffee new file mode 100644 index 000000000..7223111f0 --- /dev/null +++ b/client/components/rich_text2/utils/config.coffee @@ -0,0 +1,25 @@ +Immutable = require 'immutable' +Decorators = require './decorators.coffee' + +exports.blockRenderMap = -> + return Immutable.Map({ + 'unstyled': {element: 'p'} + }) + +exports.inlineStyles = (type) -> + # for menu display only + styles = [ ] + if type isnt 'postscript' + styles.push({label: 'I', name: 'ITALIC'}) + if type isnt 'caption' + styles.push({label: 'B', name: 'BOLD'}) + return styles + +exports.decorators = (linked) -> + decorators = [] + if linked + decorators.push({ + strategy: Decorators.findLinkEntities + component: Decorators.Link + }) + return decorators \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 92c46d168..d0e1ff1c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,12 @@ # yarn lockfile v1 -"@artsy/reaction-force@^0.1.49": - version "0.1.49" - resolved "https://registry.yarnpkg.com/@artsy/reaction-force/-/reaction-force-0.1.49.tgz#3899011c56a37b9fdeb92e06532ddc40d3fb4a8d" +"@artsy/reaction-force@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@artsy/reaction-force/-/reaction-force-0.3.1.tgz#fbbd92bd3c324afc6a32d96aba8f39be5173ae63" dependencies: - "@storybook/addon-options" "^3.1.2" - "@storybook/react" "^3.1.3" + "@storybook/addon-options" "^3.2.1" + "@storybook/react" "^3.2.0" artsy-passport "^2.0.3" artsy-xapp "^1.0.4" babel-polyfill "^6.23.0" @@ -20,9 +20,11 @@ isomorphic-fetch "^2.2.1" isomorphic-relay "^0.7.4" lodash "^4.17.4" + moment-timezone "^0.5.13" numeral "^2.0.4" prop-types "^15.5.10" react "^15.5.4" + react-markdown "^2.5.0" react-relay "https://github.com/alloy/relay/releases/download/v0.9.3/react-relay-0.9.3.tgz" react-sizeme "^2.3.2" react-styled-flexboxgrid "^2.0.0" @@ -39,44 +41,44 @@ component-type "^1.2.1" join-component "^1.1.0" -"@storybook/addon-actions@^3.1.6": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-3.1.6.tgz#0cbf00ede57ff00d1dfe02e554043d6963940064" +"@storybook/addon-actions@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-3.2.0.tgz#e5d7f63fec89cae1b98bbc124694153b7409317f" dependencies: - "@storybook/addons" "^3.1.6" + "@storybook/addons" "^3.2.0" deep-equal "^1.0.1" json-stringify-safe "^5.0.1" - prop-types "^15.5.8" - react-inspector "^2.0.0" + prop-types "^15.5.10" + react-inspector "^2.1.1" uuid "^3.1.0" -"@storybook/addon-links@^3.1.6": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-3.1.6.tgz#62c8a839e54ff0adb04c6023dae467b336ced5d9" +"@storybook/addon-links@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-3.2.0.tgz#28c73d1fd7fa37591139f3fb16e60a43113ad643" dependencies: - "@storybook/addons" "^3.1.6" + "@storybook/addons" "^3.2.0" -"@storybook/addon-options@^3.1.2": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-options/-/addon-options-3.1.6.tgz#0cf3a24e075f4802fda42a13a2199646b0e2303d" +"@storybook/addon-options@^3.2.1": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@storybook/addon-options/-/addon-options-3.2.3.tgz#ea3740d289d429ce767e9699bf3a647dd741592d" dependencies: - "@storybook/addons" "^3.1.6" + "@storybook/addons" "^3.2.0" -"@storybook/addons@^3.1.6": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-3.1.6.tgz#29ef2348550f5a74d5e83dd75d04714cac751c39" +"@storybook/addons@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-3.2.0.tgz#e1446cc5613af179701673276267cee71859bf41" -"@storybook/channel-postmessage@^3.1.6": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-3.1.6.tgz#867768a2ca2efbd796432300fe5e9b834d9c2ca5" +"@storybook/channel-postmessage@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-3.2.0.tgz#612ff53120bf266660cb9328bac9ad671228a2f7" dependencies: - "@storybook/channels" "^3.1.6" + "@storybook/channels" "^3.2.0" global "^4.3.2" json-stringify-safe "^5.0.1" -"@storybook/channels@^3.1.6": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-3.1.6.tgz#81d61591bf7613dd2bcd81d26da40aeaa2899034" +"@storybook/channels@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-3.2.0.tgz#d75395212db76b49e3335f50cce5bc763cf0b5c6" "@storybook/react-fuzzy@^0.4.0": version "0.4.0" @@ -87,20 +89,20 @@ fuse.js "^3.0.1" prop-types "^15.5.9" -"@storybook/react@^3.1.3": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-3.1.6.tgz#9393bb987ff08ee5f49c4557d12eb84377dee5d2" +"@storybook/react@^3.2.0": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-3.2.3.tgz#4e5aff80b61328288dcf9e3a6b2a2890eab880be" dependencies: - "@storybook/addon-actions" "^3.1.6" - "@storybook/addon-links" "^3.1.6" - "@storybook/addons" "^3.1.6" - "@storybook/channel-postmessage" "^3.1.6" - "@storybook/ui" "^3.1.6" + "@storybook/addon-actions" "^3.2.0" + "@storybook/addon-links" "^3.2.0" + "@storybook/addons" "^3.2.0" + "@storybook/channel-postmessage" "^3.2.0" + "@storybook/ui" "^3.2.3" airbnb-js-shims "^1.1.1" autoprefixer "^7.1.1" - babel-core "^6.24.1" + babel-core "^6.25.0" babel-loader "^7.0.0" - babel-plugin-react-docgen "^1.5.0" + babel-plugin-react-docgen "^1.6.0" babel-preset-es2015 "^6.24.1" babel-preset-es2016 "^6.24.1" babel-preset-react "^6.24.1" @@ -108,7 +110,7 @@ babel-preset-stage-0 "^6.24.1" babel-runtime "^6.23.0" case-sensitive-paths-webpack-plugin "^2.0.0" - chalk "^1.1.3" + chalk "^2.0.1" commander "^2.9.0" common-tags "^1.4.0" configstore "^3.1.0" @@ -122,6 +124,7 @@ json-loader "^0.5.4" json-stringify-safe "^5.0.1" json5 "^0.5.1" + lodash.flattendeep "^4.4.0" lodash.pick "^4.4.0" postcss-flexbugs-fixes "^3.0.0" postcss-loader "^2.0.5" @@ -131,18 +134,18 @@ redux "^3.6.0" request "^2.81.0" serve-favicon "^2.4.3" - shelljs "^0.7.7" + shelljs "^0.7.8" style-loader "^0.17.0" url-loader "^0.5.8" util-deprecate "^1.0.2" - uuid "^3.0.1" - webpack "^2.5.1" + uuid "^3.1.0" + webpack "^2.5.1 || ^3.0.0" webpack-dev-middleware "^1.10.2" webpack-hot-middleware "^2.18.0" -"@storybook/ui@^3.1.6": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-3.1.6.tgz#5d47c6003a2d78c06ede43861089747d986d918e" +"@storybook/ui@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-3.2.3.tgz#9579c191a1695d3d5aaabcef93a7a5456d8f19af" dependencies: "@storybook/react-fuzzy" "^0.4.0" babel-runtime "^6.23.0" @@ -156,12 +159,14 @@ lodash.sortby "^4.7.0" mantra-core "^1.7.0" podda "^1.2.2" - prop-types "^15.5.8" + prop-types "^15.5.10" qs "^6.4.0" - react-inspector "^2.0.0" + react-icons "^2.2.5" + react-inspector "^2.1.1" react-komposer "^2.0.0" - react-modal "^1.7.6" - react-split-pane "^0.1.63" + react-modal "^1.7.7" + react-split-pane "^0.1.65" + react-treebeard "^2.0.3" redux "^3.6.0" JSONStream@^1.0.3: @@ -256,10 +261,14 @@ airtable@^0.4.5: request "2.79.0" xhr "2.3.3" -ajv-keywords@^1.0.0, ajv-keywords@^1.1.1: +ajv-keywords@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" +ajv-keywords@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.0.tgz#a296e17f7bfae7c1ce4f7e0de53d29cb32162df0" + ajv@^4.7.0, ajv@^4.9.1: version "4.11.5" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.5.tgz#b6ee74657b993a01dce44b7944d56f485828d5bd" @@ -276,6 +285,15 @@ ajv@^5.0.0: json-schema-traverse "^0.3.0" json-stable-stringify "^1.0.1" +ajv@^5.1.5: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + json-schema-traverse "^0.3.0" + json-stable-stringify "^1.0.1" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -322,6 +340,12 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" +ansi-styles@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" + dependencies: + color-convert "^1.9.0" + "antigravity@git://github.com/artsy/antigravity.git": version "0.1.3" resolved "git://github.com/artsy/antigravity.git#e6a20591a6be418facca1b4670db24f11208cfdd" @@ -376,6 +400,10 @@ array-filter@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" +array-find@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -578,7 +606,7 @@ babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.0" -babel-core@^6.24.1: +babel-core@^6.24.1, babel-core@^6.25.0: version "6.25.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.25.0.tgz#7dd42b0463c742e9d5296deb3ec67a9322dad729" dependencies: @@ -769,9 +797,9 @@ babel-plugin-dynamic-import-node@1.0.2: babel-template "^6.24.1" babel-types "^6.24.1" -babel-plugin-react-docgen@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-1.5.0.tgz#0339717ad51f4a5ce4349330b8266ea5a56f53b4" +babel-plugin-react-docgen@^1.6.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-1.7.0.tgz#87e72d3d54b182a30706b740bb4d116f59aadc80" dependencies: babel-types "^6.24.1" lodash "4.x.x" @@ -1476,6 +1504,10 @@ boom@2.x.x: dependencies: hoek "2.x.x" +bowser@^1.0.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.1.tgz#a4de8f18a1a0dc9531eb2a92a1521fb6a9ba96a5" + bowser@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.0.tgz#169de4018711f994242bff9a8009e77a1f35e003" @@ -1752,9 +1784,9 @@ camelcase@^1.0.1, camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" caniuse-api@^1.5.2: version "1.6.1" @@ -1792,6 +1824,10 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" +chain-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc" + chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -1802,6 +1838,14 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + character-parser@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-1.2.1.tgz#c0dde4ab182713b919b970959a123ecc1a30fcd6" @@ -1816,7 +1860,7 @@ cheerio@^0.19.0: htmlparser2 "~3.8.1" lodash "^3.2.0" -chokidar@^1.0.0, chokidar@^1.0.1, chokidar@^1.4.3: +chokidar@^1.0.0, chokidar@^1.0.1: version "1.6.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" dependencies: @@ -1831,6 +1875,21 @@ chokidar@^1.0.0, chokidar@^1.0.1, chokidar@^1.4.3: optionalDependencies: fsevents "^1.0.0" +chokidar@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + cipher-base@^1.0.0, cipher-base@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" @@ -1942,7 +2001,7 @@ coffeeify@^1.0.0: convert-source-map "^1.1.2" through "^2.3.8" -color-convert@^1.3.0: +color-convert@^1.3.0, color-convert@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" dependencies: @@ -2027,6 +2086,24 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +commonmark-react-renderer@^4.2.4: + version "4.3.3" + resolved "https://registry.yarnpkg.com/commonmark-react-renderer/-/commonmark-react-renderer-4.3.3.tgz#9c4bca138bc83287bae792ccf133738be9cbc6fa" + dependencies: + in-publish "^2.0.0" + lodash.assign "^4.2.0" + lodash.isplainobject "^4.0.6" + pascalcase "^0.1.1" + xss-filters "^1.2.6" + +commonmark@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/commonmark/-/commonmark-0.24.0.tgz#b80de0182c546355643aa15db12bfb282368278f" + dependencies: + entities "~ 1.1.1" + mdurl "~ 1.0.1" + string.prototype.repeat "^0.2.0" + component-emitter@^1.2.0, component-emitter@~1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -2229,6 +2306,14 @@ create-react-class@^15.5.2, create-react-class@^15.6.0: loose-envify "^1.3.1" object-assign "^4.1.1" +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -2595,6 +2680,10 @@ doctrine@^2.0.0: esutils "^2.0.2" isarray "^1.0.0" +dom-helpers@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a" + dom-serializer@0, dom-serializer@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" @@ -2735,20 +2824,20 @@ encoding@^0.1.11: dependencies: iconv-lite "~0.4.13" -enhanced-resolve@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.1.0.tgz#9f4b626f577245edcf4b2ad83d86e17f4f421dec" +enhanced-resolve@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" dependencies: graceful-fs "^4.1.2" memory-fs "^0.4.0" object-assign "^4.0.1" - tapable "^0.2.5" + tapable "^0.2.7" entities@1.0: version "1.0.0" resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" -entities@~1.1.1: +"entities@~ 1.1.1", entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" @@ -3010,10 +3099,26 @@ evp_bytestokey@^1.0.0: dependencies: create-hash "^1.1.1" +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + exenv@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.0.tgz#3835f127abf075bfe082d0aed4484057c78e3c89" +exenv@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" + exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -3153,6 +3258,10 @@ fast-deep-equal@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-0.1.0.tgz#5c6f4599aba6b333ee3342e2ed978672f1001f8d" +fast-deep-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -3243,14 +3352,7 @@ find-cache-dir@^1.0.0: make-dir "^1.0.0" pkg-dir "^2.0.0" -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -find-up@^2.1.0: +find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" dependencies: @@ -3453,6 +3555,10 @@ get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + getpass@^0.1.1: version "0.1.6" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" @@ -3784,7 +3890,7 @@ https-proxy-agent@^0.3.5: debug "2" extend "3" -hyphenate-style-name@^1.0.2: +hyphenate-style-name@^1.0.1, hyphenate-style-name@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b" @@ -3845,6 +3951,10 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" +in-publish@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + indent-string@^1.1.0: version "1.2.2" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-1.2.2.tgz#db99bcc583eb6abbb1e48dcbb1999a986041cb6b" @@ -3886,9 +3996,16 @@ inline-source-map@~0.6.0: dependencies: source-map "~0.5.3" -inline-style-prefixer@^3.0.2: - version "3.0.6" - resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-3.0.6.tgz#b27fe309b4168a31eaf38c8e8c60ab9e7c11731f" +inline-style-prefixer@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz#c153c7e88fd84fef5c602e95a8168b2770671fe7" + dependencies: + bowser "^1.0.0" + hyphenate-style-name "^1.0.1" + +inline-style-prefixer@^3.0.6: + version "3.0.7" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-3.0.7.tgz#0ccc92e5902fe6e0d28d975c4258443f880615f8" dependencies: bowser "^1.6.0" css-in-js-utils "^1.0.3" @@ -4115,7 +4232,7 @@ is-resolvable@^1.0.0: dependencies: tryit "^1.0.1" -is-stream@^1.0.1: +is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4133,10 +4250,6 @@ is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - isarray@0.0.1, isarray@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -4149,6 +4262,10 @@ isemail@2.x.x: version "2.2.1" resolved "https://registry.yarnpkg.com/isemail/-/isemail-2.2.1.tgz#0353d3d9a62951080c262c2aa0a42b8ea8e9e2a6" +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -4473,29 +4590,19 @@ lexical-scope@^1.2.0: dependencies: astw "^2.0.0" -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" + strip-bom "^3.0.0" loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@^0.2.16: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" @@ -4582,6 +4689,10 @@ lodash.create@3.1.1: lodash._basecreate "^3.0.0" lodash._isiterateecall "^3.0.0" +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" @@ -4594,6 +4705,10 @@ lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + lodash.keys@^3.0.0, lodash.keys@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -4671,6 +4786,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3 dependencies: js-tokens "^3.0.0" +lru-cache@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lsmod@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lsmod/-/lsmod-1.0.0.tgz#9a00f76dca36eb23fa05350afe1b585d4299e64b" @@ -4709,10 +4831,20 @@ math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" +"mdurl@~ 1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -4786,6 +4918,10 @@ mime@*, mime@1.3.4, mime@1.3.x, mime@^1.2.11, mime@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" +mimic-fn@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + min-document@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" @@ -5087,6 +5223,12 @@ normalize-url@^1.4.0: query-string "^4.1.0" sort-keys "^1.0.0" +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + npmlog@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" @@ -5268,11 +5410,13 @@ os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" dependencies: + execa "^0.7.0" lcid "^1.0.0" + mem "^1.1.0" os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: version "1.0.2" @@ -5291,6 +5435,10 @@ outpipe@^1.1.0: dependencies: shell-quote "^1.4.2" +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + p-limit@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" @@ -5361,6 +5509,10 @@ parseurl@~1.3.1: dependencies: tape "^4.5.1" +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + passport-facebook@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/passport-facebook/-/passport-facebook-2.1.1.tgz#c39d0b52ae4d59163245a4e21a7b9b6321303311" @@ -5433,12 +5585,6 @@ path-browserify@0.0.0, path-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - dependencies: - pinkie-promise "^2.0.0" - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -5451,6 +5597,10 @@ path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" @@ -5463,13 +5613,11 @@ path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" dependencies: - graceful-fs "^4.1.2" pify "^2.0.0" - pinkie-promise "^2.0.0" pause@0.0.1: version "0.0.1" @@ -5879,7 +6027,13 @@ prop-by-string@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prop-by-string/-/prop-by-string-1.0.1.tgz#a3768a9412de26bbcf982eb19fb8d5ecf573101a" -prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9: +prop-types@15.5.8: + version "15.5.8" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394" + dependencies: + fbjs "^0.8.9" + +prop-types@^15.5.1, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9: version "15.5.10" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" dependencies: @@ -5910,6 +6064,10 @@ ps-tree@0.0.x: dependencies: event-stream "~0.5" +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + public-encrypt@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" @@ -5975,6 +6133,15 @@ querystringify@0.0.x: version "0.0.4" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" +radium@^0.19.0: + version "0.19.4" + resolved "https://registry.yarnpkg.com/radium/-/radium-0.19.4.tgz#56aa49fde6181d2f5e1fa57b4710ffd0c23de820" + dependencies: + array-find "^1.0.0" + exenv "^1.2.1" + inline-style-prefixer "^2.0.5" + prop-types "^15.5.8" + random-bytes@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" @@ -6070,9 +6237,21 @@ react-html-attributes@^1.3.0: dependencies: html-element-attributes "^1.0.0" -react-inspector@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-2.0.0.tgz#c945932f2c2bf2fab7873c6e07d83881404b9313" +react-icon-base@2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/react-icon-base/-/react-icon-base-2.0.7.tgz#0bd18736bd6ce79ca6d69ce8387a07fb8d4ceffe" + dependencies: + prop-types "15.5.8" + +react-icons@^2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-2.2.5.tgz#f942501c21a4cc0456ce2bbee5032c93f6051dcf" + dependencies: + react-icon-base "2.0.7" + +react-inspector@^2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-2.1.4.tgz#2123fab74f68ae3136fbd02392fadb764326d04d" dependencies: babel-runtime "^6.23.0" is-dom "^1.0.9" @@ -6097,7 +6276,16 @@ react-komposer@^2.0.0: react-stubber "^1.0.0" shallowequal "^0.2.2" -react-modal@^1.7.6, react-modal@^1.7.7: +react-markdown@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-2.5.0.tgz#b1c61904fee5895886803bd9df7db23c3dc3a89e" + dependencies: + commonmark "^0.24.0" + commonmark-react-renderer "^4.2.4" + in-publish "^2.0.0" + prop-types "^15.5.1" + +react-modal@^1.7.7: version "1.9.7" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-1.9.7.tgz#07ef56790b953e3b98ef1e2989e347983c72871d" dependencies: @@ -6131,12 +6319,12 @@ react-sizeme@^2.3.2: invariant "^2.2.2" lodash "^4.17.4" -react-split-pane@^0.1.63: - version "0.1.63" - resolved "https://registry.yarnpkg.com/react-split-pane/-/react-split-pane-0.1.63.tgz#fadb3960cc659911dd05ffbc88acee4be9f53583" +react-split-pane@^0.1.65: + version "0.1.65" + resolved "https://registry.yarnpkg.com/react-split-pane/-/react-split-pane-0.1.65.tgz#37c0b5ea5c960827e7f3621d0a114b0f8c9c8918" dependencies: - inline-style-prefixer "^3.0.2" - prop-types "^15.5.8" + inline-style-prefixer "^3.0.6" + prop-types "^15.5.10" react-style-proptype "^3.0.0" react-static-container@^1.0.1: @@ -6161,6 +6349,27 @@ react-styled-flexboxgrid@^2.0.0: dependencies: lodash.isinteger "^4.0.4" +react-transition-group@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-1.2.0.tgz#b51fc921b0c3835a7ef7c571c79fc82c73e9204f" + dependencies: + chain-function "^1.0.0" + dom-helpers "^3.2.0" + loose-envify "^1.3.1" + prop-types "^15.5.6" + warning "^3.0.0" + +react-treebeard@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/react-treebeard/-/react-treebeard-2.0.3.tgz#cd644209c1be2fe2be3ae4bca8350ed6abf293d6" + dependencies: + babel-runtime "^6.23.0" + deep-equal "^1.0.1" + prop-types "^15.5.8" + radium "^0.19.0" + shallowequal "^0.2.2" + velocity-react "^1.3.1" + react-url-query@^1.1.4: version "1.2.0" resolved "https://registry.yarnpkg.com/react-url-query/-/react-url-query-1.2.0.tgz#35e4985ec6172cab257ac6d0d33cd4051a4c8fe8" @@ -6193,20 +6402,20 @@ read-only-stream@^2.0.0: dependencies: readable-stream "^2.0.2" -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" + find-up "^2.0.0" + read-pkg "^2.0.0" -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" dependencies: - load-json-file "^1.0.0" + load-json-file "^2.0.0" normalize-package-data "^2.3.2" - path-type "^1.0.0" + path-type "^2.0.0" read@1.0.x: version "1.0.7" @@ -6732,6 +6941,16 @@ shasum@^1.0.0: json-stable-stringify "~0.0.0" sha.js "~2.4.4" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + shell-quote@^1.4.2, shell-quote@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" @@ -6741,7 +6960,7 @@ shell-quote@^1.4.2, shell-quote@^1.6.1: array-reduce "~0.0.0" jsonify "~0.0.0" -shelljs@^0.7.5, shelljs@^0.7.7: +shelljs@^0.7.5: version "0.7.7" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.7.tgz#b2f5c77ef97148f4b4f6e22682e10bba8667cff1" dependencies: @@ -6749,6 +6968,14 @@ shelljs@^0.7.5, shelljs@^0.7.7: interpret "^1.0.0" rechoir "^0.6.2" +shelljs@^0.7.8: + version "0.7.8" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + should-equal@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-0.5.0.tgz#c797f135f3067feb69ebecdb306b1c3fe21b3e6f" @@ -6829,9 +7056,9 @@ source-list-map@^0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" -source-list-map@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1" +source-list-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" source-map-support@^0.4.2: version "0.4.15" @@ -6949,7 +7176,7 @@ string-template@~0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" -string-width@^1.0.1, string-width@^1.0.2: +string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" dependencies: @@ -6980,6 +7207,10 @@ string.prototype.padstart@^3.0.0: es-abstract "^1.4.3" function-bind "^1.0.2" +string.prototype.repeat@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf" + string.prototype.trim@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" @@ -7002,16 +7233,14 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - dependencies: - is-utf8 "^0.2.0" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + strip-json-comments@~0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-0.1.3.tgz#164c64e370a8a3cc00c9e01b539e569823f0ee54" @@ -7110,7 +7339,7 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.2.3: +supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: @@ -7122,6 +7351,12 @@ supports-color@^4.0.0: dependencies: has-flag "^2.0.0" +supports-color@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.2.1.tgz#65a4bb2631e90e02420dba5554c375a4754bb836" + dependencies: + has-flag "^2.0.0" + svg-tag-names@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/svg-tag-names/-/svg-tag-names-1.1.1.tgz#9641b29ef71025ee094c7043f7cdde7d99fbd50a" @@ -7167,9 +7402,9 @@ table@^3.7.8: slice-ansi "0.0.4" string-width "^2.0.0" -tapable@^0.2.5, tapable@~0.2.5: - version "0.2.6" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d" +tapable@^0.2.7: + version "0.2.8" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" tape@^4.5.1: version "4.6.3" @@ -7376,7 +7611,7 @@ uglify-js@2.x.x, uglify-js@^2.6.2: uglify-to-browserify "~1.0.0" yargs "~3.10.0" -uglify-js@^2.4.19, uglify-js@^2.8.27: +uglify-js@^2.4.19, uglify-js@^2.8.29: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" dependencies: @@ -7406,6 +7641,14 @@ uglifyify@^3.0.1: through "~2.3.4" uglify-js "2.x.x" +uglifyjs-webpack-plugin@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" + dependencies: + source-map "^0.5.6" + uglify-js "^2.8.29" + webpack-sources "^1.0.1" + uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -7546,6 +7789,19 @@ vary@^1, vary@~1.1.0, vary@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" +velocity-animate@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/velocity-animate/-/velocity-animate-1.5.0.tgz#fc8771d8dfe1136ff02a707e10fbb0957c4b030f" + +velocity-react@^1.3.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/velocity-react/-/velocity-react-1.3.3.tgz#d6d47276cfc8be2a75623879b20140ac58c1b82b" + dependencies: + lodash "^3.10.1" + prop-types "^15.5.8" + react-transition-group "^1.1.2" + velocity-animate "^1.4.0" + vendors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" @@ -7584,12 +7840,12 @@ watchify@*: through2 "^2.0.0" xtend "^4.0.0" -watchpack@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.3.1.tgz#7d8693907b28ce6013e7f3610aa2a1acf07dad87" +watchpack@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac" dependencies: async "^2.1.2" - chokidar "^1.4.3" + chokidar "^1.7.0" graceful-fs "^4.1.2" webidl-conversions@^2.0.0: @@ -7622,38 +7878,39 @@ webpack-hot-middleware@^2.18.0: querystring "^0.2.0" strip-ansi "^3.0.0" -webpack-sources@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.2.3.tgz#17c62bfaf13c707f9d02c479e0dcdde8380697fb" +webpack-sources@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf" dependencies: - source-list-map "^1.1.1" + source-list-map "^2.0.0" source-map "~0.5.3" -webpack@^2.5.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.6.1.tgz#2e0457f0abb1ac5df3ab106c69c672f236785f07" +"webpack@^2.5.1 || ^3.0.0": + version "3.5.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.5.3.tgz#e68653963bda146e212832c04a4d8041d2b4b8c8" dependencies: acorn "^5.0.0" acorn-dynamic-import "^2.0.0" - ajv "^4.7.0" - ajv-keywords "^1.1.1" + ajv "^5.1.5" + ajv-keywords "^2.0.0" async "^2.1.2" - enhanced-resolve "^3.0.0" + enhanced-resolve "^3.4.0" + escope "^3.6.0" interpret "^1.0.0" json-loader "^0.5.4" json5 "^0.5.1" loader-runner "^2.3.0" - loader-utils "^0.2.16" + loader-utils "^1.1.0" memory-fs "~0.4.1" mkdirp "~0.5.0" node-libs-browser "^2.0.0" source-map "^0.5.3" - supports-color "^3.1.0" - tapable "~0.2.5" - uglify-js "^2.8.27" - watchpack "^1.3.1" - webpack-sources "^0.2.3" - yargs "^6.0.0" + supports-color "^4.2.1" + tapable "^0.2.7" + uglifyjs-webpack-plugin "^0.4.6" + watchpack "^1.4.0" + webpack-sources "^1.0.1" + yargs "^8.0.2" whatwg-encoding@^1.0.1: version "1.0.1" @@ -7682,9 +7939,15 @@ whet.extend@~0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + +which@^1.2.9: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" wide-align@^1.1.0: version "1.1.0" @@ -7815,6 +8078,10 @@ xmldom@0.1.x: version "0.1.27" resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" +xss-filters@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/xss-filters/-/xss-filters-1.2.7.tgz#59fa1de201f36f2f3470dcac5f58ccc2830b0a9a" + xss@^0.2.7: version "0.2.18" resolved "https://registry.yarnpkg.com/xss/-/xss-0.2.18.tgz#6df5fb5ca28bdc51e78624ff63f19e13ebd73bab" @@ -7836,6 +8103,10 @@ y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + yaml@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/yaml/-/yaml-0.2.3.tgz#b5450e92e76ef36b5dd24e3660091ebaeef3e5c7" @@ -7844,29 +8115,29 @@ yamlparser@>=0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/yamlparser/-/yamlparser-0.0.2.tgz#32393e6afc70c8ca066b6650ac6738b481678ebc" -yargs-parser@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" +yargs-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" dependencies: - camelcase "^3.0.0" + camelcase "^4.1.0" -yargs@^6.0.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" +yargs@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" dependencies: - camelcase "^3.0.0" + camelcase "^4.1.0" cliui "^3.2.0" decamelize "^1.1.1" get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" + os-locale "^2.0.0" + read-pkg-up "^2.0.0" require-directory "^2.1.1" require-main-filename "^1.0.1" set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" + string-width "^2.0.0" + which-module "^2.0.0" y18n "^3.2.1" - yargs-parser "^4.2.0" + yargs-parser "^7.0.0" yargs@~3.10.0: version "3.10.0" From 1efbb1bcfbf5a95da3ec27858b97f47958269e1a Mon Sep 17 00:00:00 2001 From: Eve Essex Date: Fri, 11 Aug 2017 20:33:32 -0400 Subject: [PATCH 4/9] paragraph can save, has pop-up menu, makes links --- .../content2/section_list/index.coffee | 3 +- .../content2/section_list/index.styl | 2 +- .../rich_text2/components/paragraph.coffee | 81 ++++++++++++++----- 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/client/apps/edit/components/content2/section_list/index.coffee b/client/apps/edit/components/content2/section_list/index.coffee index ff6e7cf3c..3a3e343cd 100644 --- a/client/apps/edit/components/content2/section_list/index.coffee +++ b/client/apps/edit/components/content2/section_list/index.coffee @@ -41,8 +41,7 @@ module.exports = React.createClass if @state.editingIndex or @state.editingIndex is 0 then false else true setPostscript: (html) -> - html = null if html is '

' - debugger + html = null unless html.length @props.article.set('postscript', html) @props.saveArticle() diff --git a/client/apps/edit/components/content2/section_list/index.styl b/client/apps/edit/components/content2/section_list/index.styl index 2dbfaffc8..3f8dd35bf 100644 --- a/client/apps/edit/components/content2/section_list/index.styl +++ b/client/apps/edit/components/content2/section_list/index.styl @@ -6,7 +6,7 @@ .edit-sections__postscript max-width standard-body-w-margin margin 0 auto - padding 60px 20px 20px + padding 20px .public-DraftEditorPlaceholder-root position absolute color gray-dark-color diff --git a/client/components/rich_text2/components/paragraph.coffee b/client/components/rich_text2/components/paragraph.coffee index 5fe6b3f9e..87c922a61 100644 --- a/client/components/rich_text2/components/paragraph.coffee +++ b/client/components/rich_text2/components/paragraph.coffee @@ -1,15 +1,16 @@ -# A basic paragraph component supports bold and italic styles, -# optionally links, and linebreaks, and can format in -# types of postscript, caption or lead paragraph +# A basic paragraph component: supports bold and italic styles +# optionally allows links and linebreaks +# or format in types 'postscript', 'caption' and 'lead paragraph' -# RichTextParagraph { +# Paragraph { # html *required # onChange *required # placeholder # layout: article.layout -# postscript: default false -# linked: default false -# linebreaks: default true +# postscript: default=false +# linked: default=false +# linebreaks: default=true +# type: 'postscript' | 'caption' | 'lead paragraph' # } React = require 'react' @@ -22,17 +23,19 @@ Utils = require '../utils/index.coffee' CompositeDecorator, Editor, EditorState, + Entity, RichUtils, Modifier } = require 'draft-js' { convertToHTML, convertFromHTML } = require 'draft-convert' { div, a } = React.DOM editor = (props) -> React.createElement Editor, props components = require('@artsy/reaction-force/dist/components/publishing/index').default +EditNav = React.createFactory require './edit_nav.coffee' InputUrl = React.createFactory require './input_url.coffee' Text = React.createFactory components.Text module.exports = React.createClass - displayName: 'RichTextParagraph' + displayName: 'Paragraph' getInitialState: -> editorState: EditorState.createEmpty( @@ -46,9 +49,10 @@ module.exports = React.createClass componentDidMount: -> if $(@props.html).text().length - blocksFromHTML = @convertFromHTML(@props.html) + html = Utils.standardizeSpacing @props.html + blocksFromHTML = @convertFromHTML(html) @setState - html: @props.html + html: html editorState: EditorState.createWithContent( blocksFromHTML, new CompositeDecorator Config.decorators(@hasLinks()) @@ -73,7 +77,6 @@ module.exports = React.createClass blocksFromHTML = convertFromHTML({ htmlToEntity: (nodeName, node) -> if nodeName is 'a' - debugger data = {url: node.href} return Entity.create( 'LINK', @@ -87,17 +90,18 @@ module.exports = React.createClass html = convertToHTML({ entityToHTML: (entity, originalText) -> if entity.type is 'LINK' - debugger return a { href: entity.data.url} return originalText })(editorState.getCurrentContent()) - html = html - .replace /(\r\n|\n|\r)/gm, '' - .replace /<\/p>

/g, ' ' - .replace(/ /g, '  ') + html = Utils.standardizeSpacing html html = if html is '

' then '' else html return html + availableBlocks: -> + blockMap = Config.blockRenderMap() + available = Object.keys(blockMap.toObject()) + return Array.from(available) + handleKeyCommand: (e) -> return 'handled' if @props.linebreaks is false and e is 'split-block' if e is 'link-prompt' @@ -110,6 +114,30 @@ module.exports = React.createClass @onChange newState if newState return 'not-handled' + toggleInlineStyle: (inlineStyle) -> + selection = Utils.getSelectionDetails(@state.editorState) + @onChange RichUtils.toggleInlineStyle(@state.editorState, inlineStyle) + + onPaste: (text, html) -> + { editorState } = @state + unless html + html = '
' + text + '
' + html = Utils.stripGoogleStyles html + blocksFromHTML = @convertFromHTML html + convertedHtml = blocksFromHTML.getBlocksAsArray().map (contentBlock) => + unstyled = Utils.stripCharacterStyles contentBlock, true + unless unstyled.getType() in @availableBlocks() or unstyled.getType() is 'LINK' + unstyled = unstyled.set 'type', 'unstyled' + return unstyled + blockMap = ContentState.createFromBlockArray(convertedHtml, blocksFromHTML.getBlocksAsArray()).blockMap + newState = Modifier.replaceWithFragment( + editorState.getCurrentContent() + editorState.getSelection() + blockMap + ) + @onChange EditorState.push(editorState, newState, 'insert-fragment') + return true + promptForLink: (e) -> { editorState } = @state e.preventDefault() if e @@ -134,7 +162,6 @@ module.exports = React.createClass selectionTarget: selectionTarget confirmLink: (url) -> - debugger { editorState } = @state contentState = editorState.getCurrentContent() contentStateWithEntity = contentState.createEntity( @@ -176,15 +203,33 @@ module.exports = React.createClass urlValue: @state.urlValue } + checkSelection: -> + debugger + if !window.getSelection().isCollapsed + location = Utils.getSelectionLocation $(ReactDOM.findDOMNode(@refs.editor)).offset() + selectionTargetL = Config.inlineStyles().length * 45 + @setState showMenu: true, selectionTarget: Utils.stickyControlsBox(location, -43, selectionTargetL) + else + @setState showMenu: false + render: -> Text { layout: @props.layout postscript: @props.postscript }, + if @state.showMenu + EditNav { + styles: Config.inlineStyles() + toggleStyle: @toggleInlineStyle + promptForLink: @promptForLink + position: @state.selectionTarget + } div { className: 'rich-text--paragraph' onClick: @focus onBlur: @onBlur + onMouseUp: @checkSelection + onKeyUp: @checkSelection }, editor { ref: 'editor' @@ -196,6 +241,6 @@ module.exports = React.createClass handleReturn: @handleReturn handleKeyCommand: @handleKeyCommand keyBindingFn: Utils.keyBindingFnCaption - # handlePastedText: @onPaste + handlePastedText: @onPaste } @printUrlInput() \ No newline at end of file From bd4327e85d7d8db9de56eb44d7ae7925505ef42d Mon Sep 17 00:00:00 2001 From: Eve Essex Date: Sat, 12 Aug 2017 17:39:09 -0400 Subject: [PATCH 5/9] replace lead paragraph --- .../content2/section_container/index.coffee | 34 +++++++++---------- .../content2/section_list/index.coffee | 7 ++-- .../content2/sections/header/index.coffee | 15 ++++---- .../sections/header/test/index.coffee | 20 +++++------ .../content2/sections/text/index.coffee | 4 +-- .../content2/sections/text/test/index.coffee | 4 +-- .../rich_text2/components/paragraph.coffee | 24 ++++++------- 7 files changed, 55 insertions(+), 53 deletions(-) diff --git a/client/apps/edit/components/content2/section_container/index.coffee b/client/apps/edit/components/content2/section_container/index.coffee index 572f33729..2b4aa2342 100644 --- a/client/apps/edit/components/content2/section_container/index.coffee +++ b/client/apps/edit/components/content2/section_container/index.coffee @@ -6,14 +6,14 @@ React = require 'react' _ = require 'underscore' -SectionText = React.createFactory require '../sections/text/index.coffee' -SectionVideo = React.createFactory require '../../content/sections/video/index.coffee' -SectionSlideshow = React.createFactory require '../../content/sections/slideshow/index.coffee' -SectionEmbed = React.createFactory require '../../content/sections/embed/index.coffee' -SectionFullscreen = React.createFactory require '../../content/sections/fullscreen/index.coffee' -SectionCallout = React.createFactory require '../../content/sections/callout/index.coffee' -SectionImageCollection = React.createFactory require '../sections/image_collection/index.coffee' -SectionImage = React.createFactory require '../../content/sections/image/index.coffee' +Text = React.createFactory require '../sections/text/index.coffee' +Video = React.createFactory require '../../content/sections/video/index.coffee' +Slideshow = React.createFactory require '../../content/sections/slideshow/index.coffee' +Embed = React.createFactory require '../../content/sections/embed/index.coffee' +Fullscreen = React.createFactory require '../../content/sections/fullscreen/index.coffee' +Callout = React.createFactory require '../../content/sections/callout/index.coffee' +ImageCollection = React.createFactory require '../sections/image_collection/index.coffee' +Image = React.createFactory require '../../content/sections/image/index.coffee' { div, nav, button } = React.DOM icons = -> require('../../icons.jade') arguments... @@ -64,15 +64,15 @@ module.exports = React.createClass dangerouslySetInnerHTML: __html: $(icons()).filter('.remove').html() } (switch @props.section.get('type') - when 'text' then SectionText - when 'video' then SectionVideo - when 'slideshow' then SectionSlideshow - when 'embed' then SectionEmbed - when 'fullscreen' then SectionFullscreen - when 'callout' then SectionCallout - when 'image_set' then SectionImageCollection - when 'image_collection' then SectionImageCollection - when 'image' then SectionImage + when 'text' then Text + when 'video' then Video + when 'slideshow' then Slideshow + when 'embed' then Embed + when 'fullscreen' then Fullscreen + when 'callout' then Callout + when 'image_set' then ImageCollection + when 'image_collection' then ImageCollection + when 'image' then Image )( section: @props.section sections: @props.sections diff --git a/client/apps/edit/components/content2/section_list/index.coffee b/client/apps/edit/components/content2/section_list/index.coffee index 3a3e343cd..5e40e8b6a 100644 --- a/client/apps/edit/components/content2/section_list/index.coffee +++ b/client/apps/edit/components/content2/section_list/index.coffee @@ -8,8 +8,6 @@ SectionContainer = React.createFactory require '../section_container/index.coffe SectionTool = React.createFactory require '../section_tool/index.coffee' DragContainer = React.createFactory require '../../../../../components/drag_drop/index.coffee' Paragraph = React.createFactory require '../../../../../components/rich_text2/components/paragraph.coffee' -components = require('@artsy/reaction-force/dist/components/publishing/index').default -Text = React.createFactory components.Text { div } = React.DOM module.exports = React.createClass @@ -52,7 +50,7 @@ module.exports = React.createClass ref: 'sections' }, SectionTool { sections: @props.sections, index: -1, key: 1 } - if @props.sections.length > 0 + if @props.sections.length DragContainer { items: @props.sections.models onDragEnd: @onDragEnd @@ -84,7 +82,8 @@ module.exports = React.createClass html: @props.article.get('postscript') or '' onChange: @setPostscript placeholder: 'Postscript (optional)' - postscript: true + type: 'postscript' linked: true layout: @props.article.get('layout') } + # TODO - Author Preview diff --git a/client/apps/edit/components/content2/sections/header/index.coffee b/client/apps/edit/components/content2/sections/header/index.coffee index ef76f9e0b..0df2452ac 100644 --- a/client/apps/edit/components/content2/sections/header/index.coffee +++ b/client/apps/edit/components/content2/sections/header/index.coffee @@ -1,9 +1,8 @@ React = require 'react' moment = require 'moment' -RichTextParagraph = React.createFactory require '../../../../../../components/rich_text2/components/input_paragraph.coffee' +Paragraph = React.createFactory require '../../../../../../components/rich_text2/components/paragraph.coffee' { div, p, textarea } = React.DOM - module.exports = React.createClass displayName: 'SectionHeader' @@ -23,10 +22,14 @@ module.exports = React.createClass div { className: 'edit-header__lead-paragraph' }, - RichTextParagraph { - text: @props.article.get('lead_paragraph') + Paragraph { + html: @props.article.get('lead_paragraph') or '' onChange: @setLeadParagraph - placeholder: 'Lead paragraph (optional)' + placeholder: 'Lead Paragraph (optional)' + type: 'lead_paragraph' + linked: true + linebreaks: false + layout: @props.article.get('layout') } render: -> @@ -48,7 +51,7 @@ module.exports = React.createClass onKeyUp: @setTitle ref: 'title' } - unless @props.article.get('title')?.length > 0 + unless @props.article.get('title')?.length div { className: 'edit-required' } if layout is 'classic' diff --git a/client/apps/edit/components/content2/sections/header/test/index.coffee b/client/apps/edit/components/content2/sections/header/test/index.coffee index cf56cf482..be72c2c91 100644 --- a/client/apps/edit/components/content2/sections/header/test/index.coffee +++ b/client/apps/edit/components/content2/sections/header/test/index.coffee @@ -19,8 +19,8 @@ describe 'SectionHeader: Classic', -> benv.expose $: benv.require 'jquery' global.HTMLElement = () => {} SectionHeader = benv.require resolve(__dirname, '../index.coffee') - RichTextParagraph = benv.require resolve(__dirname, '../../../../../../../components/rich_text2/components/input_paragraph.coffee') - SectionHeader.__set__ 'RichTextParagraph', React.createFactory RichTextParagraph + Paragraph = benv.require resolve(__dirname, '../../../../../../../components/rich_text2/components/paragraph.coffee') + SectionHeader.__set__ 'Paragraph', React.createFactory Paragraph @article = new Article _.extend fixtures().articles, layout: 'classic' author: @@ -69,7 +69,7 @@ describe 'SectionHeader: Classic', -> describe 'Lead Paragraph', -> it 'renders a lead paragraph component', -> - $(ReactDOM.findDOMNode(@component)).html().should.containEql '
' + $(ReactDOM.findDOMNode(@component)).html().should.containEql '
' it 'Can display a saved lead paragraph', -> $(ReactDOM.findDOMNode(@component)).html().should.containEql 'Just before the lines start forming...' @@ -112,8 +112,8 @@ describe 'SectionHeader: Standard', -> benv.expose $: benv.require 'jquery' global.HTMLElement = () => {} SectionHeader = benv.require resolve(__dirname, '../index.coffee') - RichTextParagraph = benv.require resolve(__dirname, '../../../../../../../components/rich_text2/components/input_paragraph.coffee') - SectionHeader.__set__ 'RichTextParagraph', React.createFactory RichTextParagraph + Paragraph = benv.require resolve(__dirname, '../../../../../../../components/rich_text2/components/input_paragraph.coffee') + SectionHeader.__set__ 'Paragraph', React.createFactory Paragraph @article = new Article _.extend fixtures().articles, layout: 'standard' author: @@ -143,15 +143,15 @@ describe 'SectionHeader: Standard', -> describe 'Lead Paragraph', -> it 'renders a lead paragraph component if field has text', -> - $(ReactDOM.findDOMNode(@component)).html().should.containEql '
' + $(ReactDOM.findDOMNode(@component)).html().should.containEql '
' + + it 'Can display a saved lead paragraph', -> + $(ReactDOM.findDOMNode(@component)).text().should.containEql 'Just before the lines start forming...' it 'does not render lead paragraph component if field is empty', -> @component.props.article.set('lead_paragraph', '') @component.forceUpdate() - $(ReactDOM.findDOMNode(@component)).html().should.not.containEql '
' - - it 'Can display a saved lead paragraph', -> - $(ReactDOM.findDOMNode(@component)).html().should.containEql 'Just before the lines start forming...' + $(ReactDOM.findDOMNode(@component)).html().should.not.containEql '
' it '#setLeadParagraph sets and saves article lead paragraph on change', -> @component.setLeadParagraph('

A new paragraph

') diff --git a/client/apps/edit/components/content2/sections/text/index.coffee b/client/apps/edit/components/content2/sections/text/index.coffee index 154d7aec5..cbd21541a 100644 --- a/client/apps/edit/components/content2/sections/text/index.coffee +++ b/client/apps/edit/components/content2/sections/text/index.coffee @@ -5,7 +5,7 @@ sd = require('sharify').data window.process = {env: {NODE_ENV: sd.NODE_ENV}} components = require('@artsy/reaction-force/dist/components/publishing/index').default Config = require './draft_config.coffee' -EditNav = React.createFactory require '../../../../../../components/rich_text2/components/edit_nav.coffee' +Nav = React.createFactory require '../../../../../../components/rich_text2/components/edit_nav.coffee' InputUrl = React.createFactory require '../../../../../../components/rich_text2/components/input_url.coffee' Text = React.createFactory components.Text Utils = require '../../../../../../components/rich_text2/utils/index.coffee' @@ -282,7 +282,7 @@ module.exports = React.createClass }, Text { layout: @props.article.get 'layout' }, if @state.showMenu - EditNav { + Nav { hasFeatures: @state.hasFeatures blocks: Config.blockTypes @props.article.get('layout'), @state.hasFeatures toggleBlock: @toggleBlockType diff --git a/client/apps/edit/components/content2/sections/text/test/index.coffee b/client/apps/edit/components/content2/sections/text/test/index.coffee index 0dd2dbe05..1c513c9d4 100644 --- a/client/apps/edit/components/content2/sections/text/test/index.coffee +++ b/client/apps/edit/components/content2/sections/text/test/index.coffee @@ -38,11 +38,11 @@ describe 'Section Text', -> InputUrl = benv.requireWithJadeify( resolve(__dirname, '../../../../../../../components/rich_text2/components/input_url'), ['icons'] ) - EditNav = benv.requireWithJadeify( + Nav = benv.requireWithJadeify( resolve(__dirname, '../../../../../../../components/rich_text2/components/edit_nav'), ['icons'] ) @SectionText.__set__ 'InputUrl', React.createFactory InputUrl - @SectionText.__set__ 'EditNav', React.createFactory EditNav + @SectionText.__set__ 'Nav', React.createFactory Nav @SectionText.__set__ 'Utils.stickyControlsBox', sinon.stub().returns {top: 20, left: 40} @SectionText.__set__ 'Utils.getSelectionLocation', sinon.stub().returns({top: 20, left: 40}) @sections = new Backbone.Collection [ diff --git a/client/components/rich_text2/components/paragraph.coffee b/client/components/rich_text2/components/paragraph.coffee index 87c922a61..b6a3ad655 100644 --- a/client/components/rich_text2/components/paragraph.coffee +++ b/client/components/rich_text2/components/paragraph.coffee @@ -30,7 +30,7 @@ Utils = require '../utils/index.coffee' { div, a } = React.DOM editor = (props) -> React.createElement Editor, props components = require('@artsy/reaction-force/dist/components/publishing/index').default -EditNav = React.createFactory require './edit_nav.coffee' +Nav = React.createFactory require './edit_nav.coffee' InputUrl = React.createFactory require './input_url.coffee' Text = React.createFactory components.Text @@ -43,7 +43,7 @@ module.exports = React.createClass ) focus: false showUrlInput: false - showMenu: false + showNav: false urlValue: '' selectionTarget: null @@ -157,7 +157,7 @@ module.exports = React.createClass url = linkInstance.getData().url @setState showUrlInput: true - showMenu: false + showNav: false urlValue: url selectionTarget: selectionTarget @@ -173,7 +173,7 @@ module.exports = React.createClass newEditorState = EditorState.set editorState, { currentContent: contentStateWithEntity } @setState({ showUrlInput: false - showMenu: false + showNav: false urlValue: '' selectionTarget: null }) @@ -204,22 +204,22 @@ module.exports = React.createClass } checkSelection: -> - debugger if !window.getSelection().isCollapsed location = Utils.getSelectionLocation $(ReactDOM.findDOMNode(@refs.editor)).offset() - selectionTargetL = Config.inlineStyles().length * 45 - @setState showMenu: true, selectionTarget: Utils.stickyControlsBox(location, -43, selectionTargetL) + selectionTargetL = Config.inlineStyles(@props.type).length * 50 + selectionTargetL = selectionTargetL + 50 if @hasLinks() + @setState showNav: true, selectionTarget: Utils.stickyControlsBox(location, -43, selectionTargetL) else - @setState showMenu: false + @setState showNav: false render: -> Text { layout: @props.layout - postscript: @props.postscript + postscript: @props.type is 'postscript' }, - if @state.showMenu - EditNav { - styles: Config.inlineStyles() + if @state.showNav + Nav { + styles: Config.inlineStyles(@props.type) toggleStyle: @toggleInlineStyle promptForLink: @promptForLink position: @state.selectionTarget From 47de20cea86babec29c6c16c82a1bdb39f3a5123 Mon Sep 17 00:00:00 2001 From: Eve Essex Date: Sat, 12 Aug 2017 18:41:27 -0400 Subject: [PATCH 6/9] testing --- .../sections/image_collection/index.coffee | 2 +- .../content2/section_container/index.coffee | 2 - .../section_container/test/index.coffee | 2 +- .../content2/section_list/index.coffee | 29 ++-- .../content2/section_list/test/index.coffee | 10 +- .../content2/sections/header/index.coffee | 6 +- .../content2/sections/header/index.styl | 4 +- .../sections/header/test/index.coffee | 2 +- .../sections/image_collection/index.coffee | 5 +- .../content2/sections/text/index.coffee | 2 +- .../content2/sections/text/test/index.coffee | 2 +- .../components/input_paragraph.coffee | 105 ------------ .../{edit_nav.coffee => nav.coffee} | 2 +- .../rich_text2/components/paragraph.coffee | 19 ++- .../rich_text2/test/input_paragraph.coffee | 95 ----------- .../test/{edit_nav.coffee => nav.coffee} | 26 +-- .../rich_text2/test/paragraph.coffee | 153 ++++++++++++++++++ 17 files changed, 212 insertions(+), 254 deletions(-) delete mode 100644 client/components/rich_text2/components/input_paragraph.coffee rename client/components/rich_text2/components/{edit_nav.coffee => nav.coffee} (98%) delete mode 100644 client/components/rich_text2/test/input_paragraph.coffee rename client/components/rich_text2/test/{edit_nav.coffee => nav.coffee} (89%) create mode 100644 client/components/rich_text2/test/paragraph.coffee diff --git a/client/apps/edit/components/content/sections/image_collection/index.coffee b/client/apps/edit/components/content/sections/image_collection/index.coffee index e37515b63..767be3ebf 100644 --- a/client/apps/edit/components/content/sections/image_collection/index.coffee +++ b/client/apps/edit/components/content/sections/image_collection/index.coffee @@ -12,7 +12,7 @@ DragContainer = React.createFactory require '../../../../../../components/drag_d { div, section, ul, li } = React.DOM components = require('@artsy/reaction-force/dist/components/publishing/index').default -ImageSetPreview = React.createFactory components.ImagesetPreviewClassic +ImageSetPreview = React.createFactory components.ImageSetPreviewClassic module.exports = React.createClass displayName: 'SectionImageCollection' diff --git a/client/apps/edit/components/content2/section_container/index.coffee b/client/apps/edit/components/content2/section_container/index.coffee index 2b4aa2342..3c58eb786 100644 --- a/client/apps/edit/components/content2/section_container/index.coffee +++ b/client/apps/edit/components/content2/section_container/index.coffee @@ -11,7 +11,6 @@ Video = React.createFactory require '../../content/sections/video/index.coffee' Slideshow = React.createFactory require '../../content/sections/slideshow/index.coffee' Embed = React.createFactory require '../../content/sections/embed/index.coffee' Fullscreen = React.createFactory require '../../content/sections/fullscreen/index.coffee' -Callout = React.createFactory require '../../content/sections/callout/index.coffee' ImageCollection = React.createFactory require '../sections/image_collection/index.coffee' Image = React.createFactory require '../../content/sections/image/index.coffee' { div, nav, button } = React.DOM @@ -69,7 +68,6 @@ module.exports = React.createClass when 'slideshow' then Slideshow when 'embed' then Embed when 'fullscreen' then Fullscreen - when 'callout' then Callout when 'image_set' then ImageCollection when 'image_collection' then ImageCollection when 'image' then Image diff --git a/client/apps/edit/components/content2/section_container/test/index.coffee b/client/apps/edit/components/content2/section_container/test/index.coffee index cd3673433..73469ba09 100644 --- a/client/apps/edit/components/content2/section_container/test/index.coffee +++ b/client/apps/edit/components/content2/section_container/test/index.coffee @@ -38,7 +38,7 @@ describe 'SectionContainer', -> dragOver: 4 article: new Article } - @SectionContainer.__set__ 'SectionText', -> + @SectionContainer.__set__ 'Text', -> @component = ReactDOM.render React.createElement(@SectionContainer, @props ), $("
")[0], => setTimeout => @component.setState = sinon.stub() diff --git a/client/apps/edit/components/content2/section_list/index.coffee b/client/apps/edit/components/content2/section_list/index.coffee index 5e40e8b6a..e90bb9a27 100644 --- a/client/apps/edit/components/content2/section_list/index.coffee +++ b/client/apps/edit/components/content2/section_list/index.coffee @@ -59,20 +59,21 @@ module.exports = React.createClass article: @props.article }, @props.sections.map (section, i) => - [ - SectionContainer { - sections: @props.sections - section: section - index: i - editing: @state.editingIndex is i - ref: 'section' + i - key: section.cid - channel: @props.channel - onSetEditing: @onSetEditing - article: @props.article - } - SectionTool { sections: @props.sections, index: i, key: i } - ] + unless section.get('type') is 'callout' + [ + SectionContainer { + sections: @props.sections + section: section + index: i + editing: @state.editingIndex is i + ref: 'section' + i + key: section.cid + channel: @props.channel + onSetEditing: @onSetEditing + article: @props.article + } + SectionTool { sections: @props.sections, index: i, key: i } + ] if @props.channel.isEditorial() div { className: 'edit-sections__postscript' diff --git a/client/apps/edit/components/content2/section_list/test/index.coffee b/client/apps/edit/components/content2/section_list/test/index.coffee index 3174a9010..43ecb7e57 100644 --- a/client/apps/edit/components/content2/section_list/test/index.coffee +++ b/client/apps/edit/components/content2/section_list/test/index.coffee @@ -20,18 +20,18 @@ describe 'SectionList', -> global.HTMLElement = () => {} @SectionList = benv.require resolve(__dirname, '../index') DragContainer = benv.require resolve(__dirname, '../../../../../../components/drag_drop/index') - RichTextParagraph = benv.require resolve( - __dirname, '../../../../../../components/rich_text/components/input_paragraph.coffee' + Paragraph = benv.require resolve( + __dirname, '../../../../../../components/rich_text2/components/paragraph.coffee' ) @SectionList.__set__ 'SectionTool', @SectionTool = sinon.stub() @SectionContainer = benv.requireWithJadeify( resolve(__dirname, '../../section_container/index'), ['icons'] ) - @SectionContainer.__set__ 'SectionText', text = sinon.stub() - @SectionContainer.__set__ 'SectionImageCollection', image_collection = sinon.stub() + @SectionContainer.__set__ 'Text', text = sinon.stub() + @SectionContainer.__set__ 'ImageCollection', image_collection = sinon.stub() @SectionList.__set__ 'SectionContainer', React.createFactory @SectionContainer @SectionList.__set__ 'DragContainer', React.createFactory DragContainer - @SectionList.__set__ 'RichTextParagraph', React.createFactory RichTextParagraph + @SectionList.__set__ 'Paragraph', React.createFactory Paragraph @props = { article: new Backbone.Model {layout: 'feature'} sections: @sections = new Sections [ diff --git a/client/apps/edit/components/content2/sections/header/index.coffee b/client/apps/edit/components/content2/sections/header/index.coffee index 0df2452ac..39b27d58e 100644 --- a/client/apps/edit/components/content2/sections/header/index.coffee +++ b/client/apps/edit/components/content2/sections/header/index.coffee @@ -23,11 +23,11 @@ module.exports = React.createClass className: 'edit-header__lead-paragraph' }, Paragraph { - html: @props.article.get('lead_paragraph') or '' + html: @props.article.get('lead_paragraph') onChange: @setLeadParagraph placeholder: 'Lead Paragraph (optional)' type: 'lead_paragraph' - linked: true + linked: false linebreaks: false layout: @props.article.get('layout') } @@ -63,5 +63,5 @@ module.exports = React.createClass @props.article.get('author').name p { className: 'date' }, @props.article.getPublishDate() - if layout is 'standard' and @props.article.get('lead_paragraph').length + if layout is 'standard' and @props.article.get('lead_paragraph')?.length @renderLeadParagraph() diff --git a/client/apps/edit/components/content2/sections/header/index.styl b/client/apps/edit/components/content2/sections/header/index.styl index 652a30121..f51c4cc95 100644 --- a/client/apps/edit/components/content2/sections/header/index.styl +++ b/client/apps/edit/components/content2/sections/header/index.styl @@ -2,6 +2,7 @@ padding-top 60px .public-DraftEditorPlaceholder-root position absolute + top 1em color gray-dark-color &__title position relative @@ -82,5 +83,4 @@ garamond l-body max-width classic-body-width margin 20px auto 0 auto - .public-DraftEditorPlaceholder-root - color gray-dark-color + diff --git a/client/apps/edit/components/content2/sections/header/test/index.coffee b/client/apps/edit/components/content2/sections/header/test/index.coffee index be72c2c91..21bcbf062 100644 --- a/client/apps/edit/components/content2/sections/header/test/index.coffee +++ b/client/apps/edit/components/content2/sections/header/test/index.coffee @@ -112,7 +112,7 @@ describe 'SectionHeader: Standard', -> benv.expose $: benv.require 'jquery' global.HTMLElement = () => {} SectionHeader = benv.require resolve(__dirname, '../index.coffee') - Paragraph = benv.require resolve(__dirname, '../../../../../../../components/rich_text2/components/input_paragraph.coffee') + Paragraph = benv.require resolve(__dirname, '../../../../../../../components/rich_text2/components/paragraph.coffee') SectionHeader.__set__ 'Paragraph', React.createFactory Paragraph @article = new Article _.extend fixtures().articles, layout: 'standard' diff --git a/client/apps/edit/components/content2/sections/image_collection/index.coffee b/client/apps/edit/components/content2/sections/image_collection/index.coffee index 67f6ba7e1..3103904c7 100644 --- a/client/apps/edit/components/content2/sections/image_collection/index.coffee +++ b/client/apps/edit/components/content2/sections/image_collection/index.coffee @@ -10,9 +10,8 @@ Controls = React.createFactory require './components/controls.coffee' DragContainer = React.createFactory require '../../../../../../components/drag_drop/index.coffee' { fillWidth } = require '../../../../../../components/fill_width/index.coffee' { div, section, ul, li } = React.DOM - components = require('@artsy/reaction-force/dist/components/publishing/index').default -ImagesetPreviewClassic = React.createFactory components.ImagesetPreviewClassic +ImageSetPreviewClassic = React.createFactory components.ImageSetPreviewClassic module.exports = React.createClass displayName: 'SectionImageCollection' @@ -115,7 +114,7 @@ module.exports = React.createClass }, if hasImages if !@props.editing and @props.section.get('type') is 'image_set' - ImagesetPreviewClassic { + ImageSetPreviewClassic { images: images } else diff --git a/client/apps/edit/components/content2/sections/text/index.coffee b/client/apps/edit/components/content2/sections/text/index.coffee index cbd21541a..ccccf84fe 100644 --- a/client/apps/edit/components/content2/sections/text/index.coffee +++ b/client/apps/edit/components/content2/sections/text/index.coffee @@ -5,7 +5,7 @@ sd = require('sharify').data window.process = {env: {NODE_ENV: sd.NODE_ENV}} components = require('@artsy/reaction-force/dist/components/publishing/index').default Config = require './draft_config.coffee' -Nav = React.createFactory require '../../../../../../components/rich_text2/components/edit_nav.coffee' +Nav = React.createFactory require '../../../../../../components/rich_text2/components/nav.coffee' InputUrl = React.createFactory require '../../../../../../components/rich_text2/components/input_url.coffee' Text = React.createFactory components.Text Utils = require '../../../../../../components/rich_text2/utils/index.coffee' diff --git a/client/apps/edit/components/content2/sections/text/test/index.coffee b/client/apps/edit/components/content2/sections/text/test/index.coffee index 1c513c9d4..76d54b954 100644 --- a/client/apps/edit/components/content2/sections/text/test/index.coffee +++ b/client/apps/edit/components/content2/sections/text/test/index.coffee @@ -39,7 +39,7 @@ describe 'Section Text', -> resolve(__dirname, '../../../../../../../components/rich_text2/components/input_url'), ['icons'] ) Nav = benv.requireWithJadeify( - resolve(__dirname, '../../../../../../../components/rich_text2/components/edit_nav'), ['icons'] + resolve(__dirname, '../../../../../../../components/rich_text2/components/nav'), ['icons'] ) @SectionText.__set__ 'InputUrl', React.createFactory InputUrl @SectionText.__set__ 'Nav', React.createFactory Nav diff --git a/client/components/rich_text2/components/input_paragraph.coffee b/client/components/rich_text2/components/input_paragraph.coffee deleted file mode 100644 index c861ba333..000000000 --- a/client/components/rich_text2/components/input_paragraph.coffee +++ /dev/null @@ -1,105 +0,0 @@ -# A basic paragraph component including bold and italic by default - -# RichTextParagraph { -# text *required -# onChange *required -# placeholder -# styleMap: ['italic', 'underline'] #overrides default styles -# } - -React = require 'react' -sd = require('sharify').data -window.process = {env: {NODE_ENV: sd.NODE_ENV}} -{ ContentState, - Editor, - EditorState, - RichUtils, - Modifier } = require 'draft-js' -{ convertToHTML, convertFromHTML } = require 'draft-convert' -{ div } = React.DOM -editor = (props) -> React.createElement Editor, props - -module.exports = React.createClass - displayName: 'RichTextParagraph' - - getInitialState: -> - editorState: EditorState.createEmpty() - focus: false - styleMap: @props.styleMap or ['bold', 'italic'] - - componentDidMount: -> - if $(@props.text).text().length - blocksFromHTML = @convertFromHTML(@props.text) - @setState - html: @props.text - editorState: EditorState.createWithContent(blocksFromHTML) - - onChange: (editorState) -> - html = @convertToHtml editorState - @setState editorState: editorState, html: html - @props.onChange(html) - - focus: -> - @setState focus: true - @refs.editor.focus() - - onBlur: -> - @setState focus: false - - convertFromHTML: (html) -> - blocksFromHTML = convertFromHTML({})(html) - return blocksFromHTML - - convertToHtml: (editorState) -> - html = convertToHTML({})(editorState.getCurrentContent()) - html = html.replace(/ /g, '  ') - return html - - onPaste: (text, html) -> - { editorState } = @state - unless html - html = '
' + text + '
' - blocksFromHTML = @convertFromHTML(html) - convertedHtml = blocksFromHTML.getBlocksAsArray().map (contentBlock) => - unstyled = @stripPastedStyles contentBlock - unless unstyled.getType() is 'unstyled' - unstyled = unstyled.set('type', 'unstyled') - return unstyled - blockMap = ContentState.createFromBlockArray(convertedHtml, blocksFromHTML.entityMap).blockMap - newState = Modifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), blockMap) - this.onChange(EditorState.push(editorState, newState, 'insert-fragment')) - return true - - stripPastedStyles: (contentBlock) -> - styleMap = @state.styleMap.map (style) -> - return style.toUpperCase() - characterList = contentBlock.getCharacterList().map (character) -> - if character.get('style') not in styleMap - character.set('style', character.get('style').clear()) - unstyled = contentBlock.set('characterList', characterList) - return unstyled - - handleKeyCommand: (e) -> - if e in @state.styleMap - newState = RichUtils.handleKeyCommand @state.editorState, e - if newState - @onChange newState - return true - return false - - render: -> - div { className: 'rich-text--paragraph' }, - div { - className: 'rich-text--paragraph__input' - onClick: @focus - onBlur: @onBlur - }, - editor { - ref: 'editor' - placeholder: @props.placeholder - editorState: @state.editorState - spellCheck: true - onChange: @onChange - handlePastedText: @onPaste - handleKeyCommand: @handleKeyCommand - } diff --git a/client/components/rich_text2/components/edit_nav.coffee b/client/components/rich_text2/components/nav.coffee similarity index 98% rename from client/components/rich_text2/components/edit_nav.coffee rename to client/components/rich_text2/components/nav.coffee index 9f5be29a5..4bf35eed7 100644 --- a/client/components/rich_text2/components/edit_nav.coffee +++ b/client/components/rich_text2/components/nav.coffee @@ -4,7 +4,7 @@ _ = require 'underscore' icons = -> require('../utils/icons.jade') arguments... module.exports = React.createClass - displayName: 'EditNav' + displayName: 'RichTextNav' onToggle: (e) -> e.preventDefault() diff --git a/client/components/rich_text2/components/paragraph.coffee b/client/components/rich_text2/components/paragraph.coffee index b6a3ad655..ad32135b8 100644 --- a/client/components/rich_text2/components/paragraph.coffee +++ b/client/components/rich_text2/components/paragraph.coffee @@ -30,7 +30,7 @@ Utils = require '../utils/index.coffee' { div, a } = React.DOM editor = (props) -> React.createElement Editor, props components = require('@artsy/reaction-force/dist/components/publishing/index').default -Nav = React.createFactory require './edit_nav.coffee' +Nav = React.createFactory require './nav.coffee' InputUrl = React.createFactory require './input_url.coffee' Text = React.createFactory components.Text @@ -46,6 +46,7 @@ module.exports = React.createClass showNav: false urlValue: '' selectionTarget: null + linebreaks: @props.linebreaks or true componentDidMount: -> if $(@props.html).text().length @@ -71,7 +72,9 @@ module.exports = React.createClass @refs.editor.focus() onBlur: -> - @setState focus: false + @setState + focus: false + showNav: false convertFromHTML: (html) -> blocksFromHTML = convertFromHTML({ @@ -103,7 +106,7 @@ module.exports = React.createClass return Array.from(available) handleKeyCommand: (e) -> - return 'handled' if @props.linebreaks is false and e is 'split-block' + return 'handled' if @state.linebreaks is false and e is 'split-block' if e is 'link-prompt' @promptForLink() if @hasLinks() return 'handled' @@ -122,7 +125,10 @@ module.exports = React.createClass { editorState } = @state unless html html = '
' + text + '
' + html = Utils.standardizeSpacing html html = Utils.stripGoogleStyles html + unless @state.linebreaks + html = html.replace(/<\/p>

/g, '') blocksFromHTML = @convertFromHTML html convertedHtml = blocksFromHTML.getBlocksAsArray().map (contentBlock) => unstyled = Utils.stripCharacterStyles contentBlock, true @@ -135,7 +141,8 @@ module.exports = React.createClass editorState.getSelection() blockMap ) - @onChange EditorState.push(editorState, newState, 'insert-fragment') + newState = EditorState.push(editorState, newState, 'insert-fragment') + @onChange newState return true promptForLink: (e) -> @@ -206,7 +213,7 @@ module.exports = React.createClass checkSelection: -> if !window.getSelection().isCollapsed location = Utils.getSelectionLocation $(ReactDOM.findDOMNode(@refs.editor)).offset() - selectionTargetL = Config.inlineStyles(@props.type).length * 50 + selectionTargetL = Config.inlineStyles(@props.type).length * 25 selectionTargetL = selectionTargetL + 50 if @hasLinks() @setState showNav: true, selectionTarget: Utils.stickyControlsBox(location, -43, selectionTargetL) else @@ -221,7 +228,7 @@ module.exports = React.createClass Nav { styles: Config.inlineStyles(@props.type) toggleStyle: @toggleInlineStyle - promptForLink: @promptForLink + promptForLink: @promptForLink if @hasLinks() position: @state.selectionTarget } div { diff --git a/client/components/rich_text2/test/input_paragraph.coffee b/client/components/rich_text2/test/input_paragraph.coffee deleted file mode 100644 index 8e3ddc580..000000000 --- a/client/components/rich_text2/test/input_paragraph.coffee +++ /dev/null @@ -1,95 +0,0 @@ -benv = require 'benv' -{ resolve } = require 'path' -sinon = require 'sinon' - -describe 'RichTextParagraph', -> - - beforeEach (done) -> - benv.setup => - benv.expose - $: benv.require 'jquery' - React: require 'react' - ReactDOM: require 'react-dom' - ReactTestUtils: require 'react-addons-test-utils' - ReactDOMServer: require 'react-dom/server' - Draft: require 'draft-js' - window.jQuery = $ - @r = - find: ReactTestUtils.findRenderedDOMComponentWithClass - simulate: ReactTestUtils.Simulate - @d = - EditorState: Draft.EditorState - global.HTMLElement = () => {} - global.HTMLAnchorElement = () => {} - @RichTextParagraph = benv.require resolve __dirname, '../components/input_paragraph' - @props = { - text: '

Here is the lead paragraph for this article.

' - placeholder: 'Lead paragraph (optional)' - onChange: sinon.stub() - } - @rendered = ReactDOMServer.renderToString React.createElement(@RichTextParagraph, @props) - - @component = ReactDOM.render React.createElement(@RichTextParagraph, @props), (@$el = $ "
")[0], => setTimeout => - @component.state.editorState.getSelection().isCollapsed = sinon.stub().returns false - selection = @component.state.editorState.getSelection() - newSelection = selection.merge({ - anchorKey: @component.state.editorState.getCurrentContent().getFirstBlock().key - anchorOffset: 0 - focusKey: @component.state.editorState.getCurrentContent().getFirstBlock().key - focusOffset: 7 - }) - newEditorState = @d.EditorState.acceptSelection(@component.state.editorState, newSelection) - @component.onChange newEditorState - done() - - afterEach -> - benv.teardown() - - it 'Shows a placeholder if provided and empty', -> - $(@rendered).text().should.eql 'Lead paragraph (optional)' - - it 'Renders an existing lead paragraph', -> - $(ReactDOM.findDOMNode(@component)).text().should.eql 'Here is the lead paragraph for this article.' - @component.state.html.should.eql '

Here is  the lead paragraph for  this article.

' - - it 'Can toggle bold styles by default', -> - @component.handleKeyCommand('bold') - @component.state.html.should.eql '

Here is  the lead paragraph for  this article.

' - - it 'Can toggle italic styles by default', -> - @component.handleKeyCommand('italic') - @component.state.html.should.eql '

Here is  the lead paragraph for  this article.

' - - it 'Ignores unsuppored styles', -> - @component.handleKeyCommand('underline') - @component.state.html.should.eql '

Here is  the lead paragraph for  this article.

' - - it 'Can override default styles via props.styleMap', -> - @props.styleMap = ['bold'] - @props.text = '

Here is the a paragraph for this article.

' - component = ReactDOM.render React.createElement(@RichTextParagraph, @props), (@$el = $ "
")[0] - component.state.editorState.getSelection().isCollapsed = sinon.stub().returns false - selection = component.state.editorState.getSelection() - newSelection = selection.merge({ - anchorKey: component.state.editorState.getCurrentContent().getFirstBlock().key - anchorOffset: 0 - focusKey: component.state.editorState.getCurrentContent().getFirstBlock().key - focusOffset: 7 - }) - newEditorState = @d.EditorState.acceptSelection(component.state.editorState, newSelection) - component.onChange newEditorState - - component.handleKeyCommand('italic') - component.state.html.should.eql '

Here is the a paragraph for this article.

' - - component.handleKeyCommand('bold') - component.state.html.should.eql '

Here is the a paragraph for this article.

' - - it 'Strips unsuported html out of pasted text', -> - @component.onPaste 'Available at: Espacio Valverde Galleries Sector, Booth 9F01', '

Available at: Espacio Valverde • Galleries Sector, Booth 9F01

' - @component.state.html.should.eql '

Available at: Espacio Valverde • Galleries Sector, Booth 9F01  the lead paragraph for  this article.

' - - it 'Sends converted html to parent onChange', -> - @component.onChange @component.state.editorState - @component.props.onChange.called.should.eql true - @component.props.onChange.args[0][0].should.eql '

Here is  the lead paragraph for  this article.

' diff --git a/client/components/rich_text2/test/edit_nav.coffee b/client/components/rich_text2/test/nav.coffee similarity index 89% rename from client/components/rich_text2/test/edit_nav.coffee rename to client/components/rich_text2/test/nav.coffee index 4913d34e9..0766c8b79 100644 --- a/client/components/rich_text2/test/edit_nav.coffee +++ b/client/components/rich_text2/test/nav.coffee @@ -13,14 +13,14 @@ r = { blockTypes, inlineStyles } = require '../../../apps/edit/components/content2/sections/text/draft_config.coffee' -describe 'RichText: EditNav', -> +describe 'RichText: Nav', -> describe 'Classic: partner', -> beforeEach (done) -> benv.setup => benv.expose $: benv.require 'jquery' - @EditNav = benv.requireWithJadeify( - resolve(__dirname, '../components/edit_nav'), ['icons'] + @Nav = benv.requireWithJadeify( + resolve(__dirname, '../components/nav'), ['icons'] ) @props = { hasFeatures: false @@ -32,7 +32,7 @@ describe 'RichText: EditNav', -> makePlainText: @makePlainText = sinon.stub() position: { top: 20, left: 20 } } - @component = ReactDOM.render React.createElement(@EditNav, @props), (@$el = $ "
")[0], => setTimeout => + @component = ReactDOM.render React.createElement(@Nav, @props), (@$el = $ "
")[0], => setTimeout => done() afterEach -> @@ -81,8 +81,8 @@ describe 'RichText: EditNav', -> beforeEach (done) -> benv.setup => benv.expose $: benv.require 'jquery' - @EditNav = benv.requireWithJadeify( - resolve(__dirname, '../components/edit_nav'), ['icons'] + @Nav = benv.requireWithJadeify( + resolve(__dirname, '../components/nav'), ['icons'] ) @props = { hasFeatures: true @@ -94,7 +94,7 @@ describe 'RichText: EditNav', -> makePlainText: @makePlainText = sinon.stub() position: { top: 20, left: 20 } } - @component = ReactDOM.render React.createElement(@EditNav, @props), (@$el = $ "
")[0], => setTimeout => + @component = ReactDOM.render React.createElement(@Nav, @props), (@$el = $ "
")[0], => setTimeout => done() afterEach -> @@ -128,8 +128,8 @@ describe 'RichText: EditNav', -> beforeEach (done) -> benv.setup => benv.expose $: benv.require 'jquery' - @EditNav = benv.requireWithJadeify( - resolve(__dirname, '../components/edit_nav'), ['icons'] + @Nav = benv.requireWithJadeify( + resolve(__dirname, '../components/nav'), ['icons'] ) @props = { hasFeatures: true @@ -141,7 +141,7 @@ describe 'RichText: EditNav', -> makePlainText: @makePlainText = sinon.stub() position: { top: 20, left: 20 } } - @component = ReactDOM.render React.createElement(@EditNav, @props), (@$el = $ "
")[0], => setTimeout => + @component = ReactDOM.render React.createElement(@Nav, @props), (@$el = $ "
")[0], => setTimeout => done() afterEach -> @@ -176,8 +176,8 @@ describe 'RichText: EditNav', -> beforeEach (done) -> benv.setup => benv.expose $: benv.require 'jquery' - @EditNav = benv.requireWithJadeify( - resolve(__dirname, '../components/edit_nav'), ['icons'] + @Nav = benv.requireWithJadeify( + resolve(__dirname, '../components/nav'), ['icons'] ) @props = { hasFeatures: true @@ -189,7 +189,7 @@ describe 'RichText: EditNav', -> makePlainText: @makePlainText = sinon.stub() position: { top: 20, left: 20 } } - @component = ReactDOM.render React.createElement(@EditNav, @props), (@$el = $ "
")[0], => setTimeout => + @component = ReactDOM.render React.createElement(@Nav, @props), (@$el = $ "
")[0], => setTimeout => done() afterEach -> diff --git a/client/components/rich_text2/test/paragraph.coffee b/client/components/rich_text2/test/paragraph.coffee new file mode 100644 index 000000000..a804de9a5 --- /dev/null +++ b/client/components/rich_text2/test/paragraph.coffee @@ -0,0 +1,153 @@ +benv = require 'benv' +{ resolve } = require 'path' +sinon = require 'sinon' +React = require 'react' +ReactDOM = require 'react-dom' +ReactTestUtils = require 'react-addons-test-utils' +ReactDOMServer = require 'react-dom/server' +Draft = require 'draft-js' +{ EditorState, Modifier } = require 'draft-js' + +r = + find: ReactTestUtils.findRenderedDOMComponentWithClass + simulate: ReactTestUtils.Simulate + +describe 'Paragraph', -> + + beforeEach (done) -> + benv.setup => + benv.expose + $: benv.require 'jquery' + window.jQuery = $ + global.Node = () => {} + global.HTMLElement = () => {} + global.HTMLAnchorElement = () => {} + window.getSelection = sinon.stub().returns( + isCollapsed: false + getRangeAt: sinon.stub().returns( + getClientRects: sinon.stub.returns([{ + bottom: 170 + height: 25 + left: 425 + right: 525 + top: 145 + width: 95 + }]) + ) + ) + @Paragraph = benv.require resolve __dirname, '../components/paragraph' + @Paragraph.__set__ 'Modifier', Modifier + @Paragraph.__set__ 'EditorState', EditorState + @Utils = benv.require resolve __dirname, '../utils' + @Paragraph.__set__ 'Utils', @Utils + Config = require '../utils/config.coffee' + @Paragraph.__set__ 'Config', Config + Nav = benv.requireWithJadeify( + resolve(__dirname, '../components/nav'), ['icons'] + ) + @Paragraph.__set__ 'Nav', React.createFactory Nav + InputUrl = benv.requireWithJadeify( + resolve(__dirname, '../components/input_url'), ['icons'] + ) + @Paragraph.__set__ 'InputUrl', React.createFactory InputUrl + @Paragraph.__set__ 'Utils.stickyControlsBox', sinon.stub().returns {top: 20, left: 40} + @Paragraph.__set__ 'Utils.getSelectionLocation', sinon.stub().returns({top: 40, left: 20}) + @Paragraph.__set__ 'Utils.stripGoogleStyles', @stripGoogleStyles = sinon.stub().returns('

hello

here again.

') + @leadParagraph = '

Here is the lead paragraph for this article.

' + @postscript = '

Illustration by Tomi Um.

' + @props = { + html: @postscript + placeholder: 'Postscript (optional)' + onChange: sinon.stub() + linked: true + } + @component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => setTimeout => + @component.stickyControlsBox = sinon.stub().returns {top: 20, left: 40} + @component.state.editorState.getSelection().isCollapsed = sinon.stub().returns false + selection = @component.state.editorState.getSelection() + newSelection = selection.merge({ + anchorKey: @component.state.editorState.getCurrentContent().getFirstBlock().key + anchorOffset: 0 + focusKey: @component.state.editorState.getCurrentContent().getFirstBlock().key + focusOffset: 12 + }) + newEditorState = EditorState.acceptSelection(@component.state.editorState, newSelection) + @component.onChange newEditorState + done() + + afterEach -> + benv.teardown() + + describe 'Render', -> + + it 'Shows a placeholder if provided and empty', -> + component = ReactDOMServer.renderToString React.createElement(@Paragraph, @props) + $(component).text().should.containEql 'Postscript (optional)' + + it 'Renders existing html', -> + $(ReactDOM.findDOMNode(@component)).text().should.containEql 'Illustration by Tomi Um.' + + it 'Renders existing link entities', -> + $(ReactDOM.findDOMNode(@component)).html().should.containEql '' + + describe 'Key commands', -> + + it 'Can toggle bold styles', -> + @component.setState = sinon.stub() + @component.handleKeyCommand('bold') + @component.setState.args[0][0].html.should.containEql 'Illustration' + + it 'Can toggle italic styles', -> + @component.setState = sinon.stub() + @component.handleKeyCommand('italic') + @component.setState.args[0][0].html.should.containEql 'Illustration' + + it 'Can toggle a link prompt', -> + @component.setState = sinon.stub() + @component.handleKeyCommand('link-prompt') + @component.setState.args[0][0].selectionTarget.should.eql { top: 20, left: 40 } + @component.setState.args[0][0].showUrlInput.should.eql true + + describe '#hasLinks', -> + + it 'returns true if props.linked', -> + component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => + haslinks = component.hasLinks() + haslinks.should.eql true + + it 'returns true if type is postscript', -> + @props.type = 'postscript' + @props.linked = null + component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => + haslinks = component.hasLinks() + haslinks.should.eql true + + it 'returns true if type is caption', -> + @props.type = 'caption' + @props.linked = null + component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => + haslinks = component.hasLinks() + haslinks.should.eql true + + describe 'Links', -> + + it '#confirmLink can save a link as html', -> + @component.confirmLink('http://artsy.net') + (@component.state.html.match(/
+ + it 'calls stripGoogleStyles', -> + @component.onChange = sinon.stub() + @component.onPaste('hello here again.', '

hello

here again.

') + @stripGoogleStyles.called.should.eql true + + xit 'strips linebreaks if linebreaks is false', -> + @component.setState linebreaks: false + + + it 'calls standardizeSpacing', -> + @Utils.standardizeSpacing = sinon.stub() + @component.onChange = sinon.stub() + @component.onPaste('hello here again.', '

hello

here again.

') + @Utils.standardizeSpacing.called.should.eql true From 7c6e81185883b2a13ec4927435eeea52dbaf0329 Mon Sep 17 00:00:00 2001 From: Eve Essex Date: Thu, 17 Aug 2017 15:00:36 -0400 Subject: [PATCH 7/9] better linebreak handling, more tests --- .../content2/section_list/index.styl | 3 + .../rich_text2/components/paragraph.coffee | 20 +++-- client/components/rich_text2/index.styl | 2 + .../rich_text2/test/paragraph.test.coffee | 78 +++++++++++++++++-- 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/client/apps/edit/components/content2/section_list/index.styl b/client/apps/edit/components/content2/section_list/index.styl index 3f8dd35bf..72534bf2e 100644 --- a/client/apps/edit/components/content2/section_list/index.styl +++ b/client/apps/edit/components/content2/section_list/index.styl @@ -10,6 +10,9 @@ .public-DraftEditorPlaceholder-root position absolute color gray-dark-color + top 27px + Garamond s23 + font-style italic .standard max-width standard-max-width diff --git a/client/components/rich_text2/components/paragraph.coffee b/client/components/rich_text2/components/paragraph.coffee index 65902899e..b1b009d45 100644 --- a/client/components/rich_text2/components/paragraph.coffee +++ b/client/components/rich_text2/components/paragraph.coffee @@ -1,5 +1,5 @@ # A basic paragraph component: supports bold and italic styles -# optionally allows links and linebreaks +# optionally allows links and stripping of linebreaks # or format in types 'postscript', 'caption' and 'lead paragraph' # Paragraph { @@ -9,7 +9,7 @@ # layout: article.layout # postscript: default=false # linked: default=false -# linebreaks: default=true +# stripLinebreaks: default=false # type: 'postscript' | 'caption' | 'lead paragraph' # } @@ -46,11 +46,11 @@ module.exports = React.createClass showNav: false urlValue: '' selectionTarget: null - linebreaks: @props.linebreaks or true componentDidMount: -> if $(@props.html).text().length html = Utils.standardizeSpacing @props.html + html = @stripLinebreaks(html) if @props.stripLinebreaks blocksFromHTML = @convertFromHTML(html) @setState html: html @@ -77,6 +77,7 @@ module.exports = React.createClass showNav: false convertFromHTML: (html) -> + html = @stripLinebreaks(html) if @props.stripLinebreaks blocksFromHTML = convertFromHTML({ htmlToEntity: (nodeName, node) -> if nodeName is 'a' @@ -106,7 +107,7 @@ module.exports = React.createClass return Array.from(available) handleKeyCommand: (e) -> - return 'handled' if @state.linebreaks is false and e is 'split-block' + return 'handled' if @props.stripLinebreaks is true and e is 'split-block' if e is 'link-prompt' @promptForLink() if @hasLinks() return 'handled' @@ -121,14 +122,19 @@ module.exports = React.createClass selection = Utils.getSelectionDetails(@state.editorState) @onChange RichUtils.toggleInlineStyle(@state.editorState, inlineStyle) + + stripLinebreaks: (html) -> + html = html.replace(/<\/p>

/g, '') + return html + onPaste: (text, html) -> { editorState } = @state unless html html = '

' + text + '
' html = Utils.standardizeSpacing html html = Utils.stripGoogleStyles html - unless @state.linebreaks - html = html.replace(/<\/p>

/g, '') + html = @stripLinebreaks(html) if @props.stripLinebreaks + html = html.replace(/<\/p>

/g, '') blocksFromHTML = @convertFromHTML html convertedHtml = blocksFromHTML.getBlocksAsArray().map (contentBlock) => unstyled = Utils.stripCharacterStyles contentBlock, true @@ -214,7 +220,7 @@ module.exports = React.createClass if !window.getSelection().isCollapsed location = Utils.getSelectionLocation $(ReactDOM.findDOMNode(@refs.editor)).offset() selectionTargetL = Config.inlineStyles(@props.type).length * 25 - selectionTargetL = selectionTargetL + 50 if @hasLinks() + selectionTargetL = selectionTargetL + 25 if @hasLinks() @setState showNav: true, selectionTarget: Utils.stickyControlsBox(location, -43, selectionTargetL) else @setState showNav: false diff --git a/client/components/rich_text2/index.styl b/client/components/rich_text2/index.styl index 8214e4872..e4c90d7e8 100644 --- a/client/components/rich_text2/index.styl +++ b/client/components/rich_text2/index.styl @@ -81,6 +81,8 @@ border none border-right 1px solid gray-darkest-color transition color .15s + &:last-child + border-right none &:hover color gray-color &[name='ITALIC'], diff --git a/client/components/rich_text2/test/paragraph.test.coffee b/client/components/rich_text2/test/paragraph.test.coffee index 76944e156..528e2c754 100644 --- a/client/components/rich_text2/test/paragraph.test.coffee +++ b/client/components/rich_text2/test/paragraph.test.coffee @@ -108,33 +108,99 @@ describe 'Rich Text: Paragraph', -> @component.setState.args[0][0].selectionTarget.should.eql { top: 20, left: 40 } @component.setState.args[0][0].showUrlInput.should.eql true - describe '#hasLinks', -> + describe 'Nav', -> - it 'returns true if props.linked', -> + it 'Prints Italic and Bold buttons by default', -> + @props.linked = null + component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "

")[0], => + component.setState showNav: true + $(ReactDOM.findDOMNode(component)).html().should.containEql '' + $(ReactDOM.findDOMNode(component)).html().should.not.containEql '' + + it 'Does not show bold if type is caption', -> + @props.linked = null + @props.type = 'caption' + component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => + component.setState showNav: true + $(ReactDOM.findDOMNode(component)).html().should.containEql '' + + it 'Can toggle bold styles', -> + @component.setState showNav: true + @component.setState = sinon.stub() + r.simulate.mouseDown r.find @component, 'BOLD' + @component.setState.args[0][0].html.should.containEql 'Illustration' + + it 'Can toggle italic styles', -> + @component.setState showNav: true + @component.setState = sinon.stub() + r.simulate.mouseDown r.find @component, 'ITALIC' + @component.setState.args[0][0].html.should.containEql 'Illustration' + + it 'Can toggle a link prompt', -> + @component.setState showNav: true + @component.setState = sinon.stub() + r.simulate.mouseDown r.find @component, 'link' + @component.setState.args[0][0].selectionTarget.should.eql { top: 20, left: 40 } + @component.setState.args[0][0].showUrlInput.should.eql true + + describe 'Links', -> + + it '#hasLinks returns true if props.linked', -> component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => haslinks = component.hasLinks() haslinks.should.eql true - it 'returns true if type is postscript', -> + it '#hasLinks returns true if type is postscript', -> @props.type = 'postscript' @props.linked = null component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => haslinks = component.hasLinks() haslinks.should.eql true - it 'returns true if type is caption', -> + it '#hasLinks returns true if type is caption', -> @props.type = 'caption' @props.linked = null component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => haslinks = component.hasLinks() haslinks.should.eql true - describe 'Links', -> - it '#confirmLink can save a link as html', -> @component.confirmLink('http://artsy.net') (@component.state.html.match(/
+ + it 'allows linebreaks by default', -> + @props.html = '

Here one paragraph.

Here is second paragraph

' + component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => + $(ReactDOM.findDOMNode(component)).find('p').length.should.eql 2 + + it 'strips linebreaks if props.stripLinebreaks', -> + @props.stripLinebreaks = true + @props.html = '

Here one paragraph.

Here is second paragraph

' + component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => + $(ReactDOM.findDOMNode(component)).find('p').length.should.eql 1 + + it 'interrupts key command for linebreak if props.stripLinebreaks', -> + @props.stripLinebreaks = true + @props.html = '

Here one paragraph.

Here is second paragraph

' + component = ReactDOM.render React.createElement(@Paragraph, @props), (@$el = $ "
")[0], => + keyResponse = component.handleKeyCommand('split-block') + keyResponse.should.eql 'handled' + describe '#onPaste', -> it 'calls stripGoogleStyles', -> From cbd42b277b7147172fc0de1f4caae529b1def842 Mon Sep 17 00:00:00 2001 From: Eve Essex Date: Thu, 17 Aug 2017 16:20:39 -0400 Subject: [PATCH 8/9] kill handleBackspace error --- client/apps/edit/components/content2/sections/text/index.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/apps/edit/components/content2/sections/text/index.coffee b/client/apps/edit/components/content2/sections/text/index.coffee index 07c51fc86..775711d9b 100644 --- a/client/apps/edit/components/content2/sections/text/index.coffee +++ b/client/apps/edit/components/content2/sections/text/index.coffee @@ -93,7 +93,7 @@ module.exports = React.createClass selection = Utils.getSelectionDetails(@state.editorState) # only merge a section if cursor is in first character of first block if selection.isFirstBlock and selection.anchorOffset is 0 and - @props.sections.models[@props.index - 1].get('type') is 'text' + @props.sections.models[@props.index - 1]?.get('type') is 'text' mergeIntoHTML = @props.sections.models[@props.index - 1].get('body') @props.sections.models[@props.index - 1].destroy() newHTML = mergeIntoHTML + @state.html From 3a2cb2874872e9546e99b5d4f3fa04764148cdff Mon Sep 17 00:00:00 2001 From: Eve Essex Date: Fri, 18 Aug 2017 13:01:23 -0400 Subject: [PATCH 9/9] merge conflicts, bump reaction version --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 529612c4a..24e16417c 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "deploy": "sh scripts/deploy.sh" }, "dependencies": { - "@artsy/reaction-force": "^0.3.1", + "@artsy/reaction-force": "^0.3.2", "@artsy/express-reloadable": "^1.0.2", "airtable": "^0.4.5", "artsy-backbone-mixins": "git://github.com/artsy/artsy-backbone-mixins.git", diff --git a/yarn.lock b/yarn.lock index 62cc9d29f..2aca27a5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8,9 +8,9 @@ dependencies: chokidar "^1.7.0" -"@artsy/reaction-force@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@artsy/reaction-force/-/reaction-force-0.3.1.tgz#fbbd92bd3c324afc6a32d96aba8f39be5173ae63" +"@artsy/reaction-force@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@artsy/reaction-force/-/reaction-force-0.3.2.tgz#aa3fee9c1bc57b76dcdfadeecf8e169d7bbfe46e" dependencies: "@storybook/addon-options" "^3.2.1" "@storybook/react" "^3.2.0" @@ -8105,7 +8105,7 @@ ua-parser@^0.3.5: dependencies: yamlparser ">=0.0.2" -uglify-js@2.x.x, uglify-js@^2.6.2, uglify-js@^2.8.29: +uglify-js@2.x.x, uglify-js@^2.4.19, uglify-js@^2.6.2, uglify-js@^2.8.29: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" dependencies: