From c6feb245bd3e706bc430e7735825c1ee80f8eece Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Wed, 25 Jul 2018 18:06:12 +0200 Subject: [PATCH] Keep entered text when pressing Escape. --- .../components/src/form-token-field/index.js | 30 +++++++++++++------ .../src/form-token-field/test/index.js | 24 +++++++++++++-- .../test/lib/token-field-wrapper.js | 3 +- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/packages/components/src/form-token-field/index.js b/packages/components/src/form-token-field/index.js index 7e3a6d87e9d9e8..b662442cd839fe 100644 --- a/packages/components/src/form-token-field/index.js +++ b/packages/components/src/form-token-field/index.js @@ -27,6 +27,7 @@ const initialState = { isExpanded: false, selectedSuggestionIndex: -1, selectedSuggestionScroll: false, + showSuggestions: false, }; class FormTokenField extends Component { @@ -133,7 +134,7 @@ class FormTokenField extends Component { } break; case ESCAPE: - preventDefault = this.handleEscapeKey(); + preventDefault = this.handleEscapeKey( event ); event.stopPropagation(); break; default: @@ -193,6 +194,9 @@ class FormTokenField extends Component { const separator = this.props.tokenizeOnSpace ? /[ ,\t]+/ : /[,\t]+/; const items = text.split( separator ); const tokenValue = last( items ) || ''; + const inputHasMinimumChars = tokenValue.trim().length > 1; + const matchingSuggestions = this.getMatchingSuggestions( tokenValue ); + const hasVisibleSuggestions = inputHasMinimumChars && !! matchingSuggestions.length; if ( items.length > 1 ) { this.addNewTokens( items.slice( 0, -1 ) ); @@ -203,15 +207,16 @@ class FormTokenField extends Component { selectedSuggestionIndex: -1, selectedSuggestionScroll: false, isExpanded: false, + showSuggestions: false, } ); this.props.onInputChange( tokenValue ); - const inputHasMinimumChars = tokenValue.trim().length > 1; if ( inputHasMinimumChars ) { - const matchingSuggestions = this.getMatchingSuggestions( tokenValue ); - - this.setState( { isExpanded: !! matchingSuggestions.length } ); + this.setState( { + isExpanded: hasVisibleSuggestions, + showSuggestions: hasVisibleSuggestions, + } ); if ( !! matchingSuggestions.length ) { this.props.debouncedSpeak( sprintf( _n( @@ -289,8 +294,14 @@ class FormTokenField extends Component { return true; // preventDefault } - handleEscapeKey() { - this.setState( initialState ); + handleEscapeKey( event ) { + this.setState( { + incompleteTokenValue: event.target.value, + isExpanded: false, + selectedSuggestionIndex: -1, + selectedSuggestionScroll: false, + showSuggestions: false, + } ); return true; // preventDefault } @@ -379,6 +390,8 @@ class FormTokenField extends Component { incompleteTokenValue: '', selectedSuggestionIndex: -1, selectedSuggestionScroll: false, + isExpanded: false, + showSuggestions: false, } ); if ( this.state.isActive ) { @@ -527,6 +540,7 @@ class FormTokenField extends Component { instanceId, className, } = this.props; + const { showSuggestions } = this.state; const classes = classnames( className, 'components-form-token-field', { 'is-active': this.state.isActive, 'is-disabled': disabled, @@ -537,8 +551,6 @@ class FormTokenField extends Component { tabIndex: '-1', }; const matchingSuggestions = this.getMatchingSuggestions(); - const inputHasMinimumChars = this.state.incompleteTokenValue.trim().length > 1; - const showSuggestions = inputHasMinimumChars && !! matchingSuggestions.length; if ( ! disabled ) { tokenFieldProps = Object.assign( {}, tokenFieldProps, { diff --git a/packages/components/src/form-token-field/test/index.js b/packages/components/src/form-token-field/test/index.js index ea476f8c6997f1..c7e6d6e67ee753 100644 --- a/packages/components/src/form-token-field/test/index.js +++ b/packages/components/src/form-token-field/test/index.js @@ -118,12 +118,16 @@ describe( 'FormTokenField', function() { describe( 'displaying tokens', function() { it( 'should render default tokens', function() { + wrapper.setState( { + showSuggestions: true, + } ); expect( wrapper.state.tokens ).toEqual( [ 'foo', 'bar' ] ); } ); it( 'should display tokens with escaped special characters properly', function() { wrapper.setState( { tokens: fixtures.specialTokens.textEscaped, + showSuggestions: true, } ); expect( getTokensHTML() ).toEqual( fixtures.specialTokens.htmlEscaped ); } ); @@ -137,6 +141,7 @@ describe( 'FormTokenField', function() { // through unescaped to the HTML. wrapper.setState( { tokens: fixtures.specialTokens.textUnescaped, + showSuggestions: true, } ); expect( getTokensHTML() ).toEqual( fixtures.specialTokens.htmlUnescaped ); } ); @@ -144,6 +149,9 @@ describe( 'FormTokenField', function() { describe( 'suggestions', function() { it( 'should not render suggestions unless we type at least two characters', function() { + wrapper.setState( { + showSuggestions: true, + } ); expect( getSuggestionsText() ).toEqual( [] ); setText( 'th' ); expect( getSuggestionsText() ).toEqual( fixtures.matchingSuggestions.th ); @@ -157,23 +165,28 @@ describe( 'FormTokenField', function() { } ); it( 'suggestions that begin with match are boosted', function() { + wrapper.setState( { + showSuggestions: true, + } ); setText( 'so' ); expect( getSuggestionsText() ).toEqual( fixtures.matchingSuggestions.so ); } ); it( 'should match against the unescaped values of suggestions with special characters', function() { - setText( '& S' ); wrapper.setState( { tokenSuggestions: fixtures.specialSuggestions.textUnescaped, + showSuggestions: true, } ); + setText( '& S' ); expect( getSuggestionsText() ).toEqual( fixtures.specialSuggestions.matchAmpersandUnescaped ); } ); it( 'should match against the unescaped values of suggestions with special characters (including spaces)', function() { - setText( 's &' ); wrapper.setState( { tokenSuggestions: fixtures.specialSuggestions.textUnescaped, + showSuggestions: true, } ); + setText( 's &' ); expect( getSuggestionsText() ).toEqual( fixtures.specialSuggestions.matchAmpersandSequence ); } ); @@ -181,16 +194,23 @@ describe( 'FormTokenField', function() { setText( 'amp' ); wrapper.setState( { tokenSuggestions: fixtures.specialSuggestions.textUnescaped, + showSuggestions: true, } ); expect( getSuggestionsText() ).toEqual( fixtures.specialSuggestions.matchAmpersandEscaped ); } ); it( 'should match suggestions even with trailing spaces', function() { + wrapper.setState( { + showSuggestions: true, + } ); setText( ' at ' ); expect( getSuggestionsText() ).toEqual( fixtures.matchingSuggestions.at ); } ); it( 'should manage the selected suggestion based on both keyboard and mouse events', function() { + wrapper.setState( { + showSuggestions: true, + } ); setText( 'th' ); expect( getSuggestionsText() ).toEqual( fixtures.matchingSuggestions.th ); expect( getSelectedSuggestion() ).toBe( null ); diff --git a/packages/components/src/form-token-field/test/lib/token-field-wrapper.js b/packages/components/src/form-token-field/test/lib/token-field-wrapper.js index 1e3fb2811a8348..bb12a6894b634d 100644 --- a/packages/components/src/form-token-field/test/lib/token-field-wrapper.js +++ b/packages/components/src/form-token-field/test/lib/token-field-wrapper.js @@ -26,6 +26,7 @@ class TokenFieldWrapper extends Component { this.state = { tokenSuggestions: suggestions, tokens: Object.freeze( [ 'foo', 'bar' ] ), + showSuggestions: false, }; this.onTokensChange = this.onTokensChange.bind( this ); } @@ -33,7 +34,7 @@ class TokenFieldWrapper extends Component { render() { return (