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

Commit f9948c6

Browse files
author
Robert Messerle
committed
feat(autocomplete): added initial files for autocomplete
feat(autocomplete): adds accessibility support TODO: wire aria-activedescendant as a watched property with a value of the active listItem id refactor(autocomplete): re-organizes aria changes to live in controller chore(autocomplete): removes temporary comments refactor(autocomplete): renames ambiguous directive refactor(styles): renames `visuallyhidden` to `visually-hidden` for consistency. refactor(autocomplete): removes unused template file refactor(autocomplete): uses `$mdConstant` rather than hard-coded values refactor(autocomplete): various cleanup based on feedback refactor(autocomplete): cleans up scope confusion refactor(autocomplete): includes theming, updates directive name to prevent potential conflicts
1 parent fb3623a commit f9948c6

17 files changed

+613
-14
lines changed

config/karma.conf.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module.exports = function(config) {
1010
// demos in the tests, and Karma doesn't support advanced
1111
// globbing.
1212
'src/components/*/*.js',
13-
'src/components/tabs/js/*.js'
13+
'src/components/*/js/*.js'
1414
];
1515

1616
var COMPILED_SRC = [

docs/app/css/style.css

-11
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,6 @@ code:not(.highlight) {
8383
-webkit-font-smoothing: auto;
8484
}
8585

86-
.visuallyhidden {
87-
border: 0;
88-
clip: rect(0 0 0 0);
89-
height: 1px;
90-
margin: -1px;
91-
overflow: hidden;
92-
padding: 0;
93-
position: absolute;
94-
text-transform: none;
95-
width: 1px;
96-
}
9786
.md-sidenav-inner {
9887
background: #fff;
9988
}

docs/app/partials/menu-link.tmpl.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<md-button ng-class="{'active' : isSelected()}"
22
ng-href="#{{section.url}}">
33
{{section | humanizeDoc}}
4-
<span class="visuallyhidden"
4+
<span class="visually-hidden"
55
ng-if="isSelected()">
66
current page
77
</span>

docs/app/partials/menu-toggle.tmpl.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
{{section.name}}
77
<span aria-hidden="true" class="md-toggle-icon"
88
ng-class="{'toggled' : isOpen()}"></span>
9-
<span class="visuallyhidden">
9+
<span class="visually-hidden">
1010
Toggle {{isOpen()? 'expanded' : 'collapsed'}}
1111
</span>
1212
</md-button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
md-autocomplete {
2+
background: '{{background-50}}';
3+
button {
4+
background: '{{background-200}}';
5+
}
6+
ul {
7+
background: '{{background-50}}';
8+
li {
9+
border-top: 1px solid '{{background-400}}';
10+
color: '{{background-900}}';
11+
.highlight {
12+
color: '{{background-600}}';
13+
}
14+
&:hover,
15+
&.selected {
16+
background: '{{background-200}}';
17+
}
18+
}
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
(function () {
2+
'use strict';
3+
/**
4+
* @ngdoc module
5+
* @name material.components.autocomplete
6+
*/
7+
/*
8+
* @see js folder for autocomplete implementation
9+
*/
10+
angular.module('material.components.autocomplete', [ 'material.core' ]);
11+
})();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
@keyframes md-autocomplete-list-out {
2+
0% {
3+
animation-timing-function: linear;
4+
}
5+
50% {
6+
opacity: 0;
7+
height: 40px;
8+
animation-timing-function: ease-in;
9+
}
10+
100% {
11+
height: 0;
12+
opacity: 0;
13+
}
14+
}
15+
@keyframes md-autocomplete-list-in {
16+
0% {
17+
opacity: 0;
18+
height: 0;
19+
animation-timing-function: ease-out;
20+
}
21+
50% {
22+
opacity: 0;
23+
height: 40px;
24+
}
25+
100% {
26+
opacity: 1;
27+
height: 40px;
28+
}
29+
}
30+
md-content {
31+
overflow: visible;
32+
}
33+
md-autocomplete {
34+
box-shadow: 0 2px 5px rgba(black, 0.25);
35+
border-radius: 2px;
36+
display: block;
37+
height: 40px;
38+
position: relative;
39+
overflow: visible;
40+
41+
md-autocomplete-wrap {
42+
display: block;
43+
position: relative;
44+
overflow: visible;
45+
height: 40px;
46+
47+
md-progress-linear {
48+
position: absolute;
49+
bottom: 0; left: 0; width: 100%;
50+
height: 3px;
51+
transition: none;
52+
53+
.md-container {
54+
transition: none;
55+
top: auto;
56+
height: 3px;
57+
}
58+
&.ng-enter {
59+
transition: opacity 0.15s linear;
60+
&.ng-enter-active {
61+
opacity: 1;
62+
}
63+
}
64+
&.ng-leave {
65+
transition: opacity 0.15s linear;
66+
&.ng-leave-active {
67+
opacity: 0;
68+
}
69+
}
70+
}
71+
}
72+
input {
73+
position: absolute;
74+
left: 0;
75+
top: 0;
76+
width: 100%;
77+
box-sizing: border-box;
78+
border: none;
79+
box-shadow: none;
80+
padding: 0 15px;
81+
font-size: 14px;
82+
line-height: 40px;
83+
height: 40px;
84+
outline: none;
85+
z-index: 2;
86+
background: transparent;
87+
}
88+
button {
89+
position: absolute;
90+
top: 10px;
91+
right: 10px;
92+
line-height: 20px;
93+
z-index: 2;
94+
text-align: center;
95+
width: 20px;
96+
height: 20px;
97+
cursor: pointer;
98+
border: none;
99+
border-radius: 50%;
100+
padding: 0;
101+
font-size: 12px;
102+
&.ng-enter {
103+
transform: scale(0);
104+
transition: transform 0.15s ease-out;
105+
&.ng-enter-active {
106+
transform: scale(1);
107+
}
108+
}
109+
&.ng-leave {
110+
transition: transform 0.15s ease-out;
111+
&.ng-leave-active {
112+
transform: scale(0);
113+
}
114+
}
115+
}
116+
ul {
117+
position: absolute;
118+
top: 100%;
119+
left: 0;
120+
right: 0;
121+
box-shadow: 0 2px 5px rgba(black, 0.25);
122+
margin: 0;
123+
list-style: none;
124+
padding: 0;
125+
overflow: auto;
126+
max-height: 41px * 5.5;
127+
li {
128+
border-top: 1px solid #ddd;
129+
padding: 0 15px;
130+
line-height: 40px;
131+
font-size: 14px;
132+
overflow: hidden;
133+
height: 40px;
134+
transition: background 0.15s linear;
135+
cursor: pointer;
136+
margin: 0;
137+
&.ng-enter {
138+
animation: md-autocomplete-list-in 0.2s;
139+
}
140+
&.ng-leave {
141+
animation: md-autocomplete-list-out 0.2s;
142+
}
143+
}
144+
}
145+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
describe('<md-autocomplete>', function() {
2+
3+
beforeEach(module('material.components.autocomplete'));
4+
5+
function compile (str, scope) {
6+
var container;
7+
inject(function ($compile) {
8+
container = $compile(str)(scope);
9+
scope.$apply();
10+
});
11+
return container;
12+
}
13+
14+
function createScope () {
15+
var scope;
16+
var items = ['foo', 'bar', 'baz'].map(function (item) { return { display: item }; });
17+
inject(function ($rootScope) {
18+
scope = $rootScope.$new();
19+
scope.match = function (term) {
20+
return items.filter(function (item) {
21+
return item.display.indexOf(term) === 0;
22+
});
23+
};
24+
scope.searchText = '';
25+
scope.selectedItem = null;
26+
});
27+
return scope;
28+
}
29+
30+
describe('basic functionality', function () {
31+
it('should fail', inject(function($timeout, $mdConstant) {
32+
var scope = createScope();
33+
var template = '\
34+
<md-autocomplete\
35+
md-selected-item="selectedItem"\
36+
md-search-text="searchText"\
37+
md-items="item in match(searchText)"\
38+
md-item-text="display"\
39+
placeholder="placeholder">\
40+
<span md-highlight-text="searchText">{{item.display}}</span>\
41+
</md-autocomplete>';
42+
var element = compile(template, scope);
43+
var ctrl = element.controller('mdAutocomplete');
44+
45+
expect(scope.searchText).toBe('');
46+
expect(scope.selectedItem).toBe(null);
47+
48+
element.scope().searchText = 'fo';
49+
ctrl.keydown({});
50+
element.scope().$apply();
51+
$timeout.flush();
52+
53+
expect(scope.searchText).toBe('fo');
54+
expect(scope.match(scope.searchText).length).toBe(1);
55+
expect(element.find('li').length).toBe(1);
56+
57+
ctrl.keydown({ keyCode: $mdConstant.KEY_CODE.DOWN_ARROW, preventDefault: angular.noop });
58+
ctrl.keydown({ keyCode: $mdConstant.KEY_CODE.ENTER, preventDefault: angular.noop });
59+
scope.$apply();
60+
expect(scope.searchText).toBe('foo');
61+
}));
62+
});
63+
64+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<div ng-app="inputBasicDemo" ng-controller="DemoCtrl as ctrl" layout="column">
2+
3+
<md-content class="md-padding" layout="column">
4+
5+
<md-autocomplete
6+
md-selected-item="ctrl.selectedItem"
7+
md-search-text="ctrl.searchText"
8+
md-items="item in ctrl.getItems(ctrl.searchText)"
9+
md-item-text="display"
10+
placeholder="select a state...">
11+
<span md-highlight-text="ctrl.searchText">{{item.display}}</span>
12+
</md-autocomplete>
13+
14+
<p>Current search term: "{{ctrl.searchText}}"</p>
15+
16+
</md-content>
17+
18+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
angular
2+
.module('autocompleteDemo', ['ngMaterial'])
3+
.controller('DemoCtrl', DemoCtrl);
4+
5+
function DemoCtrl ($timeout, $q) {
6+
var self = this;
7+
this.selectedItem = null;
8+
this.searchText = null;
9+
this.states = 'Alabama, Alaska, Arizona, Arkansas, California, Colorado, Connecticut, Deleware,\
10+
Florida, Georgia, Hawaii, Idaho, Illanois, Indiana, Iowa, Kansas, Kentucky, Louisiana,\
11+
Maine, Maryland, Massachusetts, Michigan, Minnesota, Mississippi, Missouri, Montana,\
12+
Nebraska, Nevada, New Hampshire, New Jersey, New Mexico, New York, North Carolina,\
13+
North Dakota, Ohio, Oklahoma, Oregon, Pennsylvania, Rhode Island, South Carolina,\
14+
South Dakota, Tennessee, Texas, Utah, Vermont, Virginia, Washington, West Virginia,\
15+
Wisconsin, Wyoming'.split(/, +/g).map(function (state) { return { value: state.toLowerCase(), display: state }; });
16+
this.getItems = getItems;
17+
18+
function getItems (query) {
19+
if (!query) return [];
20+
var deferred = $q.defer();
21+
var lowercaseQuery = angular.lowercase(query);
22+
var results = self.states.filter(function (state) { return state.value.indexOf(lowercaseQuery) === 0; });
23+
$timeout(function () { deferred.resolve(results); }, Math.random() * 1000, false);
24+
return deferred.promise;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
md-content {
2+
min-height: 500px;
3+
}

0 commit comments

Comments
 (0)