Skip to content

Commit 3e78ab2

Browse files
committed
Merge remote-tracking branch 'upstream/master' into snav-focus
2 parents ab0e985 + 26eb7ce commit 3e78ab2

File tree

83 files changed

+1966
-421
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+1966
-421
lines changed

GETTING_STARTED.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ Add HammerJS to your application via [npm](https://www.npmjs.com/package/hammerj
4646
(such as the [Google CDN](https://developers.google.com/speed/libraries/#hammerjs)), or served
4747
directly from your app.
4848

49+
#### If you want to include HammerJS from npm, you can install it:
50+
51+
```bash
52+
npm install --save hammerjs
53+
npm install --save-dev @types/hammerjs
54+
```
55+
56+
Import HammerJS on your app's module.
57+
58+
**src/app/app.module.ts**
59+
```ts
60+
import 'hammerjs';
61+
```
62+
4963
## Configuring SystemJS
5064
If your project is using SystemJS for module loading, you will need to add `@angular/material`
5165
to the SystemJS configuration:

docs/theming-your-components.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#Theming your custom components
1+
# Theming your custom components
22
In order to style your own components with Angular Material's tooling, the component's styles must be defined with Sass.
33

44
## Using `@mixin` to automatically apply a theme
@@ -12,7 +12,7 @@ We can better theming our custom components adding a `@mixin` function to its th
1212

1313
All you need is to create a `@mixin` function in the custom-component-theme.scss
1414

15-
```sass
15+
```scss
1616
// Import all the tools needed to customize the theme and extract parts of it
1717
@import '~@angular/material/core/theming/all-theme';
1818

@@ -29,11 +29,11 @@ All you need is to create a `@mixin` function in the custom-component-theme.scss
2929
}
3030
}
3131
```
32-
Now you just have have to call the `@mixin` function to apply the theme:
32+
Now you just have to call the `@mixin` function to apply the theme:
3333

34-
```sass
34+
```scss
3535
// Import a pre-built theme
36-
@import '~@angular/material/core/theming/prebuilt/deep-purple-amber';
36+
@import '~@angular/material/core/theming/prebuilt/deeppurple-amber';
3737
// Import your custom input theme file so you can call the custom-input-theme function
3838
@import 'app/candy-carousel/candy-carousel-theme.scss';
3939

@@ -52,8 +52,8 @@ All styles that are not affected by the theme should be placed in a `candy-carou
5252
Styles that are affected by the theme should be placed in a separated theming file as `_candy-carousel-theme.scss` and the file should have a `_` before the name. This file should contain the `@mixin` function responsible for applying the theme to the component.
5353

5454

55-
## Using colors from a pallete
56-
You can consume the theming functions from the `@angular/material/core/theming/theming` and Material pallete vars from `@angular/material/core/theming/palette`. You can use the `md-color` function to extract a specific color from a palette. For example:
55+
## Using colors from a palette
56+
You can consume the theming functions from the `@angular/material/core/theming/theming` and Material palette vars from `@angular/material/core/theming/palette`. You can use the `md-color` function to extract a specific color from a palette. For example:
5757

5858
```scss
5959
// Import theming functions
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
describe('progress-circle', () => {
2+
beforeEach(() => browser.get('/progress-circle'));
3+
4+
it('should render a determinate progress circle', () => {
5+
expect(element(by.css('md-progress-circle')).isPresent()).toBe(true);
6+
});
7+
8+
it('should render an indeterminate progress circle', () => {
9+
expect(element(by.css('md-progress-circle[mode="indeterminate"]')).isPresent()).toBe(true);
10+
});
11+
12+
it('should render a spinner', () => {
13+
expect(element(by.css('md-spinner')).isPresent()).toBe(true);
14+
});
15+
});

scripts/ci/forbidden-identifiers.js

Lines changed: 85 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@ const blocked_statements = [
2626
'\\bxit\\(',
2727
'\\bdebugger;',
2828
'from \\\'rxjs/Rx\\\'',
29+
'\\r'
2930
];
3031

3132
const sourceFolders = ['./src', './e2e'];
32-
const libRoot = 'src/lib';
3333
const blockedRegex = new RegExp(blocked_statements.join('|'), 'g');
34-
const importRegex = /from\s+'(.*)';/g;
3534

3635
/*
3736
* Verify that the current PR is not adding any forbidden identifiers.
@@ -47,69 +46,105 @@ findTestFiles()
4746
.then(files => files.map(name => ({ name, content: fs.readFileSync(name, 'utf-8') })))
4847

4948
/* Run checks against content of the filtered files. */
50-
.then(diffList => {
49+
.then(diffList => findErrors(diffList))
5150

52-
return diffList.reduce((errors, diffFile) => {
53-
let fileName = diffFile.name;
54-
let content = diffFile.content.split('\n');
55-
let lineCount = 0;
51+
/* Groups similar errors to simplify console output */
52+
.then(errors => groupErrors(errors))
5653

57-
content.forEach(line => {
58-
lineCount++;
54+
/* Print the resolved errors to the console */
55+
.then(errors => printErrors(errors))
56+
57+
.catch(err => {
58+
// An error occurred in this script. Output the error and the stack.
59+
console.error(err.stack || err);
60+
process.exit(2);
61+
});
62+
63+
/**
64+
* Finds errors inside of changed files by running them against the blocked statements.
65+
* @param {{name: string, content: string}[]} diffList
66+
*/
67+
function findErrors(diffList) {
68+
return diffList.reduce((errors, diffFile) => {
69+
let fileName = diffFile.name;
70+
let content = diffFile.content.split('\n');
71+
let lineNumber = 0;
5972

60-
let matches = line.match(blockedRegex);
61-
let scopeImport = path.relative(libRoot, fileName).startsWith('..')
62-
? isRelativeScopeImport(fileName, line)
63-
: false;
73+
content.forEach(line => {
74+
lineNumber++;
6475

65-
if (matches || scopeImport) {
76+
let matches = line.match(blockedRegex);
6677

67-
let error = {
68-
fileName: fileName,
69-
lineNumber: lineCount,
70-
statement: matches && matches[0] || scopeImport.invalidStatement
71-
};
78+
if (matches) {
7279

73-
if (scopeImport) {
74-
error.messages = [
75-
'You are using an import statement, which imports a file from another scope package.',
76-
`Please import the file by using the following path: ${scopeImport.scopeImportPath}`
77-
];
78-
}
80+
errors.push({
81+
fileName,
82+
lineNumber,
83+
statement: matches[0]
84+
});
7985

80-
errors.push(error);
81-
}
82-
});
86+
}
87+
});
8388

84-
return errors;
89+
return errors;
8590

86-
}, []);
87-
})
91+
}, []);
92+
}
8893

89-
/* Print the resolved errors to the console */
90-
.then(errors => {
91-
if (errors.length > 0) {
92-
console.error('Error: You are using one or more blocked statements:\n');
94+
/**
95+
* Groups similar errors in the same file which are present over a range of lines.
96+
* @param {{fileName: string, lineNumber: number, statement: string}[]} errors
97+
*/
98+
function groupErrors(errors) {
9399

94-
errors.forEach(entry => {
95-
if (entry.messages) {
96-
entry.messages.forEach(message => console.error(` ${message}`))
97-
}
100+
let initialError, initialLine, previousLine;
98101

99-
console.error(` ${entry.fileName}@${entry.lineNumber}, Statement: ${entry.statement}.\n`);
100-
});
102+
return errors.filter(error => {
101103

102-
process.exit(1);
104+
if (initialError && isSimilarError(error)) {
105+
previousLine = error.lineNumber;
106+
107+
// Overwrite the lineNumber with a string, which indicates the range of lines.
108+
initialError.lineNumber = `${initialLine}-${previousLine}`;
109+
110+
return false;
103111
}
104-
})
105112

106-
.catch(err => {
107-
// An error occurred in this script. Output the error and the stack.
108-
console.error('An error occurred during execution:');
109-
console.error(err.stack || err);
110-
process.exit(2);
113+
initialLine = previousLine = error.lineNumber;
114+
initialError = error;
115+
116+
return true;
111117
});
112118

119+
/** Checks whether the given error is similar to the previous one. */
120+
function isSimilarError(error) {
121+
return initialError.fileName === error.fileName &&
122+
initialError.statement === error.statement &&
123+
previousLine === (error.lineNumber - 1);
124+
}
125+
}
126+
127+
/**
128+
* Prints all errors to the console and terminates the process if errors were present.
129+
* @param {{fileName: string, lineNumber: number, statement: string}[]} errors
130+
*/
131+
function printErrors(errors) {
132+
if (errors.length > 0) {
133+
134+
console.error('Error: You are using one or more blocked statements:\n');
135+
136+
errors.forEach(entry => {
137+
138+
// Stringify the statement to represent line-endings or other unescaped characters.
139+
let statement = JSON.stringify(entry.statement);
140+
141+
console.error(` ${entry.fileName}@${entry.lineNumber}, Statement: ${statement}.\n`);
142+
});
143+
144+
// Exit the process with an error exit code to notify the CI.
145+
process.exit(1);
146+
}
147+
}
113148

114149
/**
115150
* Resolves all files, which should run against the forbidden identifiers check.
@@ -120,8 +155,8 @@ function findTestFiles() {
120155
return findChangedFiles();
121156
}
122157

123-
var files = sourceFolders.map(folder => {
124-
return glob(`${folder}/**/*.ts`);
158+
let files = sourceFolders.map(folder => {
159+
return glob(`${folder}/**/*`);
125160
}).reduce((files, fileSet) => files.concat(fileSet), []);
126161

127162
return Promise.resolve(files);
@@ -150,72 +185,6 @@ function findChangedFiles() {
150185
});
151186
}
152187

153-
/**
154-
* Checks the line for any relative imports of a scope package, which should be imported by using
155-
* the scope package name instead of the relative path.
156-
* @param fileName Filename to validate the path
157-
* @param line Line to be checked.
158-
*/
159-
function isRelativeScopeImport(fileName, line) {
160-
if (fileName.startsWith(libRoot)) {
161-
return false;
162-
}
163-
164-
let importMatch = importRegex.exec(line);
165-
166-
// We have to reset the last index of the import regex, otherwise we
167-
// would have incorrect matches in the next execution.
168-
importRegex.lastIndex = 0;
169-
170-
// Skip the check if the current line doesn't match any imports.
171-
if (!importMatch) {
172-
return false;
173-
}
174-
175-
let importPath = importMatch[1];
176-
177-
// Skip the check when the import doesn't start with a dot, because the line
178-
// isn't importing any file relatively. Also applies to scope packages starting
179-
// with `@`.
180-
if (!importPath.startsWith('.')) {
181-
return false;
182-
}
183-
184-
// Transform the relative import path into an absolute path.
185-
importPath = path.resolve(path.dirname(fileName), importPath);
186-
187-
let fileScope = findScope(fileName);
188-
let importScope = findScope(importPath);
189-
190-
if (fileScope.path !== importScope.path) {
191-
192-
// Creates a valid import statement, which uses the correct scope package.
193-
let validImportPath = `@angular/material`;
194-
195-
return {
196-
fileScope: fileScope.name,
197-
importScope: importScope.name,
198-
invalidStatement: importMatch[0],
199-
scopeImportPath: validImportPath
200-
}
201-
}
202-
203-
function findScope(filePath) {
204-
// Normalize the filePath, to avoid issues with the different environments path delimiter.
205-
filePath = path.normalize(filePath);
206-
207-
// Iterate through all scope paths and try to find them inside of the file path.
208-
var scopePath = filePath.indexOf(path.normalize(libRoot)) == -1 ? libRoot : filePath;
209-
210-
// Return an object containing the name of the scope and the associated path.
211-
return {
212-
name: path.basename(scopePath),
213-
path: scopePath
214-
}
215-
}
216-
217-
}
218-
219188
/**
220189
* Executes a process command and wraps it inside of a promise.
221190
* @returns {Promise.<String>}

src/demo-app/button-toggle/button-toggle-demo.html

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
<p>
2+
<md-checkbox (change)="isVertical = $event.checked">Show Button Toggles Vertical</md-checkbox>
3+
</p>
4+
15
<h1>Exclusive Selection</h1>
26

37
<section class="demo-section">
4-
<md-button-toggle-group name="alignment">
8+
<md-button-toggle-group name="alignment" [vertical]="isVertical">
59
<md-button-toggle value="left"><md-icon>format_align_left</md-icon></md-button-toggle>
610
<md-button-toggle value="center"><md-icon>format_align_center</md-icon></md-button-toggle>
711
<md-button-toggle value="right"><md-icon>format_align_right</md-icon></md-button-toggle>
@@ -12,7 +16,7 @@ <h1>Exclusive Selection</h1>
1216
<h1>Disabled Group</h1>
1317

1418
<section class="demo-section">
15-
<md-button-toggle-group name="checkbox" disabled>
19+
<md-button-toggle-group name="checkbox" [vertical]="isVertical" disabled>
1620
<md-button-toggle value="bold">
1721
<md-icon class="md-24">format_bold</md-icon>
1822
</md-button-toggle>
@@ -27,7 +31,7 @@ <h1>Disabled Group</h1>
2731

2832
<h1>Multiple Selection</h1>
2933
<section class="demo-section">
30-
<md-button-toggle-group multiple>
34+
<md-button-toggle-group multiple [vertical]="isVertical">
3135
<md-button-toggle>Flour</md-button-toggle>
3236
<md-button-toggle>Eggs</md-button-toggle>
3337
<md-button-toggle>Sugar</md-button-toggle>
@@ -40,7 +44,7 @@ <h1>Single Toggle</h1>
4044

4145
<h1>Dynamic Exclusive Selection</h1>
4246
<section class="demo-section">
43-
<md-button-toggle-group name="pies" [(ngModel)]="favoritePie">
47+
<md-button-toggle-group name="pies" [(ngModel)]="favoritePie" [vertical]="isVertical">
4448
<md-button-toggle *ngFor="let pie of pieOptions" [value]="pie">
4549
{{pie}}
4650
</md-button-toggle>

src/demo-app/button-toggle/button-toggle-demo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {MdUniqueSelectionDispatcher} from '@angular/material';
88
providers: [MdUniqueSelectionDispatcher],
99
})
1010
export class ButtonToggleDemo {
11+
isVertical = false;
1112
favoritePie = 'Apple';
1213
pieOptions = [
1314
'Apple',

src/demo-app/chips/chips-demo.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<div class="chips-demo">
2+
<section>
3+
<h3>Static Chips</h3>
4+
5+
<md-chip-list>
6+
<md-chip>Basic Chip</md-chip>
7+
<md-chip class="selected md-primary">Primary</md-chip>
8+
<md-chip class="selected md-accent">Accent</md-chip>
9+
<md-chip class="selected md-warn">Warn</md-chip>
10+
</md-chip-list>
11+
</section>
12+
</div>

src/demo-app/chips/chips-demo.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.chips-demo {
2+
}

0 commit comments

Comments
 (0)