Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit b114302

Browse files
devversionkara
authored andcommitted
feat(autocomplete): allow developers to specify amount of dropdown items. (#9307)
* Adds an attribute, which allows developer to overwrite the amount of items shown in the autocomplete dropdown. - When the dropdown doesn't fit into the possible space it will shrink accordingly (as same as before) - When the specified amount of items is higher than the current matches then it will shrink (as same as before) * Fixes the item height deviations between CSS and JavaScript. * Removed the unnecessary CSS for the dropdown height, because everything will be handled from the JS (same as before) * Added tests for the dropdown height calculation, which make sure that everything is now calculated properly. Fixes #9306. Closes #8751.
1 parent f7d6d10 commit b114302

File tree

4 files changed

+141
-13
lines changed

4 files changed

+141
-13
lines changed

src/components/autocomplete/autocomplete.scss

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
$autocomplete-option-height: 48px !default;
1+
// The default item height is also specified in the JavaScript.
2+
$md-autocomplete-item-height: 48px !default;
23

34
@keyframes md-autocomplete-list-out {
45
0% {
@@ -201,25 +202,24 @@ md-autocomplete {
201202
.md-virtual-repeat-container.md-autocomplete-suggestions-container {
202203
position: absolute;
203204
box-shadow: 0 2px 5px rgba(black, 0.25);
204-
height: 41px * 5.5;
205-
max-height: 41px * 5.5;
206205
z-index: $z-index-tooltip;
207206
}
208207

209208
.md-virtual-repeat-container.md-not-found {
210-
height: 48px;
209+
height: $md-autocomplete-item-height;
211210
}
212211

213212
.md-autocomplete-suggestions {
214213
margin: 0;
215214
list-style: none;
216215
padding: 0;
216+
217217
li {
218218
font-size: 14px;
219219
overflow: hidden;
220220
padding: 0 15px;
221-
line-height: $autocomplete-option-height;
222-
height: $autocomplete-option-height;
221+
line-height: $md-autocomplete-item-height;
222+
height: $md-autocomplete-item-height;
223223
transition: background 0.15s linear;
224224
margin: 0;
225225
white-space: nowrap;

src/components/autocomplete/autocomplete.spec.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,26 @@ describe('<md-autocomplete>', function() {
17981798

17991799
describe('dropdown position', function() {
18001800

1801+
var DEFAULT_MAX_ITEMS = 5;
1802+
var DEFAULT_ITEM_HEIGHT = 48;
1803+
1804+
var dropdownItems = DEFAULT_MAX_ITEMS;
1805+
1806+
/**
1807+
* Function to create fake matches with the given dropdown items.
1808+
* Useful when running tests against the dropdown max items calculations.
1809+
* @returns {Array} Fake matches.
1810+
*/
1811+
function fakeItemMatch() {
1812+
var matches = [];
1813+
1814+
for (var i = 0; i < dropdownItems; i++) {
1815+
matches.push('Item ' + i);
1816+
}
1817+
1818+
return matches;
1819+
}
1820+
18011821
it('should adjust the width when the window resizes', inject(function($timeout, $window) {
18021822
var scope = createScope();
18031823

@@ -1998,6 +2018,104 @@ describe('<md-autocomplete>', function() {
19982018
document.body.removeChild(parent[0]);
19992019
}));
20002020

2021+
it('should calculate the height from the default max items', inject(function($timeout) {
2022+
var scope = createScope();
2023+
2024+
scope.match = fakeItemMatch;
2025+
2026+
var template =
2027+
'<div>' +
2028+
'<md-autocomplete ' +
2029+
'md-search-text="searchText" ' +
2030+
'md-items="item in match(searchText)" ' +
2031+
'md-item-text="item" ' +
2032+
'md-min-length="0" ' +
2033+
'placeholder="placeholder">' +
2034+
'<span md-highlight-text="searchText">{{item}}</span>' +
2035+
'</md-autocomplete>' +
2036+
'</div>';
2037+
2038+
var parent = compile(template, scope);
2039+
var element = parent.find('md-autocomplete');
2040+
var ctrl = element.controller('mdAutocomplete');
2041+
2042+
// Add container to the DOM to be able to test the rect calculations.
2043+
document.body.appendChild(parent[0]);
2044+
2045+
$timeout.flush();
2046+
2047+
// Focus the autocomplete and trigger a query to be able to open the dropdown.
2048+
ctrl.focus();
2049+
scope.$apply('searchText = "Query 1"');
2050+
waitForVirtualRepeat(element);
2051+
2052+
var scrollContainer = document.body.querySelector('.md-virtual-repeat-container');
2053+
2054+
expect(scrollContainer).toBeTruthy();
2055+
expect(scrollContainer.style.maxHeight).toBe(DEFAULT_MAX_ITEMS * DEFAULT_ITEM_HEIGHT + 'px');
2056+
2057+
dropdownItems = 6;
2058+
2059+
// Trigger a new query to request an update of the items and dropdown.
2060+
scope.$apply('searchText = "Query 2"');
2061+
2062+
// The dropdown should not increase its height because of the new extra item.
2063+
expect(scrollContainer.style.maxHeight).toBe(DEFAULT_MAX_ITEMS * DEFAULT_ITEM_HEIGHT + 'px');
2064+
2065+
document.body.removeChild(parent[0]);
2066+
}));
2067+
2068+
it('should calculate its height from the specified max items', inject(function($timeout) {
2069+
var scope = createScope();
2070+
var maxDropdownItems = 2;
2071+
2072+
// Set the current dropdown items to the new maximum.
2073+
dropdownItems = maxDropdownItems;
2074+
scope.match = fakeItemMatch;
2075+
2076+
var template =
2077+
'<div>' +
2078+
'<md-autocomplete ' +
2079+
'md-search-text="searchText" ' +
2080+
'md-items="item in match(searchText)" ' +
2081+
'md-item-text="item" ' +
2082+
'md-min-length="0" ' +
2083+
'md-dropdown-items="' + maxDropdownItems +'"' +
2084+
'placeholder="placeholder">' +
2085+
'<span md-highlight-text="searchText">{{item}}</span>' +
2086+
'</md-autocomplete>' +
2087+
'</div>';
2088+
2089+
var parent = compile(template, scope);
2090+
var element = parent.find('md-autocomplete');
2091+
var ctrl = element.controller('mdAutocomplete');
2092+
2093+
// Add container to the DOM to be able to test the rect calculations.
2094+
document.body.appendChild(parent[0]);
2095+
2096+
$timeout.flush();
2097+
2098+
// Focus the autocomplete and trigger a query to be able to open the dropdown.
2099+
ctrl.focus();
2100+
scope.$apply('searchText = "Query 1"');
2101+
waitForVirtualRepeat(element);
2102+
2103+
var scrollContainer = document.body.querySelector('.md-virtual-repeat-container');
2104+
2105+
expect(scrollContainer).toBeTruthy();
2106+
expect(scrollContainer.style.maxHeight).toBe(maxDropdownItems * DEFAULT_ITEM_HEIGHT + 'px');
2107+
2108+
dropdownItems = 6;
2109+
2110+
// Trigger a new query to request an update of the items and dropdown.
2111+
scope.$apply('searchText = "Query 2"');
2112+
2113+
// The dropdown should not increase its height because of the new extra item.
2114+
expect(scrollContainer.style.maxHeight).toBe(maxDropdownItems * DEFAULT_ITEM_HEIGHT + 'px');
2115+
2116+
document.body.removeChild(parent[0]);
2117+
}));
2118+
20012119
});
20022120

20032121
describe('md-highlight-text', function() {

src/components/autocomplete/js/autocompleteController.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ angular
22
.module('material.components.autocomplete')
33
.controller('MdAutocompleteCtrl', MdAutocompleteCtrl);
44

5-
var ITEM_HEIGHT = 41,
6-
MAX_HEIGHT = 5.5 * ITEM_HEIGHT,
5+
var ITEM_HEIGHT = 48,
6+
MAX_ITEMS = 5,
77
MENU_PADDING = 8,
88
INPUT_PADDING = 2; // Padding provided by `md-input-container`
99

@@ -92,7 +92,12 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
9292
* @returns {*}
9393
*/
9494
function positionDropdown () {
95-
if (!elements) return $mdUtil.nextTick(positionDropdown, false, $scope);
95+
if (!elements) {
96+
return $mdUtil.nextTick(positionDropdown, false, $scope);
97+
}
98+
99+
var dropdownHeight = ($scope.dropdownItems || MAX_ITEMS) * ITEM_HEIGHT;
100+
96101
var hrect = elements.wrap.getBoundingClientRect(),
97102
vrect = elements.snap.getBoundingClientRect(),
98103
root = elements.root.getBoundingClientRect(),
@@ -112,14 +117,14 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
112117
minWidth: width + 'px',
113118
maxWidth: Math.max(hrect.right - root.left, root.right - hrect.left) - MENU_PADDING + 'px'
114119
};
115-
if (top > bot && root.height - hrect.bottom - MENU_PADDING < MAX_HEIGHT) {
120+
if (top > bot && root.height - hrect.bottom - MENU_PADDING < dropdownHeight) {
116121
styles.top = 'auto';
117122
styles.bottom = bot + 'px';
118-
styles.maxHeight = Math.min(MAX_HEIGHT, hrect.top - root.top - MENU_PADDING) + 'px';
123+
styles.maxHeight = Math.min(dropdownHeight, hrect.top - root.top - MENU_PADDING) + 'px';
119124
} else {
120125
styles.top = (top - offset) + 'px';
121126
styles.bottom = 'auto';
122-
styles.maxHeight = Math.min(MAX_HEIGHT, root.bottom + $mdUtil.scrollTop() - hrect.bottom - MENU_PADDING) + 'px';
127+
styles.maxHeight = Math.min(dropdownHeight, root.bottom + $mdUtil.scrollTop() - hrect.bottom - MENU_PADDING) + 'px';
123128
}
124129

125130
elements.$.scrollContainer.css(styles);

src/components/autocomplete/js/autocompleteDirective.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ angular
8282
* will select on case-insensitive match
8383
* @param {string=} md-escape-options Override escape key logic. Default is `blur clear`.<br/>
8484
* Options: `blur | clear`, `none`
85+
* @param {string=} md-dropdown-items Specifies the maximum amount of items to be shown in
86+
* the dropdown.<br/><br/>
87+
* When the dropdown doesn't fit into the viewport, the dropdown will shrink
88+
* as less as possible.
8589
*
8690
* @usage
8791
* ### Basic Example
@@ -165,7 +169,8 @@ function MdAutocomplete ($$mdSvgRegistry) {
165169
autoselect: '=?mdAutoselect',
166170
menuClass: '@?mdMenuClass',
167171
inputId: '@?mdInputId',
168-
escapeOptions: '@?mdEscapeOptions'
172+
escapeOptions: '@?mdEscapeOptions',
173+
dropdownItems: '=?mdDropdownItems'
169174
},
170175
compile: function(tElement, tAttrs) {
171176
var attributes = ['md-select-on-focus', 'md-no-asterisk', 'ng-trim'];

0 commit comments

Comments
 (0)