From 3bddad6857bb071d8ccd4d67f3a76810b6d2666e Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 1 Jun 2017 10:49:45 +0100 Subject: [PATCH 1/8] Chrome: Allow adding, removing tags from posts --- .eslintrc.json | 1 + components/form-token-field/style.scss | 4 +- components/index.js | 1 + editor/sidebar/post-taxonomies/style.scss | 5 + .../sidebar/post-taxonomies/tags-selector.js | 108 ++++++++++++++++-- 5 files changed, 105 insertions(+), 14 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 4fe4696451600..763603865944f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -8,6 +8,7 @@ ], "env": { "browser": false, + "es6": true, "node": true, "mocha": true }, diff --git a/components/form-token-field/style.scss b/components/form-token-field/style.scss index 21dba50794231..2c496f6413928 100644 --- a/components/form-token-field/style.scss +++ b/components/form-token-field/style.scss @@ -127,7 +127,7 @@ input[type="text"].components-form-token-field__input { } .components-form-token-field__token-text, -.components-form-token-field__remove-token { +.components-form-token-field__remove-token.components-icon-button { display: inline-block; line-height: 24px; background: $dark-gray-500; @@ -142,7 +142,7 @@ input[type="text"].components-form-token-field__input { text-overflow: ellipsis; } -.components-form-token-field__remove-token { +.components-form-token-field__remove-token.components-icon-button { cursor: pointer; border-radius: 0 4px 4px 0; padding: 0 2px; diff --git a/components/index.js b/components/index.js index 1582340f6b380..5a7e9876cf3f8 100644 --- a/components/index.js +++ b/components/index.js @@ -1,6 +1,7 @@ export { default as Button } from './button'; export { default as Dashicon } from './dashicon'; export { default as FormToggle } from './form-toggle'; +export { default as FormTokenField } from './form-token-field'; export { default as HtmlEmbed } from './html-embed'; export { default as IconButton } from './icon-button'; export { default as Panel } from './panel'; diff --git a/editor/sidebar/post-taxonomies/style.scss b/editor/sidebar/post-taxonomies/style.scss index 79756bf2ec3a4..e6331b7cf749d 100644 --- a/editor/sidebar/post-taxonomies/style.scss +++ b/editor/sidebar/post-taxonomies/style.scss @@ -1,3 +1,8 @@ .editor-post-taxonomies__tags-selector { margin-top: 10px; + + .spinner { + float: none; + margin: 0; + } } diff --git a/editor/sidebar/post-taxonomies/tags-selector.js b/editor/sidebar/post-taxonomies/tags-selector.js index f0918e3ce6445..568e9761a498e 100644 --- a/editor/sidebar/post-taxonomies/tags-selector.js +++ b/editor/sidebar/post-taxonomies/tags-selector.js @@ -1,36 +1,120 @@ +/** + * External dependencies + */ +import { connect } from 'react-redux'; +import { unescape, find } from 'lodash'; + /** * WordPress dependencies */ import { Component } from 'element'; -import FormTokenField from 'components/form-token-field'; +import { Spinner, FormTokenField } from 'components'; +import { getEditedPostAttribute } from '../../selectors'; +import { editPost } from '../../actions'; + +const DEFAULT_TAGS_QUERY = { + number: 1000, + order_by: 'count', + order: 'DESC', +}; +const MAX_TERMS_SUGGESTIONS = 20; class TagsSelector extends Component { constructor() { super( ...arguments ); - this.onTokensChange = this.onTokensChange.bind( this ); + this.onTagsChange = this.onTagsChange.bind( this ); this.state = { - tokens: [ 'React', 'Vue' ], + loading: true, + availableTags: [], }; } - onTokensChange( value ) { - this.setState( { tokens: value } ); + componentDidMount() { + this.fetchTagsRequest = new wp.api.collections.Tags().fetch( DEFAULT_TAGS_QUERY ) + .done( ( tags ) => { + this.setState( { + loading: false, + availableTags: tags, + } ); + } ) + .fail( ( xhr ) => { + if ( xhr.statusText === 'abort' ) { + return; + } + this.setState( { + loading: false, + } ); + } ); + } + + componentWillUnmount() { + if ( this.fetchTagsRequest ) { + this.fetchTagsRequest.abort(); + } + } + + onTagsChange( tagNames ) { + const newTagNames = tagNames.filter( ( tagName ) => + ! find( this.state.availableTags, ( tag ) => tag.name === tagName ) + ); + const tagNamesToIds = ( names, availableTags ) => { + return names + .map( ( tagName ) => + find( availableTags, ( tag ) => tag.name === tagName ).id + ); + }; + + if ( newTagNames.length === 0 ) { + return this.props.onUpdateTags( tagNamesToIds( tagNames, this.state.availableTags ) ); + } + const createTag = ( tagName ) => new wp.api.models.Tag( { name: tagName } ).save(); + Promise + .all( newTagNames.map( createTag ) ) + .then( ( newTags ) => { + const newAvailableTags = this.state.availableTags.concat( newTags ); + this.setState( { availableTags: newAvailableTags } ); + return this.props.onUpdateTags( tagNamesToIds( tagNames, newAvailableTags ) ); + } ); } render() { - const suggestions = [ 'React', 'Vue', 'Angular', 'Cycle', 'PReact', 'Inferno' ]; + const { tags = [] } = this.props; + const { loading, availableTags } = this.state; + const selectedTags = tags.map( ( tagId ) => { + const tagObject = find( this.state.availableTags, ( tag ) => tag.id === tagId ); + return tagObject ? tagObject.name : ''; + } ); + const tagNames = availableTags.map( ( tag ) => tag.name ); return (
- + { loading && } + { ! loading && + + }
); } } -export default TagsSelector; +export default connect( + ( state ) => { + return { + tags: getEditedPostAttribute( state, 'tags' ), + }; + }, + ( dispatch ) => { + return { + onUpdateTags( tags ) { + dispatch( editPost( { tags } ) ); + }, + }; + } +)( TagsSelector ); From 4042170ade06e02da33d7e4538a6a63fd375d1ea Mon Sep 17 00:00:00 2001 From: Joen Asmussen Date: Thu, 1 Jun 2017 12:27:17 +0200 Subject: [PATCH 2/8] Polish the token chips a bit. --- components/form-token-field/style.scss | 28 +++++++++++++------------- components/form-token-field/token.js | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/components/form-token-field/style.scss b/components/form-token-field/style.scss index 2c496f6413928..f979edfc3a114 100644 --- a/components/form-token-field/style.scss +++ b/components/form-token-field/style.scss @@ -4,8 +4,9 @@ margin: 0; padding: 0; background-color: $white; + border-radius: 4px; border: 1px solid $light-gray-500; - color: $dark-gray-800; + color: $dark-gray-700; cursor: text; transition: all .15s ease-in-out; @@ -28,7 +29,7 @@ display: flex; flex-wrap: wrap; align-items: flex-start; - padding: 5px 14px 5px 0; + padding: 4px; } // Token input @@ -37,13 +38,13 @@ input[type="text"].components-form-token-field__input { width: auto; max-width: 100%; margin: 2px 0 2px 8px; - padding: 0 0 0 6px; + padding: 0; line-height: 24px; background: inherit; border: 0; outline: none; font-family: inherit; - font-size: 14px; + font-size: $default-font-size; color: $dark-gray-800; box-shadow: none; @@ -54,10 +55,10 @@ input[type="text"].components-form-token-field__input { // Tokens .components-form-token-field__token { - font-size: 14px; + font-size: $default-font-size; display: flex; - margin: 2px 0 2px 8px; - color: $white; + margin: 2px 4px 2px 0; + color: $dark-gray-700; overflow: hidden; &.is-success { @@ -130,13 +131,13 @@ input[type="text"].components-form-token-field__input { .components-form-token-field__remove-token.components-icon-button { display: inline-block; line-height: 24px; - background: $dark-gray-500; + background: $light-gray-500; transition: all .2s cubic-bezier( .4, 1, .4, 1 ); } .components-form-token-field__token-text { - border-radius: 4px 0 0 4px; - padding: 0 4px 0 6px; + border-radius: 12px 0 0 12px; + padding: 0 4px 0 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -144,15 +145,14 @@ input[type="text"].components-form-token-field__input { .components-form-token-field__remove-token.components-icon-button { cursor: pointer; - border-radius: 0 4px 4px 0; + border-radius: 0 12px 12px 0; padding: 0 2px; font-size: 10px; - color: $light-gray-500; + color: $dark-gray-500; line-height: 10px; &:hover { - color: white; - background: $dark-gray-600; + color: $dark-gray-700; } } diff --git a/components/form-token-field/token.js b/components/form-token-field/token.js index f42d46cb0759d..e16734d413435 100644 --- a/components/form-token-field/token.js +++ b/components/form-token-field/token.js @@ -43,7 +43,7 @@ function Token( { From deed57faedbc0cbc9eed559f66b11d2ab94c7610 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 1 Jun 2017 11:56:13 +0100 Subject: [PATCH 3/8] Chrome: Optimistic update to the selected tags --- .../sidebar/post-taxonomies/tags-selector.js | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/editor/sidebar/post-taxonomies/tags-selector.js b/editor/sidebar/post-taxonomies/tags-selector.js index 568e9761a498e..ded442e42c634 100644 --- a/editor/sidebar/post-taxonomies/tags-selector.js +++ b/editor/sidebar/post-taxonomies/tags-selector.js @@ -26,6 +26,7 @@ class TagsSelector extends Component { this.state = { loading: true, availableTags: [], + selectedTags: [], }; } @@ -36,6 +37,7 @@ class TagsSelector extends Component { loading: false, availableTags: tags, } ); + this.updateSelectedTags( this.props.tags ); } ) .fail( ( xhr ) => { if ( xhr.statusText === 'abort' ) { @@ -53,7 +55,24 @@ class TagsSelector extends Component { } } + componentWillReceiveProps( newProps ) { + if ( newProps.tags !== this.props.tags ) { + this.updateSelectedTags( newProps.tags ); + } + } + + updateSelectedTags( tags = [] ) { + const selectedTags = tags.map( ( tagId ) => { + const tagObject = find( this.state.availableTags, ( tag ) => tag.id === tagId ); + return tagObject ? tagObject.name : ''; + } ); + this.setState( { + selectedTags, + } ); + } + onTagsChange( tagNames ) { + this.setState( { selectedTags: tagNames } ); const newTagNames = tagNames.filter( ( tagName ) => ! find( this.state.availableTags, ( tag ) => tag.name === tagName ) ); @@ -78,12 +97,7 @@ class TagsSelector extends Component { } render() { - const { tags = [] } = this.props; - const { loading, availableTags } = this.state; - const selectedTags = tags.map( ( tagId ) => { - const tagObject = find( this.state.availableTags, ( tag ) => tag.id === tagId ); - return tagObject ? tagObject.name : ''; - } ); + const { loading, availableTags, selectedTags } = this.state; const tagNames = availableTags.map( ( tag ) => tag.name ); return ( From 197c19b17c638a6a7541c72f2b7a02f99530cd10 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 1 Jun 2017 12:01:41 +0100 Subject: [PATCH 4/8] Tests: Removing slow token field tests --- components/form-token-field/test/index.js | 95 ----------------------- 1 file changed, 95 deletions(-) diff --git a/components/form-token-field/test/index.js b/components/form-token-field/test/index.js index 0d0dceaf1d077..82585f702075b 100644 --- a/components/form-token-field/test/index.js +++ b/components/form-token-field/test/index.js @@ -197,36 +197,6 @@ describe( 'FormTokenField', function() { setText( ' at ' ); expect( getSuggestionsText() ).to.deep.equal( fixtures.matchingSuggestions.at ); } ); - - it( 'should manage the selected suggestion based on both keyboard and mouse events', test( function() { - // We need a high timeout here to accomodate Travis CI - this.timeout( 10000 ); - - setText( 't' ); - expect( getSuggestionsText() ).to.deep.equal( fixtures.matchingSuggestions.t ); - expect( getSelectedSuggestion() ).to.equal( null ); - sendKeyDown( keyCodes.downArrow ); // 'the' - expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'he' ] ); - sendKeyDown( keyCodes.downArrow ); // 'to' - expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'o' ] ); - - const hoverSuggestion = tokenFieldNode.find( '.components-form-token-field__suggestion' ).at( 5 ); // 'it' - expect( getSuggestionNodeText( hoverSuggestion ) ).to.deep.equal( [ 'i', 't' ] ); - - // before sending a hover event, we need to wait for - // SuggestionList#_scrollingIntoView to become false - this.clock.tick( 100 ); - - hoverSuggestion.simulate( 'mouseEnter' ); - expect( getSelectedSuggestion() ).to.deep.equal( [ 'i', 't' ] ); - sendKeyDown( keyCodes.upArrow ); - expect( getSelectedSuggestion() ).to.deep.equal( [ 'wi', 't', 'h' ] ); - sendKeyDown( keyCodes.upArrow ); - expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'his' ] ); - hoverSuggestion.simulate( 'click' ); - expect( getSelectedSuggestion() ).to.equal( null ); - expect( getTokensHTML() ).to.deep.equal( [ 'foo', 'bar', 'it' ] ); - } ) ); } ); describe( 'adding tokens', function() { @@ -323,25 +293,6 @@ describe( 'FormTokenField', function() { ); } ) ); - it( 'should add the suggested token when the (non-blank) input field loses focus', test( function() { - testOnBlur( - 't', // initialText - true, // selectSuggestion - [ 't', 'o' ], // expectedSuggestion - [ 'foo', 'bar', 'to' ] // expectedTokens - ); - } ) ); - - it( 'should not add the suggested token when the (blank) input field loses focus', test( function() { - testOnBlur( - '', // initialText - true, // selectSuggestion - 'of', // expectedSuggestion - [ 'foo', 'bar' ], // expectedTokens - this.clock - ); - } ) ); - it( 'should not lose focus when a suggestion is clicked', test( function() { // prevents regression of https://github.com/Automattic/wp-calypso/issues/1884 @@ -362,52 +313,6 @@ describe( 'FormTokenField', function() { sendKeyDown( keyCodes.tab ); expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'baz', 'quux', 'bar' ] ); } ); - - it( 'should add tokens from the selected matching suggestion using Tab', function() { - setText( 't' ); - expect( getSelectedSuggestion() ).to.equal( null ); - sendKeyDown( keyCodes.downArrow ); // 'the' - expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'he' ] ); - sendKeyDown( keyCodes.downArrow ); // 'to' - expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'o' ] ); - sendKeyDown( keyCodes.tab ); - expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'bar', 'to' ] ); - expect( getSelectedSuggestion() ).to.equal( null ); - } ); - - it( 'should add tokens from the selected matching suggestion using Enter', function() { - setText( 't' ); - expect( getSelectedSuggestion() ).to.equal( null ); - sendKeyDown( keyCodes.downArrow ); // 'the' - expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'he' ] ); - sendKeyDown( keyCodes.downArrow ); // 'to' - expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'o' ] ); - sendKeyDown( keyCodes.enter ); - expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'bar', 'to' ] ); - expect( getSelectedSuggestion() ).to.equal( null ); - } ); - - it( 'should add tokens from the selected suggestion using Tab', function() { - expect( getSelectedSuggestion() ).to.equal( null ); - sendKeyDown( keyCodes.downArrow ); // 'the' - expect( getSelectedSuggestion() ).to.equal( 'the' ); - sendKeyDown( keyCodes.downArrow ); // 'of' - expect( getSelectedSuggestion() ).to.equal( 'of' ); - sendKeyDown( keyCodes.tab ); - expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'bar', 'of' ] ); - expect( getSelectedSuggestion() ).to.equal( null ); - } ); - - it( 'should add tokens from the selected suggestion using Enter', function() { - expect( getSelectedSuggestion() ).to.equal( null ); - sendKeyDown( keyCodes.downArrow ); // 'the' - expect( getSelectedSuggestion() ).to.equal( 'the' ); - sendKeyDown( keyCodes.downArrow ); // 'of' - expect( getSelectedSuggestion() ).to.equal( 'of' ); - sendKeyDown( keyCodes.enter ); - expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'bar', 'of' ] ); - expect( getSelectedSuggestion() ).to.equal( null ); - } ); } ); describe( 'adding multiple tokens when pasting', function() { From 0fc2c3e0b5ecfbf020457dab9b06591978664d52 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 1 Jun 2017 12:04:45 +0100 Subject: [PATCH 5/8] Components: Fix style indentation --- components/form-token-field/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/form-token-field/style.scss b/components/form-token-field/style.scss index f979edfc3a114..29413b21e6b6c 100644 --- a/components/form-token-field/style.scss +++ b/components/form-token-field/style.scss @@ -4,7 +4,7 @@ margin: 0; padding: 0; background-color: $white; - border-radius: 4px; + border-radius: 4px; border: 1px solid $light-gray-500; color: $dark-gray-700; cursor: text; From 8f20d1d4509592554b7a7b31e12e64c1845996c7 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 1 Jun 2017 16:58:02 +0100 Subject: [PATCH 6/8] Chrome: Fix the tags selector query --- editor/sidebar/post-taxonomies/tags-selector.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/sidebar/post-taxonomies/tags-selector.js b/editor/sidebar/post-taxonomies/tags-selector.js index ded442e42c634..3692a129c65cc 100644 --- a/editor/sidebar/post-taxonomies/tags-selector.js +++ b/editor/sidebar/post-taxonomies/tags-selector.js @@ -13,8 +13,8 @@ import { getEditedPostAttribute } from '../../selectors'; import { editPost } from '../../actions'; const DEFAULT_TAGS_QUERY = { - number: 1000, - order_by: 'count', + number: -1, + orderby: 'count', order: 'DESC', }; const MAX_TERMS_SUGGESTIONS = 20; From bbb9eaf3fdd3e566bc22f1095cd38e8395f79de1 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 1 Jun 2017 17:03:47 +0100 Subject: [PATCH 7/8] Chrome: Disable the tags input when loading --- editor/sidebar/post-taxonomies/style.scss | 5 ----- .../sidebar/post-taxonomies/tags-selector.js | 20 +++++++++---------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/editor/sidebar/post-taxonomies/style.scss b/editor/sidebar/post-taxonomies/style.scss index e6331b7cf749d..79756bf2ec3a4 100644 --- a/editor/sidebar/post-taxonomies/style.scss +++ b/editor/sidebar/post-taxonomies/style.scss @@ -1,8 +1,3 @@ .editor-post-taxonomies__tags-selector { margin-top: 10px; - - .spinner { - float: none; - margin: 0; - } } diff --git a/editor/sidebar/post-taxonomies/tags-selector.js b/editor/sidebar/post-taxonomies/tags-selector.js index 3692a129c65cc..9d1af1ac29cc7 100644 --- a/editor/sidebar/post-taxonomies/tags-selector.js +++ b/editor/sidebar/post-taxonomies/tags-selector.js @@ -8,7 +8,7 @@ import { unescape, find } from 'lodash'; * WordPress dependencies */ import { Component } from 'element'; -import { Spinner, FormTokenField } from 'components'; +import { FormTokenField } from 'components'; import { getEditedPostAttribute } from '../../selectors'; import { editPost } from '../../actions'; @@ -102,16 +102,14 @@ class TagsSelector extends Component { return (
- { loading && } - { ! loading && - - } +
); } From 0905da46a9bf57638748dd398ec5f2056bccf0dc Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 1 Jun 2017 17:29:20 +0100 Subject: [PATCH 8/8] Revert "Tests: Removing slow token field tests" This reverts commit d384b451149a6d971bece019591fc6c655a83572. --- components/form-token-field/test/index.js | 95 +++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/components/form-token-field/test/index.js b/components/form-token-field/test/index.js index 82585f702075b..0d0dceaf1d077 100644 --- a/components/form-token-field/test/index.js +++ b/components/form-token-field/test/index.js @@ -197,6 +197,36 @@ describe( 'FormTokenField', function() { setText( ' at ' ); expect( getSuggestionsText() ).to.deep.equal( fixtures.matchingSuggestions.at ); } ); + + it( 'should manage the selected suggestion based on both keyboard and mouse events', test( function() { + // We need a high timeout here to accomodate Travis CI + this.timeout( 10000 ); + + setText( 't' ); + expect( getSuggestionsText() ).to.deep.equal( fixtures.matchingSuggestions.t ); + expect( getSelectedSuggestion() ).to.equal( null ); + sendKeyDown( keyCodes.downArrow ); // 'the' + expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'he' ] ); + sendKeyDown( keyCodes.downArrow ); // 'to' + expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'o' ] ); + + const hoverSuggestion = tokenFieldNode.find( '.components-form-token-field__suggestion' ).at( 5 ); // 'it' + expect( getSuggestionNodeText( hoverSuggestion ) ).to.deep.equal( [ 'i', 't' ] ); + + // before sending a hover event, we need to wait for + // SuggestionList#_scrollingIntoView to become false + this.clock.tick( 100 ); + + hoverSuggestion.simulate( 'mouseEnter' ); + expect( getSelectedSuggestion() ).to.deep.equal( [ 'i', 't' ] ); + sendKeyDown( keyCodes.upArrow ); + expect( getSelectedSuggestion() ).to.deep.equal( [ 'wi', 't', 'h' ] ); + sendKeyDown( keyCodes.upArrow ); + expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'his' ] ); + hoverSuggestion.simulate( 'click' ); + expect( getSelectedSuggestion() ).to.equal( null ); + expect( getTokensHTML() ).to.deep.equal( [ 'foo', 'bar', 'it' ] ); + } ) ); } ); describe( 'adding tokens', function() { @@ -293,6 +323,25 @@ describe( 'FormTokenField', function() { ); } ) ); + it( 'should add the suggested token when the (non-blank) input field loses focus', test( function() { + testOnBlur( + 't', // initialText + true, // selectSuggestion + [ 't', 'o' ], // expectedSuggestion + [ 'foo', 'bar', 'to' ] // expectedTokens + ); + } ) ); + + it( 'should not add the suggested token when the (blank) input field loses focus', test( function() { + testOnBlur( + '', // initialText + true, // selectSuggestion + 'of', // expectedSuggestion + [ 'foo', 'bar' ], // expectedTokens + this.clock + ); + } ) ); + it( 'should not lose focus when a suggestion is clicked', test( function() { // prevents regression of https://github.com/Automattic/wp-calypso/issues/1884 @@ -313,6 +362,52 @@ describe( 'FormTokenField', function() { sendKeyDown( keyCodes.tab ); expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'baz', 'quux', 'bar' ] ); } ); + + it( 'should add tokens from the selected matching suggestion using Tab', function() { + setText( 't' ); + expect( getSelectedSuggestion() ).to.equal( null ); + sendKeyDown( keyCodes.downArrow ); // 'the' + expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'he' ] ); + sendKeyDown( keyCodes.downArrow ); // 'to' + expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'o' ] ); + sendKeyDown( keyCodes.tab ); + expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'bar', 'to' ] ); + expect( getSelectedSuggestion() ).to.equal( null ); + } ); + + it( 'should add tokens from the selected matching suggestion using Enter', function() { + setText( 't' ); + expect( getSelectedSuggestion() ).to.equal( null ); + sendKeyDown( keyCodes.downArrow ); // 'the' + expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'he' ] ); + sendKeyDown( keyCodes.downArrow ); // 'to' + expect( getSelectedSuggestion() ).to.deep.equal( [ 't', 'o' ] ); + sendKeyDown( keyCodes.enter ); + expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'bar', 'to' ] ); + expect( getSelectedSuggestion() ).to.equal( null ); + } ); + + it( 'should add tokens from the selected suggestion using Tab', function() { + expect( getSelectedSuggestion() ).to.equal( null ); + sendKeyDown( keyCodes.downArrow ); // 'the' + expect( getSelectedSuggestion() ).to.equal( 'the' ); + sendKeyDown( keyCodes.downArrow ); // 'of' + expect( getSelectedSuggestion() ).to.equal( 'of' ); + sendKeyDown( keyCodes.tab ); + expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'bar', 'of' ] ); + expect( getSelectedSuggestion() ).to.equal( null ); + } ); + + it( 'should add tokens from the selected suggestion using Enter', function() { + expect( getSelectedSuggestion() ).to.equal( null ); + sendKeyDown( keyCodes.downArrow ); // 'the' + expect( getSelectedSuggestion() ).to.equal( 'the' ); + sendKeyDown( keyCodes.downArrow ); // 'of' + expect( getSelectedSuggestion() ).to.equal( 'of' ); + sendKeyDown( keyCodes.enter ); + expect( wrapper.state( 'tokens' ) ).to.deep.equal( [ 'foo', 'bar', 'of' ] ); + expect( getSelectedSuggestion() ).to.equal( null ); + } ); } ); describe( 'adding multiple tokens when pasting', function() {