Skip to content

Commit 77f7144

Browse files
committed
Merge remote-tracking branch 'upstream/master' into offline-compiler
2 parents c98dc4a + 3c74ae0 commit 77f7144

29 files changed

+963
-693
lines changed

GETTING_STARTED.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,17 @@ bootstrap(MyAppComponent, [
176176
directives: [MD_CARD_DIRECTIVES, MD_BUTTON_DIRECTIVES, MdIcon],
177177
providers: [MdIconRegistry]
178178
```
179-
179+
180+
- Add the icon package to the list of Material components in your `system-config.ts`:
181+
182+
**src/system-config.ts**
183+
```ts
184+
// put the names of any of your Material components here
185+
const materialPkgs:string[] = [
186+
...
187+
'icon'
188+
];
189+
```
180190

181191

182192

angular-cli-build.js

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,6 @@ const Angular2App = require('angular-cli/lib/broccoli/angular2-app');
99
const Funnel = require('broccoli-funnel');
1010
const MergeTree = require('broccoli-merge-trees');
1111
const autoPrefixerTree = require('broccoli-autoprefixer');
12-
const BroccoliPlugin = require('broccoli-plugin');
13-
14-
15-
class NgcBroccoli extends BroccoliPlugin {
16-
constructor(subTree, options) {
17-
super([subTree], {});
18-
this._options = options;
19-
}
20-
21-
build() {
22-
require('reflect-metadata');
23-
const tsc = require('@angular/tsc-wrapped');
24-
const CodeGenerator = require('@angular/compiler-cli').CodeGenerator;
25-
26-
function codegen(ngOptions, program, host) {
27-
return CodeGenerator.create(ngOptions, program, host).codegen();
28-
}
29-
30-
return tsc.main('./src/demo-app', path.relative(process.cwd(), this.inputPaths[0], './src/demo-app'), codegen)
31-
.then(function (code) {
32-
console.log(123, code);
33-
})
34-
.catch(function (e) {
35-
console.error(e.stack);
36-
console.error('Compilation failed');
37-
process.exit(1);
38-
});
39-
}
40-
}
4112

4213

4314
module.exports = function(defaults) {
@@ -52,20 +23,7 @@ module.exports = function(defaults) {
5223
// Include the scss sources in the output for when we publish.
5324
const scssSources = new Funnel('src', {include: ['**/*.scss']});
5425

55-
let trees = new MergeTree([appTree, cssAutoprefixed, scssSources], { overwrite: true });
56-
57-
if (process.env['MD_NGC']) {
58-
trees = new MergeTree([
59-
trees,
60-
new Funnel('src', {include: ['demo-app/**/*'], destDir: 'src'}),
61-
]);
62-
63-
trees = new NgcBroccoli(trees, {
64-
project: 'src/demo-app'
65-
});
66-
}
67-
68-
return trees;
26+
return new MergeTree([appTree, cssAutoprefixed, scssSources], { overwrite: true });
6927
};
7028

7129

@@ -121,8 +79,6 @@ function _buildAppTree(defaults) {
12179
inputNode = _buildE2EAppInputTree();
12280
sourceDir = 'src/e2e-app';
12381
break;
124-
125-
case 'ngc':
12682
default:
12783
inputNode = _buildDemoAppInputTree();
12884
sourceDir = 'src/demo-app';

e2e/components/tabs/tabs.e2e.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import ElementArrayFinder = protractor.ElementArrayFinder;
2+
import ElementFinder = protractor.ElementFinder;
3+
4+
describe('tabs', () => {
5+
describe('basic behavior', () => {
6+
let tabGroup: ElementFinder;
7+
let tabLabels: ElementArrayFinder;
8+
let tabBodies: ElementArrayFinder;
9+
10+
beforeEach(() => {
11+
browser.get('/tabs');
12+
tabGroup = element(by.css('md-tab-group'));
13+
tabLabels = element.all(by.css('.md-tab-label'));
14+
tabBodies = element.all(by.css('.md-tab-body'));
15+
});
16+
17+
it('should change tabs when the label is clicked', () => {
18+
tabLabels.get(1).click();
19+
expect(getActiveStates(tabLabels)).toEqual([false, true, false]);
20+
expect(getActiveStates(tabBodies)).toEqual([false, true, false]);
21+
22+
tabLabels.get(0).click();
23+
expect(getActiveStates(tabLabels)).toEqual([true, false, false]);
24+
expect(getActiveStates(tabBodies)).toEqual([true, false, false]);
25+
});
26+
27+
it('should change focus with keyboard interaction', () => {
28+
tabLabels.get(0).click();
29+
expect(getFocusStates(tabLabels)).toEqual([true, false, false]);
30+
31+
pressKey(protractor.Key.RIGHT);
32+
expect(getFocusStates(tabLabels)).toEqual([false, true, false]);
33+
34+
pressKey(protractor.Key.RIGHT);
35+
expect(getFocusStates(tabLabels)).toEqual([false, false, true]);
36+
37+
pressKey(protractor.Key.RIGHT);
38+
expect(getFocusStates(tabLabels)).toEqual([false, false, true]);
39+
40+
pressKey(protractor.Key.LEFT);
41+
expect(getFocusStates(tabLabels)).toEqual([false, true, false]);
42+
43+
pressKey(protractor.Key.LEFT);
44+
expect(getFocusStates(tabLabels)).toEqual([true, false, false]);
45+
46+
pressKey(protractor.Key.LEFT);
47+
expect(getFocusStates(tabLabels)).toEqual([true, false, false]);
48+
});
49+
});
50+
});
51+
52+
/**
53+
* A helper function to perform the sendKey action
54+
* @param key
55+
*/
56+
function pressKey(key: string) {
57+
browser.actions().sendKeys(key).perform();
58+
}
59+
60+
/**
61+
* Returns an array of true/false that represents the focus states of the provided elements
62+
* @param elements
63+
* @returns {webdriver.promise.Promise<Promise<boolean>[]>|webdriver.promise.Promise<T[]>}
64+
*/
65+
function getFocusStates(elements: ElementArrayFinder) {
66+
return elements.map(element => {
67+
return element.getText().then(elementText => {
68+
return browser.driver.switchTo().activeElement().getText().then(activeText => {
69+
return activeText === elementText;
70+
});
71+
});
72+
});
73+
}
74+
75+
/**
76+
* Returns an array of true/false that represents the active states for the provided elements
77+
* @param elements
78+
* @returns {webdriver.promise.Promise<Promise<boolean>[]>|webdriver.promise.Promise<T[]>}
79+
*/
80+
function getActiveStates(elements: ElementArrayFinder) {
81+
return getClassStates(elements, 'md-active');
82+
}
83+
84+
/**
85+
* Returns an array of true/false values that represents whether the provided className is on
86+
* each element
87+
* @param elements
88+
* @param className
89+
* @returns {webdriver.promise.Promise<Promise<boolean>[]>|webdriver.promise.Promise<T[]>}
90+
*/
91+
function getClassStates(elements: ElementArrayFinder, className: string) {
92+
return elements.map(element => {
93+
return element.getAttribute('class').then(classes => {
94+
return classes.split(/ +/g).indexOf(className) >= 0;
95+
});
96+
});
97+
}

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@
4848
"broccoli-autoprefixer": "^4.1.0",
4949
"broccoli-funnel": "^1.0.1",
5050
"broccoli-merge-trees": "^1.1.1",
51-
"broccoli-plugin": "^1.2.1",
52-
"broccoli-replace": "^0.12.0",
5351
"browserstacktunnel-wrapper": "^1.4.2",
5452
"conventional-changelog": "^1.1.0",
5553
"ember-cli-inject-live-reload": "^1.4.0",

scripts/ci/forbidden-identifiers.js

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
const child_process = require('child_process');
1010
const fs = require('fs');
11+
const path = require('path');
1112

1213
const exec = function(cmd) {
1314
return new Promise(function(resolve, reject) {
@@ -33,7 +34,16 @@ const blocked_statements = [
3334
'from \\\'rxjs/Rx\\\'',
3435
];
3536

37+
// Retrieves all scope packages dynamically from the source.
38+
// Developers shouldn't be able to import from other scope packages by using relative paths.
39+
const componentFolders = fs
40+
.readdirSync(`${__dirname}/../../src/components`)
41+
.map(componentName => `src/components/${componentName}`);
42+
43+
const scopePackages = ['src/core'].concat(componentFolders);
44+
3645
const blockedRegex = new RegExp(blocked_statements.join('|'), 'g');
46+
const importRegex = /from\s+'(.*)';/g;
3747

3848
/**
3949
* Find the fork point between HEAD of the current branch, and master.
@@ -85,6 +95,63 @@ function findChangedFiles() {
8595
});
8696
}
8797

98+
/**
99+
* Checks the line for any relative imports of a scope package, which should be imported by using
100+
* the scope package name instead of the relative path.
101+
* @param fileName Filename to validate the path
102+
* @param line Line to be checked.
103+
*/
104+
function isRelativeScopeImport(fileName, line) {
105+
let importMatch = importRegex.exec(line);
106+
107+
// We have to reset the last index of the import regex, otherwise we
108+
// would have incorrect matches in the next execution.
109+
importRegex.lastIndex = 0;
110+
111+
// Skip the check if the current line doesn't match any imports.
112+
if (!importMatch) {
113+
return false;
114+
}
115+
116+
let importPath = importMatch[1];
117+
118+
// Skip the check when the import doesn't start with a dot, because the line
119+
// isn't importing any file relatively. Also applies to scope packages starting
120+
// with `@`.
121+
if (!importPath.startsWith('.')) {
122+
return false;
123+
}
124+
125+
// Transform the relative import path into an absolute path.
126+
importPath = path.resolve(path.dirname(fileName), importPath);
127+
128+
let currentScope = findScope(fileName);
129+
let importScope = findScope(importPath);
130+
131+
if (currentScope !== importScope) {
132+
// Transforms the invalid import statement into a correct import statement, which uses the scope package.
133+
let scopeImport = `@angular2-material/${path.basename(importScope)}/${path.relative(importScope, importPath)}`;
134+
135+
return {
136+
currentScope: currentScope,
137+
importScope: importScope,
138+
invalidStatement: importMatch[0],
139+
scopeImportPath: scopeImport
140+
}
141+
}
142+
143+
return false;
144+
145+
function findScope(filePath) {
146+
// Normalize the filePath as well, to avoid issues with the different environments path delimiter.
147+
filePath = path.normalize(filePath);
148+
149+
// Iterate through all scopes and try to find them inside of the file path.
150+
return scopePackages.filter(scope => filePath.indexOf(path.normalize(scope)) !== -1).pop();
151+
}
152+
153+
}
154+
88155

89156
// Find all files, check for errors, and output every errors found.
90157
findChangedFiles()
@@ -116,23 +183,35 @@ findChangedFiles()
116183
content.forEach(function(line) {
117184
ln++;
118185

119-
let m = line.match(blockedRegex);
120-
if (m) {
186+
let matches = line.match(blockedRegex);
187+
let isScopeImport = isRelativeScopeImport(fileName, line);
188+
189+
if (matches || isScopeImport) {
121190
// Accumulate all errors at once.
122-
errors.push({
191+
let error = {
123192
fileName: fileName,
124193
lineNumber: ln,
125-
statement: m[0]
126-
});
194+
statement: matches && matches[0] || isScopeImport.invalidStatement
195+
};
196+
197+
if (isScopeImport) {
198+
error.message =
199+
' You are using an import statement, which imports a file from another scope package.\n' +
200+
` Please import the file by using the following path: ${isScopeImport.scopeImportPath}`;
201+
}
202+
203+
errors.push(error);
127204
}
128205
});
129206
return errors;
130207
}, []);
131208
})
132209
.then(errors => {
133210
if (errors.length > 0) {
134-
console.error('Error: You are using a statement in your commit, which is not allowed.');
211+
console.error('Error: You are using a statement in your commit, which is not allowed.\n');
212+
135213
errors.forEach(entry => {
214+
if (entry.message) console.error(entry.message);
136215
console.error(` ${entry.fileName}@${entry.lineNumber}, Statement: ${entry.statement}.\n`);
137216
});
138217

scripts/release/stage-release.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ set -exu
99

1010

1111
# Clear dist/ and deploy/ so that we guarantee there are no stale artifacts.
12-
#rm -rf ./dist
13-
#rm -rf ./deploy
14-
#
15-
## Perform a build with the modified tsconfig.json.
16-
#ng build
12+
rm -rf ./dist
13+
rm -rf ./deploy
14+
15+
# Perform a build with the modified tsconfig.json.
16+
ng build
1717

1818
# Inline the css and html into the component ts files.
1919
npm run inline-resources

src/components/menu/menu.html

Whitespace-only changes.

src/components/menu/menu.scss

Whitespace-only changes.

src/components/menu/menu.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {
2+
it,
3+
describe,
4+
expect,
5+
beforeEach,
6+
inject,
7+
} from '@angular/core/testing';
8+
import {TestComponentBuilder} from '@angular/compiler/testing';
9+
import {Component} from '@angular/core';
10+
11+
import {MD_MENU_DIRECTIVES} from './menu';
12+
13+
describe('MdMenu', () => {
14+
let builder: TestComponentBuilder;
15+
16+
beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
17+
builder = tcb;
18+
}));
19+
20+
it('should add and remove focus class on focus/blur', () => {
21+
var template = ``;
22+
return builder.overrideTemplate(TestList, template)
23+
.createAsync(TestList).then((fixture) => {
24+
expect(true).toBe(true);
25+
});
26+
});
27+
28+
});
29+
30+
@Component({
31+
selector: 'test-menu',
32+
template: ``,
33+
directives: [MD_MENU_DIRECTIVES]
34+
})
35+
class TestList {}

src/components/menu/menu.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {Component, ViewEncapsulation} from '@angular/core';
2+
3+
@Component({
4+
moduleId: module.id,
5+
selector: 'md-menu',
6+
templateUrl: 'menu.html',
7+
styleUrls: ['menu.css'],
8+
encapsulation: ViewEncapsulation.None
9+
})
10+
export class MdMenu {}
11+
12+
export const MD_MENU_DIRECTIVES = [MdMenu];
13+

0 commit comments

Comments
 (0)