From 6dd9950d51d7071a484b77c96e34872796da3241 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Sat, 22 Oct 2016 20:30:43 +0200 Subject: [PATCH] feat(autocomplete): option to toggle the clear button * Adds a new attribute, which allows developers to explicitly show the clear button for all types of autocomplete's. Closes #4841. Closes #2727 --- src/components/autocomplete/autocomplete.scss | 32 +++++--- .../autocomplete/autocomplete.spec.js | 74 +++++++++++++++++++ .../autocomplete/js/autocompleteController.js | 8 +- .../autocomplete/js/autocompleteDirective.js | 48 +++++++++--- 4 files changed, 139 insertions(+), 23 deletions(-) diff --git a/src/components/autocomplete/autocomplete.scss b/src/components/autocomplete/autocomplete.scss index 86118f729f..c1762a69f9 100644 --- a/src/components/autocomplete/autocomplete.scss +++ b/src/components/autocomplete/autocomplete.scss @@ -1,5 +1,7 @@ // The default item height is also specified in the JavaScript. $md-autocomplete-item-height: 48px !default; +$md-autocomplete-clear-size: 30px !default; +$md-autocomplete-input-offset: 20px !default; @keyframes md-autocomplete-list-out { 0% { @@ -55,14 +57,24 @@ md-autocomplete { md-autocomplete-wrap { height: auto; } - button { - position: absolute; - top: auto; - bottom: 0; - right: 0; - width: 30px; - height: 30px; + + .md-show-clear-button { + + button { + display: block; + position: absolute; + right: 0; + top: $md-autocomplete-input-offset; + width: $md-autocomplete-clear-size; + height: $md-autocomplete-clear-size; + } + + input { + // Add padding to the end of the input to avoid overlapping with the clear button. + @include rtl-prop(padding-right, padding-left, $md-autocomplete-clear-size, 0); + } } + } md-autocomplete-wrap { @@ -130,12 +142,12 @@ md-autocomplete { line-height: 40px; height: 40px; } - button { + .md-show-clear-button button { position: relative; line-height: 20px; text-align: center; - width: 30px; - height: 30px; + width: $md-autocomplete-clear-size; + height: $md-autocomplete-clear-size; cursor: pointer; border: none; border-radius: 50%; diff --git a/src/components/autocomplete/autocomplete.spec.js b/src/components/autocomplete/autocomplete.spec.js index 8baedf9fed..72893353c6 100644 --- a/src/components/autocomplete/autocomplete.spec.js +++ b/src/components/autocomplete/autocomplete.spec.js @@ -1337,6 +1337,80 @@ describe('', function() { }); + describe('clear button', function() { + + it('should show the clear button for inset autocomplete', function() { + var scope = createScope(); + + var template = + ' ' + + '{{item.display}}' + + ''; + + var element = compile(template, scope); + var ctrl = element.controller('mdAutocomplete'); + var wrapEl = element.find('md-autocomplete-wrap'); + + expect(ctrl.scope.clearButton).toBe(true); + expect(wrapEl).toHaveClass('md-show-clear-button'); + }); + + it('should not show the clear button for floating label autocomplete', function() { + var scope = createScope(); + + var template = + ' ' + + '{{item.display}}' + + ''; + + var element = compile(template, scope); + var ctrl = element.controller('mdAutocomplete'); + var wrapEl = element.find('md-autocomplete-wrap'); + + expect(ctrl.scope.clearButton).toBe(false); + expect(wrapEl).not.toHaveClass('md-show-clear-button'); + }); + + it('should allow developers to toggle the clear button', function() { + + var scope = createScope(); + + var template = + '' + + '{{item.display}}' + + ''; + + var element = compile(template, scope); + var ctrl = element.controller('mdAutocomplete'); + var wrapEl = element.find('md-autocomplete-wrap'); + + expect(ctrl.scope.clearButton).toBeFalsy(); + expect(wrapEl).not.toHaveClass('md-show-clear-button'); + + scope.$apply('showButton = true'); + + expect(ctrl.scope.clearButton).toBe(true); + expect(wrapEl).toHaveClass('md-show-clear-button'); + }); + + }); + describe('xss prevention', function() { it('should not allow html to slip through', inject(function($timeout, $material) { diff --git a/src/components/autocomplete/js/autocompleteController.js b/src/components/autocomplete/js/autocompleteController.js index b4ea3eae51..a7be7da260 100644 --- a/src/components/autocomplete/js/autocompleteController.js +++ b/src/components/autocomplete/js/autocompleteController.js @@ -73,7 +73,13 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming, * Initialize the controller, setup watchers, gather elements */ function init () { - $mdUtil.initOptionalProperties($scope, $attrs, { searchText: '', selectedItem: null }); + + $mdUtil.initOptionalProperties($scope, $attrs, { + searchText: '', + selectedItem: null, + clearButton: false + }); + $mdTheming($element); configureWatchers(); $mdUtil.nextTick(function () { diff --git a/src/components/autocomplete/js/autocompleteDirective.js b/src/components/autocomplete/js/autocompleteDirective.js index 96d4dcad22..b81ab764ee 100644 --- a/src/components/autocomplete/js/autocompleteDirective.js +++ b/src/components/autocomplete/js/autocompleteDirective.js @@ -87,6 +87,7 @@ angular * make suggestions * @param {number=} md-delay Specifies the amount of time (in milliseconds) to wait before looking * for results + * @param {boolean=} md-clear-button Whether the clear button for the autocomplete input should show up or not. * @param {boolean=} md-autofocus If true, the autocomplete will be automatically focused when a `$mdDialog`, * `$mdBottomsheet` or `$mdSidenav`, which contains the autocomplete, is opening.

* Also the autocomplete will immediately focus the input element. @@ -151,6 +152,17 @@ angular * In this example, our code utilizes `md-item-template` and `md-not-found` to specify the * different parts that make up our component. * + * ### Clear button for the input + * By default, for floating label autocomplete's the clear button is not showing up + * ([See specs](https://material.google.com/components/text-fields.html#text-fields-auto-complete-text-field)) + * + * Nevertheless, developers are able to explicitly toggle the clear button for all types of autocomplete's. + * + * + * + * + * + * * ### Example with validation * *
@@ -232,7 +244,8 @@ function MdAutocomplete ($$mdSvgRegistry) { inputId: '@?mdInputId', escapeOptions: '@?mdEscapeOptions', dropdownItems: '=?mdDropdownItems', - dropdownPosition: '@?mdDropdownPosition' + dropdownPosition: '@?mdDropdownPosition', + clearButton: '=?mdClearButton' }, compile: function(tElement, tAttrs) { var attributes = ['md-select-on-focus', 'md-no-asterisk', 'ng-trim', 'ng-pattern']; @@ -250,6 +263,11 @@ function MdAutocomplete ($$mdSvgRegistry) { // Retrieve the state of using a md-not-found template by using our attribute, which will // be added to the element in the template function. ctrl.hasNotFound = !!element.attr('md-has-not-found'); + + // By default the inset autocomplete should show the clear button when not explicitly overwritten. + if (!angular.isDefined(attrs.mdClearButton) && !scope.floatingLabel) { + scope.clearButton = true; + } } }, template: function (element, attr) { @@ -269,8 +287,11 @@ function MdAutocomplete ($$mdSvgRegistry) { return '\ \ + ng-class="{ \'md-whiteframe-z1\': !floatingLabel, \ + \'md-menu-showing\': !$mdAutocompleteCtrl.hidden, \ + \'md-show-clear-button\': !!clearButton }">\ ' + getInputElement() + '\ + ' + getClearButton() + '\ \ - \ - \ - Clear\ - \ - '; + aria-expanded="{{!$mdAutocompleteCtrl.hidden}}"/>'; } } + + function getClearButton() { + return '' + + ''; + } } }; }